JMS消息队列的概念阐述

JMS是什么

百度百科对其定义

1, JMS即Java消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。Java消息服务是一个与具体平台无关的API,绝大多数MOM提供商都对JMS提供支持。
2, JMS是一种与厂商无关的 API,用来访问消息收发系统消息,它类似于JDBC(Java Database Connectivity)。

为什么使用JMS

JMS的应用场景有多种,但主要应用场景有如下几种:解耦,异步,削峰限流

1. 解耦:

传统模式:
这里写图片描述

缺点:
如果存在多个系统,每个系统间的耦合性都极强。如果后来有新的系统准备接入,那么作为被接入的系统,将可能需要修改代码以适应新系统的需求

中间件模式:
这里写图片描述
优点:
将消息写入消息队列,需要消息的系统自己从消息队列中订阅,从而系统A不需要做任何修改。实现了代码上的解耦,即使新添了系统,也只需要订阅该消息队列上的某个主题即可实现传统模式的代码嵌合作用。

例如:
这里写图片描述
支付系统和交易系统在此时就已经进行分离了,支付系统只需要在用户支付成功,往消息队列放入消息,订阅它的交易系统就会消费这个消息,进行一系列的活动。就算以后新的系统需要接入支付系统,也只需要订阅该主题即可

2. 异步:
传统模式:
这里写图片描述
缺点:
许多业务逻辑以同步方式运行,太耗费时间。

中间件模式:
这里写图片描述
优点:
将消息写入消息队列,非必要的业务逻辑以异步的方式运行,加快响应速度

**例如:**邮箱发送和手机短信发送,现在都是采用异步的形式,可以更快响应的用户。但也可能会出现没有发送,所以大部分会允许用户再发一次的操作。

3. 削峰:
传统模式
这里写图片描述
缺点:
并发量大的时候,所有的请求直接怼到数据库,造成数据库连接异常

中间件模式:
这里写图片描述

优点:
系统A慢慢的按照数据库能处理的并发量,从消息队列中慢慢拉取消息。在生产中,这个短暂的高峰期积压是允许的。

**例如:**秒杀活动和限流活动中适用,目前关于这方面了解得比较少,还需要在学习中得到具体应用。

消息队列的优势

JMS在官方说法有两大优势:异步,可靠。

**1,异步:**如上面所说,JMS天然支持异步。消费者获取消息,不需要主动发送请求,消息会自动推动给消费者。

此处的消费者就是客户端,服务端就是生产者。关于生产者和消费者等词语概念,下面会描述

2,可靠:
JMS保证消息只会递送一次。大家都遇到过重复创建消息问题,而JMS能帮你避免该问题,只是避免而不是杜绝,所以在一些糟糕的环境下还是有可能会出现重复。

使用消息队列的缺点##

**1,系统可用性降低:**因为多了个消息队列,便多了不确定性。一旦消息队列宕机,将会导致系统崩溃,所以系统可用性降低

**2,系统复杂性增加:**添加了消息队列,就需要考虑构成一个高可用的消息队列。需要考虑一致性,考虑消息不被重复消费,如何保证保证消息可靠传输等等问题,增加业务逻辑等工作量

JMS消息模型##

JMS定义了这两种消息发送模型的规范,它们相互独立。任何JMS的提供者可以实现其中的一种或两种模型,这是它们自己的选择。JMS规范提供了通用接口保证我们基于JMS API编写的程序适用于任何一种模型。

1、点对点消息传送模型(P2P)
在该模型,存在三种角色:消息队列,发送者,接收者。发送者发送一个消息给消息队列,该队列保存了所有发送给它的消息(除了被接收者消费掉的和过期的消息)。

特性:

  1. 每个消息只有一个接受者
  2. 消息发送者和消息接受者并没有时间依赖性。
  3. 当消息发送者发送消息的时候,无论接收者程序在不在运行,都能获取到消息;
  4. 当接收者收到消息的时候,会发送确认收到通知(acknowledgement)ACK包。

点对点消息模型图:
这里写图片描述

2、发布/订阅消息传送模型
这有点像redis的发布订阅和java的观察者模式,存在订阅主题的概念。
在发布/订阅消息模型中,发布者发布一个消息,该消息通过topic传递给所有的客户端。在这种模型中,发布者和订阅者彼此不知道对方,是匿名的且可以动态发布和订阅topic。topic主要用于保存和传递消息,且会一直保存消息直到消息被传递给客户端。

特性:

  1. 、一个消息可以传递给多个订阅者
  2. 、发布者和订阅者有时间依赖性,只有当客户端创建订阅后才能接受消息,且订阅者需一直保持活动状态以接收消息。
  3. 为了缓和这样严格的时间相关性,JMS允许订阅者创建一个可持久化的订阅。这样,即使订阅者没有被激活(运行),它也能接收到发布者的消息。

发布/订阅消息模型图:
这里写图片描述

接收消息##

在JMS中,消息的接收可以使用以下两种方式:

**1、同步方式:**使用同步方式接收消息的话,消息订阅者调用receive()方法。在receive()中,消息未到达或在到达指定时间之前,方法会阻塞,直到消息可用。

(1)、 目的地是Queue

consumer = session.createConsumer(queue);
Message message = consumer.receive(); //同步方式接收

(2)、目的地是Destination

consumer = session.createConsumer(destination);
//同步方式接受信息,如果还没有获取到则会阻塞直到接收到信息
Message messages = consumer.receive();

**2.异步方式:**使用异步方式接收消息的话,消息订阅者需注册一个消息监听者,类似于事件监听器,只要消息到达,JMS服务提供者会通过调用监听器的onMessage()递送消息。

异步接受是采用了监听器方式


consumer.setMessageListener(new MessageListener(){
					@Override
					public void onMessage(Message message) {
						TextMessage textMessage = (TextMessage)message;
						try {
							String value = textMessage.getText();
							System.out.println("value: "+value);
						} catch (JMSException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
	

JMS编程接口

JMS应用程序由如下基本模块组成:

1、管理对象(Administered objects)-连接工厂(Connection Factories)和目的地(Destination)
2、连接对象(Connections)
3、会话(Sessions)
4、消息生产者(Message Producers)
5、消息消费者(Message Consumers)
6、消息监听者(Message Listeners)

这里写图片描述

(1)、Connection Factories

创建Connection对象的工厂,针对两种不同的jms消息模型,分别有QueueConnectionFactory和TopicConnectionFactory两种。可以通过JNDI来查找ConnectionFactory对象。客户端使用一个连接工厂对象连接到JMS服务提供者,它创建了JMS服务提供者和客户端之间的连接。JMS客户端(如发送者或接受者)会在JNDI名字空间中搜索并获取该连接。使用该连接,客户端能够与目的地通讯,往队列或话题发送/接收消息。
    (2)、Destination

目的地指明消息被发送的目的地以及客户端接收消息的来源。JMS使用两种目的地,队列和话题。

(3)、Connection

Connection表示在客户端和JMS系统之间建立的链接(对TCP/IP socket的包装)。Connection可以产生一个或多个Session。跟ConnectionFactory一样,Connection也有两种类型:QueueConnection和TopicConnection。

连接对象封装了与JMS提供者之间的虚拟连接,如果我们有一个ConnectionFactory对象,可以使用它来创建一个连接。

(4)、Session

Session 是我们对消息进行操作的接口,可以通过session创建生产者、消费者、消息等。Session 提供了事务的功能,如果需要使用session发送/接收多个消息时,可以将这些发送/接收动作放到一个事务中。

(5)、Producter

消息生产者由Session创建,用于往目的地发送消息。生产者实现MessageProducer接口,我们可以为目的地、队列或话题创建生产者;

(6)、Consumer

消息消费者由Session创建,用于接收被发送到Destination的消息。

(7)、MessageListener

消息监听器。如果注册了消息监听器,一旦消息到达,将自动调用监听器的onMessage方法。EJB中的MDB(Message-Driven Bean)就是一种MessageListener。

JMS消息结构

JMS消息分为三部分组成:消息头,消息属性,消息体

消息头

预定义了若干字段用于客户端与JMS提供者之间识别和发送消息

消息属性
我们可以给消息设置自定义属性,这些属性主要是提供给应用程序的。对于实现消息过滤功能,消息属性非常有用,JMS API定义了一些标准属性,JMS服务提供者可以选择性的提供部分标准属性。

消息体:

在消息体中,JMS API定义了五种类型的消息格式,让我们可以以不同的形式发送和接受消息,并提供了对已有消息格式的兼容。

消息队列的选用

基于ActiveMQ,RabbitMQ,RocketMQ,Kafka进行解析。
这里写图片描述

保证消息队列是高可用的

如何保证消息队列的高可用?重点就在于集群模式,因为集群模式下消息队列群不容易瘫痪,会有其他队列进行替补消费。
以RocketMQ为例:

RocketMQ有多种模式。单机模式,多 master 模式,多 master 多 slave 异步复制模式,多 master 多 slave 同步双写模式等等
这里写图片描述
且RocketMQ并不完全遵循JMS规范。有NameServer group,Producer group,Broker集群,consumer group

保证消息消费的幂等性

消息消费的幂等性,也就是消息不重复消费。

造成重复消费原因其实都是类似的,在于回馈机制。正常情况下,消费者在消费消息时候,消费完毕后,会发送一个确认信息给消息队列,消息队列就知道该消息被消费了,就会将该消息从消息队列中删除。

不同的消息队列发送的确认信息形式不同,例如RabbitMQ是发送一个ACK确认消息,RocketMQ是返回一个CONSUME_SUCCESS成功标志,kafka实际上有个offset的概念。

造成重复消费的原因?,就是因为网络传输等等故障,确认信息没有传送到消息队列,导致消息队列不知道自己已经消费过该消息了,再次将该消息分发给其他的消费者。

那么如何怎么来保证消息消费的幂等性呢?实际上我们只要保证多条相同的数据过来的时候只处理一条或者说多条处理和处理一条造成的结果相同即可,但是具体怎么做要根据业务需求来定。比如:

(1)拿到这个消息做数据库的insert操作。给这个消息做一个唯一主键或者唯一约束,那么就算出现重复消费的情况,就会导致主键冲突。

(2)拿到这个消息做redis的set的操作.redis就是天然幂等性

(3)准备一个第三方介质,来做消费记录。以redis为例,给消息分配一个全局id,只要消费过该消息,将< id,message>以K-V形式写入redis。那消费者开始消费前,先去redis中查询有没消费记录即可。

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值