本文是在go.19.11的代码下分析的
option go_package = "./user";
// 用户登录
message LoginRequest {
string Mobile = 1;
string Password = 2;
}
message LoginResponse {
int64 Id = 1;
string Name = 2;
int64 Gender = 3;
string Mobile = 4;
}
首先我们如果用grpc的client调用登录的这个接口用作示例
用protoc,protoc-gen-go,protoc-gen-go-grpc的工具会生成两个文件,一个是proto的go结构体函数文件,一个是client,server的对应的回调函数的文件。
type LoginRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Mobile string `protobuf:"bytes,1,opt,name=Mobile,proto3" json:"Mobile,omitempty"`
Password string `protobuf:"bytes,2,opt,name=Password,proto3" json:"Password,omitempty"`
}
type LoginResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id int64 `protobuf:"varint,1,opt,name=Id,proto3" json:"Id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=Name,proto3" json:"Name,omitempty"`
Gender int64 `protobuf:"varint,3,opt,name=Gender,proto3" json:"Gender,omitempty"`
Mobile string `protobuf:"bytes,4,opt,name=Mobile,proto3" json:"Mobile,omitempty"`
}
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc v3.19.4
// source: rpc/user.proto
package user
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
const (
User_Login_FullMethodName = "/user.User/Login"
User_Register_FullMethodName = "/user.User/Register"
User_UserInfo_FullMethodName = "/user.User/UserInfo"
)
// UserClient is the client API for User service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type UserClient interface {
Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error)
Register(ctx context.Context, in *RegisterRequest, opts ...grpc.CallOption) (*RegisterResponse, error)
UserInfo(ctx context.Context, in *UserInfoRequest, opts ...grpc.CallOption) (*UserInfoResponse, error)
}
type userClient struct {
cc grpc.ClientConnInterface
}
func NewUserClient(cc grpc.ClientConnInterface) UserClient {
return &userClient{cc}
}
func (c *userClient) Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error) {
out := new(LoginResponse)
err := c.cc.Invoke(ctx, User_Login_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *userClient) Register(ctx context.Context, in *RegisterRequest, opts ...grpc.CallOption) (*RegisterResponse, error) {
out := new(RegisterResponse)
err := c.cc.Invoke(ctx, User_Register_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *userClient) UserInfo(ctx context.Context, in *UserInfoRequest, opts ...grpc.CallOption) (*UserInfoResponse, error) {
out := new(UserInfoResponse)
err := c.cc.Invoke(ctx, User_UserInfo_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// UserServer is the server API for User service.
// All implementations must embed UnimplementedUserServer
// for forward compatibility
type UserServer interface {
Login(context.Context, *LoginRequest) (*LoginResponse, error)
Register(context.Context, *RegisterRequest) (*RegisterResponse, error)
UserInfo(context.Context, *UserInfoRequest) (*UserInfoResponse, error)
mustEmbedUnimplementedUserServer()
}
// UnimplementedUserServer must be embedded to have forward compatible implementations.
type UnimplementedUserServer struct {
}
func (UnimplementedUserServer) Login(context.Context, *LoginRequest) (*LoginResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Login not implemented")
}
func (UnimplementedUserServer) Register(context.Context, *RegisterRequest) (*RegisterResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Register not implemented")
}
func (UnimplementedUserServer) UserInfo(context.Context, *UserInfoRequest) (*UserInfoResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UserInfo not implemented")
}
func (UnimplementedUserServer) mustEmbedUnimplementedUserServer() {}
// UnsafeUserServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to UserServer will
// result in compilation errors.
type UnsafeUserServer interface {
mustEmbedUnimplementedUserServer()
}
func RegisterUserServer(s grpc.ServiceRegistrar, srv UserServer) {
s.RegisterService(&User_ServiceDesc, srv)
}
func _User_Login_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(LoginRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServer).Login(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: User_Login_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServer).Login(ctx, req.(*LoginRequest))
}
return interceptor(ctx, in, info, handler)
}
func _User_Register_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RegisterRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServer).Register(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: User_Register_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServer).Register(ctx, req.(*RegisterRequest))
}
return interceptor(ctx, in, info, handler)
}
func _User_UserInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UserInfoRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServer).UserInfo(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: User_UserInfo_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServer).UserInfo(ctx, req.(*UserInfoRequest))
}
return interceptor(ctx, in, info, handler)
}
// User_ServiceDesc is the grpc.ServiceDesc for User service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var User_ServiceDesc = grpc.ServiceDesc{
ServiceName: "user.User",
HandlerType: (*UserServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Login",
Handler: _User_Login_Handler,
},
{
MethodName: "Register",
Handler: _User_Register_Handler,
},
{
MethodName: "UserInfo",
Handler: _User_UserInfo_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "rpc/user.proto",
}
从上面的文件可以看出,grpc直接生成了client和server的两个接口,一一对应,当我们调用函数时,就是调用这里的函数,client不会出现调用.proto文件之外的接口,我们现在开始分析server 的login接口。
首先grpc的server会通过可选项模式,将UnaryServerInterceptor的函数数组,通过可选项模式传给
serverOptions的chainUnaryInts 属性
unaryInterceptorOption := grpc.ChainUnaryInterceptor(s.buildUnaryInterceptors()...)
func ChainUnaryInterceptor(interceptors ...UnaryServerInterceptor) ServerOption {
return newFuncServerOption(func(o *serverOptions) {
o.chainUnaryInts = append(o.chainUnaryInts, interceptors...)
})
}
然后我们new一个server,server := grpc.NewServer(options...)
进入源码
func NewServer(opt ...ServerOption) *Server {
opts := defaultServerOptions
for _, o := range globalServerOptions {
o.apply(&opts)
}
for _, o := range opt {
o.apply(&opts)
}
s := &Server{
lis: make(map[net.Listener]bool),
opts: opts,
conns: make(map[string]map[transport.ServerTransport]bool),
services: make(map[string]*serviceInfo),
quit: grpcsync.NewEvent(),
done: grpcsync.NewEvent(),
czData: new(channelzData),
}
chainUnaryServerInterceptors(s)
chainStreamServerInterceptors(s)
.
.
.
}
前面是通过可选项模式赋值,后面就进入chainUnaryServerInterceptors(s)
// chainUnaryServerInterceptors chains all unary server interceptors into one.
func chainUnaryServerInterceptors(s *Server) {
// Prepend opts.unaryInt to the chaining interceptors if it exists, since unaryInt will
// be executed before any other chained interceptors.
interceptors := s.opts.chainUnaryInts
if s.opts.unaryInt != nil {
interceptors = append([]UnaryServerInterceptor{s.opts.unaryInt}, s.opts.chainUnaryInts...)
}
var chainedInt UnaryServerInterceptor
if len(interceptors) == 0 {
chainedInt = nil
} else if len(interceptors) == 1 {
chainedInt = interceptors[0]
} else {
chainedInt = chainUnaryInterceptors(interceptors)
}
s.opts.unaryInt = chainedInt
}
func chainUnaryInterceptors(interceptors []UnaryServerInterceptor) UnaryServerInterceptor {
return func(ctx context.Context, req any, info *UnaryServerInfo, handler UnaryHandler) (any, error) {
return interceptors[0](ctx, req, info, getChainUnaryHandler(interceptors, 0, info, handler))
}
}
func getChainUnaryHandler(interceptors []UnaryServerInterceptor, curr int, info *UnaryServerInfo, finalHandler UnaryHandler) UnaryHandler {
if curr == len(interceptors)-1 {
return finalHandler
}
return func(ctx context.Context, req any) (any, error) {
return interceptors[curr+1](ctx, req, info, getChainUnaryHandler(interceptors, curr+1, info, finalHandler))
}
}
将UnaryServerInterceptor函数数组,进行递归调用,从0到n,其中的handler,就是我们前面生成的_User_Login_Handler函数,这个就是一个洋葱模型,和gin的模式差不多,gin是for递归,其实本质差不多,各位可以去试试,所以最终的s.opts.unaryInt = chainedInt,获取到了递归的一个函数的地址。
new完一个sever,然后就是注册了,user.RegisterUserServer(grpcServer, server.NewUserServer(ctx)),将自己实现的UserServer注册进来
type UserServer struct {
svcCtx *svc.ServiceContext
user.UnimplementedUserServer
}
func NewUserServer(svcCtx *svc.ServiceContext) *UserServer {
return &UserServer{
svcCtx: svcCtx,
}
}
func (s *UserServer) Login(ctx context.Context, in *user.LoginRequest) (*user.LoginResponse, error) {
l := logic.NewLoginLogic(ctx, s.svcCtx)
return l.Login(in)
}
func (s *UserServer) Register(ctx context.Context, in *user.RegisterRequest) (*user.RegisterResponse, error) {
l := logic.NewRegisterLogic(ctx, s.svcCtx)
return l.Register(in)
}
func (s *UserServer) UserInfo(ctx context.Context, in *user.UserInfoRequest) (*user.UserInfoResponse, error) {
l := logic.NewUserInfoLogic(ctx, s.svcCtx)
return l.UserInfo(in)
}
UserServer的实现要满足前面生成的接口
type UserServer interface {
Login(context.Context, *LoginRequest) (*LoginResponse, error)
Register(context.Context, *RegisterRequest) (*RegisterResponse, error)
UserInfo(context.Context, *UserInfoRequest) (*UserInfoResponse, error)
mustEmbedUnimplementedUserServer()
}
如果没实现,就会调用这个接口
func (UnimplementedUserServer) Login(context.Context, *LoginRequest) (*LoginResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Login not implemented")
}
所以mustEmbedUnimplementedUserServer()是满足必须实现了这个接口。
最后进入注册的源码实现
func RegisterUserServer(s grpc.ServiceRegistrar, srv UserServer) {
s.RegisterService(&User_ServiceDesc, srv)
}
var User_ServiceDesc = grpc.ServiceDesc{
ServiceName: "user.User",
HandlerType: (*UserServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Login",
Handler: _User_Login_Handler,
},
{
MethodName: "Register",
Handler: _User_Register_Handler,
},
{
MethodName: "UserInfo",
Handler: _User_UserInfo_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "rpc/user.proto",
}
然后继续进入
func (s *Server) RegisterService(sd *ServiceDesc, ss any) {
if ss != nil {
ht := reflect.TypeOf(sd.HandlerType).Elem()
st := reflect.TypeOf(ss)
if !st.Implements(ht) {
logger.Fatalf("grpc: Server.RegisterService found the handler of type %v that does not satisfy %v", st, ht)
}
}
s.register(sd, ss)
}
func (s *Server) register(sd *ServiceDesc, ss any) {
s.mu.Lock()
defer s.mu.Unlock()
s.printf("RegisterService(%q)", sd.ServiceName)
if s.serve {
logger.Fatalf("grpc: Server.RegisterService after Server.Serve for %q", sd.ServiceName)
}
if _, ok := s.services[sd.ServiceName]; ok {
logger.Fatalf("grpc: Server.RegisterService found duplicate service registration for %q", sd.ServiceName)
}
info := &serviceInfo{
serviceImpl: ss,
methods: make(map[string]*MethodDesc),
streams: make(map[string]*StreamDesc),
mdata: sd.Metadata,
}
for i := range sd.Methods {
d := &sd.Methods[i]
info.methods[d.MethodName] = d
}
for i := range sd.Streams {
d := &sd.Streams[i]
info.streams[d.StreamName] = d
}
s.services[sd.ServiceName] = info
}
首先是判断自己实现的UserServer是否全部是实现了UserServer的接口,然后将
// Server is a gRPC server to serve RPC requests.
type Server struct {
opts serverOptions
.
.
.
services map[string]*serviceInfo // service name -> service info
.
.
.
}
Methods: []grpc.MethodDesc{
{
MethodName: "Login",
Handler: _User_Login_Handler,
},
.
.
.
}
service全部加入到server的services的属性中,User_ServiceDesc的结构体的
ServiceName: "user.User",所以这里的key是"user.User",serviceInfo的methods的属性其中有一个的key是"Login",value是MethodDesc上面的图中的这个(包含生成好的 _User_Login_Handler的handler的函数指针)
注册成功了,就要启动服务,我们看是怎么开始调用的server.Serve(lis)
func (s *Server) Serve(lis net.Listener) error {
for {
rawConn, err := lis.Accept()
.
.
.
go func() {
s.handleRawConn(lis.Addr().String(), rawConn)
s.serveWG.Done()
}()
}
}
func (s *Server) handleRawConn(lisAddr string, rawConn net.Conn) {
if s.quit.HasFired() {
rawConn.Close()
return
}
rawConn.SetDeadline(time.Now().Add(s.opts.connectionTimeout))
// Finish handshaking (HTTP2)
st := s.newHTTP2Transport(rawConn)
rawConn.SetDeadline(time.Time{})
if st == nil {
return
}
if !s.addConn(lisAddr, st) {
return
}
go func() {
s.serveStreams(st)
s.removeConn(lisAddr, st)
}()
}
看到上面是一个conn,一个协程,启动http2的transport,然后调用serveStreams函数
func (s *Server) serveStreams(st transport.ServerTransport) {
defer st.Close(errors.New("finished serving streams for the server transport"))
var wg sync.WaitGroup
streamQuota := newHandlerQuota(s.opts.maxConcurrentStreams)
st.HandleStreams(func(stream *transport.Stream) {
wg.Add(1)
streamQuota.acquire()
f := func() {
defer streamQuota.release()
defer wg.Done()
s.handleStream(st, stream)
}
if s.opts.numServerWorkers > 0 {
select {
case s.serverWorkerChannel <- f:
return
default:
// If all stream workers are busy, fallback to the default code path.
}
}
go f()
})
wg.Wait()
}
func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Stream) {
.
.
.
sm := stream.Method()
if sm != "" && sm[0] == '/' {
sm = sm[1:]
}
pos := strings.LastIndex(sm, "/")
service := sm[:pos]
method := sm[pos+1:]
srv, knownService := s.services[service]
if knownService {
if md, ok := srv.methods[method]; ok {
s.processUnaryRPC(ctx, t, stream, srv, md, ti)
return
}
if sd, ok := srv.streams[method]; ok {
s.processStreamingRPC(ctx, t, stream, srv, sd, ti)
return
}
}
.
.
.
}
其中会调用handleStream函数,会解析出service的值是"user.User",method是"Login",然后在前面已经注册的srv.methods找到"Login"的*MethodDesc,然后处理processUnaryRPC函数。
提醒前面生成的文件的client为
User_Login_FullMethodName = "/user.User/Login"
func (c *userClient) Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error) {
out := new(LoginResponse)
err := c.cc.Invoke(ctx, User_Login_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
这里的server解析出分割的service和method是根据客户端传过来的User_Login_FullMethodName解析出来的。
最后
func (s *Server) processUnaryRPC(ctx context.Context, t transport.ServerTransport, stream *transport.Stream, info *serviceInfo, md *MethodDesc, trInfo *traceInfo) (err error) {
.
.
.
reply, appErr := md.Handler(info.serviceImpl, ctx, df, s.opts.unaryInt)
.
.
.
if err := s.sendResponse(ctx, t, stream, reply, cp, opts, comp); err != nil {
.
.
.
}
md.Handler就是_User_Login_Handler的值,info.serviceImpl的前面注册赋值的UserServer,df是序列化函数,s.opts.unaryInt是前面洋葱模型的首地址。
最后调用
func _User_Login_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(LoginRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServer).Login(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: User_Login_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServer).Login(ctx, req.(*LoginRequest))
}
return interceptor(ctx, in, info, handler)
}
其中srv.(UserServer).Login的函数传入handler,然后interceptor(ctx, in, info, handler)作为洋葱模型,将这些函数一层一层的调用,然后返回sendResponse。
grpc的server的主体实现逻辑还是很清晰简单,可能具体的难点是细节吧,由于本文内容已经很多了,go的rpc放到下章去讲。
最后,本人能力有限,如果上面的有纰漏和问题,欢迎大佬指出