应用场景
理论上任何需要消息队列的场景都可通过此方式实现,笔者的应用场景是一个服务完成一个任务需要经过两个不同的队列,任务经过了第一个队列后,需要重新配置入参放入第二个队列。完成第一个队列后的任务放入一个用redis List模拟的消息队列,由处理方法消费消息并处理后处理放入第二个队列。
实现方法
redis异步队列常见的实现方式主要有两种:
一,新消息rpush进入消息队列,每次lpop消息队列,如果没有信息则sleep一段时间然后再次lpop查询(如果没有间隔一直lpop会阻塞其他命令的执行,或直接搞崩redis服务,因为redis是单线程的)这里sleep可以通过spring的@Scheduled的cron定时任务实现,每隔几秒请求一次查看是否有可消费信息。
二,新消息rpush进入消息队列,blpop查看是否有可消费信息,blpop会阻塞队列,可以设置时间,0代表无限时。该命令会一直阻塞队列直到有新的值进入该队列,执行lpop并返回。注:虽然是阻塞,blpop并不影响其他redis命令执行,只不过只有lpop能返回东西时才被执行。具体可查看blpop的实现原理。
注:@Scheduled定时任务如果上一次方法没有执行完,后面时间顺延,不会重复执行。具体测试
个人认为第二种方法更好,因为第一种方法会每隔几秒执行一次lpop,仍然会消耗比第二种更多的资源,而且实现上第二种更加简便。
为了防止lpop执行消息被消费后服务器突然挂掉导致该任务丢失的情况,可以采用这种方式避免:每次lpop后设置一个几十秒过期的键存储被消费的值,监听过期,如果过期则重新把该消息lpush进队列;在消息消费完成操作后再将此临时键删除,表示确认该消息成功消费。
注:要尽量保证lpop消费信息和设置临时键两部操作要不同时发生,要不都不发生,不然可能消费后还没有设置临时键就因为服务器崩掉之类的问题丢失消息了。