trpc.group/trpc-go/trpc-go@v1.0.2/docs/user_guide/client/connection_mode.zh_CN.md (about)

     1  [English](connection_mode.md) | 中文
     2  
     3  # tRPC-Go 客户端连接模式
     4  
     5  
     6  # 前言
     7  
     8  目前 tRPC-Go client,也就是请求发起的一方支持多种连接模式,包括短连接,连接池以及连接多路复用。client 默认使用连接池模式,用户可以根据自己的需要选择不同的连接模式。
     9  `注意:这里的连接池指的是 tRPC-Go 自己实现的 transport 里面的连接池,database 以及 http 都是使用插件模式将 transport 替换成开源库,不是使用这里的连接池。`
    10  
    11  # 原理和实现
    12  
    13  ### 短连接
    14  
    15  client 每次请求都会新建一个连接,请求完成后连接会被销毁。请求量很大的情况下,服务的吞吐量会受到很大的影响,性能损耗也很大。
    16  
    17  使用场景:一次性请求或者请求的被调服务是老服务,不支持在一个连接上接受多个请求的情况下使用。
    18  
    19  ### 连接池
    20  
    21  client 针对每个下游 ip 都会维护一个连接池,每次请求先从名字服务获取一个 ip,根据 ip 获取对应连接池,再从连接池中获取一个连接,请求完成后连接会被放回连接池,在请求过程中,这个连接是独占的,不可复用的。连接池内的连接按照策略进行销毁和新建。一次调用绑定一个连接,当上下游规模很大的情况下,网络中存在的连接数以 MxN 的速度扩张,带来巨大的调度压力和计算开销。
    22  
    23  使用场景:基本所有的场景都可以使用。
    24  注意:因为连接池队列的策略是先进后出,如果后端是 vip 寻址方式,有可能会导致后端不同实例连接数不均衡。此时应该尽可能基于名字服务进行寻址。
    25  
    26  ### 连接多路复用
    27  
    28  client 在同一个连接上同时发送多个请求,每个请求通过序列号 ID 进行区分,client 与每个下游服务的节点都会建立一个长连接,默认所有的请求都是通过这个长连接来发送给服务端,需要服务端支持连接复用模式。IO 复用能够极大的减少服务之间的连接数量,但是由于 TCP 的头部阻塞,当同一个连接上并发的请求的数量过多时,会带来一定的延时(几毫秒级别),可以通过增加连接多路复用的连接数量(IO 复用默认一个 ip 建立两个连接)来一定程度上减轻这个问题。
    29  
    30  使用场景:对稳定性和吞吐量有极致要求的场景。需要服务端支持单连接异步并发处理,和通过序列号 ID 来区分请求的能力,对 server 能力和协议字段都有一定的要求。
    31  注意:
    32  
    33  - 因为连接多路复用对每个后端节点只会建立 1 个连接,如果后端是 vip 寻址方式(从 client 角度看只有一个实例),不可使用连接多路复用,必须使用连接池模式。
    34  - 被调 server(注意不是你当前这个服务,是被你调用的服务)必须支持连接多路复用,即在一个连接上对每个请求异步处理,多发多收,否则,client 这边会出现大量超时失败。
    35  
    36  # 示例
    37  
    38  ### 短连接
    39  
    40  ```go
    41  opts := []client.Option{
    42  		client.WithNamespace("Development"),
    43  		client.WithServiceName("trpc.app.server.service"),
    44  		// 禁用默认的连接池,则会采用短连接模式
    45  		client.WithDisableConnectionPool(),
    46  }
    47  
    48  clientProxy := pb.NewGreeterClientProxy(opts...)
    49  req := &pb.HelloRequest{
    50  	Msg: "hello",
    51  }
    52  
    53  rsp, err := clientProxy.SayHello(ctx, req)
    54  if err != nil {
    55  	log.Error(err.Error())
    56  	return 
    57  }
    58  
    59  log.Info("req:%v, rsp:%v, err:%v", req, rsp, err)
    60  ```
    61  
    62  ### 连接池
    63  
    64  ```go
    65  // 默认采用连接池模式,不需要任何配置
    66  opts := []client.Option{
    67  		client.WithNamespace("Development"),
    68  		client.WithServiceName("trpc.app.server.service"),
    69  }
    70  
    71  clientProxy := pb.NewGreeterClientProxy(opts...)
    72  req := &pb.HelloRequest{
    73  	Msg: "hello",
    74  }
    75  
    76  rsp, err := clientProxy.SayHello(ctx, req)
    77  if err != nil {
    78  	log.Error(err.Error())
    79  	return 
    80  }
    81  
    82  log.Info("req:%v, rsp:%v, err:%v", req, rsp, err)
    83  ```
    84  
    85  自定义连接池
    86  
    87  ```go
    88  import "trpc.group/trpc-go/trpc-go/pool/connpool"
    89  
    90  /*
    91  连接池参数
    92  type Options struct {
    93  	MinIdle             int			  	// 最小空闲连接数量,由连接池后台周期性补充,0 代表不做补充
    94  	MaxIdle             int           	// 最大空闲连接数量,0 代表不做限制,框架默认值 65535
    95  	MaxActive           int           	// 用户可用连接的最大并发数,0 代表不做限制
    96  	Wait                bool          	// 可用连接达到最大并发数时,是否等待,默认为 false, 不等待
    97  	IdleTimeout         time.Duration 	// 空闲连接超时时间,0 代表不做限制,框架默认值 50s
    98  	MaxConnLifetime     time.Duration 	// 连接的最大生命周期,0 代表不做限制
    99  	DialTimeout         time.Duration 	// 建立连接超时时间,框架默认值 200ms
   100  	ForceClose          bool          	// 用户使用连接后是否强制关闭,默认为 false, 放回连接池
   101  	PushIdleConnToTail  bool			// 放回连接池时的方式,默认为 false, 采用 LIFO 获取空闲连接
   102  }
   103  */
   104  
   105  // 连接池参数可以通过 option 设置,具体请查看 trpc-go 的文档,连接池需要设置成都是全局变量。
   106  var pool = connpool.NewConnectionPool(connpool.WithMaxIdle(65535))
   107  // 默认采用连接池模式,不需要任何配置
   108  opts := []client.Option{
   109  		client.WithNamespace("Development"),
   110  		client.WithServiceName("trpc.app.server.service"),
   111  		// 设置自定义连接池
   112  		client.WithPool(pool),
   113  }
   114  
   115  clientProxy := pb.NewGreeterClientProxy(opts...)
   116  req := &pb.HelloRequest{
   117  	Msg: "hello",
   118  }
   119  
   120  rsp, err := clientProxy.SayHello(ctx, req)
   121  if err != nil {
   122  	log.Error(err.Error())
   123  	return 
   124  }
   125  
   126  log.Info("req:%v, rsp:%v, err:%v", req, rsp, err)
   127  ```
   128  
   129  ###连接多路复用
   130  
   131  ```go
   132  opts := []client.Option{
   133  		client.WithNamespace("Development"),
   134  		client.WithServiceName("trpc.app.server.service"),
   135  		// 开启连接多路复用
   136  		client.WithMultiplexed(true),
   137  }
   138  
   139  clientProxy := pb.NewGreeterClientProxy(opts...)
   140  req := &pb.HelloRequest{
   141  	Msg: "hello",
   142  }
   143  
   144  rsp, err := clientProxy.SayHello(ctx, req)
   145  if err != nil {
   146  	log.Error(err.Error())
   147  	return 
   148  }
   149  
   150  log.Info("req:%v, rsp:%v, err:%v", req, rsp, err)
   151  ```
   152  
   153  设置自定义连接多路复用
   154  
   155  ```go
   156  /*
   157  type PoolOptions struct {
   158      connectNumber int  // 设置每个地址的连接数
   159      queueSize     int  // 设置每个连接请求队列长度
   160      dropFull      bool // 队列满是否丢弃
   161  }
   162  */
   163  // 连接多路复用参数可以通过 option 设置,具体请查看 trpc-go 的文档,需要设置成都是全局变量。
   164  var m = multiplexed.New(multiplexed.WithConnectNumber(16))
   165  
   166  opts := []client.Option{
   167  		client.WithNamespace("Development"),
   168  		client.WithServiceName("trpc.app.server.service"),
   169  		// 开启连接多路复用
   170  		client.WithMultiplexed(true),
   171  		client.WithMultiplexedPool(m),
   172  }
   173  
   174  clientProxy := pb.NewGreeterClientProxy(opts...)
   175  req := &pb.HelloRequest{
   176  	Msg: "hello",
   177  }
   178  
   179  rsp, err := clientProxy.SayHello(ctx, req)
   180  if err != nil {
   181  	log.Error(err.Error())
   182  	return 
   183  }
   184  
   185  log.Info("req:%v, rsp:%v, err:%v", req, rsp, err)
   186  ```