消息中间件

陌路散爱 提交于 2020-11-14 07:31:46

  • 消息中间件为我们带来了异步特性,为系统解耦,对于大型分布式系统具有非常重大的意义

  • 这样系统变得非常复杂
  • 引入可扩展配置算是比较优雅的解决方案
    • 降低了开发部署成本
    • 并没实质性降低复杂性

引入消息中间件解耦服务调用

  • 只需要向消息中间件发送消息
  • 其他服务订阅该消息
  • 成功解耦,不用关心多少系统需要知道登陆成功这个事件
  • 各个系统互不影响

  • 上面用数据库 记录中间状态,写入时数据库不可用,还是会有问题
  • 对于需要感知的应用,需要定时轮询查看状态,完成操作也要修改
  • 能解决问题,实现简单,但是也存在问题:
    • 增加了业务库的负担
    • 依赖的复杂和不安全(发短信的服务对数据库有操作权限,这样不安全)
    • 扩展性不好

互联网时代的消息中间件

  • JMS(java message service)是JavaEE(企业版)关于消息的规范
  • ActiveMQ 等产品是对该规范的实现
    • 企业内部或小型系统直接使用JMS产品是很经济的
    • 大型系统有一些场景不适合
  • 在大型互联网中,使用消息中间件,最基础俩特点
    • 应用之间解耦
    • 操作的异步
  • 重点考虑:
    • 消息的顺序保证
    • 扩展性
    • 可靠性
    • 业务操作与消息发送的一致性
    • 及多集群订阅者
  • 消息发送一致性
    • 产生消息的业务动作和消息发送的一致性
    • 上述两种做法,第一种丢失消息的概率是很低的,
    • 但是对于必须保证一致性的场景,上面的两种方案都不可取
  • JMS 能保证消息发送的一致性吗?
    • JMS 重要要素
    • JMS 模型分为:Queue模型(PTP Domain模型)和Topic模型(pub/sub Domain模型)
      • XA 开头的接口表示支持XA协议(分布式事务协议)
        • 引入分布式事务带来的问题:
          • 带来开销增加复杂性
          • 对业务操作有限制,业务操作资源必须支持XA协议
  • 最总一致性方案:

  • 定时重复反向流程,重复查询就可以了
    • 大多数情况下,反向流程是不需要工作的

  • 新方案开销:仅仅增加了一次网络通信、一次更新消息状态,开销并不大

  • 如何解决消息中间件和使用者之间的强依赖问题?
    • 思路有三:
      • 加强消息中间件可靠性,使之100%可靠
      • 消息中间件影响业务进行的部分增强可靠性
        • 业务表和消息表放在一个库,业务应用底层调用数据库同一个事务操作这俩表,保证一致性
        • 影响有三:
          • 业务库承载消息数据
          • 消息中间件去访问业务库
          • 业务操作的对象是一个数据库,支持事务的存储,满足消息的存储
        • 变通方案
          • 较多的逻辑从消息中间件的服务端挪到了消息中间件的客户端,并且在业务应用上执行
      • 提供弱依赖支持,能够较好的保证一致性
      • 利用本地磁盘方案
        • 如果消息中间件不可用,而且写入本地磁盘也坏了,消息就丢了
        • 两种用法:
          1. 容灾方案,平时不用,出现问题才使用
          2. 直接使用,可以控制调用发送消息接口的时间,比如做批处理
    • 业务应用和发送消息一致性带来的俩限制:
      • 需要确定要发送消息的内容
      • 需要实现对业务的检查(实现反向流程)

消息模型对消息接收的影响

  • JMS Queue 模型(点对点模型):
    • peer to peer 点对点
    • 消息发送出来不能确定会被谁消费,但只有一个应用回去消费这条消息
  • JMS Topic模型
    • 发送消息和topic内部逻辑与Queue 模型一样
    • 接收消息很不相同,每个应用都能接收到所有的消息
    • 每个连接connection 都有唯一clientId
    • 下图是多链接的情况:
      • 应用3有两个连接
    • 换做topic 模型
  • 模型需要满足的条件:
    • 一个进程可以有多个connection连接到消息Server
    • topic模型,实际应用中每个集群节点可能会很大,这个发送那么多重复数据负担太大
    • 整合这俩模型:
      • 面向集群用topic模型,具体应用使用queue模型来分发
        • 级联方案:

消息订阅者订阅消息的方式:

  • 持久订阅和非持久订阅
  • 要做到可靠,选择持久订阅
    • 接收者应用停止,消息保留,一旦上线再次发送

保证消息可靠性

  • 三个阶段都可靠,才能保证消息可靠:
    • 发送阶段要有明确的返回成功,才代表成功;
      • 失败、超时、异常都代表没成功
    • 消息存储(持久化存储)
        • 基于现有可以选择分布式文件系统、关系型数据库、NOSQL
      • 使用关系型数据库,不会那么严格按照范式,更多地使用冗余和宽表
        • 比如 学生id和学生名称,很多时候要一起展示
        • 单表查询比多表联查快的多,名称字段经常作为冗余字段存在
    • 消息中间件存储,一条消息存储一条数据
      • 表设计:
      • 单条消息订阅集群比较多时,更新投递次数频繁,把该字段放到消息表
        • 无法给投递列表里面的单独的接收者建立索引,损失了这个维度的灵活性
        • 堆积消息多的时候,不能针对特定集群调度,处理效率低
      • 基于双机内存保持数据的可靠
        • 内存速度远超磁盘,但是断电消失,可考虑双机内存
        • 一旦一台出问题,停止另一台的写操作,并把数据落盘
        • 这种适用于大部分消息到了消息中间件后能大部分很快消费掉
  • 消息中间件扩容
    • 消息中间件本身没有持久状态扩容简单
    • 让发送者接收者感知到,有新的消息中间件加入集群,可使用软负载配置
      • 不同消息中间件使用相同存储,同一个消息中间件使用多个存储、
      • 消息存储中加入ServerId 来记录消息来自哪台机器
      • 需要注意:
        • 如果某个中间件长时间不可用,考虑加入新机器对应他的ServerId
        • 没处理完的消息,分给别的机器处理
  • 消息存储扩容
    • 不存在复杂查询,服务端主动调度,绕开了根据消息id 取消息
  • 消息投递可靠性保证
    • 业务处理完再确认
    • 不要吃掉异常,再确认,这样出现异常消息就丢了
    • 投递处理优化
      • batch操作
      • 一个应用上多个订阅者订阅同一个消息

订阅者视角,消息重复的产生和应对

  • 消息发送端重复发送
    • 解决:相同消息使用同一个id
  • 消息中间件向外投递重复
    • 中间件不能及时更新已投递状态,可用分布式事务来解决,但是代价高
    • 也可要消息接受者来处理,进行幂等操作:
      • 幂等是一个数学概念
      • 多次重复操作和一次操作效果一样

JMS消息确认方式:

  • AUTO_ACKNOWLEDGE
    • 接收到自动确认
  • CLIENT_ACKNOWLEDGE
    • 调用acknowledge() 函数确认
  • DUPS_ACKNOWLEDGE
    • 客户端处理函数执行完再确认
  • connection 创建queue 或topic 时设置

消息投递其他属性:

  • 优先级
  • 订阅者消息处理顺序和分级订阅
  • 自定义属性
  • 局部顺序
    • 需要一个属性区分和哪些消息一起排队

保证顺序的消息队列的设计

  • 单机多队列
  • 变推(push)为拉(pull)
  • 多个物理队列

单机多队列问题与优化

  • 队列多,查询性能差
  • 根据队列做索引

  • 好处:
  • 坏处:
  • 克服坏处:
    • 作缓存
    • 由于是顺序读取,可采用预读策略
  • 本地消息存储可靠性:
    • 把单个消息中间件机器变成Master-slave形式
    • 发送消息向中间件,等slave复制完毕再返回”成功“
  • 队列扩容:

推(Push)和拉(Pull)对比:

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!