trpc.group/trpc-go/trpc-go@v1.0.3/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  在客户端的连接池模式中,框架默认会设置一个 50 秒的空闲超时时间。
   132  
   133  * 对于 `go-net` 来说,连接池会维护一个空闲连接列表。空闲超时时间仅对列表中的空闲连接有效,并且只有在下一次尝试获取连接时,才会触发检查并关闭超时的空闲连接。
   134  * 对于 `tnet`,则是通过在每个连接上设置定时器来实现空闲超时。即便连接正在被用于客户端的调用,如果下游服务在空闲超时时间内没有返回结果,该连接仍然会因为空闲超时而被强制关闭。
   135  
   136  可以按照以下方式更改空闲超时时间:
   137  
   138  * `go-net`
   139  
   140  ```go
   141  import "trpc.group/trpc-go/trpc-go/pool/connpool"
   142  
   143  func init() {
   144  	connpool.DefaultConnectionPool = connpool.NewConnectionPool(
   145  		connpool.WithIdleTimeout(0), // 设置为 0 以禁用空闲超时
   146  	)
   147  }
   148  ```
   149  
   150  * `tnet`
   151  
   152  ```go
   153  import (
   154  	"trpc.group/trpc-go/trpc-go/pool/connpool"
   155  	tnettrans "trpc.group/trpc-go/trpc-go/transport/tnet"
   156  )
   157  
   158  func init() {
   159  	tnettrans.DefaultConnPool = connpool.NewConnectionPool(
   160  	      connpool.WithDialFunc(tnettrans.Dial),
   161  	      connpool.WithIdleTimeout(0), // 设置为 0 以禁用空闲超时
   162  	      connpool.WithHealthChecker(tnettrans.HealthChecker),
   163        )
   164  }
   165  ```
   166  
   167  **注**:服务端默认也设置了一个空闲超时时间,为 60 秒。这个时间比客户端的默认时间长,以确保在大多数情况下,是客户端主动触发空闲超时并关闭连接,而不是服务端强制进行清理。服务端空闲超时时间的修改方法,请参见服务端使用文档。
   168  ### 连接多路复用
   169  
   170  ```go
   171  opts := []client.Option{
   172  		client.WithNamespace("Development"),
   173  		client.WithServiceName("trpc.app.server.service"),
   174  		// 开启连接多路复用
   175  		client.WithMultiplexed(true),
   176  }
   177  
   178  clientProxy := pb.NewGreeterClientProxy(opts...)
   179  req := &pb.HelloRequest{
   180  	Msg: "hello",
   181  }
   182  
   183  rsp, err := clientProxy.SayHello(ctx, req)
   184  if err != nil {
   185  	log.Error(err.Error())
   186  	return 
   187  }
   188  
   189  log.Info("req:%v, rsp:%v, err:%v", req, rsp, err)
   190  ```
   191  
   192  设置自定义连接多路复用
   193  
   194  ```go
   195  /*
   196  type PoolOptions struct {
   197      connectNumber int  // 设置每个地址的连接数
   198      queueSize     int  // 设置每个连接请求队列长度
   199      dropFull      bool // 队列满是否丢弃
   200  }
   201  */
   202  // 连接多路复用参数可以通过 option 设置,具体请查看 trpc-go 的文档,需要设置成都是全局变量。
   203  var m = multiplexed.New(multiplexed.WithConnectNumber(16))
   204  
   205  opts := []client.Option{
   206  		client.WithNamespace("Development"),
   207  		client.WithServiceName("trpc.app.server.service"),
   208  		// 开启连接多路复用
   209  		client.WithMultiplexed(true),
   210  		client.WithMultiplexedPool(m),
   211  }
   212  
   213  clientProxy := pb.NewGreeterClientProxy(opts...)
   214  req := &pb.HelloRequest{
   215  	Msg: "hello",
   216  }
   217  
   218  rsp, err := clientProxy.SayHello(ctx, req)
   219  if err != nil {
   220  	log.Error(err.Error())
   221  	return 
   222  }
   223  
   224  log.Info("req:%v, rsp:%v, err:%v", req, rsp, err)
   225  ```