Используйте Consul для регистрации и обнаружения сервисов

gRPC

Регистрация и обнаружение служб часто используются в архитектуре распределенных служб.В число широко используемых в отрасли инструментов регистрации и обнаружения служб входят:ZooKeeper,etcd,ConsulиEureka. Основными функциями Consul являются обнаружение служб, проверка работоспособности, хранение KV, безопасная служебная связь и несколько центров обработки данных. Различия между Consul и некоторыми другими инструментами можно посмотреть здесь.Consul vs. Other Software.

Зачем нужна регистрация службы и обнаружение службы?

Предполагая, что в распределенной системе есть две службы Service-A (далее «SA») и Service-B (далее «SB»), когда SA хочет вызвать SB, мы сначала думаем о непосредственно в SA Запросите IP-адрес и порт прослушивания сервера, на котором расположен SB. Нет проблем, когда масштаб службы мал, но есть некоторые проблемы, когда масштаб службы велик и для каждой службы развернуто более одного экземпляра, например поскольку SB развернула три экземпляра SB-1, SB-2 и SB-3.В настоящее время SA хочет позвонить SB, какой IP-адрес экземпляра службы следует запросить? Или прописать IP-адреса трех экземпляров службы в коде S-A и выбирать один из IP-адресов каждый раз, когда вызывается S-B? Это кажется очень негибким, и тогда мы подумалиNginxДостаточно, чтобы очень хорошо решить эту проблему, ввестиNginxПосле того, как текущая структура стала следующей:

После внедрения Nginx была решена проблема развертывания нескольких экземпляров S-B, а также выполнена балансировка нагрузки между экземплярами S-B. Однако текущая архитектура сталкивается с новыми проблемами. Распределенные системы часто должны обеспечивать высокую доступность и могут достигать динамического масштабирования. В архитектуре, в которой представлен Nginx, если экземпляр службы SB-1 недоступен, Nginx по-прежнему будет отчитываться перед SB-1. Распределить запросы так, чтобы служба была недоступна, мы хотим, чтобы Nginx больше не выделял ей запросы после смерти SB-1, а когда мы вновь развернем SB-4 и SB-5, Nginx также сможет распределять запросы на SB-4. и SB-5, Nginx необходимо обновлять файл конфигурации и перезапускать Nginx каждый раз при изменении экземпляра службы. Кажется, что использовать Nginx очень неудобно, и необходимо вручную наблюдать, какие сервисы не работают. Если Nginx имеет проверку работоспособности сервисов и может динамически изменять конфигурацию сервисов, это инструмент, который нам нужен. Это регистрация сервисов. и обслуживание Откройте для себя полезность инструментов. Ниже представлена ​​схема архитектуры после введения инструментов регистрации и обнаружения служб:

В этой схеме:

  • Во-первых, после запуска экземпляра S-B он регистрирует свою собственную служебную информацию (в основном IP-адрес и номер порта, на котором находится служба) в инструменте регистрации. Способы регистрации разных сервисов инструментальных средств регистрации различаются, конкретный способ регистрации Консула будет описан далее.
  • После того как служба зарегистрирует информацию о службе с помощью средства регистрации, средство регистрации может выполнить проверку работоспособности службы, чтобы определить, какие экземпляры службы доступны, а какие недоступны.
  • После запуска S-A он может получить IP-адреса и порты всех работоспособных экземпляров S-B с помощью инструментов регистрации служб и обнаружения служб и поместить эту информацию в свою собственную память, а S-A может использовать эту информацию для вызова S-B.
  • S-A может обновлять служебную информацию S-B, хранящуюся в памяти, наблюдая за инструментом регистрации. Например, если S-B-1 зависнет, механизм проверки работоспособности пометит его как недоступный, такие изменения информации будут отслеживаться S-A, и S-A будет обновлять служебную информацию S-B-1 в своей памяти.

Таким образом, в дополнение к функциям регистрации и обнаружения самой службы, инструменты регистрации и обнаружения службы нуждаются как минимум в функциях проверки работоспособности и уведомления об изменении состояния.

Consul

Как инструмент распределенного обслуживания, Консул часто развертывается в кластере, чтобы избежать единой точки отказа.Узлы кластера Консул делятся на два типа: Сервер и Клиент (все узлы также называются Агентами).Узел Сервер сохраняет Узел-клиент отвечает за проверку работоспособности и пересылку запросов данных на сервер; узел-сервер имеет узел-лидер и несколько узлов-последователей, узел-лидер будет синхронизировать данные с узлом-ведомым, а когда узел-лидер зависнет, он запустится. механизм выборов для создания нового Лидера.

Клиентский узел очень легкий и не имеет состояния.Он перенаправляет запросы на чтение и запись на серверный узел с помощью RPC, а также может напрямую отправлять запросы на чтение и запись на серверный узел. Ниже представлена ​​диаграмма архитектуры Consul:

Установка Consule и конкретное использование, а также другие детали можно просмотретьофициальная документация. Ниже показано, как я использовал Docker для создания кластера Consul с 3 узлами сервера и 1 узлом клиента.

# 这是第一个 Consul 容器,其启动后的 IP 为172.17.0.5
docker run -d --name=c1 -p 8500:8500 -e CONSUL_BIND_INTERFACE=eth0 consul agent --server=true --bootstrap-expect=3 --client=0.0.0.0 -ui

docker run -d --name=c2 -e CONSUL_BIND_INTERFACE=eth0 consul agent --server=true --client=0.0.0.0 --join 172.17.0.5

docker run -d --name=c3 -e CONSUL_BIND_INTERFACE=eth0 consul agent --server=true --client=0.0.0.0 --join 172.17.0.5

#下面是启动 Client 节点
docker run -d --name=c4 -e CONSUL_BIND_INTERFACE=eth0 consul agent --server=false --client=0.0.0.0 --join 172.17.0.5

Переменные среды, указанные при запуске контейнераCONSUL_BIND_INTERFACEПо сути, это эквивалентно указанию времени запуска Консула.--bindПеременные параметры, например, можно заменить команду запуска контейнера c1 на следующую, и будет достигнут тот же эффект.

docker run -d --name=c1 -p 8500:8500 -e consul agent --server=true --bootstrap-expect=3 --client=0.0.0.0 --bind='{{ GetInterfaceIP "eth0" }}' -ui

Управляйте Консулом с помощьюCommandsиHTTP APIДва способа, введите любой контейнер для выполненияconsul membersВы можете получить следующий вывод, указывающий, что кластер Consul был успешно построен.

Node          Address          Status  Type    Build  Protocol  DC   Segment
2dcf0c824cf0  172.17.0.7:8301  alive   server  1.4.4  2         dc1  <all>
64746cffa116  172.17.0.6:8301  alive   server  1.4.4  2         dc1  <all>
77af7d94a8ca  172.17.0.5:8301  alive   server  1.4.4  2         dc1  <all>
6c71148f0307  172.17.0.8:8301  alive   client  1.4.4  2         dc1  <default>

Кодекс Практика

Предположим теперь, что есть сервисный узел-сервер, написанный на Node.js, которому нужно передатьgRPCспособ вызвать сервис go-server, написанный на Go. Ниже приведен файл службы и типа данных, определенный с помощью Protobuf.hello.proto.

syntax = "proto3";

package hello;
option go_package = "hello";

// The greeter service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (stream HelloRequest) returns (stream HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

Используйте команду для генерации кода Go из определения Protobuf:protoc --go_out=plugins=grpc:./hello ./*.protoВы получите файл hello.pb.go в каталоге hello, а затем реализуете службу RPC, которую мы определили в файле hello.go.

// hello.go
package hello

import "context"

type GreeterServerImpl struct {}

func (g *GreeterServerImpl) SayHello(c context.Context, h *HelloRequest) (*HelloReply, error)  {
	result := &HelloReply{
		Message: "hello" + h.GetName(),
	}
	return result, nil
}

Ниже находится входной файлmain.go, в основном для регистрации службы, которую мы определили, в gRPC и создания/pingИнтерфейс используется для последующих проверок работоспособности Консула.

package main

import (
	"go-server/hello"
	"google.golang.org/grpc"
	"net"
	"net/http"
)

func main() {
	lis1, _ := net.Listen("tcp", ":8888")
	lis2, _ := net.Listen("tcp", ":8889")
	grpcServer := grpc.NewServer()
	hello.RegisterGreeterServer(grpcServer, &hello.GreeterServerImpl{})
	go grpcServer.Serve(lis1)
	go grpcServer.Serve(lis2)
	
	http.HandleFunc("/ping", func(res http.ResponseWriter, req *http.Request){
		res.Write([]byte("pong"))
	})
	http.ListenAndServe(":8080", nil)
}

На данный момент весь код на стороне go-server написан, видно, что в коде нет ничего связанного с Консулом, использовать Консул для регистрации сервиса можно без какого-либо вмешательства в код проекта. Следующее, что нужно сделать, это зарегистрировать go-server в Consul. Зарегистрировать сервис в Consul можно, напрямую вызвав REST API, предоставляемый Consul, а также есть конфигурационный файл, не навязчивый для проекта. Детали файла конфигурации сервиса Consul можно посмотретьПроверьте это здесь. Ниже приведен файл конфигурации для регистрации нашего сервиса через файл конфигурацииservices.json:

{
  "services": [
    {
      "id": "hello1",
      "name": "hello",
      "tags": [
        "primary"
      ],
      "address": "172.17.0.9",
      "port": 8888,
      "checks": [
        {
          "http": "http://172.17.0.9:8080/ping",
          "tls_skip_verify": false,
          "method": "GET",
          "interval": "10s",
          "timeout": "1s"
        }
      ]
    },{
      "id": "hello2",
      "name": "hello",
      "tags": [
        "second"
      ],
      "address": "172.17.0.9",
      "port": 8889,
      "checks": [
        {
          "http": "http://172.17.0.9:8080/ping",
          "tls_skip_verify": false,
          "method": "GET",
          "interval": "10s",
          "timeout": "1s"
        }
      ]
    }
  ]
}

в файле конфигурации172.17.0.9Представляет IP-адрес сервера, на котором расположен go-server,portЭто другой порт, который служба прослушивает.checkЧастью определения является проверка работоспособности, которую Консул запрашивает каждые 10 секунд./pingИнтерфейс использует это, чтобы определить, исправна ли служба. Скопируйте этот файл конфигурации в каталог /consul/config контейнера c4 и выполнитеconsul reloadПосле команды сервис hello в конфигурационном файле регистрируется у Consul. путем выполнения на хостеcurl http://localhost:8500/v1/catalog/services\?prettyВы можете увидеть приветственный сервис, который мы зарегистрировали. Вот код службы node-server:

const grpc = require('grpc');
const axios = require('axios');
const protoLoader = require('@grpc/proto-loader');
const packageDefinition = protoLoader.loadSync(
  './hello.proto',
  {
    keepCase: true,
    longs: String,
    enums: String,
    defaults: true,
    oneofs: true
  });
const hello_proto = grpc.loadPackageDefinition(packageDefinition).hello;

function getRandNum (min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min + 1)) + min;
};

const urls = []
async function getUrl() {
  if (urls.length) return urls[getRandNum(0, urls.length-1)];
  const { data } = await axios.get('http://172.17.0.5:8500/v1/health/service/hello');
  for (const item of data) {
    for (const check of item.Checks) {
      if (check.ServiceName === 'hello' && check.Status === 'passing') {
        urls.push(`${item.Service.Address}:${item.Service.Port}`)
      }
    }
  }
  return urls[getRandNum(0, urls.length - 1)];
}

async function main() {
  const url = await getUrl();
  const client = new hello_proto.Greeter(url, grpc.credentials.createInsecure());
   
  client.sayHello({name: 'jack'}, function (err, response) {
    console.log('Greeting:', response.message);
  }); 
}

main()

Адрес 172.17.0.5 в коде это IP адрес контейнера c1.В проекте node-server адрес сервиса hello получается напрямую через API предоставляемый Консулом.После получения сервиса нам нужно отфильтровать из адреса исправной службы, а затем случайным образом получить адрес из всех Выберите один из адресов для вызова. Мониторинга Consul в коде нет, реализация мониторинга может отфильтровывать адрес службы здоровья, постоянно опрашивая API выше для обновленияurlsмассив, чтобы сделать это. Теперь запустите node-server, чтобы вызвать службу go-server. Регистрация и обнаружение сервисов привносят в сервисы возможности динамического масштабирования, а также в определенной степени усложняют архитектуру. Помимо обнаружения и регистрации сервисов, Consul также имеет множество приложений в центрах конфигурации и распределенных блокировках.