对于大多数应用来说,可采用消息服务中间件来提升系统异步通信,消息解耦问题。
消息的作用:异步处理、应用解耦、流量削峰。
异步消息中的两个概念:消息代理、目的地
当消息发送者发送消息,由消息代理接管,消息代理保证消息传送到指定的目的地。
异步消息有两种形式的目的地
1)队列:点对点通信
消息发送者发送消息,消息代理将其放入一个队列中,消息接收者从队列中获取消息内容,消息读取后被移出队列。消息只有唯一的发送者和接受者,但并不是说只能有一个接收者
2) 主题:发布/订阅消息通信
发送者(发布者)发送消息到主题,多个接收者(订阅者)监听(订阅)这个主题,那么就会在消息到达时同时收到消息
两个常见的消息服务规范:
1)JMS(Java Message Service):Java 消息服务。基于 JVM 消息代理规范。ActiveMQ、 HornetMQ 是 JMS 实现。
2) AMQP( Advanced Message Queuing Protocol):高级消息队列协议,也是一个消息代理的规范,兼容 JMS。RabbitMQ 是 AMQP 的实现。
JMS(Java Message Service) 即 Java 消息服务应用程序接口,是一个 Java 平台中关于面向消息中间件(MOM)的 API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。
Java 消息服务是一个
与具体平台无关的 API
,绝大多数 MOM 提供商都对JMS提供支持。
JMS 是 java 的消息服务,JMS 的客户端之间可以通过 JMS 服务进行异步的消息传输
。
Java 消息服务为标准消息协议和消息服务提供了一组通用接口,包括创建、发送、读取消息等,用于支持 Java 应用程序开发。
在J2EE中,当两个应用程序使用JMS进行通信时,它们之间并不是直接相连的,而是通过一个共同的消息收发服务连接起来,可以达到解耦的效果
。
在一些场景下 RPC 的同步方式可能不太适合业务逻辑的处理,并且这种方式在某些场景下会导致业务的紧耦合。基于异步交互模型的 JMS 解决了 RPC 产生的紧耦合问题,它提供了一个可以通过网络访问的抽象消息队列
。
JMS 接收消息的方式:
(1)同步:使用同步方式接收消息的话,
消息订阅者调用 receive() 方法。在receive()中,消息未到达或在到达指定时间之前,方法会阻塞,直到消息可用
。
(2)异步:使用异步方式接收消息的话,消息订阅者需注册一个消息监听者,类似于事件监听器,只要消息到达,JMS 服务提供者会通过调用监听器的 onMessage() 递送消息
。
(1)异步
JMS 天生就是
异步
的,客户端获取消息的时候,不需要主动发送请求,消息会自动发送给可用的客户端
。
(2)可靠
JMS 保证
消息只会递送一次
。大家都遇到过重复创建消息问题,而JMS能帮你避免该问题。
(1) MQ 消息队列
MQ(Message QueueMQ)
消息队列,是一个用于消息的接受和转发的容器,可用于消息的推送
。很多人都说 MQ 通过将消息的发送和接收分离,从而来实现应用程序的异步和解偶。这个给人的直觉是——MQ 是异步的,用来解耦的,但是这个只是 MQ 的效果而不是目的。MQ 真正的目的是为了通讯,屏蔽底层复杂的通讯协议,定义了一套应用层的、更加简单的通讯协议
。
在消息队列中,定义了两个对象——发送数据的叫生产者,接收数据的叫消费者。并且提供了一个 SDK,让我们可以定义自己的生产者和消费者实现消息通讯而无视底层通讯协议。
● Producer:消息生产者,负责产生和发送消息到 Broker;
● Broker:消息处理中心,负责消息存储、确认、重试等,一般其中会包含多个 queue;
● Consumer:消息消费者,负责从 Broker 中获取消息,并进行相应处理。
(2) MOM 消息中间件
消息队列中间件(MOM:Message Orient MiddleWare),将信息以消息的形式,从一个应用程序传输到另一个或者多个应用程序。
消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题
。实现高性能,高可用,可伸缩和最终一致性架构,是大型分布式系统不可缺少的中间件。
消息中间件的用途和优点1️⃣消息异步接受:类似于手机短信的行为,消息发送者不需要等待消息接受者的响应,减少软件多系统集成的耦合度;
2️⃣负责建立网络通信的通道,进行数据的可靠传送,只有接受方接受到消息后才可删除,多个消息也可以组成原子事务;
3️⃣保证数据不重发,不丢失;
4️⃣能够实现跨平台操作,能够为不同操作系统上的软件集成技工数据传送服务;
(1)JMS 体系结构
1)JMS 提供者
连接面向消息中间件的,JMS 接口的一个实现。提供者可以是 Java 平台的 JMS 实现,也可以是非 Java 平台的面向消息中间件的适配器。
2)JMS 客户
生产或消费消息的基于 Java 的应用程序或对象。
3)JMS 生产者
创建并发送消息的 JMS 客户。
4)JMS 消费者
接收消息的 JMS 客户。
5)JMS 消息
包括可以在 JMS 客户之间传递的数据的对象
6)JMS 队列
一个容纳被发送的等待阅读的消息的区域。与队列的 FIFO 不同,,消息的接收顺序并不一定要与消息的发送顺序相同
。一旦一个消息被阅读,该消息将被从队列中移走
。
7)JMS主题
一种支持发送消息给多个订阅者的机制。
(2)JMS 消息结构
JMS 消息由三部分组成:
1)消息头
JMS 消息头预定义了若干字段用于客户端与 JMS 提供者之间识别和发送消息,预编译头如下:
2)消息属性
我们可以给消息设置自定义属性,这些属性主要是提供给应用程序的。对于实现消息过滤功能,消息属性非常有用,JMS API定义了一些标准属性,JMS服务提供者可以选择性的提供部分标准属性。3)消息体
在消息体中,JMS API定义了五种类型的消息格式,让我们可以以不同的形式发送和接受消息,并提供了对已有消息格式的兼容。不同的消息类型如下:● Text message:javax.jms.TextMessage,表示一个文本对象。
● Object message:javax.jms.ObjectMessage,表示一个可序列化的 Java 对象。
● Bytes message:javax.jms.BytesMessage,表示字节流数据。
● Stream message:javax.jms.StreamMessage,表示 Java 原始值数据流。
● Map message:javax.jms.MapMessage,表示属性集合的键值对。
(3)消息传输模型
(1)点对点消息传送模型
在点对点消息传送模型中,应用程序由消息队列,发送者,接收者组成。每一个消息发送给一个特殊的消息队列,该队列保存了所有发送给它的消息(除了被接收者消费掉的和过期的消息)。点对点消息模型有一些特性,如下:● 每个消息只有一个接收者;
● 消息发送者和接收者并没有时间依赖性;
● 当消息发送者发送消息的时候,无论接收者程序在不在运行,都能获取到消息;
● 当接收者收到消息的时候,会发送确认收到通知(acknowledgement)。
(2)发布/订阅消息传递模型
在发布/订阅消息模型中,发布者发布一个消息,该消息通过 topic 传递给所有的客户端。在这种模型中,发布者和订阅者彼此不知道对方,是匿名的且可以动态发布和订阅topic。topic主要用于保存和传递消息,且会一直保存消息直到消息被传递给客户端。发布/订阅消息模型特性如下:
● 一个消息可以传递给多个订阅者
● 发布者和订阅者有时间依赖性,只有当客户端创建订阅后才能接受消息,且订阅者需一直保持活动状态以接收消息。为了缓和这样严格的时间相关性,JMS 允许订阅者创建一个可持久化的订阅。这样,即使订阅者没有被激活(运行),它也能接收到发布者的消息。
JMS 的传递方式有以下两种● 标记为 NON_PERSISTENT 的
非持久性消息最多投递一次
。
● 标记为 PRESISTENT 的持久性消息将使用暂存后再转发的机理投递
。如果一个 JMS 服务离线,持久性消息不会丢失,但是等到这个服务恢复联机是才会被传递;采用非持久性消息可能降低内务和需要的存储器,所以默认的消息传递方式是非持久性的,但是这种传递方式只有当你不需要接收所有的消息是才使用。
(1)发送消息的基本步骤
1)创建连接使用的工厂类 JMS ConnectionFactory ;
2)使用管理对象 JMS ConnectionFactory建立连接 Connection,并启动
;
3)使用连接 Connection 建立会话 Session
;
4)使用会话 Session 和管理对象 Destination 创建消息生产者 MessageSender
;
5)使用消息生产者 MessageSender 发送消息;
(2)消息接收者从JMS接受消息的步骤
1)创建连接使用的工厂类 JMS ConnectionFactory ;
2)使用管理对象 JMSConnectionFactory 建立连接Connection,并启动
3)使用连接 Connection 建立会话 Session
;
4)使用会话 Session 和管理对象 Destination 创建消息接收者 MessageReceiver
;
5)使用消息接收者 MessageReceiver 接受消息,需要用 setMessageListener 将 MessageListener 接口绑定到 MessageReceiver 消息接收者;必须实现 MessageListener 接口,需要定义 onMessage 事件方法。
JMS 应用程序由如下基本模块组成:
1)管理对象(Administered objects):
预先配置的JMS对象,由系统管理员为使用JMS的客户端创建,主要有两个被管理的对象(连接工厂(ConnectionFactory)和目的地(Destination))。这两个管理对象由JMS系统管理员通过使用Application Server管理控制台创建,存储在应用程序服务器的JNDI名字空间或JNDI注册表。
2)连接工厂(Connection Factories)
创建Connection对象的工厂,针对两种不同的jms消息模型,分别有QueueConnectionFactory 和 TopicConnectionFactory 两种。可以通过 JNDI 来查找 ConnectionFactory 对象。客户端使用一个连接工厂对象连接到 JMS 服务提供者,它创建了JMS服务提供者和客户端之间的连接。JMS 客户端(如发送者或接受者)会在 JNDI 名字空间中搜索并获取该连接。使用该连接,客户端能够与目的地通讯,往队列或话题发送/接收消息。
// 使用 JNDI 查找连接工厂对象 Context ctx = new InitialContext(); QueueConnectionFactory queueConnFactory = (QueueConnectionFactory) ctx.lookup ("primaryQCF"); Queue purchaseQueue = (Queue) initialCtx.lookup ("Purchase_Queue"); Queue returnQueue = (Queue) ctx.lookup ("Return_Queue"); // 直接实例化连接工厂 ConnectionFactory connFactory = new com.sun.messaging.ConnectionFactory();
3)JMS 连接(Connection)
表示 JMS 客户端和服务器端之间的一个活动的连接,是由
客户端通过调用连接工厂的方法建立的
。// ConnectionFactory对象,可以使用它来创建一个连接 Connection connection = connectionFactory.createConnection(); // 创建完连接后,需要在程序使用结束后关闭它: connection.close();
4)JMS会话(Session)
表示 JMS 客户与服务器之间的会话状态。
JMS会话建立在 JMS 连接之上,表示客户与服务器之间的一个会话进程
。 Session是一个单线程上下文,用于生产和消费消息,可以创建出消息生产者和消息消费者。Session对象实现了Session接口,在创建完连接后,我们可以使用它创建Session。// 这里面提供了参数两个参数,第一个参数是是否支持事务,第二个是事务的类型 Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
5)JMS 目的(Destination)
又称消息队列,是实际的消息源。 目的地指明消息被发送的目的地以及客户端接收消息的来源。JMS使用两种目的地,队列和话题。如下代码指定了一个队列和话题。
// 创建一个队列 Session QueueSession ses = con.createQueueSession (false,Session.AUTO_ACKNOWLEDGE); Queue t = (Queue) ctx.lookup ("myQueue"); // create QueueReceiver QueueReceiver receiver = ses.createReceiver(t); // 创建一个话题 Session TopicSession ses = con.createTopicSession (false, Session.AUTO_ACKNOWLEDGE); Topic t = (Topic) ctx.lookup ("myTopic"); //create TopicSubscriber TopicSubscriber receiver = ses.createSubscriber(t);
6)JMS 生产者(Message Producer)和消费者(Message Consumer)
消息生产者由 Session 创建,用于往目的地发送消息。生产者实现 MessageProducer 接口,我们可以为目的地、队列或话题创建生产者;
MessageProducer producer = session.createProducer(dest); MessageProducer producer = session.createProducer(queue); MessageProducer producer = session.createProducer(topic); // 创建完消息生产者后,可以使用send方法发送消息 producer.send(message);
消息消费者由Session创建,用于接受目的地发送的消息。消费者实现MessageConsumer接口,,我们可以为目的地、队列或话题创建消费者;
MessageConsumer consumer = session.createConsumer(dest); MessageConsumer consumer = session.createConsumer(queue); MessageConsumer consumer = session.createConsumer(topic);
8)JMS 消息监听器
JMS 消息监听器是消息的默认事件处理者,他实现了MessageListener接口,该接口包含一个onMessage方法,在该方法中需要定义消息达到后的具体动作。通过调用setMessageListener方法我们给指定消费者定义了消息监听器。如果注册了消息监听器,一旦消息到达,将自动调用监听器的onMessage方法。
Listener myListener = new Listener(); consumer.setMessageListener(myListener);
(1)消息的生产
1)连接工厂(Connection Factories)
使用JNDI找到ConnectionFactory对象,或者直接实例化一个ConnectionFactory,最终得到一个QueueConnectionFactory或者TopicConnectionFactory的实例,通过这个实例为生产者创建连接。
// 使用 JNDI 查找连接工厂对象 Context ctx = new InitialContext(); ConnectionFactory cf1 =(ConnectionFactory)ctx.lookup("jms / QueueConnectionFactory"); ConnectionFactory cf2 = (ConnectionFactory) ctx.lookup("/jms/TopicConnectionFactory"); // 直接实例化连接工厂 ConnectionFactory connFactory = new com.sun.messaging.ConnectionFactory();
3)JMS 连接(Connection)
使用 ConnectionFactory 创建连接 Connection。
// ConnectionFactory对象,可以使用它来创建一个连接 Connection connection = connectionFactory.createConnection(); // 调用结束后调用connection.close()关闭所有已经创建的连接 connection.close();
4)JMS会话(Session)
使用 Connection 对象创建 Session。这些 Session 将一组发送和接收合并到一个原子单元内,并提供事务上下文。
// 这里面提供了参数两个参数,第一个参数是是否支持事务,第二个是事务的类型 Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
5)JMS 目的(Destination)
客户端使用Destination对象来指定它消费的消息的来源或者它生产的消息的目标。在 point to point 消息传递中,Destination 为 Queue,在消息传递的发布/订阅模型中,为 Topic。
// // 使用 JNDI 查找 Destination 对象 Destination dest = (Queue) ctx.lookup("jms/SomeQueue"); // 直接实例化 Queue q = new com.sun.messaging.Queue("world");
6)JMS 生产者(Message Producer)
通过 Session 和 Destination 创建 MessageProducer,MessageProducer 用来发送消息。下面的代码中没有说明 Destination 的使用,但是每一个消息必须指定 Destination。
MessageProducer producer = session.createProducer(SomeQueue OR SomeTopic); // 创建完消息生产者后,可以使用 send 方法发送消息 producer.send(message);
(2)消息的消费
连接工厂、连接、会话和 Destination 对象同消息的生产。
消息的消费分为同步消费和异步消费两种。
1)同步消费如果是点对点模式的消费者,通过 Session 和 Destination 创建 MessageConsumer,然后用 MessageProducer 的 receive() 方法来接收消息。
MessageConsumer consumer = session.createConsumer(SomeQueue);
如果是发布/订阅模式的消费者,可以使用 Session.createDurableSubscriber() 创建一个持久的 topic 订阅者,然后用 receive() 方法来接收消息。
MessageConsumer consumer = session.createDurableSubscriber(topic);
MessageConsumer 不是主动模式,而是被动模式。在启动连接之前,消息不会传递,必须先启动连接,才能接收消息。
connection.start(); // 可传入一个long型参数来指定超时时间,单位是ms Message msg = consumer.receive();
2)异步消费
如果需要异步通信,需要实例化 MessageListener 并在 MessageConsumer 中注册这个监听器。
MessageListener listener = new MyListener(); consumer.setMessageListener(listener);
为了避免丢失消息,注册监听器后,调用连接的start()方法,当消息开始传递,JMS 会自动调用监听器的 onMessage() 接收消息。