# Day18 gRPC进阶 下篇

# 服务注册与发现

# 部署 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

# 接口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

常用于业务配置场景下, 如新功能上线设置开关

  1. 项目很复杂,等上下游都发布更新后再打开开关
  2. 轮播图等偏运营的数据展示
  3. 智能推荐、广告位等展示数量、展示位置等
  • 搭建 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
上次更新: 12/29/2022, 8:45:12 AM