主要的实现代码如下。
/** * 放入延时队列 * @param queueMsg */ private void delay(QueueMsg queueMsg){ String msg = JSON.toJSONString(queueMsg); jedis.zadd(queueKey,System.currentTimeMillis()+5000,msg); } /** * 处理队列中从消息 */ private void lpop(){ while (!Thread.interrupted()){ // 从队列中取出,权重为0到当前时间的数据,并且数量只取一个 Set<String> strings = jedis.zrangeByScore(queueKey, 0, System.currentTimeMillis(), 0, 1); // 如果消息为空,就歇会儿再取。 if(strings.isEmpty()){ try { //休息一会儿 Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); break; } continue; } String next = strings.iterator().next(); // 如果抢到了消息 if(jedis.zrem(queueKey,next)>0){ // 反序列化后获取到消息 QueueMsg queueMsg = JSON.parseObject(next, QueueMsg.class); // 进行消息处理 handleMsg(queueMsg); } } } 订阅模式Redis的主题订阅模式,其实并不想过多总结,因为由于它本身的一些缺点,导致它的应用场景比较窄。
前面总结的用Redis的list实现的消息队列,虽然可以使用,但是并不支持消息多播的场景,即一个生产者,将消息放入到多个队列中,然后多个消费者进行消费。
这种消息多播的场景常用来做分布式系统中的解耦。用哦publish进行生产者发送消息,消费者使用subscribe进行获取消息。
例如:我向jimoerChannel发送了一条消息 b-tree
127.0.0.1:6379> publish jimoerChannel b-tree (integer) 1订阅这个渠道的消费者立马收到了一条b-tree的消息。
127.0.0.1:6379> subscribe jimoerChannel Reading messages... (press Ctrl-C to quit) 1) "subscribe" 2) "jimoerChannel" 3) (integer) 1 1) "message" 2) "jimoerChannel" 3) "b-tree"我前面也说到了,Redis的pub/sub订阅模式,其实最大的缺点就是,消息不能持久化,这样就导致,若是消费者挂了或是没有消费者,那么消息就会被直接丢弃。因为这个原因,所以导致他的使用场景比较少。
IO模型 Redis的过期策略Redis的过期策略是适用于所有数据结构的。数据一到过期时间就自动删除,Redis会将设置了过期时间的key 放置在一个字典表里。
定期删除Redis会定期遍历字典表里面数据来删除过期的Key。
Redis默认的定期删除策略是每秒进行10次过期扫描,即每100ms扫描一次。并不是扫描全部设置了过期时间的key,而是随机扫描20个key,删除掉已经过期的key,如果过期的比率超过25%,那么就继续进行扫描。
因为定期删除是随机抽取一些key来进行过期删除,所以如果key并没有被定期扫描到,那么过期的key就不会被删除。所以Redis还提供了惰性删除的策略,就是当去查询某些key的时候,若是key已经过期了,那么就会删除key,然后返回null。
另外一点当在集群条件下,主从同步情况中,主节点中的key过期后,会在aof中生成一条删除指令,然后同步到从节点,这样的从节点在接收到aof的删除指令后,删除掉从节点的key,因为主从同步的时候是异步的所以,短暂的会出现主节点已经没有数据了,但是从节点还存在。
但是若是定期删除也没有扫描到key,而且好长时间也没去去使用key,那么这部分过期的key就会一直占用的内存。
所以Redis又提供了内存淘汰机制。
当Redis的内存出现不足时,就会持续的和磁盘进行交互,这样就会导致Redis卡顿,效率降低等情况。这在线上是不允许发生的,所以Redis提供了配置参数 maxmemory 来限制内存超出期望大小。
当内存使用情况超过maxmemory的值时,Redis提供了以下几种策略,来让使用者通过配置决定该如何腾出内存空间来继续提供服务。
noeviction 不会继续提供写请求(del请求可以),读请求可以,写请求会报错,这样保证的数据不会丢失,但是业务不可用,这是默认的策略。
volatile-lru 会将设置了过期时间的key中,淘汰掉最近最少使用的key。没有设置过期时间的key不会被淘汰,保证了需要持久化的数据不丢。
volatile-ttl 尝试将设置了过期时间的key中,剩余生命周期越短,越容易被淘汰。
volatile-random 尝试将从设置了过期时间的key中,随机选择一些key进行淘汰。
allkeys-lru 从所有key中,淘汰掉最近最少使用的key。
allkeys-random 从所有key中,随机淘汰一部分key。