5.1 消息中间件的价值
消息中间件是大型分布式系统衍生出来的,为了解决一个系统会调用下游很多个服务,任何一个下游的修改都需要上游这个服务的配合修改,这样会把这个系统做的无比复杂,用用户登陆的case 来说明下:
上图中一个用户登陆系统会调用N个系统,每次这个登陆系统需要新加一个功能模块就需要修改这个登陆系统来适配;这样会导致登陆系统代码很复杂,包含了各种功能模块,彼此之间的耦合关系也很紧密;对登录系统来说维护成本高、稳定性问题也会大大提升;所以这时可以采取消息中间件的方式来实现系统的解耦合。
用户登陆系统只需要在登陆成功后发送一条消息给消息中间件,无需调用任何下游系统;下游系统需要对登录系统进行处理时,直接订阅消息中间件,消息中间件会把消息直接发送给系统即可,至于下游系统怎么处理消息、处理是否成功就是他们的事情了。 -------------> 异步
- 所以消息中间件的价值:就是异步、解耦合、简单化分布式系统
- 减轻业务和数据库的负担,业务只需要最简单的事情(登录系统只做登录有关的事情)
- 系统解耦合、减轻了系统的依赖
- 扩展性好:随时可以加业务方,对登录系统透明,直接订阅消息即可
5.2 互联网时代的消息中间件
在互联网时代我们采用消息中间件可以实现系统的解耦和异步,这时分布式系统的两个非常重要的特点,也是最基本的要求;除此之外消息中间件需要关注的特点有下面几个:
什么是消息发送一致性:就是业务操作的动作跟发送消息的成功是一致的,比如业务操作成功了,消息就必须成功;业务操作失败了消息就一定失败;听起来很简单的事情,但在极端的情况下, 还是有一定难度的:
- 业务操作成功了,但这时这个应用crash了,导致消息发送失败
- 业务操作成功了,消息也发送成功了,但是消息中间件stop了,这时消息也丢了
解决方案: 通过事务的思路来解决,但同时也增加了维护信息,增加了系统的复杂性
5.2.2 消息模型对消息接收的影响
前面我们讲了消息发送端的形式,下面我们来看下消息模型;JMS 有2中模型:Queue 模型和Topic 模型
- JMS Queue 模型
如下图所示,应用1 应用2 把消息发送给消息中间件,所有的消息会组成一个消息的队列;应用3 应用4会读取Queue 的队列消息,一旦一个消息被消费,那另外一个消息就不能消费了;因为消息都已经没有了;所以Queue 模式也被称作是peer to peer (就是我们常说的点到点,端到端)
- JMS Topic 模型
如下图JMS Topic 模型所示,从消息的发送和中间件的处理消息Topic 模型跟Queue 模型是一致的,但Topic 模型所有的应用都可以收到所有的消息(当然可以订阅自己需要的那部分);这样的模型更加适合业务的发展;除非消息都是只有一个消费体 【这个模式有叫做:pub/sub 发布、订阅模式】 典型的生产者--消费者的架构
分析了JMS 的Queue模型和Topic 模型,我来看看我们需要什么样的消息模型,先分析下我们的业务场景需求:
- 消息的发送和接收都是集群
- 同一个消息的接收方可能是多个集群进行处理
- 不同的集群对同一消息处理不能相互干扰
如果满足上面的需求,Queue模型和Topic 模型都不能使用,需要结合2者的优点才可以满足;最简单的方案就是Queue模型和Topic 模型联级使用;
这周方式相对比较复杂,可以简化为:
5.2.3 消息订阅的方式
消息的订阅方式分为2种:持久订阅和非持久订阅
- 非持久订阅:当订阅消息的应用启动时就会自动建立订阅,会接收消息中间件发送过来的信息;但一旦应用服务异常,则在异常的时间段内消息发送失败后不会保存。
- 持久订阅:需要应用方人工显示订阅,一旦订阅消息后消息会一直保存,即使应用放异常,在服务恢复正常后也会从滞留的消息开始追
5.2.4 保证消息的可靠性
应用A 通过中间件发送给应用B 中间需要3个环节,应用A 把消息发送给消息中间件,消息中间件完成了消息的存储,消息中间件把消息发送给订阅的集群(应用B);我们从这3个方面来保证消息的可靠性
- 消息发送端的可靠性
这一步是消息的起点,需要消息中间件返回一个状态,明确告诉应用方已经发送成功或者失败,这样应用方才会做相应的处理;比如如果消息发送失败,会进行消息放松的重试;并且维护消息的状态(发送成功、待处理、已经处理、处理成功、处理失败)
- 消息存储的可靠性
消息中间件收到消息后肯定要把消息进行存储,因为消息要保存和容错的要求,必须是持久化存储,这就要存储到磁盘上;这时就要针对存储进行选型;主要是有下面三种的存储类型:一种是自己写一个存储引擎(比如写一个mysql 这个成功太大) 一种是选择分布式文件系统(分布式db) 一种是采用nosql(hbase mongdb nosql) ;比较建议采用nosql 数据,比较简单、容易扩展;但最常见的还是mysql 进行存储; 这时我们就要把消息存储到mysql :
- 消息的header 部分
主要包括消息的基本信息:消息的id、创建时间、投递次数、优先级等属性
- 消息的body部分
消息的数据,登录用户的信息
- 消息的投递对象(如果是订阅的话就没有,提供一个web 工具,让消费者自己按照规则进行订阅)
这个消息要投递的目的应用(集群的id clusterid 不同的应用保证clusterid 是唯一的)
- 消息投递端的可靠性
这个还是依赖业务的响应,只有投递的接收方收到消息并回复后才可以确定消息已经投递成功,可以删除了
5.2.5 消息的其他特性
- 消息的优先级,可以对消息的优先级进行处理;比如消息优先级高的先投递、消息的优先级低的后投递(方式有很多种)
- 消息的顺序问题
- 消息投递的方式(push /pull 推还是拉2种方式)