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, LAN 통신 주소로 포트는 기본 8301 이며 일반적으로 이 값은 서버의 내부 IP 주소입니다.

  • advertise, WAN 통신 주소로 포트는 기본 8302 이며 일반적으로 이 값은 서버의 외부 IP 주소입니다.

  • data-dir, 데이터 저장 디렉토리입니다.

  • config-dir, 설정 저장 디렉토리로 consul 은 디렉토리 내의 모든 json 파일을 읽습니다.

  • bootstrap, 현재 server 가 가이드 모드로 진입했음을 표시하며 Raft 선거 시 자신에게 투표합니다. 클러스터에서 이 모드의 server 는 하나를 초과할 수 없습니다.

  • bootstrap-expect, 클러스터에서 예상되는 server 수로 지정된 수에 도달하기 전까지 클러스터는 선거 투표를 시작하지 않으며 bootstrap과 동시에 사용할 수 없습니다.

  • retry-join, agent 시작 후 지정된 노드에 지속적으로 가입을 시도하며 다음과 같은 일부 서비스 디스커버리 방법도 지원합니다.

    aliyun aws azure digitalocean gce hcp k8s linode mdns os packet scaleway softlayer tencentcloud triton vsphere
  • ui, Web 백그라운드 실행

  • node, 실행 노드 이름으로 클러스터에서 고유해야 합니다.

TIP

더 많은 agent 매개변수 설명은 Agents - CLI Reference | Consul | HashiCorp Developer 를 참조하세요. 일부 매개변수는 엔터프라이즈 버전만 사용할 수 있습니다.

성공적으로 실행된 후 127.0.0.1:8500 에 접속하면 Web 인터페이스를 볼 수 있습니다.

dev01 아이콘이 별이면 leader 노드임을 나타냅니다.

종료 시 다른 노드가 현재 노드의 종료를 인지할 수 있도록 프로세스를 강제로 종료하는 것은 권장되지 않으며 명령어를 사용할 수 있습니다.

sh
consul leave

또는

sh
consul force-leave

ctrl+c 를 사용하여 consul agent 가 우아하게 종료되도록 할 수도 있습니다.

개념

이것은 consul 클러스터 다이어그램으로 그림은 두 부분으로 나뉘며 제어면과 데이터면입니다. consul 은 제어면만 담당하며 서비스 클러스터와 클라이언트로 나뉩니다. 서비스 클러스터는 팔로워와 리더로 나뉘며 전체적으로 consul 클러스터는 하나의 데이터센터를 구성합니다. 다음은 용어에 대한 설명입니다.

  • Agent(에이전트): 또는 노드라고 하는 것이 더 적절하며 각 agent 는 장기간 실행되는 데몬 프로세스이며 HTTP 와 DNS 인터페이스를 외부에 노출하고 헬스체크와 서비스 동기화를 담당합니다.
  • Server(서비스 에이전트): consul server 로서 Raft 선거 참여, 클러스터 상태 유지, 쿼리 응답, 다른 데이터센터와 데이터 교환, 리더 및 다른 데이터센터에 쿼리 전달 등의职责이 있습니다.
  • Client(클라이언트 에이전트): client 는 server 에 비해 상태가 없으며 Raft 선거에 참여하지 않고 모든 요청을 server 로 전달하는 것만 담당하며 백그라운드와 관련된 유일한 일은 LAN 가십 전달 (LAN gossip pool) 입니다.
  • Leader(리더): leader 는 모든 server 의 리더이며 리더는 하나만 있어야 합니다. leader 는 Raft 선거 알고리즘을 통해 선출되며 각 leader 는 자신의 임기가 있으며 임기 동안 다른 server 는 어떤 요청을 받든 leader 에게 알려야 하므로 leader 의 데이터가 가장 최신입니다.
  • Gossip(가십): Consul 은 Serf(동일 회사의 또 다른 제품) 를 기반으로 구축되었으며 gossip 프로토콜을 사용합니다. 이 프로토콜은 노드 간 무작위 통신에 전용되며 UDP 와 유사하며 consul 은 이 프로토콜을 사용하여 서비스 클러스터 간에 서로 알립니다.
  • Data Center(데이터센터): LAN 내의 consul 클러스터를 하나의 데이터센터라고 하며 consul 은 멀티 데이터센터를 지원합니다. 멀티 데이터센터 통신 방식은 WAN gossip 입니다.

TIP

더 많은 단어 및 용어는 Glossary | Consul | HashiCorp Developer 에서 확인할 수 있습니다.

consul 클러스터에서 server 의 수는 엄격하게 제어되어야 합니다. 왜냐하면它们은 LAN gossip 과 WAN gossip, Raft 선거에 직접 참여하며 데이터를 저장해야 하기 때문에 server 가 많을수록 통신 비용이 높아집니다. 반면 client 의 수는 많아도 문제가 없으며 전달만 담당하고 선거에 참여하지 않으며 리소스 점유율이 낮습니다. 그림의 클러스터에서 각 서비스는 client 를 통해 자신을 server 에 등록하며 server 가 다운되면 client 는 다른 사용 가능한 server 를自行 찾습니다.

클러스터 구축 예제

다음은 간단한 consul 멀티 노드 클러스터 구축 예제를 소개합니다. 먼저 4 대의 가상 머신을 준비합니다.

4 대의 가상 머신 중 3 대는 server, 1 대는 client 입니다. 공식 권장 사항은 server 의 수가 홀수이며 3 대 이상이어야 합니다. 여기서는 vm00-vm02 를 server 로, vm03 을 client 로 사용합니다.

server 의 경우 다음 명령어를 실행하여 server agent 를 생성합니다.

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

client 의 경우 다음 명령어를 실행하여 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은 모든 출처의 요청을 허용함을 의미하며 server 매개변수 없이 client 매개변수만 있는 경우 agent 가 client 모드로 실행됨을 나타냅니다.

모든 agent 가 실행된 후 retry-join의 역할은 join 명령어를 자동으로 실행하는 것과 같으며 실패 후 지속적으로 시도합니다. 기본 재시도 시간은 30 초입니다.

sh
$ consul join 192.168.48.138

join 완료 후 각 노드는 상대방의 존재를 알게 되며 vm00 이 bootstrap 모드로 지정되었으므로 기본 leader 가 됩니다. bootstrap 모드를 지정하지 않은 경우 모든 노드가 join 시 지정한 노드가 기본 leader 가 됩니다. leader 가 선출되기 전까지 클러스터는 정상적으로 작동할 수 없으며 Web 인터페이스 접속 시 500 이 반환되고 일부 명령어도 정상적으로 작동하지 않습니다. 클러스터에 bootstrap 모드로 지정된 노드가 있는 경우 클러스터의 다른 노드는 더 이상 bootstrap 모드로 지정되어서는 안 되며 동시에 다른 노드는 bootstrap-expect 매개변수를 사용해서는 안 됩니다. 사용하면 자동으로 비활성화됩니다.

이때 leader 노드에서 (실제로 이때는 어떤 노드에서든 확인할 수 있습니다) 데이터센터의 구성원 정보를 확인하는 명령어를 실행합니다.

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 프로토콜을 통해 다른 노드에 브로드캐스트되며 임의의 노드가 현재 데이터센터에 가입할 때 모든 노드가 이 변화를 감지합니다.

멀티 데이터센터 구축 예제

5 대의 가상 머신을 준비합니다. vm00-vm02 는 이전 예제의 클러스터로 dc1 데이터센터에 속하며 건드리지 않습니다. vm03-vm04 는 dc2 데이터센터에 속하며 데이터센터는 agent 시작 시 기본값이 dc1 입니다.

TIP

여기서는 데모를 위해 server 만 구축하고 client 는 생략합니다.

먼저 vm03 을 각각 시작하여 기본 leader 로 설정합니다.

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 노드에 자동으로 join 하도록 합니다.

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

이때 vm00 과 vm03 에서 각각 members 를 확인합니다.

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 의 임의의 노드에 join 하도록 합니다. 여기서는 vm01 이 vm03 에 join 하도록 합니다.

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

join 성공 후 WAN members 를 확인하는 명령어를 실행합니다.

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 의 임의의 노드에 join 하기만 하면 두 데이터센터의 모든 노드가 이 변화를 감지하며 members 를 확인할 때 두 데이터센터의 노드를 모두 볼 수 있습니다.

다음으로 vm00 노드에서 KV 데이터를 추가해 봅니다.

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 client 와 함께 있는) 의 경우 agent service 등록을 직접 사용할 수 있으며 그렇지 않은 경우 catalog register 를 사용하여 등록해야 합니다.

consul 은 HTTP API 의 SDK 를 제공하며 다른 언어의 SDK 는 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)
  }
  // 创建 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)
  }
}

클라이언트 코드는 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 by www.golangdev.cn edit