JMS (ActiveMQ)
JMS 2.1规范
消息头
- JMSDestination(required): 目的地, 是一个topic/queue. 系统名_topic/queue_事件(队列)名
- JMSMessageID(required) 消息id, 要以ID:开头, 一般自动生成即可.
- JMSDeliveryMode(required) 发送模式, NON_PERSISTENT/PERSISTENT
- JMSTimestamp(required) 发送时间(send()时间), 需打印该时间戳
- JMSExpiration(optional) 过期时间, ms, 发送时间+TTL, 默认永不过期
- JMSPriority(optional) 0-4为普通优先级, 5-9为高优先级
消息属性
pubSession.createTextMessage().setStringProperty("username", "xx");
消息体
- TextMessage: String
- MapMessage: Map<String, Object>
- ByteMessage: byte[]
- StreamMessage: java数据流
- ObjectMessage: 可序列化的object
AMQP (RabbitMQ)
AMQP 0.9.1标准 消息发给交换机, 交换机使用绑定规则将消息发到队列
- vhost
相似业务和耦合度强的业务分配到同意vhost中, 不要在
/下安排虚拟主机. - 代码规范
- 生产者和消费者使用长链接, connection and channel are meat to be long-lived. 避免每发消息都建立连接.
- 多线程时每个线程独占一个channel, 可考虑使用Channel Pooling, Spring AMQP已具备此功能. (多线程共享channel可能导致帧错误进而导致连接异常, broker异常关闭)
- 生产者和消费者不能共用channel.
- Clients should try to connect all cluster node.
- Client要设置心跳检测, 因为consumer无法感知服务器异常. 心跳时间为10-40s(可30s)
- 每个jvm process中channel<100个
- producer确认消息已发送至mq, 对失败数据处理, 具备重发机制
- consumer幂等性处理
- 对于消费者, 不同使用者实例使用不同的使用者标签, 不然可能导致自动连接恢复和监视数据错乱. 如果不确定, 请不要声明, 使其随机生成即可.
- 交换机设计原则
- 一个应用只监听一个队列, 尽量单一化
- 由发送方决定交换机, 发送的内容为"我做了什么", 而不是"我要对方做什么". 因为后者与接收方业务关联, 接收方业务变化, 交换机也要随之变化, 耦合太强, 复杂度上升.
- 交换机不发信息给其他交换机, 否则会使业务逻辑从代码向队列平台扩散, 可能形成死循环
- 路由器设计规则
- 如无必要, 不适用routing key, 增加排查难度
- fanout: 没有特殊情况尽量使用该模式, 其他需求在代码中实现
- direct: consumer在逻辑上分开不熟(如地域原因)
- topic: 需要模糊匹配
- 命名: 小数点隔开. 队列.事件.交换机.xx.xx
- 持久化
- queue持久化(durable: true):
channel.queueDeclare(QUEUE_NAME, true, false, false, null) - 消息持久化(deliveryMode:2):
channel.basicPublish("", QUEUE_NAME, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());
- queue持久化(durable: true):
- consumer尽量采用事件订阅机制, 非轮训(效率低)
对比
JSM vs AMQP
| -- JSM -- | -- AMQP -- |
|---|---|
| NO | 跨语言和平台 |
| peer2peer/pub-sub | direct/fanout/topic/head.. |
| 多种消息类型Text/Map/Byte.. | byte[] 复杂消息可序列化后发送 |