当前位置: 首页 > 后端技术 > Node.js

Redis消息队列攻略

时间:2023-04-03 16:13:06 Node.js

Redis消息队列在程序员圈耕耘了太多年。我见过太多的程序员使用redis,他们中的一些人喜欢将redis用作缓存。最典型的就是redis除了用来存储用户session之外,还用作消息队列,由此可见redis在互联网上的应用有多广泛。Redis用作消息队列。redis支持的数据结构可以支持这类业务,主要是利用list这种数据结构的特点。Redis列表相当于编程语言中的LinkedList。是双向列表结构,也就是说在列表中添加和删除元素的速度非常快,时间复杂度是O(1),但是在查找元素的时候需要遍历列表,时间复杂度为O(n)。由于列表的元素操作类似于消息队列的操作,redis可以应用于消息队列的场景,当然在适用栈的场景也能胜任。需要提醒的是,如果生产环境对消息的可靠性要求非常高(比如订单支付的消费消息),请使用专业的消息队列(比如:rmq、amq等)。),对消息的丢失有一定的容忍度,程序完全可以使用redis。比如我们的日志采集程序list数据结构的命令就是移出,获取list的第一个元素。如果列表中没有元素,则列表将被阻塞,直到等待超时或找到弹出元素。BLPOPkey1[key2]超时移出并获取列表的最后一个元素,如果列表中没有元素,则列表将被阻塞,直到等待超时或找到弹出元素。BRPOPkey1[key2]timeout从列表中弹出一个值,将弹出的元素插入另一个列表并返回它;如果列表没有元素,它将阻塞列表,直到等待超时或找到弹出元素。BRPOPLPUSHsourcedestinationtimeout通过索引获取列表中的元素LINDEXkeyindex在列表元素之前或之后插入元素LINSERTkeyBEFORE|AFTERpivotvalue获取列表的长度LLENkey移出并获取列表的第一个元素LPOPkey设置一个或多个InsertavalueintotheheadoflistLPUSHkeyvalue1[value2]InsertavalueintheheadinanexistinglistLPUSHXkeyvalue获取列表指定范围内的元素LRANGEkeystartstop从列表中移除元素LREMkeycountvaluesetthelistbyindex元素的值LSETkeyindexvalue修剪一个列表(trim),即让列表只保留指定范围内的元素,不在指定范围内的元素将被删除。LTRIMkeystartstop移除列表的最后一个元素,返回值为被移除的元素。RPOPkey移除列表的最后一个元素并将该元素添加到另一个列表并返回消息队列的问题仍然是消费者和生产者之间的问题。只要是这样的场景,就会牵扯到两端的失衡。具体可以表现为:生产者的生产速度大于消费者的消费速度,面临消息不断堆积的问题问题是,随着消息数据的堆积,队列是否应该启用限流措施,丢弃一些消息,或者持久化消息数据。对于基于redis的消息队列,一般是可以容忍部分消息丢失的业务,所以很多人选择丢弃消息。另一种方案是基于redis的单线程机制,可以增加消费者的数量,只针对消息只消费一次的场景。当然你也可以选择持久化的方案,但是会影响redis的性能。消费者的消费速度大于生产者的生产速度。有的同学会说,这样很好。是的,从某种意义上说,它比相反的情况要好。毕竟可以避免消息的堆积。但是consumer不消费消息,会导致consumer进程一直在浪费CPU资源,也会增加redis的QPS。类似这种死循环的场景,一般最常用的解决方案就是让线程休眠一小段时间,这样既减少了消费者CPU,也降低了redis的QPS。但是sleep有个问题,会导致处理消息的延迟。比如你睡一秒,消息的延迟处理可能会延迟一秒。虽然这在大多数场景下都不是问题,但是作为程序员的你怎么能呢?能不追求极致完美吗?关于消息延迟的问题,最暴力最简单的方法就是增加消费者客户端,这样可以通过多个消费者端的交错来减少延迟间隔。当然redis的设计者也考虑过这个问题。都有Blpop命令Redis的Blpop命令移出并获取列表的第一个元素。如果列表中没有元素,则列表将被阻塞,直到等待超时或找到弹出元素。redis127.0.0.1:6379>BLPOPLIST1LIST2..LISTNTIMEOUT还可以设置超时自动返回,岂不完美。但是顺带一提,redis连接空闲一段时间后,服务器可能会主动断开连接,blpop命令会抛出异常,最好有重试或者其他策略。作为一个专业的消息队列,需要支持一个消息被多个不同的业务消费(一个消息被多次消费),但是redis是基于自身的list数据结构实现的伪队列,所以这个业务场景不要考虑redis,或者自己封装一个类似于分发器的中间件。基于Redis的消息队列没有Ack保证。也就是说,Redis并不知道一条消息是否被正常处理,这在很大程度上限制了它的适用场景。最后还是建议专业的MQ使用不要用redis。毕竟MQ并不是redis的设计初衷,而是太多人把redis当作MQ来用,所以redis的作者基于redis的核心代码实现了一个消息队列:disque,说不定会是redis的核心组件以后地址是https://github.com/antirez/di...除了disque,RedisStream也是一个比较好的利用redis做MQ的方案,有兴趣的同学可以研究一下。任何业务场景都不要想得太简单更多精彩文章分布式并发系列架构设计系列趣味学习算法与数据结构系列设计模式系列

猜你喜欢