# Day18 gRPC进阶 下篇
# 服务注册与发现
- 课前预习:深入了解服务注册与发现 (opens new window)
- 基于
Consul
搭建服务
# 部署 Consul
- 单机版
datacenter-deploy-service-discovery
// 拉取镜像配置
git clone https://github.com/hashicorp/learn-consul-docker.git
cd learn-consul-docker/datacenter-deploy-service-discovery
// 启动即可
docker-compose up -d
- 集群版
datacenter-deploy-secure
- 管理后台 http://127.0.0.1:8500/ (opens new window)
# 接口API文档
// 注册服务
PUT 127.0.0.1:8500/v1/agent/service/register
{
"id": "redis-127.0.0.1-6379",
"name": "redis",
"tags": ["redis"],
"address": "127.0.0.1",
"port": 6379
}
// 获取服务
GET 127.0.0.1:8500/v1/agent/services/
// 注销服务
PUT 127.0.0.1:8500/v1/agent/service/deregister/redis-127.0.0.1-6379
# Go集成 Consul
- 安装依赖
go get github.com/hashicorp/consul/api
- 注册服务
func RegisterDemo() {
// 1. 连接上consul
client, err := api.NewClient(api.DefaultConfig())
if err != nil {
log.Fatalf("api.NewClient failed, err:%v\n", err)
}
// 2. 注册服务
srv := &api.AgentServiceRegistration{
ID: "hello-127.0.0.1-8974",
Name: "hello",
Tags: []string{"hello"},
Address: "127.0.0.1",
Port: 8974,
}
client.Agent().ServiceRegister(srv)
}
- 发现服务
func DiscoverDemo() {
// 1. 连接上consul
client, err := api.NewClient(api.DefaultConfig())
if err != nil {
log.Fatalf("api.NewClient failed, err:%v\n", err)
}
// 2. 支持过滤发现
m, err := client.Agent().ServicesWithFilter(`Service=="hello"`)
if err != nil {
log.Fatalf("client.Agent().ServicesWithFilter:%v\n", err)
}
fmt.Printf("%#v\n", m)
for k, v := range m {
fmt.Printf("k=%v, v=%s:%d\n", k, v.Address, v.Port)
}
}
- 注销服务
func DeregisterDemo() {
// 1. 连接上consul
client, err := api.NewClient(api.DefaultConfig())
if err != nil {
log.Fatalf("api.NewClient failed, err:%v\n", err)
}
// 2. 注销服务
err = client.Agent().ServiceDeregister("hello-127.0.0.1-8974")
if err != nil {
log.Fatalf("client.Agent().ServiceDeregister err: %v\n", err)
}
}
- 完善服务端优雅停止服务
func main() {
// 1. 注册 RPC 接口服务
s := grpc.NewServer()
proto.RegisterServerServer(s, &server{})
// 2. 注册发现服务,IP+Port
RegisterDemo()
// 3. 启 TCP 监听
lis, err := net.Listen("tcp", ":8974")
if err != nil {
log.Fatalf("net.Liste err: %v\n", err)
}
// 单独goroutine处理s.Serve
go func() {
// 4. 启动 RPC 服务
err = s.Serve(lis)
if err != nil {
log.Fatalf("s.Serve err: %v\n", err)
}
}()
// 在代码里接收操作系统发来的中断信号
quitChan := make(chan os.Signal, 1)
// syscall.SIGTERM(kill)、syscall.SIGINT(ctrl+c)、syscall.SIGKILL(kill -9)
signal.Notify(quitChan, syscall.SIGTERM, syscall.SIGINT)
<-quitChan // 一直卡住,直到收到中断信号
fmt.Println("启动服务退出流程....")
DeregisterDemo()
}
// 信号介绍 https://zhuanlan.zhihu.com/p/140531888
# 健康检查 Check
健康检查用于保证服务正常可用,本质就是注册中心每隔一段时间请求服务1次,要求
- gPRC本身支持健康检查
- Consul支持定期发出健康检查的请求
# gRPC主服务配置
import (
"google.golang.org/grpc/health"
healthpb "google.golang.org/grpc/health/grpc_health_v1"
)
// 创建 RPC 服务
s := grpc.NewServer()
// gPRC本身支持健康检查
healthServer := health.NewServer()
healthpb.RegisterHealthServer(s, healthServer)
# gRPC注册服务配置
func RegisterDemo() {
...
// 健康检查,告诉Consul每隔一段时间探活一下
check := &api.AgentServiceCheck{
GRPC: "192.168.1.4:8974", // 这里一定是外部可以访问的地址
Timeout: "10s", // 超时时间
Interval: "10s", // 运行检查的频率
// 指定时间后自动注销不健康的服务节点
// 最小超时时间为1分钟,收获不健康服务的进程每30秒运行一次,因此触发注销的时间可能略长于配置的超时时间
DeregisterCriticalServiceAfter: "1m",
}
srv := &api.AgentServiceRegistration{
Check: check,
....
}
client.Agent().ServiceRegister(srv)
}
- 注意点:拦截器要放开监控检查探活
# 负载均衡 LB
保证服务均衡及可用性
package main
import _ "github.com/mbobakov/grpc-consul-resolver"
conn, err := grpc.Dial(
// consul服务
"consul://127.0.0.1:8500/hello?healthy=true",
// 指定round_robin策略
grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`),
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
# 配置中心 Apollo
常用于业务配置场景下, 如新功能上线设置开关
- 项目很复杂,等上下游都发布更新后再打开开关
- 轮播图等偏运营的数据展示
- 智能推荐、广告位等展示数量、展示位置等
- 搭建
docker-quick-start
git clone https://github.com/apolloconfig/apollo-quick-start.git
cd apollo-quick-start
docker-compose -f docker-compose-arm64.yml up
go get -u github.com/philchia/agollo/v4