Skip to content

Consul

consul es una solución que permite a los equipos gestionar de forma segura las conexiones de red entre servicios y entornos multi-nube, proporcionando descubrimiento de servicios, malla de servicios, gobernanza de tráfico, actualización automática de infraestructura de red y otras funcionalidades.

Documentación oficial: Consul by HashiCorp

Código abierto: hashicorp/consul

Consul es una herramienta de descubrimiento y registro de servicios de código abierto desarrollada por HashiCorp, utiliza el algoritmo de elección Raft y está escrita en Go, lo que hace que su despliegue sea muy ligero. Consul tiene las siguientes características:

  • Descubrimiento de servicios
  • Registro de servicios
  • Comprobación de estado
  • Almacenamiento clave-valor
  • Multi-datacenter

En realidad, consul puede hacer más que solo descubrimiento de servicios, también puede funcionar como centro de configuración distribuido. Hay muchas otras herramientas de código abierto similares, como zookeeper, nacos, que no se presentarán en detalle aquí.

Instalación

Para Ubuntu, ejecute los siguientes comandos para instalar usando 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

O también puede descargar el paquete de instalación correspondiente desde el sitio web oficial Install Consul. Dado que consul está desarrollado en Go, el paquete de instalación en sí es solo un archivo binario ejecutable, por lo que la instalación es bastante conveniente. Después de una instalación exitosa, ejecute el siguiente comando para verificar la versión.

sh
$ consul version

Si la salida es normal, no hay problema:

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)

Inicio rápido

A continuación se presenta cómo configurar rápidamente un solo nodo de consul. Generalmente, un solo nodo se usa para pruebas durante el desarrollo. Si un solo nodo funciona sin problemas, es muy probable que un clúster de múltiples nodos también funcione. Configurar un solo nodo es muy simple, solo requiere un comando:

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

Generalmente habrá la siguiente salida:

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

Una breve explicación de los parámetros:

  • agent es un subcomando, es el comando principal de consul. consul agent ejecuta un nuevo agente de consul. Cada nodo es un agente.

  • dev es el modo de ejecución del agente. Hay tres modos en total: dev, client, server.

  • bind es la dirección de comunicación de red local (LAN), el puerto predeterminado es 8301. Generalmente, este valor es la dirección IP interna del servidor.

  • advertise es la dirección de comunicación de red de área amplia (WAN), el puerto predeterminado es 8302. Generalmente, este valor es la dirección IP externa del servidor.

  • data-dir es el directorio de almacenamiento de datos.

  • config-dir es el directorio de configuración. Consul leerá todos los archivos json en este directorio.

  • bootstrap indica que el servidor actual entra en modo de arranque. Se votará a sí mismo durante la elección Raft. No puede haber más de un servidor en este modo en el clúster.

  • bootstrap-expect es el número esperado de servidores en el clúster. Hasta que no se alcance este número, el clúster no comenzará la elección. No se puede usar al mismo tiempo que bootstrap.

  • retry-join después de que el agente se inicia, intentará constantemente unirse a los nodos especificados. También admite los siguientes métodos de descubrimiento de servicios de proveedores:

    aliyun aws azure digitalocean gce hcp k8s linode mdns os packet scaleway softlayer tencentcloud triton vsphere
  • ui ejecuta la interfaz web.

  • node es el nombre del nodo de ejecución, debe ser único en el clúster.

TIP

Para más explicaciones sobre los parámetros del agente, visite Agents - CLI Reference | Consul | HashiCorp Developer. Tenga en cuenta que algunos parámetros solo están disponibles en la versión empresarial.

Después de ejecutar correctamente, visite 127.0.0.1:8500 para navegar por la interfaz web.

El ícono de dev01 es una estrella, lo que indica que es el nodo líder.

Al salir, para permitir que otros nodos detecten la salida del nodo actual, no se recomienda finalizar forzosamente el proceso. Puede usar el comando:

sh
consul leave

O

sh
consul force-leave

También puede usar ctrl+c para permitir que el agente de consul salga elegantemente.

Conceptos

Este es un diagrama de un clúster de consul, dividido en dos partes: el plano de control y el plano de datos. Consul solo se encarga del plano de control, dividido en clúster de servicios y clientes. El clúster de servicios se divide en seguidores y líderes. En general, el clúster de consul en el diagrama constituye un centro de datos. A continuación se explican algunos términos:

  • Agent (Agente): o sería más apropiado llamarlo nodo. Cada agente es un proceso guardián que se ejecuta durante mucho tiempo. Exponen interfaces HTTP y DNS, y son responsables de las comprobaciones de estado y la sincronización de servicios.
  • Server (Agente de servicio): como servidor de consul, sus responsabilidades incluyen participar en la elección Raft, mantener el estado del clúster, responder consultas, intercambiar datos con otros centros de datos y reenviar consultas a líderes y otros centros de datos.
  • Client (Agente cliente): el cliente es sin estado en comparación con el servidor. No participa en la elección Raft. Su única función es reenviar todas las solicitudes al servidor. Lo único que participa en relación con el fondo es el reenvío de rumores de red local (LAN gossip pool).
  • Leader (Líder): el líder es el jefe de todos los servidores, y solo puede haber un líder. El líder se elige mediante el algoritmo de elección Raft. Cada líder tiene su propio mandato. Durante su mandato, cualquier solicitud recibida por otros servidores debe informarse al líder, por lo que los datos del líder son los más actualizados.
  • Gossip (Rumor): Consul está construido sobre Serf (otro producto de la misma empresa). Utiliza el protocolo gossip, que está diseñado para la comunicación aleatoria entre nodos, similar a UDP. Consul usa este protocolo para notificarse mutuamente en el clúster de servicios.
  • Data Center (Centro de datos): un clúster de consul dentro de una red local se llama centro de datos. Consul admite múltiples centros de datos. La forma de comunicación entre múltiples centros de datos es WAN gossip.

TIP

Puede obtener más información sobre más vocabulario y términos en Glossary | Consul | HashiCorp Developer.

En un clúster de consul, el número de servidores debe controlarse estrictamente, ya que participan directamente en LAN gossip y WAN gossip, elección Raft y deben almacenar datos. Cuantos más servidores, mayor es el costo de comunicación. El número de clientes puede ser mayor sin problema, ya que solo son responsables del reenvío y no participan en la elección, ocupando muy pocos recursos. En el clúster del diagrama, cada servicio se registra en el servidor a través del cliente. Si un servidor falla, el cliente buscará automáticamente otros servidores disponibles.

Ejemplo de configuración de clúster

A continuación se presenta un ejemplo simple de configuración de un clúster de consul de múltiples nodos. Primero prepare cuatro máquinas virtuales:

De las cuatro máquinas virtuales, tres son servidores y una es cliente. La recomendación oficial es que el número de servidores sea preferiblemente impar y preferiblemente mayor o igual a tres. Aquí usaremos vm00-vm02 como servidores y vm03 como cliente.

Para el servidor, ejecute el siguiente comando para crear un agente de servidor:

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

Para el cliente, ejecute el siguiente comando para crear un agente de cliente:

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

Los comandos ejecutados son respectivamente:

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

Explicación de algunos parámetros:

  • client: 0.0.0.0 significa permitir solicitudes de todas las fuentes. Si solo hay parámetro client sin parámetro server, representa que el agente se ejecutará en modo cliente.

Después de que todos los agentes se ejecuten correctamente, la función de retry-join es equivalente a ejecutar automáticamente el comando join. Si falla, seguirá intentando. El tiempo de reintento predeterminado es de 30 segundos:

sh
$ consul join 192.168.48.138

Después de completar el join, cada nodo conoce la existencia del otro. Dado que vm00 especificó el modo bootstrap, será el líder predeterminado. Si no se especifica el modo bootstrap, el nodo especificado en el join será el líder predeterminado. Antes de que se elija el líder, el clúster no puede funcionar normalmente. El acceso a la interfaz web devolverá 500 y algunos comandos tampoco funcionarán correctamente. Si hay un nodo en el clúster que especifica el modo bootstrap, entonces ningún otro nodo en el clúster debe especificar el modo bootstrap. Al mismo tiempo, otros nodos no deben usar el parámetro bootstrap-expect. Si se usa, se deshabilitará automáticamente.

En este momento, ejecute el siguiente comando en el nodo líder (en realidad, en este momento se puede verificar en cualquier nodo) para ver la información de los miembros del centro de datos:

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: nombre del nodo
  • Address: dirección de comunicación
  • Status: alive significa activo, left significa fuera de línea
  • Type: tipo de agente, dos modos: server y client
  • Build: versión de consul utilizada por el nodo. Consul puede funcionar con nodos de diferentes versiones dentro de un cierto rango
  • Protocol: se refiere a la versión del protocolo Raft utilizado. Este protocolo debe ser consistente en todos los nodos
  • DC: Data Center, centro de datos. Todos los nodos en la salida pertenecen al centro de datos dc1
  • Partition: partición a la que pertenece el nodo, función de la versión empresarial. Cada nodo solo puede comunicarse con nodos de la misma partición
  • Segment: segmento de red al que pertenece el nodo, función de la versión empresarial

De la misma manera, si desea que un nodo salga, debe usar consul leave para permitir que el nodo salga elegantemente y notifique a otros nodos que va a salir. En el caso de múltiples nodos, la salida elegante de un nodo es particularmente importante, ya que esto está relacionado con la consistencia de los datos.

TIP

Durante la demostración, las máquinas virtuales tenían todos los firewalls desactivados. En un entorno de producción real, por razones de seguridad, se deben habilitar. Para ello, debe prestar atención a todos los puertos utilizados por consul: Required Ports | Consul | HashiCorp Developer.

A continuación, pruebe brevemente la consistencia de los datos. Agregue los siguientes datos en la máquina virtual vm00:

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

Después de guardar, acceda a otros nodos a través de la API HTTP y descubrirá que los datos también existen (el valor está codificado en 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}]

De hecho, la función de descubrimiento y registro de servicios proporcionada por consul se transmite a otros nodos a través del protocolo gossip. Y cuando cualquier nodo se une al centro de datos actual, todos los nodos detectarán este cambio.

Ejemplo de configuración de múltiples centros de datos

Prepare cinco máquinas virtuales. vm00-vm02 son el clúster del ejemplo anterior y pertenecen al centro de datos dc1. No los toque. vm03-vm04 pertenecen al centro de datos dc2. El centro de datos es dc1 por defecto cuando se inicia el agente.

TIP

Aquí, para la demostración, solo se configuran servidores, omitiendo los clientes.

Primero inicie vm03 respectivamente, estableciéndolo como el líder predeterminado:

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

Inicie vm04, permitiéndole unirse automáticamente al nodo 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

En este momento, verifique members en vm00 y vm03 respectivamente:

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>

Se puede ver que el campo DC es diferente. Dado que aquí es una demostración con máquinas virtuales, todas están en el mismo segmento de red. En la realidad, dos centros de datos pueden ser clústeres de servidores en ubicaciones diferentes. A continuación, permita que cualquier nodo de dc1 se una a cualquier nodo de dc2. Aquí haremos que vm01 se una a vm03:

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

Después de que el join sea exitoso, ejecute el comando para ver los members de la red de área amplia (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

Siempre que cualquier nodo de dc1 se una a cualquier nodo de dc2, todos los nodos de ambos centros de datos detectarán este cambio. Al verificar members, también puede ver los nodos de ambos centros de datos.

A continuación, intente agregar un dato KV en el nodo vm00:

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

Intente leer los datos en el nodo vm01. Puede ver que los datos dentro del mismo centro de datos están sincronizados:

sh
$ consul kv get name
consul

Luego intente leer los datos en vm03 en un centro de datos diferente. Descubrirá que los datos entre diferentes centros de datos no están sincronizados:

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

Si desea sincronizar datos entre múltiples centros de datos, puede consultar hashicorp/consul-replicate: Consul cross-DC KV replication daemon.

Registro y descubrimiento de servicios

Hay dos formas de registrar servicios en consul: registro mediante archivo de configuración y registro mediante API. Para facilitar las pruebas, aquí preparamos de antemano un servicio Hello World (ejemplo del artículo gRPC), desplegado en dos instancias en diferentes ubicaciones. Para el método de registro mediante archivo de configuración, puede consultar Register external services with Consul service discovery | Consul | HashiCorp Developer. Aquí solo se presentará el registro a través de la API HTTP.

TIP

Para servicios locales (junto con el cliente de consul), puede usar directamente el registro de servicio del agente. De lo contrario, debe usar el registro de catálogo para registrarse.

Consul proporciona SDK para la API HTTP. Para SDK en otros idiomas, consulte Libraries and SDKs - HTTP API | Consul | HashiCorp Developer. Aquí descargamos la dependencia de Go:

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

Registre activamente el servicio en consul cuando el servicio se inicie y cancele el registro del servicio en consul cuando el servicio se cierre. A continuación se muestra un ejemplo:

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{
        // Debe ser único
    ID:      "hello-service1",
    Service: "hello-service",
        // Desplegado en dos instancias, un puerto es 8080, el otro es 8081
    Port:    8080,
  }
)

// Registrar servicio
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)
}

// Cancelar registro de servicio
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()

  // Escuchar puerto
  listen, err := net.Listen("tcp", ":8080")
  if err != nil {
    panic(err)
  }
  // Crear servidor gRPC
  server := grpc.NewServer(
    grpc.Creds(insecure.NewCredentials()),
  )
  // Registrar servicio
  pb.RegisterSayHelloServer(server, &HelloRpc{})
  log.Println("server running...")
  // Ejecutar
  err = server.Serve(listen)
  if err != nil {
    panic(err)
  }
}

El código del cliente utiliza un resolvedor personalizado de consul para consultar el servicio correspondiente en el centro de registro y resolverlo a una dirección real.

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,
       // Estrategia de round-robin
       ServiceConfig: c.cc.ParseServiceConfig(
          `{"loadBalancingPolicy":"round_robin"}`),
    })
}

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

func (c ConsulResolver) Close() {

}

El cliente registra el resolvedor al iniciarse:

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() {
    // Registrar builder
    resolver.Register(
       // Registrar resolvedor personalizado de consul
       myresolver.NewConsulResolverBuilder("192.168.48.138:8500"),
    )
}

func main() {

    // Establecer conexión, sin verificación de cifrado
    conn, err := grpc.Dial("consul:hello-service",
       grpc.WithTransportCredentials(insecure.NewCredentials()),
    )
    if err != nil {
       panic(err)
    }
    defer conn.Close()
    // Crear cliente
    client := hello2.NewSayHelloClient(conn)
    for range time.Tick(time.Second) {
       // Llamada remota
       helloRep, err := client.Hello(context.Background(), &hello2.HelloReq{Name: "client"})
       if err != nil {
          panic(err)
       }
       log.Printf("received grpc resp: %+v", helloRep.String())
    }

}

Primero inicie el servidor, luego inicie el cliente. Hay dos servidores que proporcionan el mismo servicio, solo que las direcciones son diferentes. La estrategia de equilibrio de carga del cliente es round-robin. Se puede ver por el intervalo de tiempo de los registros del servidor que la estrategia está funcionando.

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"

Este es un caso simple de uso de consul combinado con gRPC para implementar el registro y descubrimiento de servicios.

Golang editado por www.golangdev.cn