gRPC服务发现与服务治理,目前常见解决方案有以下两种

  • Nginx + consul + consul-template
  • Envoy

本文粗略讲解一下两种方案的优缺点

一. nginx + consul + consul-template

实现步骤

  1. grpc微服务注册到consul, consul会定时发送心跳到grcp微服务
  2. 当consul节点有更新时, consul-template生成nginx-consul.conf文件
  3. nginx reload完成服务更新, 当有请求时upstream转发到grpc微服务

nginx-consul.conf大致如下

# nginx需要安装grpc模块
upstream User {
    server 127.0.0.1:17000;
    server 127.0.0.1:18000;
}
server {
    listen       19000 http2;
    server_name  _;
    
    location /protobuf.User {
        default_type application/grpc;
        grpc_pass grpc://User;
    }
}	
复制代码

优点

  1. 简单易用,文档丰富

缺点

  1. consul心跳只是以ping tcp端口为标准, 无法确定服务是否正常响应
  2. 当有节点上线或下线时, 需要nginx reload. 有一定风险 (微服务在运行时难免会触发隐藏Bug或者panic, 如果每次都要nginx reload来确保健康的路由,我认为代价太大)
二.Envoy

Envoy 是专为大型现代 SOA(面向服务架构)架构设计的 L7 代理和通信总线。是istio重要组件之一

大家都发现这个图里面少了consul注册中心, 是不是意味使用Envoy就不需要注册中心? 答案是看情况而定
因为Envoy支持动态配置,所以Istio中是搭配pilot使用, pilot可以通过consul,etcd等注册中心获取在线节点, 然后动态注册到Envoy.

envoy.yaml

node:
  id: id_1
  cluster: test_cluster

watchdog:
  miss_timeout: 0.2s
  megamiss_timeout: 1s
  kill_timeout: 0s
  multikill_timeout: 0s

admin:
  access_log_path: /var/log/admin_access.log
  address:
    socket_address: { address: 127.0.0.1, port_value: 10000 }

static_resources:
  listeners:
  - name: listener_0
    address:
      socket_address: { address: 127.0.0.1, port_value: 19000 }
    filter_chains:
    - filters:
      - name: envoy.http_connection_manager
        config:
          stat_prefix: ingress_http
          codec_type: HTTP2
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_service
              domains: ["*"]
              routes:
              - match: { prefix: "/protobuf.User" }
                route: { cluster: xds_cluster }
          http_filters:
          - name: envoy.router
  clusters:
  - name: xds_cluster
    connect_timeout: { seconds: 1 }
    type: STATIC
    lb_policy: ROUND_ROBIN
    http2_protocol_options: {}
    hosts:
    - socket_address: { address: 127.0.0.1, port_value: 18000 }
    - socket_address: { address: 127.0.0.1, port_value: 17000 }
    health_checks:
    - grpc_health_check: {service_name: YourServiceName}
      unhealthy_threshold : 1
      healthy_threshold: 1
      timeout: 0.5s
      interval: 0.5s
      interval_jitter: 0.5s
复制代码

这是一个静态的路由配置,带grpc健康检查, 有四个关键点

  1. listeners : 监听127.0.0.1:19000
  2. filters : 过滤grpc请求,路由到xds_cluster
  3. clusters : 由hosts组成的集群
  4. health_checks: grpc健康检查

如何实现grpc健康检查

# 引入protobuf包google.golang.org/grpc/health/grpc_health_v1并实现里面的Check和Watch方法
package health
import(
	"log"
	"context"
	pb "google.golang.org/grpc/health/grpc_health_v1"
)
type Health struct{}
func New() *Health {
	return &Health{}
}
func (h *Health) Check(ctx context.Context, in *pb.HealthCheckRequest) (*pb.HealthCheckResponse, error){
	log.Printf("checking............%s", in.Service)
	var s pb.HealthCheckResponse_ServingStatus = 1
	return &pb.HealthCheckResponse{
		Status : s,
	}, nil
}
func (h *Health) Watch(in *pb.HealthCheckRequest, w pb.Health_WatchServer) (error){
	log.Printf("watching............%s", in.Service)
	var s pb.HealthCheckResponse_ServingStatus = 1
	r := &pb.HealthCheckResponse{
		Status : s,
	}
	for {
		w.Send(r)
	}
	return nil
}
复制代码
# 服务注册
pb.RegisterHealthServer(grpcServer, health.New())
复制代码

启动envoy和grpc服务后, grpc就会收心跳(默认的心跳是60秒,yaml的心跳设置是0.5秒)
YourServiceName就是yaml中grpc_health_check的service_name参数,这里你可以自定义你想监听的方法

优点

  1. 轻量级,无侵入式.服务发现由envoy主动发现,grpc不需要特定配置,
  2. 自带grpc健康检查
  3. 动态配置

缺点

  1. 相关中文文档比较少
总结

相对于nginx, 我更倾向于envoy,首先envoy就是为微服务而生的负载匀衡工具. grpc健康检查是微服务中重要的一环. 但是nginx拥有活跃的社区,说不定不久将来也会有支持grpc健康检查的插件.