Java 消息服务(2th)
Executive Summary
核心观点(金字塔原理)
结论先行: JMS(Java消息服务)是企业级消息传送的标准API,通过点对点和发布/订阅两种消息模型,实现异构系统集成、系统解耦和高可伸缩性。
支撑论点:
- 消息传送模型:点对点(P2P)用于一对一通信支持负载均衡,发布/订阅(Pub/Sub)用于一对多广播解耦能力更强
- 核心API组件:ConnectionFactory、Destination、Connection、Session、Message、MessageProducer、MessageConsumer共7个核心接口
- 架构模式支持:支持SOA(面向服务体系结构)和EDA(事件驱动体系结构)等企业级架构模式
SWOT 分析
| 维度 | 分析 |
|---|---|
| S 优势 | 标准化API易于学习;支持异构系统集成;消息持久化保存并转发机制;灵活的消息传送模型选择 |
| W 劣势 | Java平台限定;相比直接RPC有额外复杂度;需要消息中间件支持 |
| O 机会 | 企业应用集成;异构平台对接;地理分散系统通信;构建动态可扩展系统 |
| T 威胁 | 新兴消息技术(如Kafka)的竞争;微服务架构下的技术选型多样化 |
适用场景
- 企业级Java应用的异步消息通信
- 需要系统解耦和缓解瓶颈的分布式架构
- 一对多信息广播和发布订阅场景
第1章 消息传送机制基础
- 异构集成
- 缓解系统瓶颈
- 提高可伸缩性,现实是:能将中间件扩展到
- 提高最终用户生产率
- 体系结构灵活性和敏捷性
- 企业消息传递:消息是通过网络从一个系统异步传送给其他系统的。
- 集中式体系结构,星型拓扑结构
- 分散式体系结构
- 混合体系结构
- 消息传送模型:点对点模型和发布/订阅模型,又叫消息传送域,p2p Pub/Sub
- 发布/订阅用于一对多。点对点用于一对一。
- 点对点模型:特点,发送到队列的消息被一个而且仅仅一个接受者所接收,即使可能 有 多个接受者在一个队列中侦听同一消息时,也是如此。支持负载均衡,运行多个接收者侦听同一队列,并以此来分配负载。
- 发送/订阅模型:消息被发布到一个名为主题的虚拟通道中,消息生产者称为发布者,消费者称为订阅者。与点对点不同,一个主题可以有多个订阅者接收。有时也称为广播消息。解耦能力更强。
- JMS API 共7个: ConnectionFactory,Destination,Connection,Session,Message,MessageProducer,MessageConsumer;
- 面向服务体系结构(SOA)
- 事件驱动体系结构(EDA)
- 异构平台集成
- 企业应用基础
- 企业到企业
- 地理分散
- 信息广播
- 构建动态系统
- RPC和异步消息传送
- 消息的持久化,保存并转发机制
第2章 编写一个简单的示例程序
- 通过JNDI(Java命名和目录接口)查找ConnectionFactory和Destination(Queue或Topic),这是JMS客户端获取管理对象的标准方式
- 使用ConnectionFactory创建Connection,再通过Connection创建Session,Session是生产和消费消息的单线程上下文
- 通过Session创建MessageProducer和MessageConsumer,分别用于发送和接收消息
- 发送和接收TextMessage的基本流程:生产者调用send()方法发送消息,消费者通过receive()同步接收或设置MessageListener异步接收
- 书中以一个简单的聊天(Chat)应用程序为例,演示了发布/订阅模型下的完整JMS编程步骤
第3章 深入剖析一条JMS消息
- JMS消息由三部分组成:消息头(Header)、消息属性(Properties)和消息体(Body),三者共同构成完整的消息结构
- JMS定义了五种消息体类型:TextMessage(文本)、MapMessage(键值对)、BytesMessage(字节流)、StreamMessage(Java原始类型流)和ObjectMessage(可序列化对象)
- 消息头包含多个标准字段:JMSDestination(目标地址)、JMSDeliveryMode(持久/非持久)、JMSMessageID(唯一标识)、JMSTimestamp(发送时间戳)、JMSExpiration(过期时间)、JMSPriority(优先级0-9)等
- 消息属性分为自定义属性、JMS定义属性和提供者特定属性,可用于消息过滤和附加元数据,通过setStringProperty/setIntProperty等方法设置
- 消息确认机制确保消息被成功接收,包括AUTO_ACKNOWLEDGE(自动确认)、CLIENT_ACKNOWLEDGE(客户端手动确认)和DUPS_OK_ACKNOWLEDGE(允许重复的延迟确认)三种模式
第4章 点对点消息传送模型
- Queue(队列)是点对点模型的核心概念,每条消息只能被一个消费者接收和处理,实现一对一的可靠通信
- 使用QueueSender发送消息到队列,QueueReceiver从队列接收消息;多个消费者可以监听同一队列,实现消息的负载均衡分发
- 队列中的消息具有持久性,即使没有消费者在线,消息也会被保存在队列中,直到被消费或过期
- QueueBrowser允许在不消费消息的情况下浏览队列中的消息,可用于监控队列深度和消息内容
- 通过临时队列(TemporaryQueue)可以实现请求-回复模式:发送方创建临时队列作为回复地址设置到JMSReplyTo头中,接收方处理后将响应发送到该临时队列
第5章 发布/订阅消息传送模型
- Topic(主题)是发布/订阅模型的核心概念,消息发布到主题后会被分发给所有活跃的订阅者,实现一对多的广播通信
- TopicPublisher用于将消息发布到主题,TopicSubscriber用于订阅主题并接收消息;发布者和订阅者之间完全解耦
- 持久订阅(Durable Subscription)与非持久订阅(Non-Durable Subscription)的区别:持久订阅者离线期间发布的消息会被保留,重新上线后可以接收;非持久订阅者只能接收在线期间的消息
- JMS 2.0引入了共享订阅(Shared Subscription),允许多个消费者共享同一个订阅,从而在发布/订阅模型中也能实现负载均衡
- 消息选择器(Message Selector)可与订阅结合使用,订阅者只接收满足过滤条件的消息,实现更精细的消息路由
第6章 消息过滤
- 消息选择器(Message Selector)基于SQL92条件表达式的子集,在消费者端声明过滤条件,由JMS提供者在服务端执行过滤
- 选择器语法支持比较运算符(=、<>、>、<)、逻辑运算符(AND、OR、NOT)、BETWEEN、IN、LIKE和IS NULL等操作,例如:
"price > 100 AND category = 'electronics'" - 选择器可以基于消息头字段(如JMSType、JMSPriority、JMSCorrelationID)和自定义属性进行过滤,但不能基于消息体内容过滤
- 消息选择器会带来性能影响:复杂的选择器表达式会增加消息代理的计算开销,在高吞吐量场景下应权衡使用选择器与使用多个目标地址的方案
第7章 保证消息传送和事务
- 消息确认模式决定了消息何时被视为成功消费:AUTO_ACKNOWLEDGE(自动确认)、CLIENT_ACKNOWLEDGE(客户端调用message.acknowledge()手动确认)、DUPS_OK_ACKNOWLEDGE(延迟批量确认,允许重复投递以提高性能)
- 消息持久化模式分为PERSISTENT(持久化,消息写入存储后再投递,保证不丢失)和NON_PERSISTENT(非持久化,性能更高但可能在故障时丢失消息)
- 消息优先级分为0-9共10个级别(0-4为普通优先级,5-9为加急优先级),消息过期时间(TTL)控制消息在目标中的生存时间,过期消息自动丢弃
- JMS本地事务通过Session实现,调用session.commit()提交事务中所有发送和接收的消息,调用session.rollback()回滚所有操作
- 分布式事务通过JTA(Java Transaction API)和XA协议实现,XAConnectionFactory和XASession允许JMS操作与数据库操作纳入同一个全局事务,保证跨资源的数据一致性
第8章 Java EE 和消息驱动 bean
- 消息驱动Bean(MDB, Message-Driven Bean)是Java EE中专门用于异步处理JMS消息的企业级组件,是EJB规范的一部分
- 使用@MessageDriven注解声明MDB,并通过activationConfig属性配置目标类型(Queue/Topic)和目标名称等参数
- MDB实现javax.jms.MessageListener接口,通过onMessage()回调方法接收和处理消息,每条消息触发一次调用
- EJB容器自动管理MDB的生命周期、线程池和并发处理,支持容器管理事务(CMT)和Bean管理事务(BMT),开发者无需关注底层线程安全问题
- MDB可以与其他Java EE组件(如EJB、JPA、CDI)无缝集成,通过依赖注入使用数据源、其他EJB服务等企业级资源
第9章 Spring和JMS
- JmsTemplate是Spring对JMS API的核心封装,简化了发送消息的代码:自动管理连接、会话的创建和关闭,通过convertAndSend()方法可以一行代码完成消息发送
- @JmsListener注解用于声明消息监听方法,只需在方法上标注即可自动接收指定目标的消息,支持方法参数的自动类型转换
- DefaultMessageListenerContainer是Spring提供的消息监听容器,管理消费者的生命周期和并发线程数,支持动态伸缩和优雅关闭
- Spring Boot通过自动配置(Auto-Configuration)极大简化了JMS的使用,只需在application.properties中配置broker地址,即可自动创建ConnectionFactory和JmsTemplate等Bean
- Spring的事务管理与JMS无缝集成,支持通过@Transactional注解将JMS操作与数据库操作纳入同一个本地事务或JTA全局事务
第10章 部署注意事项
- 集群部署实现高可用性(HA):通过主从(Master-Slave)或共享存储等方式部署多个Broker实例,当主节点故障时自动切换到备用节点,保证消息服务不中断
- 代理网络(Network of Brokers)将多个Broker互联,支持跨节点的消息转发和负载分摊,适用于地理分散的大规模部署场景
- 嵌入式Broker与独立部署Broker的选择:嵌入式适合测试和简单场景(如ActiveMQ Embedded),独立部署适合生产环境,便于运维和资源隔离
- 安全配置包括认证(Authentication)和授权(Authorization)两个层面:认证验证客户端身份(用户名/密码、证书等),授权控制客户端对特定目标的读写权限
- 监控和管理通过JMX(Java管理扩展)暴露Broker和目标的运行指标(队列深度、消息吞吐量、消费者数量等),结合管理控制台实现运维可视化
第11章 消息传送设计注意事项
- 请求-回复模式(Request-Reply):通过JMSReplyTo和JMSCorrelationID实现同步风格的通信,发送方等待回复消息以获取处理结果
- 消息版本控制:随着系统演进消息格式可能变化,应在消息中包含版本标识,消费者根据版本号选择不同的解析和处理逻辑,实现向后兼容
- 毒消息(Poison Message)处理:当消息反复消费失败时,应设置最大重试次数,超过阈值后将消息转移到死信队列(Dead Letter Queue)进行人工排查,避免阻塞正常消息处理
- 幂等消费者设计:由于消息可能被重复投递(网络故障、重试机制等),消费者应设计为幂等的,即多次处理同一条消息与处理一次的效果相同,可通过消息ID去重或业务层幂等操作实现
- 消息顺序性保证:JMS仅保证同一会话中单个生产者发送到同一目标的消息有序,多生产者或分布式环境下需要通过消息分组(Message Groups)或业务层序列号来保证顺序
- 容量规划:需要评估消息的产生速率、消费速率、消息大小和峰值负载,合理配置队列深度、消费者并发数和Broker的内存/磁盘资源,防止消息积压导致系统不可用