Регистрация и обнаружение служб часто используются в архитектуре распределенных служб.В число широко используемых в отрасли инструментов регистрации и обнаружения служб входят: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После того, как текущая структура стала следующей:
В этой схеме:
- Во-первых, после запуска экземпляра 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:
# 这是第一个 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 также имеет множество приложений в центрах конфигурации и распределенных блокировках.