Skip to content

Consul

Consul ist eine Lösung, die Teams ermöglicht, Netzwerkverbindungen zwischen Diensten in vor-ort und Multi-Cloud-Umgebungen sicher zu verwalten. Es bietet eine Reihe von Funktionen wie Service Discovery, Service Mesh, Datenverkehrskontrolle und automatische Aktualisierung der Netzwerkinfrastruktur.

Offizielle Dokumentation: Consul by HashiCorp

Open-Source-Adresse: hashicorp/consul

Consul ist ein Open-Source-Tool für Service Discovery und Registrierung von HashiCorp. Es verwendet den Raft-Wahalgorithmus und ist in Go entwickelt, was die Bereitstellung sehr einfach macht. Consul hat folgende Eigenschaften:

  • Service Discovery
  • Service Registrierung
  • Gesundheitsprüfung
  • Schlüssel-Wert-Speicher
  • Mehrere Rechenzentren

Tatsächlich kann Consul mehr als nur Service Discovery. Es kann auch als verteiltes Konfigurationszentrum verwendet werden. Es gibt viele ähnliche Open-Source-Tools wie Zookeeper und Nacos, auf die hier nicht weiter eingegangen wird.

Installation

Für Ubuntu können Sie Consul mit folgenden Befehlen über apt installieren:

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

Alternativ können Sie das entsprechende Installationspaket von der offiziellen Website herunterladen: Install Consul. Da Consul in Go entwickelt wurde, enthält das Installationspaket nur eine binäre ausführbare Datei, was die Installation sehr einfach macht. Nach erfolgreicher Installation können Sie die Version mit folgendem Befehl überprüfen:

sh
$ consul version

Eine korrekte Ausgabe zeigt, dass alles funktioniert:

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)

Schnellstart

Im Folgenden wird beschrieben, wie Sie schnell einen einzelnen Consul-Knoten einrichten. Einzelknoten werden normalerweise für Tests während der Entwicklung verwendet. Wenn der Einzelknoten problemlos funktioniert, wird auch ein Multi-Knoten-Cluster mit hoher Wahrscheinlichkeit funktionieren. Die Einrichtung eines Einzelknotens ist sehr einfach und erfordert nur einen Befehl:

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

Typischerweise erhalten Sie folgende Ausgabe:

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

Kurze Erläuterung der Parameter:

  • agent ist ein Unterbefehl und der Kernbefehl von Consul. consul agent startet einen neuen Consul-Agenten. Jeder Knoten ist ein Agent.

  • dev ist der Betriebsmodus des Agenten. Es gibt drei Modi: dev, client und server.

  • bind ist die LAN-Kommunikationsadresse. Der Standardport ist 8301. Normalerweise ist dies die interne Netzwerkadresse des Servers.

  • advertise ist die WAN-Kommunikationsadresse. Der Standardport ist 8302. Normalerweise ist dies die öffentliche Netzwerkadresse des Servers.

  • data-dir ist das Verzeichnis für die Datenspeicherung.

  • config-dir ist das Verzeichnis für die Konfiguration. Consul liest alle JSON-Dateien in diesem Verzeichnis.

  • bootstrap markiert den aktuellen Server als Bootstrap-Modus. Bei der Raft-Wahl gibt er seine eigene Stimme ab. In einem Cluster darf nur ein Server in diesem Modus sein.

  • bootstrap-expect ist die erwartete Anzahl von Servern im Cluster. Bis diese Anzahl erreicht ist, beginnt der Cluster nicht mit der Wahl. Kann nicht zusammen mit bootstrap verwendet werden.

  • retry-join bewirkt, dass der Agent nach dem Start wiederholt versucht, dem angegebenen Knoten beizutreten. Es werden auch folgende Cloud-Provider-Discovery-Methoden unterstützt:

    aliyun aws azure digitalocean gce hcp k8s linode mdns os packet scaleway softlayer tencentcloud triton vsphere
  • ui startet das Web-Backend.

  • node gibt den Knotennamen an, der im Cluster eindeutig sein muss.

TIP

Weitere Parametererklärungen für den Agenten finden Sie unter Agents - CLI Reference | Consul | HashiCorp Developer. Beachten Sie, dass einige Parameter nur in der Enterprise-Version verfügbar sind.

Nach erfolgreichem Start können Sie die Web-Oberfläche unter 127.0.0.1:8500 aufrufen.

Das Stern-Symbol bei dev01 zeigt an, dass es sich um den Leader-Knoten handelt.

Beim Beenden sollten Sie den Prozess nicht gewaltsam beenden, damit andere Knoten das Verlassen bemerken. Verwenden Sie stattdessen den Befehl:

sh
consul leave

oder

sh
consul force-leave

Alternativ können Sie ctrl+c drücken, um den Consul-Agenten ordnungsgemäß zu beenden.

Konzepte

Dies ist ein Diagramm eines Consul-Clusters. Die Abbildung ist in zwei Teile unterteilt: die Kontrollebene und die Datenebene. Consul ist nur für die Kontrollebene verantwortlich und ist in Server-Cluster und Clients unterteilt. Der Server-Cluster ist wiederum in Follower und Leader unterteilt. Insgesamt bildet der Consul-Cluster in der Abbildung ein Rechenzentrum. Im Folgenden werden einige Begriffe erläutert:

  • Agent: Ein Agent ist ein lang laufender Daemon-Prozess, der HTTP- und DNS-Schnittstellen nach außen bereitstellt und für Gesundheitsprüfungen und Dienstsynchronisation verantwortlich ist.

  • Server: Als Consul-Server sind seine Hauptaufgaben die Teilnahme an der Raft-Wahl, die Aufrechterhaltung des Cluster-Status, die Beantwortung von Anfragen, der Datenaustausch mit anderen Rechenzentren sowie die Weiterleitung von Anfragen an den Leader und andere Rechenzentren.

  • Client: Im Vergleich zum Server ist der Client zustandslos. Er nimmt nicht an der Raft-Wahl teil. Seine einzige Aufgabe ist die Weiterleitung aller Anfragen an den Server. Die einzige Backend-bezogene Aufgabe, an der er teilnimmt, ist die LAN-Gossip-Weiterleitung (LAN gossip pool).

  • Leader: Der Leader ist der Anführer aller Server, und es kann nur einen Leader geben. Der Leader wird durch den Raft-Wahalgorithmus gewählt. Jeder Leader hat seine eigene Amtszeit. Während der Amtszeit muss jeder Server den Leader über jede eingehende Anfrage informieren, daher sind die Daten des Leaders immer aktuell.

  • Gossip: Consul basiert auf Serf (einem anderen Produkt von HashiCorp) und verwendet das Gossip-Protokoll. Dieses Protokoll dient der zufälligen Kommunikation zwischen Knoten, ähnlich wie UDP. Consul verwendet dieses Protokoll für die gegenseitige Benachrichtigung zwischen Server-Clustern.

  • Data Center: Ein Consul-Cluster in einem LAN wird als ein Rechenzentrum bezeichnet. Consul unterstützt mehrere Rechenzentren. Die Kommunikation zwischen mehreren Rechenzentren erfolgt über WAN-Gossip.

TIP

Weitere Begriffe und Terminologie finden Sie unter Glossary | Consul | HashiCorp Developer.

In einem Consul-Cluster sollte die Anzahl der Server streng kontrolliert werden, da sie direkt an LAN-Gossip, WAN-Gossip und der Raft-Wahl teilnehmen und Daten speichern müssen. Je mehr Server, desto höher die Kommunikationskosten. Die Anzahl der Clients kann etwas höher sein, da sie nur für die Weiterleitung zuständig sind und nicht an Wahlen teilnehmen, was sehr wenige Ressourcen verbraucht. Im Cluster in der Abbildung registrieren sich die verschiedenen Dienste über Clients beim Server. Wenn ein Server ausfällt, sucht der Client automatisch nach einem anderen verfügbaren Server.

Cluster-Einrichtungsbeispiel

Im Folgenden wird ein einfaches Beispiel für einen Multi-Knoten-Consul-Cluster eingerichtet. Bereiten Sie zuerst vier virtuelle Maschinen vor:

Von den vier virtuellen Maschinen sind drei Server und einer ein Client. Offiziell wird empfohlen, dass die Anzahl der Server ungerade ist und idealerweise drei oder mehr beträgt. Hier werden vm00-vm02 als Server und vm03 als Client konfiguriert.

Für einen Server führen Sie folgenden Befehl aus, um einen Server-Agent zu erstellen:

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

Für einen Client führen Sie folgenden Befehl aus, um einen Client-Agent zu erstellen:

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

Die auszuführenden Befehle sind wie folgt:

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

Einige Parametererklärungen:

  • client: 0.0.0.0 lässt Anfragen von allen Quellen zu. Wenn nur der client-Parameter ohne server-Parameter angegeben wird, bedeutet das, dass der Agent im Client-Modus läuft.

Nachdem alle Agenten gestartet wurden, bewirkt retry-join automatisch die Ausführung des join-Befehls. Bei Fehlern wird erneut versucht, standardmäßig alle 30 Sekunden:

sh
$ consul join 192.168.48.138

Nach erfolgreichem join kennen alle Knoten einander. Da vm00 den Bootstrap-Modus angegeben hat, ist es der Standard-Leader. Wenn kein Bootstrap-Modus angegeben wurde, ist der beim join angegebene Knoten der Standard-Leader. Bevor ein Leader gewählt wurde, kann der Cluster nicht normal arbeiten. Der Zugriff auf die Web-Oberfläche gibt 500 zurück, und einige Befehle funktionieren nicht. Wenn ein Knoten im Cluster den Bootstrap-Modus angegeben hat, sollten keine anderen Knoten im Cluster den Bootstrap-Modus angeben, und andere Knoten sollten auch den Parameter bootstrap-expect nicht verwenden. Wenn er verwendet wird, wird er automatisch deaktiviert.

Führen Sie nun auf dem Leader-Knoten (tatsächlich kann jeder Knoten dies anzeigen) folgenden Befehl aus, um die Mitgliederinformationen des Rechenzentrums anzuzeigen:

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: Knotenname
  • Address: Kommunikationsadresse
  • Status: alive bedeutet aktiv, left bedeutet offline
  • Type: Agent-Typ, Server und Client sind zwei Modi
  • Build: Die vom Knoten verwendete Consul-Version. Consul kann innerhalb eines bestimmten Bereichs Knoten mit unterschiedlichen Versionen unterstützen.
  • Protocol: Die verwendete Raft-Protokollversion. Dieses Protokoll sollte bei allen Knoten identisch sein.
  • DC: Data Center, Rechenzentrum. Alle Knoten in der Ausgabe gehören zum Rechenzentrum dc1.
  • Partition: Die Partition, zu der der Knoten gehört. Eine Enterprise-Funktion. Jeder Knoten kann nur mit Knoteln derselben Partition kommunizieren.
  • Segment: Das Netzwerksegment, zu dem der Knoten gehört. Eine Enterprise-Funktion.

Ebenso sollte ein Knoten beim Verlassen consul leave verwenden, um ordnungsgemäß zu gehen und andere Knoten über das bevorstehende Verlassen zu informieren. Bei Multi-Knoten-Setups ist das ordnungsgemäße Verlassen besonders wichtig, da es die Datenkonsistenz betrifft.

TIP

Für die Demonstration wurden alle Firewalls auf den virtuellen Maschinen deaktiviert. In Produktionsumgebungen sollten diese aus Sicherheitsgründen aktiviert werden. Beachten Sie dazu alle von Consul verwendeten Ports: Required Ports | Consul | HashiCorp Developer.

Testen wir kurz die Datenkonsistenz. Fügen Sie auf der vm00 virtuellen Maschine folgende Daten hinzu:

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

Nach dem Speichern können Sie über die HTTP-API auf andere Knoten zugreifen und feststellen, dass die Daten ebenfalls vorhanden sind (der Wert ist base64-kodiert):

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

Tatsächlich werden die von Consul bereitgestellten Service Discovery- und Registrierungsfunktionen über das Gossip-Protokoll an andere Knoten übertragen. Wenn ein beliebiger Knoten dem aktuellen Rechenzentrum beitritt, werden alle Knoten diese Änderung bemerken.

Beispiel für die Einrichtung mehrerer Rechenzentren

Bereiten Sie fünf virtuelle Maschinen vor. vm00-vm02 sind der Cluster aus dem vorherigen Beispiel und gehören zum Rechenzentrum dc1. vm03-vm04 gehören zum Rechenzentrum dc2. Beim Start eines Agenten ist das Rechenzentrum standardmäßig dc1.

TIP

Für diese Demonstration werden nur Server eingerichtet, Clients werden weggelassen.

Starten Sie zuerst vm03 als Standard-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

Starten Sie dann vm04, um automatisch dem vm03-Knoten beizutreten:

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

Zeigen Sie nun die Mitglieder auf vm00 und vm03 an:

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>

Sie können sehen, dass das DC-Feld unterschiedlich ist. Da dies eine Demonstration mit virtuellen Maschinen ist, befinden sich alle im selben Netzwerksegment. In der Realität könnten zwei Rechenzentren Server-Cluster an verschiedenen Standorten sein. Lassen Sie nun einen beliebigen Knoten von dc1 einem beliebigen Knoten von dc2 beitreten. Hier lässt vm01 vm03 beitreten:

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

Nach erfolgreichem Join zeigen Sie die WAN-Mitglieder an:

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

Sobald ein beliebiger Knoten von dc1 einem beliebigen Knoten von dc2 beigetreten ist, werden alle Knoten beider Rechenzentren diese Änderung bemerken. Beim Anzeigen der Mitglieder können Sie auch die Knoten beider Rechenzentren sehen.

Versuchen Sie nun, KV-Daten auf dem vm00-Knoten hinzuzufügen:

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

Versuchen Sie, die Daten auf dem vm01-Knoten zu lesen. Sie können sehen, dass die Daten innerhalb desselben Rechenzentrums synchronisiert sind:

sh
$ consul kv get name
consul

Versuchen Sie dann, die Daten auf vm03 in einem anderen Rechenzentrum zu lesen. Sie werden feststellen, dass Daten zwischen verschiedenen Rechenzentren nicht synchronisiert werden:

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

Wenn Sie eine Synchronisation zwischen mehreren Rechenzentren wünschen, informieren Sie sich unter hashicorp/consul-replicate: Consul cross-DC KV replication daemon.

Service-Registrierung und -Discovery

Es gibt zwei Möglichkeiten zur Service-Registrierung in Consul: Registrierung über Konfigurationsdateien und Registrierung über die API. Für einfachere Tests bereiten wir hier einen Hello-World-Dienst vor (das Beispiel aus dem gRPC-Artikel) und stellen ihn zweimal an verschiedenen Standorten bereit. Informationen zur Registrierung über Konfigurationsdateien finden Sie unter Register external services with Consul service discovery | Consul | HashiCorp Developer. Hier wird nur die Registrierung über die HTTP-API beschrieben.

TIP

Für lokale Dienste (die sich auf demselben Host wie der Consul-Client befinden) kann die Agent-Service-Registrierung direkt verwendet werden. Andernfalls sollte die Catalog-Register-Methode verwendet werden.

Consul bietet ein SDK für die HTTP-API. SDKs für andere Sprachen finden Sie unter Libraries and SDKs - HTTP API | Consul | HashiCorp Developer. Hier laden wir die Go-Abhängigkeit herunter:

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

Bei Dienststart wird der Service aktiv bei Consul registriert, bei Dienstabschaltung wird er abgemeldet. Hier ist ein Beispiel:

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{
        // Muss eindeutig sein
    ID:      "hello-service1",
    Service: "hello-service",
        // Zwei Instanzen bereitgestellt, eine auf Port 8080, eine auf Port 8081
    Port:    8080,
  }
)

// Service registrieren
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)
}

// Service abmelden
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()

  // Port überwachen
  listen, err := net.Listen("tcp", ":8080")
  if err != nil {
    panic(err)
  }
  // gRPC-Server erstellen
  server := grpc.NewServer(
    grpc.Creds(insecure.NewCredentials()),
  )
  // Service registrieren
  pb.RegisterSayHelloServer(server, &HelloRpc{})
  log.Println("server running...")
  // Ausführen
  err = server.Serve(listen)
  if err != nil {
    panic(err)
  }
}

Der Client-Code verwendet einen benutzerdefinierten Consul-Resolver, um den entsprechenden Dienst beim Registrierungszentrum abzufragen und in die echte Adresse aufzulösen.

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,
       // Round-Robin-Strategie
       ServiceConfig: c.cc.ParseServiceConfig(
          `{"loadBalancingPolicy":"round_robin"}`),
    })
}

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

func (c ConsulResolver) Close() {

}

Der Client registriert den Resolver beim Start:

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 registrieren
    resolver.Register(
       // Benutzerdefinierten Consul-Resolver registrieren
       myresolver.NewConsulResolverBuilder("192.168.48.138:8500"),
    )
}

func main() {

    // Verbindung herstellen, ohne Verschlüsselungsverifizierung
    conn, err := grpc.Dial("consul:hello-service",
       grpc.WithTransportCredentials(insecure.NewCredentials()),
    )
    if err != nil {
       panic(err)
    }
    defer conn.Close()
    // Client erstellen
    client := hello2.NewSayHelloClient(conn)
    for range time.Tick(time.Second) {
       // Entfernter Aufruf
       helloRep, err := client.Hello(context.Background(), &hello2.HelloReq{Name: "client"})
       if err != nil {
          panic(err)
       }
       log.Printf("received grpc resp: %+v", helloRep.String())
    }

}

Starten Sie zuerst den Server, dann den Client. Es gibt zwei Server, die denselben Dienst bereitstellen, aber unterschiedliche Adressen haben. Die Lastverteilungsstrategie des Clients ist Round-Robin. An den Zeitintervallen in den Server-Logs kann man erkennen, dass die Strategie wirksam ist.

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"

Dies ist ein einfaches Beispiel für die Implementierung von Service-Registrierung und -Discovery mit Consul und gRPC.

Golang by www.golangdev.cn edit