Skip to content

Consul

consul هو حل يتيح للفرق إدارة اتصالات الشبكة بأمان بين الخدمات عبر بيئات متعددة السحابية والبيئات المعدة مسبقًا، حيث يوفر اكتشاف الخدمة، وشبكة الخدمة، وإدارة حركة المرور، والتحديث التلقائي للبنية التحتية للشبكة وسلسلة من الوظائف الأخرى.

الوثائق الرسمية: Consul by HashiCorp

العنوان مفتوح المصدر: hashicorp/consul

Consul هو أداة اكتشاف وتسجيل خدمة مفتوحة المصدر من شركة HashiCorp، تستخدم خوارزمية انتخاب Raft، الأداة نفسها مطورة بلغة Go، لذا فإن نشرها خفيف جدًا. يتميز Consul بالخصائص التالية:

  • اكتشاف الخدمة
  • تسجيل الخدمة
  • الفحص الصحي
  • تخزين المفتاح-القيمة
  • مراكز بيانات متعددة

في الواقع، يمكن لـ consul القيام بأكثر من اكتشاف الخدمة، حيث يمكنه أيضًا العمل كمركز تكوين موزع، وهناك العديد من الأدوات مفتوحة المصدر المشابهة مثل zookeeper و nacos، لن نقدمها بالتفصيل هنا.

التثبيت

بالنسبة لـ Ubuntu، يمكنك تنفيذ الأمر التالي للتثبيت باستخدام apt

sh
$ 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، فإن حزمة التثبيت نفسها تحتوي فقط على ملف تنفيذي ثنائي واحد، مما يجعل التثبيت سهلًا جدًا. بعد التثبيت الناجح، نفذ الأمر التالي للتحقق من الإصدار.

sh
$ 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 واحدة بسرعة، عادةً ما تُستخدم العقدة الواحدة أثناء التطوير للاختبار، إذا عملت العقدة الواحدة بشكل صحيح، فمن المرجح أن يعمل نظام العقد المتعددة بدون مشاكل. بناء عقدة واحدة بسيط جدًا، يحتاج فقط إلى سطر أوامر واحد

sh
$ 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 هو أمر فرعي، وهو الأمر الأساسي لـ consul، consul agent يعني تشغيل وكيل consul جديد، كل عقدة هي وكيل.

  • dev، هو وضع تشغيل agent، هناك ثلاثة أوضاع إجمالًا: dev، client، server

  • bind، عنوان الاتصال للشبكة المحلية، المنفذ الافتراضي 8301، عادةً ما تكون هذه القيمة هي عنوان الشبكة الداخلية للخادم

  • advertise، عنوان الاتصال للشبكة الواسعة، المنفذ الافتراضي 8302، عادةً ما تكون هذه القيمة هي عنوان الشبكة الخارجية للخادم

  • data-dir، دليل تخزين البيانات

  • config-dir، دليل التكوين، سيقرأ consul جميع ملفات json في الدليل

  • bootstrap، يحدد أن الخادم الحالي يدخل وضع الإقلاع، سيصوت لنفسه أثناء انتخاب raft، لا يمكن أن يكون هناك أكثر من خادم واحد في هذا الوضع في المجموعة

  • bootstrap-expect، وهو العدد المتوقع من الخوادم في المجموعة، قبل الوصول للعدد المحدد، لن تبدأ المجموعة عملية التصويت، لا يمكن استخدامه مع bootstrap في نفس الوقت.

  • retry-join، بعد بدء agent، سيحاول باستمرار الانضمام للعقدة المحددة، كما يدعم طرق الاكتشاف التالية من مزودي الخدمة

    aliyun aws azure digitalocean gce hcp k8s linode mdns os packet scaleway softlayer tencentcloud triton vsphere
  • ui، تشغيل واجهة الويب الخلفية

  • node، اسم العقدة المحدد، يجب أن يكون فريدًا في المجموعة.

TIP

لمزيد من شرح معاملات agent، انتقل إلى Agents - CLI Reference | Consul | HashiCorp Developer ، تجدر الإشارة إلى أن بعض المعاملات متاحة فقط في النسخة المؤسسية.

عند التشغيل بنجاح، يمكنك زيارة 127.0.0.1:8500 لتصفح واجهة الويب.

أيقونة dev01 هي نجمة، مما يعني أنها عقدة leader.

عند الخروج، لكي تتمكن العقد الأخرى من إدراك خروج العقدة الحالية، لا يُنصح بقتل العملية بالقوة، يمكنك استخدام الأمر

sh
consul leave

أو

sh
consul force-leave

يمكنك أيضًا استخدام ctrl+c للسماح لوكيل consul بالخروج بأمان.

المفاهيم

هذا رسم توضيحي لمجموعة consul، ينقسم الرسم إلى جزأين، مستوى التحكم ومستوى البيانات. consul مسؤول فقط عن مستوى التحكم، وينقسم إلى مجموعة خوادم وعملاء، وتنقسم مجموعة الخوادم إلى تابعين وقائد، بشكل عام، تشكل مجموعة consul في الرسم مركز بيانات واحد. فيما يلي شرح لبعض المصطلحات

  • Agent (الوكيل): أو من الأفضل تسميته عقدة، كل agent هو عملية خفية طويلة التشغيل، تعرض واجهات HTTP و DNS للعالم الخارجي، ومسؤولة عن الفحص الصحي ومزامنة الخدمة.
  • Server (وكيل الخادم): كخادم consul، مسؤولياته الرئيسية هي المشاركة في انتخاب Raft، والحفاظ على حالة المجموعة، والاستجابة للاستعلامات، وتبادل البيانات مع مراكز بيانات أخرى، وإعادة توجيه الاستعلامات للقائد ومراكز البيانات الأخرى.
  • Client (وكيل العميل): client عديم الحالة مقارنة بـ server، لا يشارك في انتخاب Raft، كل ما يفعله هو إعادة توجيه جميع الطلبات إلى server، الشيء الوحيد الذي يشارك فيه المتعلق بالخلفية هو نقل الشائعات عبر الشبكة المحلية (LAN gossip pool).
  • Leader (القائد): leader هو قائد جميع الخوادم، ولا يمكن أن يكون هناك إلا قائد واحد، يتم انتخاب leader من خلال خوارزمية انتخاب Raft، كل قائد له فترة ولايته، خلال فترة ولايته، يجب على أي خادم آخر إخبار leader بأي طلب يتلقاه، لذا بيانات leader هي الأحدث والأحدث.
  • Gossip (الشائعات): Consul مبني على Serf (منتج آخر من نفس الشركة)، ويستخدم بروتوكول gossip، هذا البروتوكول مخصص للاتصال العشوائي بين العقد، مشابه لـ UDP، يستخدم consul هذا البروتوكول للإشعار المتبادل بين مجموعات الخدمات.
  • Data Center (مركز البيانات): تسمى مجموعة consul داخل شبكة محلية واحدة بمركز بيانات واحد، يدعم consul مراكز بيانات متعددة، وطريقة التواصل بين مراكز البيانات المتعددة هي WAN gossip.

TIP

للمزيد من المفردات والمصطلحات يمكنك الذهاب إلى Glossary | Consul | HashiCorp Developer للمعرفة المزيد.

في مجموعة consul، يجب التحكم بشكل صارم في عدد الخوادم، لأنها تشارك مباشرة في LAN gossip و WAN gossip، وانتخابات raft، ويجب عليها تخزين البيانات، كلما زاد عدد الخوادم، ارتفعت تكلفة الاتصال. أما عدد العملاء فلا بأس بزيادته، فهو مسؤول فقط عن إعادة التوجيه، لا يشارك في الانتخابات، ويستهلك موارد قليلة جدًا. في المجموعة في الرسم، تسجل كل خدمة نفسها في الخادم من خلال العميل، وإذا تعطل خادم، سيبحث العميل تلقائيًا عن خادم آخر متاح.

مثال بناء مجموعة

فيما يلي بناء مثال بسيط لمجموعة consul متعددة العقد، أولًا نجهز أربع أجهزة افتراضية

من بين الأجهزة الافتراضية الأربعة، ثلاثة خوادم وعميل واحد، توصي الجهة الرسمية بأن يكون عدد الخوادم فرديًا، والأفضل أن يكون ثلاثة أو أكثر. هنا سنستخدم vm00-vm02 كخوادم، و vm03 كعميل،

بالنسبة للخادم، نفذ الأمر التالي لإنشاء server agent

sh
consul agent -server -bind=vm_address -client=0.0.0.0 -data-dir=/tmp/consul/ -node=agent_name -ui

بالنسبة للعميل، نفذ الأمر التالي لإنشاء client agent

sh
consul agent -client=0.0.0.0  -bind=vm_address -data-dir=/tmp/consul/ -node=agent_name -ui

الأوامر المنفذة كالتالي

sh
# 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

بعض شرح المعاملات

  • client، 0.0.0.0 تعني السماح بجميع الطلبات من أي مصدر، إذا كان هناك معامل client فقط بدون معامل server، فهذا يعني أن agent سيعمل في وضع client.

بعد تشغيل جميع الوكلاء، فإن وظيفة retry-join تعادل تنفيذ أمر join تلقائيًا، وسيعيد المحاولة عند الفشل، والوقت الافتراضي لإعادة المحاولة هو 30 ثانية

sh
$ consul join 192.168.48.138

بعد اكتمال الانضمام، جميع العقد تعرف ببعضها البعض، وبما أن vm00 حددت وضع bootstrap، فهي القائد الافتراضي، إذا لم يتم تحديد وضع bootstrap، فالعقدة المحددة في join عند انضمام جميع العقد هي القائد الافتراضي، قبل انتخاب القائد، لا يمكن للمجموعة العمل بشكل طبيعي، وستُرجع واجهة الويب 500، وبعض الأوامر لن تعمل بشكل طبيعي . إذا حددت عقدة في المجموعة وضع bootstrap، فلا يجب أن تحدد أي عقدة أخرى في المجموعة وضع bootstrap، كما لا يجب أن تستخدم العقد الأخرى معامل bootstrap-expect، وإذا استُخدم سيُعطل تلقائيًا.

في هذه اللحظة، على عقدة القائد (في الواقع يمكن عرضها من أي عقدة) نفذ الأمر لعرض معلومات أعضاء مركز البيانات

sh
$ 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، مركز البيانات، جميع العقد في المخرجات تنتمي لمركز بيانات dc1
  • Partition، القسم الذي تنتمي إليه العقدة، ميزة للنسخة المؤسسية، يمكن لكل عقدة التواصل فقط مع العقد في نفس القسم
  • Segment، الشريحة الشبكية التي تنتمي إليها العقدة، ميزة للنسخة المؤسسية

وبالمثل، إذا أردت خروج عقدة، يجب استخدام consul leave للخروج بأمان، وإعلام العقد الأخرى بالخروج، في حالة العقد المتعددة، الخروج الآمن للعقد مهم جدًا، لأنه يتعلق بتناسق البيانات.

TIP

تم إيقاف تشغيل جميع جدران الحماية في الأجهزة الافتراضية أثناء العرض التوضيحي، في بيئة الإنتاج الفعلية يجب تفعيلها للأمان، ولهذا يجب الانتباه لجميع المنافذ التي يستخدمها consul: Required Ports | Consul | HashiCorp Developer.

بعد ذلك، نجرب تناسق البيانات ببساطة، في الجهاز الافتراضي vm00 أضف البيانات التالية

sh
$ consul kv put sys_confg {"name":"consul"}
Success! Data written to: sys_confg

بعد الحفظ، من خلال HTTP API الوصول للعقد الأخرى ستجد أن البيانات موجودة أيضًا (القيمة value مرمزة بـ base64)

sh
$ 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، وعندما تنضم أي عقدة لمركز البيانات الحالي، ستدرك جميع العقد هذا التغيير.

مثال بناء مراكز بيانات متعددة

جهز خمسة أجهزة افتراضية، vm00-vm02 هي المجموعة من المثال السابق، وتنتمي لمركز بيانات dc1، لن نلمسها، vm03-vm04 تنتمي لمركز بيانات dc2، مركز البيانات عند بدء agent يكون افتراضيًا dc1.

TIP

هنا للعرض التوضيحي، سنقوم ببناء الخوادم فقط، ونستغني عن العملاء.

أولًا شغّل vm03، واجعلها القائد الافتراضي

sh
$ consul agent -server -datacenter=dc2 -bind=192.168.48.141 -client=0.0.0.0 -data-dir=/tmp/consul/ -node=agent04 -ui -bootstrap

شغّل vm04، واجعلها تنضم تلقائيًا لعقدة vm03

sh
$ 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

في هذه اللحظة، شاهد members في vm00 و vm03

sh
# 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 مختلف، لأن هنا أجهزة افتراضية للعرض التوضيحي، فجميعها في نفس الشريحة الشبكية، في الواقع قد تكون مراكز البيانات خوادم في مواقع مختلفة. بعد ذلك اجعل أي عقدة من dc1 تنضم لأي عقدة من dc2، هنا سنجعل vm01 تنضم لـ vm03

sh
$ consul join -wan 192.168.48.141
Successfully joined cluster by contacting 1 nodes.

بعد نجاح الانضمام، نفذ الأمر لعرض أعضاء الشبكة الواسعة

sh
$ 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 تنضم لأي عقدة من dc2، ستدرك جميع العقد في كلا مركزي البيانات هذا التغيير، ويمكن رؤية عقد كلا مركزي البيانات عند عرض members.

بعد ذلك، جرب إضافة بيانات KV في عقدة vm00

sh
$ consul kv put name consul
Success! Data written to: name

في عقدة vm01 جرب قراءة البيانات، يمكن ملاحظة أن البيانات في نفس مركز البيانات متزامنة

sh
$ consul kv get name
consul

ثم اذهب لـ vm03 في مركز بيانات مختلف وجرب قراءة البيانات، ستجد أن البيانات في مراكز بيانات مختلفة غير متزامنة.

sh
$ 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)، يمكن استخدام agent service للتسجيل مباشرة، وإلا يجب استخدام catalog register للتسجيل.

يوفر consul SDK لـ HTTP API، للأطراف بلغات أخرى انتقل إلى Libraries and SDKs - HTTP API | Consul | HashiCorp Developer للمعرفة المزيد. هنا سننزل تبعية go

sh
go get github.com/hashicorp/consul/api

عند بدء الخدمة، يتم تسجيل الخدمة بشكل فعال في consul، وعند إيقاف الخدمة، يتم إلغاء تسجيل الخدمة من consul، فيما يلي مثال.

go
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)
  }
  // إنشاء خادم grpc
  server := grpc.NewServer(
    grpc.Creds(insecure.NewCredentials()),
  )
  // تسجيل الخدمة
  pb.RegisterSayHelloServer(server, &HelloRpc{})
  log.Println("server running...")
  // التشغيل
  err = server.Serve(listen)
  if err != nil {
    panic(err)
  }
}

كود العميل يستخدم محلل consul المخصص للاستعلام عن الخدمة المقابلة من مركز التسجيل، وتحويلها للعنوان الحقيقي.

go
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() {

}

العميل عند البدء يسجل المحلل

go
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(
       // تسجيل محلل consul المخصص
       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())
    }

}

شغّل الخادم أولًا، ثم العميل، هناك خادمان يقدمان نفس الخدمة، فقط العنوان مختلف، استراتيجية موازنة الحمل للعميل هي الاقتراع، ويمكن ملاحظة من وقت السجل في الخادم أن الاستراتيجية فعالة.

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 لتنفيذ تسجيل واكتشاف الخدمة.

Golang تم تحريره بواسطة www.golangdev.cn