1.背景介绍
服务和服务之间的连接是开发过程中很常见的操作,为了服务解耦,减少相互依赖,增强系统稳定性,灵活性,所以会增加许许多多的服务通信链路,随着服务通信链路的增加,网络通信次数就会成倍的增长,那么随之而来的就是网络资源的消耗加剧,例如:带宽,连接数以及cpu,内存等,那么怎么在保证系统灵活稳定的前提下,保证服务间通信尽可能的高效低耗是我们本片文字要解决的核心问题。
2.问题分析
在我们服务中,有http服务,有rpc服务,这些服务大致可以分为两类做拆分,一类是同步服务,另外一类是异步服务。
1.异步服务
当我们将一个服务通过消息中间件(例如kafka)解耦的时候,那么一个服务会变成多个异步服务,服务和服务之间是依靠kafka传输数据,服务和kafka之间是tcp连接,如果我们大量使用tcp连接和kafka通信,那么随着请求数的增加,业务服务和kafka的压力都会倍增,系统稳定性根本无法谈起。
2.同步服务
当我们想把一个同步服务会变成多个同步服务,服务和服务之间是依靠tcp传输数据,也会存在大量使用tcp连接通信的情况,随着请求数的增加,业务服务的压力会倍增,系统稳定性也无法保证。
3.连接解读
那么从连接的本质去研究探索解决方案:
一、每个连接建立时都会申请内存用来做socket buffer
二、每个连接都要做三次握手四次挥手
三、每个连接关闭时都要释放内存空间
四、并发高时,会产生大量的连接,影响系统调度,会占用太多系统资源
4.突破点
通过问题分析中的连接解读,可以很容易找到几个突破点:
buffer复用
- 减少握手和挥手次数
- 减少连接关闭
- 减少连接数量
5.连接池的功能点
通过以上几个突破点,我们可以开发个连接池,具备以下功能:
保持连接数固定,可以弹性调整
- 延长连接的生命周期
- 连接可以被多次复用
3.连接池(connPool)的应用
创建连接池
connPool = connPool.NewConnPool(&connPool.Options{Dialer: dial, //dial拨号方法PoolSize: 200,//连接池的最大连接数PoolTimeout: time.Second * 250,//等待连接分配的超时时间IdleTimeout: time.Second * 100,//每个连接的存活超时时间IdleCheckFrequency: time.Millisecond * 500, //检测连接存活频率})func dial() (conn net.Conn, err error) {//拨号方法...conn, err = net.DialTimeout(...)...}
连接获取和回收
func release() {conn, _, err = connPool.GetConn(pool)//从连接池里获取连接..._, e := conn.ReleaseConn(pool, err, isBadConn)//释放连接到连接池...}()func isBadConn(err error) bool {//判断连接是否失效if _, ok := err.(*net.OpError); ok {return true}return false}