trpc.group/trpc-go/trpc-go@v1.0.3/docs/user_guide/reverse_proxy.zh_CN.md (about)

     1  [English](reverse_proxy.md) | 中文
     2  
     3  # tRPC-Go 反向代理
     4  
     5  ## 前言
     6  
     7  在某些特殊场景中,如反向代理转发服务,需要完全透传二进制 body 数据,而不进行序列化,和反序列化请求,和响应以提升转发性能,tRPC-Go 通过提供自定义序列化方式对这些场景也提供了支持。
     8  
     9  ## 实现
    10  
    11  ### 服务端透传
    12  
    13  服务端透传指的是,server 收到请求时直接把二进制 body 取出来交给 handle 处理函数,没有经过反序列化,回包时,也是直接把二进制 body 打包给上游,没有经过序列化。
    14  
    15  #### 自定义桩代码文件
    16  因为没有序列化与反序列化过程,也就是没有 pb 协议文件,所以需要用户自己编写服务桩代码和处理函数。
    17  关键点是使用`codec.Body`(或者实现 BytesBodyIn BytesBodyOut 接口,详情看 [这里](https://github.com/trpc-group/trpc-go/blob/ed918a35b8318d59afc4363d9a2a09bfcac75ab9/codec/serialization_noop.go#L26))来透传二进制,使用`通配符*`进行转发,并自己`执行 filter 拦截器`。
    18  
    19  ```go
    20  // AccessServer 处理函数接口
    21  type AccessServer interface {
    22      Forward(ctx context.Context, reqbody *codec.Body) (rspbody *codec.Body, err error)
    23  }
    24  
    25  // AccessServer_Forward_Handler 框架的消息处理回调函数
    26  func AccessServer_Forward_Handler(svr interface{}, ctx context.Context, f server.FilterFunc) (rspbody interface{}, err error) {
    27      req := &codec.Body{}
    28      filters, err := f(req)
    29      if err != nil {
    30          return nil, err
    31      }
    32      handleFunc := func(ctx context.Context, reqbody interface{}) (rspbody interface{}, err error) {
    33          return svr.(AccessServer).Forward(ctx, reqbody.(*codec.Body))
    34      }
    35      var rsp interface{}
    36      rsp, err = filters.Filter(ctx, req, handleFunc)
    37      if err != nil {
    38          return nil, err
    39      }
    40      return rsp, nil
    41  }
    42  
    43  // AccessServer_ServiceDesc 自定义服务描述信息,注意使用通配符*进行转发
    44  var AccessServer_ServiceDesc = server.ServiceDesc{ 
    45      ServiceName: "trpc.app.server.Access", 
    46      HandlerType: ((*AccessServer)(nil)), 
    47      Methods: []server.Method{ 
    48          server.Method{ 
    49              Name: "*", 
    50              Func: AccessServer_Forward_Handler, 
    51          }, 
    52      }, 
    53  } 
    54  
    55  // RegisterAccessService 注册服务
    56  func RegisterAccessService(s server.Service, svr AccessServer) { 
    57      s.Register(&AccessServer_ServiceDesc, svr) 
    58  } 
    59  ```
    60  
    61  #### 指定空序列化方式
    62  
    63  定义完桩代码以后,就可以实现处理函数并启动服务,关键点是 NewServer 时传入`WithCurrentSerializationType(codec.SerializationTypeNoop)`告诉框架当前消息只透传不序列化。
    64  
    65  ```go
    66  type AccessServerImpl struct{}
    67  
    68  // Forward 转发代理逻辑
    69  func (s *AccessServerImpl) Forward(ctx context.Context, reqbody *codec.Body) (rspbody *codec.Body, err error) {
    70      // 你自己的内部处理逻辑
    71  }
    72  
    73  func main() {
    74      s := trpc.NewServer(
    75          server.WithCurrentSerializationType(codec.SerializationTypeNoop),
    76      )  // 不序列化
    77      
    78      RegisterAccessService(s, &AccessServerImpl{})
    79  
    80      if err := s.Serve(); err != nil { 
    81          panic(err) 
    82      } 
    83  }
    84  ```
    85  
    86  ### 客户端透传
    87  
    88  客户端透传指的是,向下游发起 rpc 请求时直接把二进制 body 打包发出去,没有经过序列化,回包后,也是直接把二进制 body 返回,没有经过反序列化。
    89  
    90  #### 指定空序列化方式
    91  
    92  需要注意的是,虽然当前框架没有经过序列化,但是仍然需要告诉下游当前二进制已经通过什么序列化方式打包好了,因为下游需要通过这个序列化方式来解析,所以关键是要设置`WithSerializationType` `WithCurrentSerializationType`这两个 option。
    93  
    94  ```go
    95  ctx, msg := codec.WithCloneMessage(ctx) // 复制一个 ctx,生成 caller callee 等信息,方便框架监控上报
    96  msg.WithClientRPCName("/trpc.test.helloworld.Greeter/SayHello")  // 设置下游方法名
    97  msg.WithCalleeServiceName("trpc.test.helloworld.Greeter")  // 设置下游服务名
    98  callopts := []client.Option{
    99      client.WithProtocol("trpc"),
   100      client.WithSerializationType(codec.SerializationTypePB),          // 告诉下游当前 body 已经以 pb 序列化过了
   101      client.WithCurrentSerializationType(codec.SerializationTypeNoop), // 告诉框架当前 client 只透传不序列化
   102  }
   103  
   104  req := &codec.Body{Data: []byte("我是一个已经通过其他序列化方式打包好的二进制数据")}
   105  rsp := &codec.Body{}  // 回包后,框架会自动把二进制数据填充到这个 rsp.Data 里面
   106  err := client.DefaultClient.Invoke(ctx, req, rsp, callopts...) // req rsp 是用户自己已经序列化好的二进制数据
   107  if err != nil {
   108      return err
   109  }
   110  ```
   111  
   112  ## FAQ
   113  
   114  ### Q1:SerializationType 和 CurrentSerializationType 这两个 option 是什么意思,有什么区别
   115  
   116  框架通过提供 `SerializationType` 和 `CurrentSerializationType` 这两种概念来支持代理转发这种场景。
   117  SerializationType 主要用于网络调用的上下文传递,CurrentSerializationType 主要用于当前框架数据解析。
   118  `SerializationType`指的是 body 的原始序列化方式,正常情况都会在协议字段里面指定,tRPC 默认序列化类型是 pb。
   119  `CurrentSerializationType`指的是框架接收到数据时,真正用来执行序列化操作的方式,一般不用填,默认等于 SerializationType,当用户设置 CurrentSerializationType 时,则以用户设置为准,这样就可以允许用户自己设置任意的序列化方式,代理透传时指定 `NoopSerializationType` 即可。