tpn(taobao push
notification)在使用redis计算消息未读数的过程中,遇到了一系列的问题,下面把这个过程整理了一下,也让大家了解这个纠结的过程,供大家以后使用redis或者做类似的功能时进行参考
redis在tpn里面主要是用于计算移动千牛(Android、IOS)上的消息未读数。tpn的未读消息数是基于bizId维度的,即同一个bizId(每条消息的业务id,如果商品id、订单id等),即使有多条消息,未读数也只能算1。因此在接收消息,计算移动千牛未读数的过程中,就需要对bizId去重,这个去重的功能就是通过redis来实现的。随着消息量的不断上涨,这个基于redis的去重方案也不断变化。
一、基于redis
Set结构的未读数计算
前面说到的tpn未读数计算的最大特点就是基于bizId去重,在java里面,我们很容易想到利用HashMap或者HashSet来判重,因此最初tpn就是利用redis的Set结构来进行判重。主要利用了redis
set结构的这两个命令:SADD和SCARD
SADD key
member [member....]:将一个或多个 member 元素加入到集合 key
当中,已经存在于集合的 member 元素将被忽略。假如 key 不存在,则创建一个只包含 member 元素作成员的集合。
如果member元素不在集合里面,则返回1;如果member元素已经存在于集合当中,则返回0。
SCARD
key:返回集合 key 中元素的数量。
有了这两个命令,计算未读数的步骤就是这样的:
tpn会为用户保留7天内的消息,也就是说保存到redis
set结构中的bizId失效时间是7天,同时用户在查看消息后,就会把其对应的redis
set清空(即如果一个用户连续几天都不查看千牛的消息,那么其对应的redis
set集合里面就会保存大量的bizid)。tpn总共有6台redis机器,每台机器上部署5个redis实例,每个实例的maxmemory设为1G,总共30G的内存用于存放消息bizId。在tpn的早期,由于用户量不多,消息量也不大,redis的内存完全可以存放7天内的所有消息bizId,因此这个方案work的很好。但随着全网大多数活跃卖家开始使用千牛,tpn的消息量也随之暴涨,越来越多的消息bizId给redis带来了极大的压力,在消息高峰期,tpn的日志里会有大量的redis
timeout异常(tpn使用jedis,配置的timeout是300ms),经过分析,主要是由下面原因造成的:
缓存失效造成的超时:前面我们提到了,tpn的每个redis实例的max