twemproxy缓存集群如下:
1、半连接
产生的原因(twemproxy没有相关的机制去回收半连接):
a)应用非正常关闭产生的半连接,比如代码中jedis.quit()和jedis.diconnect()的关闭方式不对,用销毁jedispool的方式非正常关闭。
b)空闲的连接,对于lvs来说,默认设置是空闲15分钟的client到proxy连接做“reset”操作(这里的reset是三层的概念,正常是四层才会有reset,四层的底层下边就是会调用到3层的东西,这里相当于reset操作,会把client到proxy的空闲连接给reset掉,但proxy感知不到,proxy到client的半连接还存在),这里大量的空闲连接会导致半连接越堆积越多。
c)当然也有客户端异常,网络问题等等导致连接没正常关闭产生半连接。(曾经遇到过交换机升级导致应用连不上lvs,产生大量的半连接)
解决方案:
a)这里可以OS层次来设置keepalive(如果上面有其他业务,可能受影响,如果只是proxy和redis服务,可以这么设置)
b)我们用的方式是半夜凌晨crontab重启proxy的方式来释放(这种方式比较low,暴力)
c)最新版的twemproxy源码里是带这个keepalive参数回收半连接的,之前做过测试,没有效果,更改源码也没生效,可能姿势不对,后期有时间会做进一步的测试。(这种方式更温和)
2、pipeline的合理使用(大量的mbuf,导致OOM)
正常的多少次request对应有多少次的response,为了减少这种反复的创建连接然后释放,使用pipeline可以对多次的request一次response,提升数据返回的效率和避免反复创建释放连接的开销。但对于twemproxy的连接来说,每个连接都是需要mbuf的,client到proxy,proxy到后端的redis,都是需要mbuf的,默认最小的mbuf是16k,2倍的mbuf就是32k,而对于每个request都需要mbuf,这样一个大的pipeline需要吃很大的内存。对于mbuf的释放是需要连接释放了mbuf才释放,使用pipeline,对已经处理的request的mbuf不是立马能释放的,需要等这个pipeline的所有request操作一次性response返回后通道释放mbuf才能释放,这样如果业务大量的使用mbuf会导致twemproxy消耗的内存特别大,我们线上就出现过大量的pipeline把物理机的内存吃满发生OOM,OS选择当前物理机上消耗内存最大的进程然后自动kill掉,导致线上事故。综上所诉,pipeline能提高业务数据返回的速度和减少创建释放连接所需要的开销,但过多的使用pipeline会导致内存开销很大,而且pipeline会一次性会回很多包,回包也是要花时间的,需要网路传输,如果这台物理机部署多个redis集群实例,大量回包可能会把网卡带宽打满,对其他集群的实例也会造成影响,所以线上是不建议大量使用pipeline,但对于小的pipeline是可以用的,能提升性能,具体的使用可以对这个集群的网卡带宽流量、读写时间、qps、连接数等做监控,来合理的使用pipeline。
3、codis(对网络要求高、需要HA)
codis的集群架构和twemproxy是有很大区别的,codis将后端的realserver的分片信息都放在zookeeper中,会划分1024个slot,然后对于指定的分片实例来存取指定范围的slot,但是如果这个分片的实例挂了,这个时候通过proxy是无法把这个分片的key set到其他的分片上去,必须要一个备用的也是指定的这个slot区间才能代替这个分片,不然这个区间的slot的数据是get不到的,所以codis的realserver分片是需要有高可用的,而twemproxy的分片如果一个分片挂了,可以根据一致性hash,剔除分片并将key set到其他的分片上,对业务来说是不受影响的。(以上将的是这两种集群都是当做缓存来用,不提供持久化,持久化的数据存储在mysql里,如果发现缓存里的数据过期了,默认会读mysql,并把数据set到这个cache里)
codis的优势:
a)支持绝大多数redis命令,完全兼容twemproxy的所有命令(twemproxy会有很多redis的命令不支持)
b)支持原生的redis客户端(twemproxy不支持原生client,java用的客户端是jedis,php的redis方法是需要改造的,比如连接redis默认会使用select 0,这个地方需要去掉,twemproxy不支持select)
c)支持web端的集群管理监控页面(twemproxy不支持)
d)支持数据迁移,可以无缝的redis-port数据到twemproxy里
codis劣势:
a)如果只是当做缓存来用,不做持久化,数据可以丢的话,采用codis需要双倍的机器资源,这样太浪费,不需要分片的高可用
b)codis对网络的要求比较高,codis的分片集群配置信息都在zookeeper中,若果发生网络抖动,codis-proxy连不上zookeeper,重试几次认为zookeeper挂了,就会把自己给挂掉,当网络恢复了,重启codis-proxy,会向zookeeper里注册产品线信息,发现这个产品线已经存在,由于zookeeper里存的都是codis到zookeeper长连接的持久化注册集群配置信息,一旦连上重新注册发现存在codis就会把自己这个proxy给挂掉,服务起不来!这时候需要把zookeeper里的这个产品线信息清除掉重新注册才可以正常跑codis-porxy服务。这种网络异常人工干预codis清除zookeeper信息的方式不可取,会影响线上业务。
综合以上两种缓存使用方式,如果只是当做缓存用不用持久化,可以选择twemproxy这种方式,如果需要持久化这块选择codis更优。