支持连接池的netty client核心功能实现剖析
本文为原创,转载请注明出处
源码地址:
https://github.com/zhangxianwu/light-netty-client
1、连接池
由于TCP连接的建立和关闭分别会经历三次握手和四次挥手,而三次握手和四次挥手都是系统开销很大的操作。如果每次一个新的请求发起时,都为其新建一个连接,在请求处理完毕后,再将这个新的连接关闭,这样处理的代价是高昂的,尤其是在请求本身的处理逻辑比较简单时,那么新建和关闭连接的开销在整个请求处理中占的比例就会越大。
因此,需要采用连接池,将连接缓存起来,以便后续的复用。
一个TCP连接可用一个四元组标示(源IP、源端口、目的IP、目的端口),当必须为一个新的请求建立连接后,服务端处理完该请求并通过该连接发送响应,客户端接收到响应后,将该连接还回到池中。
1.1 连接的存储
池采用ConcurrentHashMap<String, LinkedBlockingQueue<Channel>>存储连接:
Key:
目的IP + 目的端口(对于一个固定的客户端来说,它所处的源IP和源端口是不变的);
value:
如果只为每个目的IP和目的端口缓存一个连接,那么在高并发的场景或者请求本身处理比较耗时的情况下,请求获取连接的延时会比较严重,因此在当前请求从池里获取连接超时后,需要根据实际的需要新建连接,并将新建的连接存储下来,所以采用LinkedBlockingQueue<Channel>实现多个连接的存储。
然而每个连接的存储会有一定的内存开销,所以在高并发的场景下,不能无限制的创建和存储连接,需要做最大数量的限制。如果能够从连接创建的地方做到最大数量限制,那么最终缓存的连接数量也就实现了最大数量的限制(最大连接数的控制在后面分析)。
1.2 连接的入池和出池
a)连接何时入池:
往pipeline添加一个handler(取名为NettyChannelPoolHandler),并继承SimpleChannelInboundHandler,实现channelRead0方法,当服务端返回响应,并被客户端decode后,会发出channel