写在前面的话:学而不思则罔,思而不学则殆。回顾和总结应该是一种比较好的思考的方式,虽然在使用过程中也需要思考,但是那种思考有些碎片化。容易在长时间之后让人没有抓住总领和全局。所以,后面要开始试着慢一点,把所学所用慢慢记录下来。
1. 消息方案的作用和应用
异步解耦、削峰填谷。还可以利用消息的广播机制用来作为分布式缓存同步的实现方案,以及在大数据时代下,对流式数据的收集,从而提供下游进行大数据分析(kafka)
2. 主要关心的问题
『顺序消息』:消费严格遵守生产的顺序。tradeoff:顺序保障 vs 吞吐率、消息积压。rocketMQ有实现-可以保证局部严格有序;淘宝notify没有。作为业务系统的开发,在基于淘宝notify的情况下,在业务代码层实现了一个保证消息顺序的方案。
业务解决的一个栗子:
业务背景:消息接收量 万TPS级;DB访问量 10万TPS级。而不同的消息可能是需要操作同一个用户的账户,所以业务上是需要消费端按照生产端的产生时序进行处理的。
技术背景:notify没有顺序消息的功能,不过能够尽量按照消息产生的时序投递给消费者,但是在消息回弹的情况下,顺序性就丧失了。
解决思路是1.利用上游业务顺序信息(业务时间、index字段等)将同一个用户的多个消息分到一组,组内按照业务顺序串行执行,不同组之间并行执行的方式 2. 错峰延迟执行,消息基本检查后先落DB,消息任务的执行延迟。让消息接受和业务执行错峰,一方面减少锁冲突,从而减低db压力,另外保证大部分情况下消息已经落地。
『重复消息』:大部分消息中间件都是只做at least once,而不做exactly once的。首先,重复的出现可能在发送端,可能在消费端。因为网络通讯本身的不可靠性,总是会出现A->B的情况下,A不知道B是否收到的问题。所以保证exactly once很困难,本质上,如果重复被幂等拦截了,这个问题也就没那么可怕了。所以大部分消息中间件都要求业务代码实现幂等,而不是自己实现exactly once。
『事务消息』:消息机制初衷是用来异步解耦的,但是为了最终数据的一致性。在一些场景下,消息生产者是需要保证消息消费者收到了自己的消息的。如果让生产者感知到消费者是否收到,然后再根据接受结果进行后续处理,那么就丧失了异步解耦的目的了。此处就用到了2PC机制。消息预发送在生产者本地事务中,如果生产者自己的事情ok,那么就通知broker消息可以投递给消费者;否则不投递。最后一点,还是存在网络问题,生产者确认和回滚的通知还是有可能因为网络问题无法确认。所以在rocketMQ中就是broker来进行一个回查,需要生产者实现回查接口。(2PC+回查机制)
『不丢失消息』:三个地方可能丢消息,生产者发送时丢了;在broker端丢了;broker投递给消费者的过程丢了。传输过程中的丢失,可以通过ack机制解决;broker端,则需要考虑自己存储的方式,是否持久化。另外对于消费端,还要考虑自己没有正常消费时,是怎么应答broker的。
『消息堆积』:消费者来不及消费的消息堆积在broker中。这块除了需要消费者快速消费掉消息,主要的内容是broker需要考虑的。按下不表,后续再完善。
3. 常见的消息中间件
ActiveMQ
RabbitMQ-是AMQP协议的一种实现
RocketMQ-自定义协议:
http://jm.taobao.org/2017/01/12/rocketmq-quick-start-in-10-minutes/
http://rocketmq.apache.org/docs/quick-start/
淘宝notify-未开源
kafka
下一篇会分析以下实现对于关键问题的tradeoff和设计思路