一. 基础
- go-zero是一个集成了Web和RPC协议的服务框架,RPC也就是指go-zero的zRPC部分, zRPC底层依赖gRPC,内置了服务注册、负载均衡、拦截器等模块,其中还包括自适应降载,自适应熔断,限流等微服务治理方案,是一个简单易用的可直接用于生产的企业级RPC框架,我们在这里先了解一个服务治理这部分,怎么实现服务注册, 服务发现,服务更新的
- zRPC支持直连和基于etcd服务发现两种方式,
- zRPC中可以分成一下几个模块
- client: zRPC客户端,负责发起请求
- server: zRPC服务端,负责处理请求
- resolver: 服务注册模块,实现了gRPC的resolver.Builder接口并注册到gRPC
- discov: 服务发现模块,基于etcd实现服务发现功能
- balancer: 负载均衡模块,实现了p2c负载均衡算法,并注册到gRPC
- interceptor: 拦截器,对请求和响应进行拦截处理
- 在我们编写zrpc服务时,通常一个zrpc服务端,对应一个zrpc客户端, 在配置文件中设置etcd地址,将服务注册到etcd上,配置文件如下, 当服务端启动时会连接etcd将当前服务的服务名,服务地址保存到etcd, 当客户端启动时也会注册,同时会获取需要的服务端调用地址列表保存到本地
Name: hello.rpc
ListenOn: 127.0.0.1:9090
Etcd:
Hosts:
- 127.0.0.1:2379
Key: hello.rpc
- 当客户端发起调用时会基于服务发现模块获取目标服务地址列表,负载均衡选择指定地址进行调用,并且基于拦截器统计请求结果,实现熔断限流等功能
- 接下来我们看一下服务注册,发现的源码,了解一下具体的执行流程
二. resolver 服务注册底层原理
- 参考博客
- 首先go-zero中rpc服务发现支持三种模式,当前只讨论服务发现模式
DirectScheme = "direct"
DiscovScheme = "discov"
KubernetesScheme = "k8s"
- 编写rpc服务端时,需要配置etcd地址,设置当前服务名,服务启动时将当前服务调用地址保存到etcd,在上方已经做了配置文件的示例, 接下来看一下启动zrpc服务端代码
- 首先会读取配置文件,获取到当前服务的配置信息与etcd连接配置,
- 创建服务运行需要的Context对象,
- 通过proto生成的RegisterXXXServer(),将当前rpc服务实例注册到rpc服务器,
- 执行zrpc.MustNewServer()创建RpcServer,
- 调用RpcServer下的Start()启动服务
import (
"flag"
"fmt"
"go_cloud_demo/rpc/internal/config"
"go_cloud_demo/rpc/internal/server"
"go_cloud_demo/rpc/internal/svc"
"go_cloud_demo/rpc/types/user"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/core/service"
"github.com/zeromicro/go-zero/zrpc"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
var configFile = flag.String("f", "rpc/etc/user.yaml", "the config file")
func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
ctx := svc.NewServiceContext(c)
s := zrpc.MustNewServer(c.RpcServerConf,
func(grpcServer *grpc.Server) {
user.RegisterUserServer(grpcServer, server.NewUserServer(ctx))
if c.Mode == service.DevMode || c.Mode == service.TestMode {
reflection.Register(grpcServer)
}
})
defer s.Stop()
fmt.Printf("Starting rpc server at %s...\n", c.ListenOn)
s.Start()
}
- 了解服务注册中的可以分为两个步骤,分别是
- 执行MustNewServer()创建rpcServer时内部创建registerEtcd函数,并将该函数封装到keepAliveServer结构体中
- 执行Start()启动服务时,内部获取到keepAliveServer中保存的registerEtcd函数并执行,实现服务注册
1. 创建registerEtcd函数,并将该函数封装到keepAliveServer结构体中
- 我们看一下zrpc.MustNewServer()创建rpcServer源码,在"zrpc/server.go"下, 在MustNewServer()中调用了NewServer()
func MustNewServer(c RpcServerConf, register internal.RegisterFn) *RpcServer {
server, err := NewServer(c, register)
if err != nil {
log.Fatal(err)
}
return server
}
func NewServer(c RpcServerConf, register internal.RegisterFn) (*RpcServer, error) {
var err error
if err = c.Validate(); err != nil {
return nil, err
}
var server internal.Server
metrics := stat.NewMetrics(c.ListenOn)
serverOptions := []internal.ServerOption{
internal.WithMetrics(metrics),
internal.WithRpcHealth(c.Health),
}
if c.HasEtcd() {
server, err = internal.NewRpcPubServer(c.Etcd, c.ListenOn, serverOptions...)
if err != nil {
return nil, err
}
} else {
server = internal.NewRpcServer(c.ListenOn, serverOptions...)