Skip to content

Consul

consul là một giải pháp cho phép các nhóm quản lý kết nối mạng một cách an toàn giữa các dịch vụ và trên các môi trường đa đám mây, nó cung cấp các chức năng như khám phá dịch vụ, lưới dịch vụ, quản trị lưu lượng, cập nhật tự động cơ sở hạ tầng mạng và nhiều tính năng khác.

Tài liệu chính thức: Consul by HashiCorp

Mã nguồn mở: hashicorp/consul

Consul là một công cụ khám phá và đăng ký dịch vụ mã nguồn mở do công ty HashiCorp phát triển, sử dụng thuật toán bầu cử Raft, công cụ này được phát triển bằng ngôn ngữ Go nên việc triển khai rất linh hoạt. Consul có các đặc điểm sau:

  • Khám phá dịch vụ
  • Đăng ký dịch vụ
  • Kiểm tra sức khỏe
  • Lưu trữ khóa-giá trị
  • Đa trung tâm dữ liệu

Thực tế consul có thể làm nhiều việc hơn là khám phá dịch vụ, nó còn có thể làm trung tâm cấu hình phân tán, có nhiều công cụ mã nguồn mở cùng loại khác như zookeeper, nacos, ở đây sẽ không giới thiệu thêm.

Cài đặt

Đối với Ubuntu, thực hiện lệnh dưới đây để cài đặt bằng 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

Hoặc cũng có thể tải xuống từ trang web chính thức Install Consul gói cài đặt tương ứng, vì consul được phát triển bằng go, bản thân gói cài đặt chỉ là một tệp thực thi nhị phân duy nhất, việc cài đặt cũng khá thuận tiện, sau khi cài đặt thành công, thực hiện lệnh sau để xem phiên bản.

sh
$ consul version

Đầu ra bình thường sẽ không có vấn đề gì

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)

Bắt đầu nhanh

Phần dưới đây giới thiệu cách nhanh chóng thiết lập một nút đơn consul, thông thường nút đơn được sử dụng để kiểm tra trong quá trình phát triển, nếu nút đơn hoạt động tốt thì hầu như cụm nhiều nút cũng sẽ không có vấn đề. Việc thiết lập nút đơn rất đơn giản, chỉ cần một dòng lệnh

sh
$ consul agent -dev -bind=192.168.48.141 -data-dir=/tmp/consul -ui -node=dev01

Thông thường sẽ có đầu ra như sau

==> 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}]"

Giải thích ý nghĩa một cách đơn giản

  • agent là lệnh con, là lệnh cốt lõi của consul, consul agent là chạy một proxy consul mới, mỗi node đều là một proxy.

  • dev, là chế độ chạy của agent, tổng cộng có ba loại dev, client, server

  • bind, địa chỉ giao tiếp mạng LAN, cổng mặc định là 8301, thông thường giá trị này là địa chỉ nội bộ của máy chủ

  • advertise, địa chỉ giao tiếp mạng WAN, cổng mặc định là 8302, thông thường giá trị này là địa chỉ bên ngoài của máy chủ

  • data-dir, thư mục lưu trữ dữ liệu

  • config-dir, thư mục lưu trữ cấu hình, consul sẽ đọc tất cả các tệp json trong thư mục

  • bootstrap, đánh dấu server hiện tại vào chế độ bootstrap, sẽ tự bỏ phiếu cho mình trong quá trình bầu cử raft, trong cụm không được có quá một server ở chế độ này

  • bootstrap-expect, số lượng server mong đợi trong cụm, trước khi đạt đến số lượng chỉ định, cụm sẽ không bắt đầu bầu cử, không thể sử dụng cùng lúc với bootstrap.

  • retry-join, sau khi agent khởi động, sẽ liên tục cố gắng gia nhập các nút được chỉ định, cũng hỗ trợ các phương pháp khám phá dịch vụ của các nhà cung cấp sau

    aliyun aws azure digitalocean gce hcp k8s linode mdns os packet scaleway softlayer tencentcloud triton vsphere
  • ui, chạy giao diện Web

  • node, tên nút thực thi, phải duy trì duy nhất trong cụm.

TIP

Về thêm các tham số agent, hãy đến Agents - CLI Reference | Consul | HashiCorp Developer , lưu ý rằng một số tham số chỉ có sẵn trong phiên bản doanh nghiệp.

Sau khi chạy thành công, truy cập 127.0.0.1:8500 để xem giao diện Web.

Biểu tượng dev01 là một ngôi sao, cho thấy nó là nút leader.

Khi thoát, để các nút khác có thể nhận biết được việc nút hiện tại thoát, không nên buộc kill tiến trình, có thể sử dụng lệnh

sh
consul leave

hoặc

sh
consul force-leave

Cũng có thể dùng ctrl+c để consul agent thoát một cách lịch sự.

Khái niệm

Đây là sơ đồ cụm consul, được chia thành hai phần, mặt phẳng điều khiển và mặt phẳng dữ liệu. consul chỉ chịu trách nhiệm mặt phẳng điều khiển, được chia thành cụm dịch vụ và máy khách, trong cụm dịch vụ lại được chia thành follower và leader, nhìn chung, cụm consul trong hình đã cấu thành một trung tâm dữ liệu. Dưới đây là giải thích một số thuật ngữ

  • Agent (Đại lý): hoặc gọi là nút sẽ phù hợp hơn, mỗi agent đều là một tiến trình daemon chạy lâu dài, chúng cung cấp giao diện HTTP và DNS bên ngoài, chịu trách nhiệm kiểm tra sức khỏe và đồng bộ hóa dịch vụ.
  • Server (Đại lý dịch vụ): Là một consul server, nhiệm vụ chính của nó bao gồm tham gia bầu cử Raft, duy trì trạng thái cụm, phản hồi truy vấn, trao đổi dữ liệu với các trung tâm dữ liệu khác, và chuyển tiếp truy vấn đến leader và các trung tâm dữ liệu khác.
  • Client (Đại lý khách hàng): client tương đối so với server là không trạng thái, nó không tham gia bầu cử Raft, việc nó làm chỉ là chuyển tiếp tất cả các yêu cầu đến server, việc duy nhất nó tham gia liên quan đến nền tảng là chuyển tiếp tin đồn mạng LAN (LAN gossip pool).
  • Leader (Lãnh đạo): leader là lãnh đạo của tất cả server, và chỉ có thể có một lãnh đạo, leader được bầu thông qua thuật toán bầu cử Raft, mỗi leader có nhiệm kỳ của mình, trong nhiệm kỳ, bất kể server khác nhận được yêu cầu gì đều phải báo cho leader, vì vậy dữ liệu của leader là cập nhật và mới nhất.
  • Gossip (Tin đồn): Consul được xây dựng dựa trên Serf (một sản phẩm khác của công ty này), nó sử dụng giao thức gossip, giao thức này chuyên dùng cho giao tiếp ngẫu nhiên giữa các nút, tương tự như UDP, consul sử dụng giao thức này để thông báo lẫn nhau trong cụm dịch vụ.
  • Data Center (Trung tâm dữ liệu): Một cụm consul trong một mạng LAN được gọi là một trung tâm dữ liệu, consul hỗ trợ đa trung tâm dữ liệu, cách thức giao tiếp giữa các trung tâm dữ liệu là WAN gossip.

TIP

Biết thêm từ vựng và thuật ngữ có thể đến Glossary | Consul | HashiCorp Developer để tìm hiểu.

Trong cụm consul, số lượng server nên được kiểm soát chặt chẽ, vì chúng trực tiếp tham gia vào LAN gossip và WAN gossip, bầu cử raft, và phải lưu trữ dữ liệu, càng nhiều server thì chi phí giao tiếp càng cao. Còn số lượng client nhiều một chút cũng không sao, nó chỉ chịu trách nhiệm chuyển tiếp, không tham gia bầu cử, chiếm rất ít tài nguyên, trong cụm ở hình, các dịch vụ thông qua client đăng ký chính mình vào server, nếu có server bị lỗi thì client sẽ tự tìm server khả dụng khác.

Ví dụ thiết lập cụm

Dưới đây là một ví dụ đơn giản về việc thiết lập cụm consul nhiều nút, trước tiên chuẩn bị bốn máy ảo

Trong bốn máy ảo, ba server và một client, khuyến nghị chính thức là số lượng server nên là số lẻ, và tốt nhất là lớn hơn hoặc bằng ba. Ở đây sẽ dùng vm00-vm02 làm server, vm03 làm client,

Đối với server, chạy lệnh sau để tạo server agent

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

Đối với client, chạy lệnh sau để tạo client agent

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

Các lệnh thực thi lần lượt như sau

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

Giải thích một số tham số

  • client, 0.0.0.0 tức là cho phép tất cả các yêu cầu từ mọi nguồn, nếu chỉ có tham số client mà không có tham số server, đại diện cho việc agent sẽ chạy ở chế độ client.

Sau khi tất cả agent chạy tốt, tác dụng của retry-join tương đương với việc tự động thực hiện lệnh join, sau khi thất bại sẽ liên tục cố gắng, thời gian重试 mặc định là 30s

sh
$ consul join 192.168.48.138

Sau khi join hoàn tất, các nút đều biết sự tồn tại của nhau, vì vm00 đã chỉ định chế độ bootstrap nên nó là leader mặc định, nếu không chỉ định chế độ bootstrap thì nút được chỉ định khi join của tất cả các nút là leader mặc định, trước khi leader được bầu ra, cụm không thể hoạt động bình thường, truy cập giao diện web sẽ trả về 500, một số lệnh cũng không hoạt động bình thường. Nếu trong cụm có nút chỉ định chế độ bootstrap, thì các nút khác trong cụm không nên có nút nào khác chỉ định chế độ bootstrap, đồng thời các nút khác cũng không nên sử dụng tham số bootstrap-expect, nếu sử dụng sẽ tự động bị vô hiệu hóa.

Lúc này trên nút leader (thực tế lúc này bất kỳ nút nào cũng có thể xem) chạy lệnh xem thông tin thành viên của data center, chạy lệnh sau

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, tức là tên nút
  • Address, địa chỉ giao tiếp
  • Status, alive biểu thị đang hoạt động, left biểu thị đã offline
  • Type, loại agent, hai chế độ server và client
  • Build, phiên bản consul mà nút này sử dụng, consul có thể hoạt động tương thích với các nút có phiên bản khác nhau trong một phạm vi nhất định
  • Protocol, đề cập đến phiên bản giao thức Raft được sử dụng, giao thức này nên thống nhất trên tất cả các nút
  • DC, Data Center, trung tâm dữ liệu, tất cả các nút trong đầu ra đều thuộc trung tâm dữ liệu dc1
  • Partition, phân vùng mà nút thuộc về, là chức năng phiên bản doanh nghiệp, mỗi nút chỉ có thể giao tiếp với các nút trong cùng phân vùng
  • Segment, phân đoạn mạng mà nút thuộc về, là chức năng phiên bản doanh nghiệp

Tương tự, nếu muốn một nút thoát, nên sử dụng consul leave để nút thoát một cách lịch sự và thông báo cho các nút khác rằng mình sẽ thoát, trong trường hợp nhiều nút, việc thoát lịch sự của nút đặc biệt quan trọng, vì điều này liên quan đến tính nhất quán của dữ liệu.

TIP

Máy ảo trong quá trình demo đã tắt tất cả các tường lửa, trong môi trường sản xuất thực tế để đảm bảo an toàn nên bật, vì vậy nên quan tâm đến tất cả các cổng mà consul sử dụng: Required Ports | Consul | HashiCorp Developer.

Tiếp theo kiểm tra đơn giản tính nhất quán của dữ liệu, thêm dữ liệu sau vào máy ảo vm00

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

Sau khi lưu, truy cập các nút khác thông qua HTTP API sẽ thấy dữ liệu cũng tồn tại (trong đó value được mã hóa 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}]

Trên thực tế, chức năng khám phá và đăng ký dịch vụ mà consul cung cấp được phát sóng đến các nút khác thông qua giao thức gossip, và khi bất kỳ nút nào gia nhập trung tâm dữ liệu hiện tại, tất cả các nút sẽ nhận biết được thay đổi này.

Ví dụ thiết lập đa trung tâm dữ liệu

Chuẩn bị năm máy ảo, vm00-vm02 là cụm của ví dụ trước, thuộc trung tâm dữ liệu dc1, không động đến nó, vm03-vm04 thuộc trung tâm dữ liệu dc2, trung tâm dữ liệu mặc định là dc1 khi agent khởi động.

TIP

Ở đây để demo, chỉ thiết lập server, bỏ qua client.

Trước tiên khởi động vm03, coi nó là leader mặc định

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

Khởi động vm04, để nó tự động join vào nút 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

Lúc này lần lượt xem members trên vm00 và 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>

Có thể thấy trường DC khác nhau, vì đây là demo máy ảo nên đều ở cùng một mạng, trong thực tế hai trung tâm dữ liệu có thể là cụm máy chủ ở các địa điểm khác nhau. Tiếp theo để một nút bất kỳ của dc1 join vào một nút bất kỳ của dc2, ở đây để vm01 join vm03

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

Sau khi join thành công, thực hiện lệnh xem members của mạng WAN

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

Chỉ cần một nút bất kỳ của dc1 join vào một nút bất kỳ của dc2, tất cả các nút của hai trung tâm dữ liệu sẽ nhận biết được thay đổi này, khi xem members cũng có thể thấy các nút của hai trung tâm dữ liệu.

Tiếp theo thử thêm một dữ liệu KV trên nút vm00

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

Thử đọc dữ liệu trên nút vm01, có thể thấy dữ liệu trong cùng trung tâm dữ liệu được đồng bộ

sh
$ consul kv get name
consul

Sau đó thử đọc dữ liệu trên vm03 ở trung tâm dữ liệu khác nhau, sẽ thấy dữ liệu giữa các trung tâm dữ liệu khác nhau không được đồng bộ.

sh
$ consul kv get name
Error! No key exists at: name

Nếu muốn đồng bộ dữ liệu đa trung tâm dữ liệu, có thể tìm hiểu hashicorp/consul-replicate: Consul cross-DC KV replication daemon.

Đăng ký và khám phá dịch vụ

Có hai cách đăng ký dịch vụ consul, đăng ký qua tệp cấu hình và đăng ký qua API. Để thuận tiện cho việc kiểm tra, ở đây chuẩn bị trước một dịch vụ Hello World (ví dụ trong bài gRPC), triển khai hai bản, mỗi bản ở vị trí khác nhau. Có thể đến Register external services with Consul service discovery | Consul | HashiCorp Developer để tìm hiểu cách đăng ký qua tệp cấu hình, ở đây chỉ giới thiệu cách đăng ký qua HTTP API.

TIP

Đối với dịch vụ cục bộ (cùng với consul client), có thể trực tiếp sử dụng đăng ký dịch vụ agent, nếu không thì nên sử dụng catalog register để đăng ký.

consul cung cấp SDK HTTP API, SDK của các ngôn ngữ khác có thể đến Libraries and SDKs - HTTP API | Consul | HashiCorp Developer để tìm hiểu. Ở đây tải xuống phụ thuộc của go

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

Chủ động đăng ký dịch vụ với consul khi dịch vụ khởi động, hủy đăng ký dịch vụ với consul khi dịch vụ đóng, dưới đây là một ví dụ.

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{
        // Phải duy trì duy nhất
    ID:      "hello-service1",
    Service: "hello-service",
        // Triển khai hai bản, một bản cổng là 8080, một bản cổng là 8081
    Port:    8080,
  }
)

// Đăng ký dịch vụ
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)
}

// Hủy đăng ký dịch vụ
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()

  // Lắng nghe cổng
  listen, err := net.Listen("tcp", ":8080")
  if err != nil {
    panic(err)
  }
  // Tạo máy chủ gprc
  server := grpc.NewServer(
    grpc.Creds(insecure.NewCredentials()),
  )
  // Đăng ký dịch vụ
  pb.RegisterSayHelloServer(server, &HelloRpc{})
  log.Println("server running...")
  // Chạy
  err = server.Serve(listen)
  if err != nil {
    panic(err)
  }
}

Mã client sử dụng bộ phân tích cú pháp tùy chỉnh của consul để truy vấn dịch vụ tương ứng từ trung tâm đăng ký và phân giải thành địa chỉ thực tế.

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,
       // Chiến lược luân phiên
       ServiceConfig: c.cc.ParseServiceConfig(
          `{"loadBalancingPolicy":"round_robin"}`),
    })
}

func (c ConsulResolver) ResolveNow(options resolver.ResolveNowOptions) {
    c.resolve()
}

func (c ConsulResolver) Close() {

}

Client đăng ký bộ phân tích cú pháp khi khởi động

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() {
    // Đăng ký builder
    resolver.Register(
       // Đăng ký bộ phân tích cú pháp consul tùy chỉnh
       myresolver.NewConsulResolverBuilder("192.168.48.138:8500"),
    )
}

func main() {

    // Thiết lập kết nối, không có xác minh mã hóa
    conn, err := grpc.Dial("consul:hello-service",
       grpc.WithTransportCredentials(insecure.NewCredentials()),
    )
    if err != nil {
       panic(err)
    }
    defer conn.Close()
    // Tạo client
    client := hello2.NewSayHelloClient(conn)
    for range time.Tick(time.Second) {
       // Gọi từ xa
       helloRep, err := client.Hello(context.Background(), &hello2.HelloReq{Name: "client"})
       if err != nil {
          panic(err)
       }
       log.Printf("received grpc resp: %+v", helloRep.String())
    }

}

Trước tiên khởi động máy chủ, sau đó khởi động client, máy chủ có hai bản, cung cấp cùng một dịch vụ, chỉ khác địa chỉ, chiến lược cân bằng tải của client là luân phiên, có thể thấy từ khoảng cách thời gian nhật ký máy chủ rằng chiến lược đã có hiệu lực.

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"

Trên đây là một ví dụ đơn giản về việc sử dụng consul kết hợp với gRPC để thực hiện đăng ký và khám phá dịch vụ.

Golang by www.golangdev.cn edit