Consul

consul เป็นโซลูชันที่ช่วยให้ทีมสามารถจัดการการเชื่อมต่อเครือข่ายระหว่างบริการและข้ามการเตรียมการและสภาพแวดล้อมคลาวด์หลายแห่งอย่างปลอดภัย ให้บริการค้นหาบริการ service mesh การจัดการทราฟฟิก การอัปเดตโครงสร้างพื้นฐานเครือข่ายอัตโนมัติ และฟังก์ชันอื่นๆ
เอกสารทางการ: Consul by HashiCorp
ที่อยู่โอเพนซอร์ส: hashicorp/consul
Consul เป็นเครื่องมือค้นหาและลงทะเบียนบริการโอเพนซอร์สจากบริษัท HashiCorp ใช้อัลกอริทึมการเลือกตั้ง Raft เครื่องมือนี้พัฒนาด้วยภาษา Go ดังนั้นการติดตั้งจึงสะดวกมาก Consul มีคุณสมบัติดังนี้
- การค้นหาบริการ
- การลงทะเบียนบริการ
- การตรวจสอบสุขภาพ
- การจัดเก็บคีย์-ค่า
- หลายศูนย์ข้อมูล
ที่จริงแล้ว consul สามารถทำได้มากกว่าการค้นหาบริการ ยังสามารถใช้เป็นศูนย์การกำหนดค่าแบบกระจายได้ มีเครื่องมือโอเพนซอร์สประเภทเดียวกันอีกมากมาย เช่น zookeeper, nacos ที่นี่จะไม่แนะนำเพิ่มเติม
การติดตั้ง
สำหรับ Ubuntu ให้รันคำสั่งด้านล่างเพื่อติดตั้งด้วย apt
$ wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
$ echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
$ sudo apt update && sudo apt install consulหรือสามารถดาวน์โหลดแพ็กเกจการติดตั้งที่สอดคล้องกันจากเว็บไซต์ทางการ Install Consul เนื่องจาก consul พัฒนาด้วย go แพ็กเกจการติดตั้งเองก็มีเพียงไฟล์ไบนารีที่รันได้เพียงไฟล์เดียว การติดตั้งก็สะดวกมาก หลังจากติดตั้งสำเร็จ รันคำสั่งด้านล่างเพื่อดูเวอร์ชัน
$ consul versionเอาต์พุตปกติก็ไม่มีปัญหา
Consul v1.16.1
Revision e0ab4d29
Build Date 2023-08-05T21:56:29Z
Protocol 2 spoken by default, understands 2 to 3 (agent will automatically use protocol >2 when speaking to compatible agents)เริ่มต้นอย่างรวดเร็ว
ต่อไปนี้จะแนะนำวิธีการสร้างโหนดเดียว consul อย่างรวดเร็ว โดยทั่วไปโหนดเดียวใช้สำหรับการทดสอบในช่วงการพัฒนา หากโหนดเดียวใช้งานได้没有问题 ส่วนใหญ่คลัสเตอร์หลายโหนดก็จะไม่มีปัญหาเช่นกัน การสร้างโหนดเดี่ยวนั้นง่ายมาก ใช้เพียงคำสั่งเดียว
$ consul agent -dev -bind=192.168.48.141 -data-dir=/tmp/consul -ui -node=dev01โดยทั่วไปจะมีเอาต์พุตดังนี้
==> Starting Consul agent...
Version: '1.16.1'
Build Date: '2023-08-05 21:56:29 +0000 UTC'
Node ID: 'be6f6b8d-9668-f7ff-8709-ed57c72ffdec'
Node name: 'dev01'
Datacenter: 'dc1' (Segment: '<all>')
Server: true (Bootstrap: false)
Client Addr: [127.0.0.1] (HTTP: 8500, HTTPS: -1, gRPC: 8502, gRPC-TLS: 8503, DNS: 8600)
Cluster Addr: 192.168.48.141 (LAN: 8301, WAN: 8302)
Gossip Encryption: false
Auto-Encrypt-TLS: false
ACL Enabled: false
Reporting Enabled: false
ACL Default Policy: allow
HTTPS TLS: Verify Incoming: false, Verify Outgoing: false, Min Version: TLSv1_2
gRPC TLS: Verify Incoming: false, Min Version: TLSv1_2
Internal RPC TLS: Verify Incoming: false, Verify Outgoing: false (Verify Hostname: false), Min Version: TLSv1_2
==> Log data will now stream in as it occurs:
2023-08-25T17:23:33.763+0800 [DEBUG] agent.grpc.balancer: switching server: target=consul://dc1.be6f6b8d-9668-f7ff-8709-ed57c72ffdec/server.dc1 from=<none> to=<none>
2023-08-25T17:23:33.767+0800 [INFO] agent.server.raft: initial configuration: index=1 servers="[{Suffrage:Voter ID:be6f6b8d-9668-f7ff-8709-ed57c72ffdec Address:192.168.48.141:8300}]"อธิบายความหมายอย่างง่าย
agentเป็นคำสั่งย่อย เป็นคำสั่งหลักของ consulconsul agentคือการรัน proxy consul ใหม่ แต่ละ node เป็น proxy หนึ่งตัวdevเป็นโหมดการทำงานของ agent มีสามแบบdev,client,serverbindที่อยู่การสื่อสาร LAN พอร์ตเริ่มต้น 8301 โดยทั่วไปค่านี้เป็นที่อยู่ภายในของเซิร์ฟเวอร์advertiseที่อยู่การสื่อสาร WAN พอร์ตเริ่มต้น 8302 โดยทั่วไปค่านี้เป็นที่อยู่นอกของเซิร์ฟเวอร์data-dirไดเรกทอรีเก็บข้อมูลconfig-dirไดเรกทอรีเก็บการกำหนดค่า consul จะอ่านไฟล์ json ทั้งหมดในไดเรกทอรีbootstrapระบุว่า server ปัจจุบันเข้าสู่โหมด bootstrap ในการเลือกตั้ง raft จะโหวตให้ตัวเอง ในคลัสเตอร์ server ที่อยู่ในโหมดนี้ต้องมีไม่เกินหนึ่งตัวbootstrap-expectจำนวน server ที่คาดหวังในคลัสเตอร์ ก่อนถึงจำนวนที่กำหนด คลัสเตอร์จะไม่เริ่มการเลือกตั้ง投票 ไม่สามารถใช้กับbootstrapพร้อมกันได้retry-joinหลังจาก agent เริ่มต้น จะพยายามเข้าร่วมโหนดที่ระบุอย่างต่อเนื่อง还支持วิธีการค้นหาบริการของผู้ให้บริการต่อไปนี้aliyun aws azure digitalocean gce hcp k8s linode mdns os packet scaleway softlayer tencentcloud triton vsphereuiรันเว็บแบ็กเอนด์nodeชื่อโหนดที่ทำงาน ต้องไม่ซ้ำกันในคลัสเตอร์
TIP
สำหรับความหมายพารามิเตอร์ agent เพิ่มเติม ไปที่ Agents - CLI Reference | Consul | HashiCorp Developer หมายเหตุพารามิเตอร์บางตัวใช้ได้เฉพาะรุ่นองค์กร
เมื่อรันสำเร็จแล้ว เข้าถึง 127.0.0.1:8500 ก็สามารถดูเว็บอินเทอร์เฟซได้

ไอคอน dev01 เป็นรูปดาว แสดงว่าเป็นโหนด leader
เมื่อออก เพื่อให้โหนดอื่นรับรู้ถึงการออกของโหนดปัจจุบัน ไม่แนะนำให้บังคับฆ่าโปรเซส สามารถใช้คำสั่ง
consul leaveหรือ
consul force-leaveหรือ ctrl+c ให้ consul agent ออกอย่างสง่างาม
แนวคิด
นี่เป็นแผนภาพคลัสเตอร์ consul ในภาพแบ่งออกเป็นสองส่วน คือ control plane และ data plane consul รับผิดชอบเฉพาะ control plane แบ่งเป็น service cluster และ client ใน service cluster แบ่งเป็น follower และ leader โดยรวมแล้ว คลัสเตอร์ consul ในภาพ構成เป็น data center หนึ่งแห่ง ต่อไปนี้จะอธิบายศัพท์บางคำ
- Agent (ตัวแทน): หรือเรียกว่า node ก็เหมาะสมกว่า แต่ละ agent เป็น daemon ที่รันเป็นเวลานาน พวกมันเปิดเผยอินเทอร์เฟซ HTTP และ DNS ภายนอก รับผิดชอบการตรวจสอบสุขภาพและการซิงค์บริการ
- Server (ตัวแทนบริการ): ในฐานะ consul server หน้าที่หลักมี participate ในการเลือกตั้ง Raft บำรุงรักษาสถานะคลัสเตอร์ ตอบคำถาม แลกเปลี่ยนข้อมูลกับศูนย์ข้อมูลอื่น และส่งต่อคำถามไปยัง leader และศูนย์ข้อมูลอื่น
- Client (ตัวแทนลูกค้า): client เทียบกับ server แล้วไม่มีสถานะ ไม่ participate ในการเลือกตั้ง Raft สิ่งที่ทำคือส่งต่อคำขอทั้งหมดไปยัง server สิ่งเดียวที่เกี่ยวข้องกับพื้นหลังคือ LAN gossip pool
- Leader (ผู้นำ): leader เป็นผู้นำของ server ทั้งหมด และมีได้เพียงคนเดียว leader ได้รับการเลือกตั้งผ่านอัลกอริทึมการเลือกตั้ง Raft แต่ละ leader มีวาระของตัวเอง ในระหว่างวาระ server อื่น ๆ ไม่ว่าจะได้รับคำขออะไรก็ต้องบอก leader ดังนั้นข้อมูลของ leader จึงทันสมัยที่สุด
- Gossip (ข่าวลือ): Consul สร้างบนพื้นฐานของ Serf (ผลิตภัณฑ์อื่นของบริษัทเดียวกัน) ใช้โปรโตคอล gossip โปรโตคอลนี้ใช้สำหรับการสื่อสารแบบสุ่มระหว่างโหนด คล้าย UDP consul ใช้โปรโตคอลนี้เพื่อแจ้งเตือนซึ่งกันและกันในคลัสเตอร์บริการ
- Data Center (ศูนย์ข้อมูล): คลัสเตอร์ consul ภายใน LAN หนึ่งแห่งเรียกว่า data center หนึ่งแห่ง consul รองรับหลายศูนย์ข้อมูล วิธีการสื่อสารหลายศูนย์ข้อมูลคือ WAN gossip
TIP
คำศัพท์และ术语เพิ่มเติมสามารถไปที่ Glossary | Consul | HashiCorp Developer เพื่อทำความเข้าใจ
ในคลัสเตอร์ consul จำนวน server ควรควบคุมอย่างเข้มงวด เพราะพวกมัน participate โดยตรงใน LAN gossip และ WAN gossip การเลือกตั้ง raft และต้องเก็บข้อมูล server มากขึ้น ต้นทุนการสื่อสารสูงขึ้น ส่วนจำนวน client มากหน่อยไม่มีปัญหา รับผิดชอบแค่ส่งต่อ ไม่ participate ในการเลือกตั้ง ใช้ทรัพยากรต่ำ ในคลัสเตอร์ในภาพ บริการต่างๆ ลงทะเบียนตัวเองกับ server ผ่าน client หากมี server ล้ม client จะค้นหา server อื่นที่ใช้ได้เอง
ตัวอย่างการสร้างคลัสเตอร์
ต่อไปนี้จะสร้างตัวอย่างคลัสเตอร์หลายโหนด consul อย่างง่าย เตรียมเครื่องเสมือนสี่เครื่องก่อน

ในเครื่องเสมือนสี่เครื่อง มีสาม server หนึ่ง client ทางการแนะนำว่าจำนวน server ควรเป็นจำนวนคี่ และควรมากกว่าหรือเท่ากับสามเครื่อง ที่นี่จะใช้ vm00-vm02 เป็น server vm03 เป็น client
สำหรับ server รันคำสั่งด้านล่างเพื่อสร้าง server agent
consul agent -server -bind=vm_address -client=0.0.0.0 -data-dir=/tmp/consul/ -node=agent_name -uiสำหรับ client รันคำสั่งด้านล่างเพื่อสร้าง client agent
consul agent -client=0.0.0.0 -bind=vm_address -data-dir=/tmp/consul/ -node=agent_name -uiคำสั่งที่รัน分别是
# vm00
consul agent -server -bind=192.168.48.138 -client=0.0.0.0 -data-dir=/tmp/consul/ -node=agent01 -ui -bootstrap
# vm01
consul agent -server -bind=192.168.48.139 -client=0.0.0.0 -data-dir=/tmp/consul/ -node=agent02 -ui -retry-join=192.168.48.138
# vm02
consul agent -server -bind=192.168.48.140 -client=0.0.0.0 -data-dir=/tmp/consul/ -node=agent03 -ui -retry-join=192.168.48.138
# vm03
consul agent -bind=192.168.48.140 -client=0.0.0.0 -data-dir=/tmp/consul/ -node=agent03 -ui -retry-join=192.168.48.138คำอธิบายพารามิเตอร์บางตัว
client0.0.0.0คืออนุญาตคำขอจากทุก来源 หากมีพารามิเตอร์ client เท่านั้น ไม่มีพารามิเตอร์ server หมายความว่า agent จะรันในโหมด client
เมื่อ agent ทั้งหมดรันดีแล้ว retry-join มีผลเท่ากับการรันคำสั่ง join อัตโนมัติ หลังจากล้มเหลวจะพยายามอย่างต่อเนื่อง เวลา重试เริ่มต้น 30 วินาที
$ consul join 192.168.48.138หลังจาก join เสร็จ แต่ละโหนดรู้จักกันแล้ว เนื่องจาก vm00 ระบุโหมด bootstrap ดังนั้นมันจึงเป็น leader เริ่มต้น หากไม่ได้ระบุโหมด bootstrap โหนดทั้งหมดที่ join จะระบุโหนดเป็น leader เริ่มต้น ก่อนที่ leader จะได้รับการเลือกตั้ง คลัสเตอร์ไม่สามารถทำงานได้ตามปกติ การเข้าถึงเว็บอินเทอร์เฟซจะคืนค่า 500 คำสั่งบางอย่างไม่สามารถทำงานได้ตามปกติ หากมีโหนดในคลัสเตอร์ระบุโหมด bootstrap แล้ว โหนดอื่นในคลัสเตอร์ไม่ควรระบุโหมด bootstrap อีก และโหนดอื่นไม่ควรใช้พารามิเตอร์ bootstrap-expect อีก หากใช้จะถูกปิดใช้งานอัตโนมัติ
这时ในโหนด leader (ที่จริงแล้วตอนนี้โหนดใดก็สามารถดูได้) รันคำสั่งด้านล่างเพื่อดูข้อมูลสมาชิก data center
$ consul members
Node Address Status Type Build Protocol DC Partition Segment
agent01 192.168.48.138:8301 alive server 1.16.1 2 dc1 default <all>
agent02 192.168.48.139:8301 alive server 1.16.1 2 dc1 default <all>
agent03 192.168.48.140:8301 alive server 1.16.1 2 dc1 default <all>
client01 192.168.48.141:8301 alive client 1.16.1 2 dc1 default <default>- Node คือชื่อโหนด
- Address คือที่อยู่การสื่อสาร
- Status
aliveหมายถึงมีชีวิตอยู่leftหมายถึงออฟไลน์ - Type ประเภท agent มีสองโหมด server และ client
- Build เวอร์ชัน consul ที่โหนดนี้ใช้ consul สามารถทำงานร่วมกับโหนดเวอร์ชันต่าง ๆ ในช่วงหนึ่ง
- Protocol หมายถึงเวอร์ชันโปรโตคอล Raft ที่ใช้ โปรโตคอลนี้ควร一致ทุกโหนด
- DC Data Center ศูนย์ข้อมูล โหนดทั้งหมดในเอาต์พุตอยู่ใน data center dc1
- Partition partition ที่โหนดสังกัด เป็นฟังก์ชันรุ่นองค์กร แต่ละโหนดสามารถสื่อสารกับโหนดใน partition เดียวกันเท่านั้น
- Segment segment ที่โหนดสังกัด เป็นฟังก์ชันรุ่นองค์กร

เช่นเดียวกัน หากต้องการให้โหนดออก ควรใช้ consul leave ให้โหนดออกอย่างสง่างาม และแจ้งโหนดอื่นว่ากำลังจะออก สำหรับกรณีหลายโหนด การออกอย่างสง่างามของโหนดสำคัญมาก เพราะเกี่ยวข้องกับความสอดคล้องของข้อมูล
TIP
เครื่องเสมือนในการสาธิตปิด firewall ทั้งหมด ในสภาพแวดล้อมการผลิตจริงเพื่อความปลอดภัยควรเปิด为此ควร关注พอร์ตทั้งหมดที่ consul ใช้: Required Ports | Consul | HashiCorp Developer
ต่อไปทดสอบความสอดคล้องของข้อมูลอย่างง่าย ในเครื่องเสมือน vm00 เพิ่มข้อมูลด้านล่าง
$ consul kv put sys_confg {"name":"consul"}
Success! Data written to: sys_confgหลังจากบันทึก ผ่าน HTTP API เข้าถึงโหนดอื่นจะพบว่าข้อมูลมีอยู่เช่นกัน (value ในนั้นเป็น base64 encoding)
$ curl http://192.168.48.138:8500/v1/kv/sys_confg
[{"LockIndex":0,"Key":"sys_confg","Flags":0,"Value":"ewogICJuYW1lIjoiY29uc3VsIgp9","CreateIndex":2518,"ModifyIndex":2518}]
$ curl http://192.168.48.139:8500/v1/kv/sys_confg
[{"LockIndex":0,"Key":"sys_confg","Flags":0,"Value":"ewogICJuYW1lIjoiY29uc3VsIgp9","CreateIndex":2518,"ModifyIndex":2518}]
$ curl http://192.168.48.140:8500/v1/kv/sys_confg
[{"LockIndex":0,"Key":"sys_confg","Flags":0,"Value":"ewogICJuYW1lIjoiY29uc3VsIgp9","CreateIndex":2518,"ModifyIndex":2518}]ที่จริงแล้ว ฟังก์ชันการค้นหาและลงทะเบียนบริการที่ consul ให้ ผ่านโปรโตคอล gossip แจ้งเตือนโหนดอื่น และเมื่อโหนดใดเข้าร่วม data center ปัจจุบัน โหนดทั้งหมดจะรับรู้การเปลี่ยนแปลงนี้
ตัวอย่างการสร้างหลายศูนย์ข้อมูล
เตรียมเครื่องเสมือนห้าเครื่อง vm00-vm02 เป็นคลัสเตอร์ในตัวอย่างก่อนหน้า สังกัด data center dc1 ไม่ต้องยุ่งกับมัน vm03-vm04 สังกัด data center dc2 data center เมื่อ agent เริ่มต้น ค่าเริ่มต้นคือ dc1

TIP
ที่นี่เพื่อสาธิต สร้างเฉพาะ server ตัด client ออก
ก่อนอื่นเริ่มต้น vm03分别将其เป็น leader เริ่มต้น
$ consul agent -server -datacenter=dc2 -bind=192.168.48.141 -client=0.0.0.0 -data-dir=/tmp/consul/ -node=agent04 -ui -bootstrapเริ่มต้น vm04 ให้มัน join โหนด vm03 อัตโนมัติ
$ consul agent -server -datacenter=dc2 -bind=192.168.48.142 -client=0.0.0.0 -data-dir=/tmp/consul/ -node=agent05 -ui -retry-join=192.168.48.141这时分别在 vm00 และ vm03 ดู members
# vm00-vm02
$ consul members
Node Address Status Type Build Protocol DC Partition Segment
agent01 192.168.48.138:8301 alive server 1.16.1 2 dc1 default <all>
agent02 192.168.48.139:8301 alive server 1.16.1 2 dc1 default <all>
agent03 192.168.48.140:8301 alive server 1.16.1 2 dc1 default <all>
# vm03-vm04
$ consul members
Node Address Status Type Build Protocol DC Partition Segment
agent04 192.168.48.141:8301 alive server 1.16.1 2 dc2 default <all>
agent05 192.168.48.142:8301 alive server 1.16.1 2 dc2 default <all>จะเห็นว่าฟิลด์ DC ต่างกัน เพราะที่นี่เป็นเครื่องเสมือนสาธิต ดังนั้นอยู่ใน subnet เดียวกัน ในความเป็นจริงสองศูนย์ข้อมูลอาจเป็นคลัสเตอร์เซิร์ฟเวอร์คนละที่ ต่อไปนี้ให้โหนดใดก็ได้ของ dc1 join โหนดใดก็ได้ของ dc2 ที่นี่ให้ vm01 join vm03
$ consul join -wan 192.168.48.141
Successfully joined cluster by contacting 1 nodes.หลังจาก join สำเร็จ รันคำสั่งดู WAN members
$ consul members -wan
Node Address Status Type Build Protocol DC Partition Segment
agent01.dc1 192.168.48.138:8302 alive server 1.16.1 2 dc1 default <all>
agent02.dc1 192.168.48.139:8302 alive server 1.16.1 2 dc1 default <all>
agent03.dc1 192.168.48.140:8302 alive server 1.16.1 2 dc1 default <all>
agent04.dc2 192.168.48.141:8302 alive server 1.16.1 2 dc2 default <all>
agent05.dc2 192.168.48.142:8302 alive server 1.16.1 2 dc2 default <all>
$ consul catalog datacenters
dc2
dc1เพียงโหนดใดก็ได้ของ dc1 join โหนดใดก็ได้ของ dc2 โหนดทั้งหมดของสองศูนย์ข้อมูลจะรับรู้การเปลี่ยนแปลงนี้ เมื่อดู members ก็เห็นโหนดของสองศูนย์ข้อมูล
ต่อไปพยายามเพิ่มข้อมูล KVหนึ่งชิ้นในโหนด vm00
$ consul kv put name consul
Success! Data written to: nameในโหนด vm01 พยายามอ่านข้อมูล จะเห็นว่าข้อมูลในศูนย์ข้อมูลเดียวกันซิงค์กัน
$ consul kv get name
consulแล้วไปที่ vm03 ในศูนย์ข้อมูลต่างกันพยายามอ่านข้อมูล จะพบว่าข้อมูลในศูนย์ข้อมูลต่างกันไม่ซิงค์กัน
$ consul kv get name
Error! No key exists at: nameหากต้องการให้ข้อมูลหลายศูนย์ข้อมูลซิงค์กัน สามารถเรียนรู้ hashicorp/consul-replicate: Consul cross-DC KV replication daemon
การลงทะเบียนและการค้นพบบริการ

วิธีการลงทะเบียนบริการ consul มีสองแบบ การลงทะเบียนด้วยไฟล์การกำหนดค่าและการลงทะเบียนด้วย API เพื่อความสะดวกในการทดสอบ ที่นี่เตรียมบริการ Hello World ล่วงหน้า (ตัวอย่างในบทความ gRPC)部署สองชุด分别อยู่ในที่ต่างกัน สำหรับวิธีการลงทะเบียนด้วยไฟล์การกำหนดค่าสามารถไปที่ Register external services with Consul service discovery | Consul | HashiCorp Developer เพื่อเรียนรู้ ที่นี่แนะนำเฉพาะการลงทะเบียนผ่าน HTTP API
TIP
สำหรับบริการท้องถิ่น (อยู่กับ consul client) สามารถใช้การลงทะเบียน agent service ได้โดยตรง มิฉะนั้นควรใช้ catalog register เพื่อลงทะเบียน
consul ให้ SDK HTTP API สำหรับภาษาอื่น ๆ可以去 Libraries and SDKs - HTTP API | Consul | HashiCorp Developer เพื่อเรียนรู้ ที่นี่ดาวน์โหลด dependency ของ go
go get github.com/hashicorp/consul/apiเมื่อเริ่มต้นบริการให้ลงทะเบียนบริการกับ consul อย่างกระตือรือร้น เมื่อปิดบริการให้ยกเลิกการลงทะเบียนกับ consul ต่อไปนี้เป็นตัวอย่าง
package main
import (
consulapi "github.com/hashicorp/consul/api"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
pb "grpc_learn/helloworld/hello"
"log"
"net"
)
var (
server01 = &consulapi.AgentService{
// ต้องไม่ซ้ำกัน
ID: "hello-service1",
Service: "hello-service",
//部署สองชุด พอร์ตหนึ่งคือ 8080 อีกพอร์ตคือ 8081
Port: 8080,
}
)
// ลงทะเบียนบริการ
func Register() {
client, _ := consulapi.NewClient(&consulapi.Config{Address: "192.168.48.138:8500"})
_, _ = client.Catalog().Register(&consulapi.CatalogRegistration{
Node: "hello-server",
Address: "192.168.2.10",
Service: server01,
}, nil)
}
// ยกเลิกการลงทะเบียนบริการ
func DeRegister() {
client, _ := consulapi.NewClient(&consulapi.Config{Address: "192.168.48.138:8500"})
_, _ = client.Catalog().Deregister(&consulapi.CatalogDeregistration{
Node: "hello-server",
Address: "192.168.2.10",
ServiceID: server01.ID,
}, nil)
}
func main() {
Register()
defer DeRegister()
// ฟังพอร์ต
listen, err := net.Listen("tcp", ":8080")
if err != nil {
panic(err)
}
// สร้างเซิร์ฟเวอร์ gprc
server := grpc.NewServer(
grpc.Creds(insecure.NewCredentials()),
)
// ลงทะเบียนบริการ
pb.RegisterSayHelloServer(server, &HelloRpc{})
log.Println("server running...")
// รัน
err = server.Serve(listen)
if err != nil {
panic(err)
}
}โค้ดไคลเอนต์ใช้ custom resolver ของ consul เพื่อสอบถามบริการที่สอดคล้องกันจากศูนย์ลงทะเบียน แปลงเป็นที่อยู่จริง
package myresolver
import (
"fmt"
consulapi "github.com/hashicorp/consul/api"
"google.golang.org/grpc/resolver"
)
func NewConsulResolverBuilder(address string) ConsulResolverBuilder {
return ConsulResolverBuilder{consulAddress: address}
}
type ConsulResolverBuilder struct {
consulAddress string
}
func (c ConsulResolverBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
consulResolver, err := newConsulResolver(c.consulAddress, target, cc)
if err != nil {
return nil, err
}
consulResolver.resolve()
return consulResolver, nil
}
func (c ConsulResolverBuilder) Scheme() string {
return "consul"
}
func newConsulResolver(address string, target resolver.Target, cc resolver.ClientConn) (ConsulResolver, error) {
var reso ConsulResolver
client, err := consulapi.NewClient(&consulapi.Config{Address: address})
if err != nil {
return reso, err
}
return ConsulResolver{
target: target,
cc: cc,
client: client,
}, nil
}
type ConsulResolver struct {
target resolver.Target
cc resolver.ClientConn
client *consulapi.Client
}
func (c ConsulResolver) resolve() {
service := c.target.URL.Opaque
services, _, err := c.client.Catalog().Service(service, "", nil)
if err != nil {
c.cc.ReportError(err)
return
}
var adds []resolver.Address
for _, catalogService := range services {
adds = append(adds, resolver.Address{Addr: fmt.Sprintf(fmt.Sprintf("%s:%d", catalogService.Address, catalogService.ServicePort))})
}
c.cc.UpdateState(resolver.State{
Addresses: adds,
// กลยุทธ์轮询
ServiceConfig: c.cc.ParseServiceConfig(
`{"loadBalancingPolicy":"round_robin"}`),
})
}
func (c ConsulResolver) ResolveNow(options resolver.ResolveNowOptions) {
c.resolve()
}
func (c ConsulResolver) Close() {
}ไคลเอนต์ลงทะเบียน resolver เมื่อเริ่มต้น
package main
import (
"context"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/resolver"
"grpc_learn/helloworld/client/myresolver"
hello2 "grpc_learn/helloworld/hello"
"log"
"time"
)
func init() {
// ลงทะเบียน builder
resolver.Register(
// ลงทะเบียน custom consul resolver
myresolver.NewConsulResolverBuilder("192.168.48.138:8500"),
)
}
func main() {
// สร้างการเชื่อมต่อ ไม่มีการตรวจสอบการเข้ารหัส
conn, err := grpc.Dial("consul:hello-service",
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err != nil {
panic(err)
}
defer conn.Close()
// สร้างไคลเอนต์
client := hello2.NewSayHelloClient(conn)
for range time.Tick(time.Second) {
// เรียกจากระยะไกล
helloRep, err := client.Hello(context.Background(), &hello2.HelloReq{Name: "client"})
if err != nil {
panic(err)
}
log.Printf("received grpc resp: %+v", helloRep.String())
}
}เริ่มต้นเซิร์ฟเวอร์ก่อน แล้วเริ่มต้นไคลเอนต์ เซิร์ฟเวอร์มีสองตัว ให้บริการเดียวกัน แค่ที่อยู่ไม่เหมือนกัน กลยุทธ์การโหลดบาลานซ์ของไคลเอนต์คือ round_robin จากช่วงเวลาล็อกของเซิร์ฟเวอร์จะเห็นว่ากลยุทธ์มีผล
2023/08/29 17:39:54 server running...
2023/08/29 21:03:46 received grpc req: name:"client"
2023/08/29 21:03:48 received grpc req: name:"client"
2023/08/29 21:03:50 received grpc req: name:"client"
2023/08/29 21:03:52 received grpc req: name:"client"
2023/08/29 21:03:54 received grpc req: name:"client"
2023/08/29 21:03:56 received grpc req: name:"client"
2023/08/29 21:03:58 received grpc req: name:"client"
2023/08/29 21:04:00 received grpc req: name:"client"ข้างต้นเป็นกรณีใช้งานอย่างง่ายของการใช้ consul ร่วมกับ gRPC เพื่อ實現การลงทะเบียนและการค้นพบบริการอย่างง่าย
