大家好我是霜华!!!
我就问大家一句话
你们面对大学抢课的那天
你们是否想起了被请求超时支配的恐惧
很多朋友认为是服务器的问题
这可能是是一方面,但更多的可能代码设计的问题
分布式跟消息队列以及缓存在并发量稍微高一点点的项目里实在太重要了
而消息队列能极大的减少RT的时间(一次响应的时间)
我做项目时候上传一个带图片的文章就要花500ms这延迟实在是高的恐怖
来看看这套业务
这套简单的业务大概是花了500ms左右,延迟真的是非常高的
但其实我们把数据交给服务器就可以直接response回去,剩下的转码磁盘化让服务器慢慢整
我们可以开个线程一个个的去调用这些业务,但这真的非常繁琐
而且逻辑非常容易,一套流程光找bug就得很久,而且出异常了怎么办,一个个加try catch可以,但这玩意一旦当问题积累到一定程度,就可能P0级别的事物,你可以去人事领薪资早早回家了
这时候可以用一个队列数据结构将业务流程数据一包包放进去再根据顺序一个个拿出来执行
这样我调用一个人帮我做事后,我就直接给用户response 回去,让该流程在后台慢慢运转,有些甚至是可以同时做的看看这个电商流程:
而且通过这种队列,能够在请求量大的时候,减少短时间内打在服务器上的次数,保证服务器安全
这时候大家可能会想到redis的list队列
以redis 的list队列实现的中间件,能够一定程度上解决这种异步的问题
这时候我们运用消费者生产者模式,消费者监听生产者的push
并且可以支持多个生产者和消费者并发进出消息(就是我们的线程啦)
每个消费者拿到的都是不同元素
Redis 的消息队列没有非常多的高级特性没有ack保证,如果对消息的可靠性有着极高要求请移步RocketMQ和kafka,
异步消息队列:
Redis 的list列表数据结构常用来作为异步消息队列使用,用rpush 和lpush 操作入队列,用lpop 和rpop 操作出队列
可以支持多个生产者和多个消费者并发进出消息,每个消费者拿到的消息都是不同的列表元素
list可以支持多个生产者和多个消费者并发进出消息,每个消费者拿到都是不同的列表元素
技术是把双刃剑你解决了问题同时也会出现问题:
最基本的问题就是麻烦,原本按照流程做挺舒服现在还得照顾这个是否有安全问题。照顾那个是否会爆异常。
问题:
1.队列空了怎么办
原本客户端通过循环pop操作来获取信息,然后进行处理,处理完了再接着获取消息再进行处理,如此循环,这便是作为消息队列消费者客户端的生命周期。、
如果队列空留,就好pop死循环,拉高CPU消耗,和Redis的QPS(每秒查询率)
这时候加个判断如果pop出null 让线程睡一会
2.延迟高
阻塞读:
上面的解决有问题,如果睡了1s,那如果突然有新的数据呢,延迟就是1s
阻塞读blocking就是一个很好的解决方法
阻塞读在队列没有数据的时候,会立即进入休眠,一旦数据到来,立刻醒过来,延迟几乎为0,用blpop/brpop替代lpop/rpop真的很丝滑哦
3.空闲连接自动断开:
如果线程一直在睡着阻塞这,Redis客户端就形成闲置连接,闲置过久服务器主动断开这时候在存取就会出现问题
所以务必自己封装消费者加些异常处理机制
4.锁冲突处理:
客户端在拿到消息后处理请求时没加锁成功,安全起见这时候是不能执行
三种方法解决:
(1)直接抛出异常让用户重新发起请求
(2)让消费者sleep睡一觉,形成阻塞,但不适用队列很多的情况
(我们这忙的热火朝天,你在这睡觉?还占这茅坑?)
(3)延时队列 将当前队列扔到另一个队列延后处理,以避开冲突
注意:
你交给消息队列的消息一定是对当前response没影响的
其实redis呢已经帮我们封装好了队列需要的生产者和消费者我们一起来看看怎么用吧。
@Configuration
public class RedisConfig {
//这是一个Redis相关的配置类
private final static Logger logger=LoggerFactory.getLogger(RedisConfig.class);