JMS&ActiveMQ

一、JMS是什么

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

JMS的优势:
①异步:JMS 原本就是一个异步的消息服务,客户端获取消息的时候,不需要主动发送请求,消息会自动发送给可用的客户端
②可靠:JMS保证消息只会递送一次。

二、JMS的两种通信模式

1、Point-to-Point Messaging Domain (点对点)

在点对点通信模式中,应用程序由消息队列,发送方,接收方组成。每个消息都被发送到一个特定的队列,接收者从队列中获取消息。队列保留着消息,直到他们被消费或超时。
特点:每个消息只要一个消费者。发送者在发送完消息之后,不管接收者有没有接受消息,都不会影响发送方发送消息到消息队列中。发送方不管是否在发送消息,接收方都可以从消息队列中去到消息。接收方在接收完消息之后,需要向消息队列应答成功。

2、Publish/Subscribe Messaging Domain (发布/订阅模式)

在发布/订阅消息模型中,发布者发布一个消息,该消息通过topic传递给所有的客户端。该模式下,发布者与订阅者都是匿名的,即发布者与订阅者都不知道对方是谁。并且可以动态的发布与订阅Topic。Topic主要用于保存和传递消息,且会一直保存消息直到消息被传递给客户端。
特点:一个消息可以传递个多个订阅者。发布者与订阅者具有时间约束,针对某个主题(Topic)的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息,而且为了消费消息,订阅者必须保持运行的状态。为了缓和这样严格的时间相关性,JMS允许订阅者创建一个可持久化的订阅。这样,即使订阅者没有被激活(运行),它也能接收到发布者的消息。

3、关键类图

三、JMS应用程序接口

1、Session 接口(会话)
表示一个单线程的上下文,用于发送和接收消息。由于会话是单线程的,所以消息是连续的,就是说消息是按照发送的顺序一个一个接收的。会话的好处是它支持事务。如果用户选择了事务支持,会话上下文将保存一组消息,直到事务被提交才发送这些消息。在提交事务之前,用户可以使用回滚操作取消这些消息。一个会话允许用户创建消息生产者来发送消息,创建消息消费者来接收消息。
2、MessageConsumer 接口(消息消费者)
由会话创建的对象,用于接收发送到目标的消息。消费者可以同步地(阻塞模式),或异步(非阻塞)接收队列和主题类型的消息。
3、MessageProducer 接口(消息生产者)
由会话创建的对象,用于发送消息到目标。用户可以创建某个目标的发送者,也可以创建一个通用的发送者,在发送消息时指定目标。
4、Message 接口(消息)
是在消费者和生产者之间传送的对象,也就是说从一个应用程序创送到另一个应用程序。一个消息有三个主要部分:
   ①消息头(必须):包含用于识别和为消息寻找路由的操作设置。
   ② 一组消息属性(可选):包含额外的属性,支持其他提供者和用户的兼容。可以创建定制的字段和过滤器(消息选择器)。
   ③一个消息体(可选):允许用户创建五种类型的消息(文本消息,映射消息,字节消息,流消息和对象消息)。
消息接口非常灵活,并提供了许多方式来定制消息的内容:简单文本 (TextMessage)、可序列化的对象 (ObjectMessage)、属性集合 (MapMessage)、字节流 (BytesMessage)、原始值流 (StreamMessage)
5、Destination 接口(目标)
目标是一个包装了消息目标标识符的被管对象,消息目标是指消息发布和接收的地点,或者是队列,或者是主题。JMS管理员创建这些对象,然后用户通过JNDI发现它们。和连接工厂一样,管理员可以创建两种类型的目标,点对点模型的队列,以及发布者/订阅者模型的主题。
6、ConnectionFactory 接口(连接工厂)
用户用来创建到JMS提供者的连接的被管对象。JMS客户通过可移植的接口访问连接,这样当下层的实现改变时,代码不需要进行修改。 管理员在JNDI名字空间中配置连接工厂,这样,JMS客户才能够查找到它们。根据消息类型的不同,用户将使用队列连接工厂,或者主题连接工厂。
7、Connection 接口(连接)
连接代表了应用程序和消息服务器之间的通信链路。在获得了连接工厂后,就可以创建一个与JMS提供者的连接。根据不同的连接类型,连接允许用户创建会话,以发送和接收队列和主题到目标。

四、ActiveMQ

1、ActiveMQ简介

ActiveMQ 是Apache出品,最流行的,能力强劲的开源消息总线。ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现,尽管JMS规范出台已经是很久的事情了,但是JMS在当今的J2EE应用中间仍然扮演着特殊的地位。

2、ActiveMQ安装和使用

官网下载apache-activemq-x.x.x-bin.zip安装包,直接解压到你想要安装的目录即按装好了。
启动:bin-->win64-->activemq.bat
启动之后,用浏览器。 http://localhost:8161/demo 可以看到各种官方提供的例子。http://localhost:8161/admin 是ActiveMQ的管理控制台,默认的账号密码都是admin

启动失败原因:
①运行bin目录下的activemq.bat,如果启动失败,则根据操作系统位数到win64/win32目录中执行activemq.bat。
②启动失败的另一个原因可能是,你机器的机器名中含中文字符或其他非合法字符,这时需要修改机器名
③某些端口被占用,也会导致启动失败,可在conf目录中修改activemq.xml,来修改端口号。启动之后,会占用至少2个端口,默认的是61616和8161。61616是等待jms client的连接,8161是ActiveMQ自带的一个web应用。

3、Sender/Receiver的代码编写步骤

需要实现接受者和发送者两部分代码的编写:

第一步:建立ConnectionFactory工厂对象,需要填入用户名、密码、以及要连接的地址,均使用默认即可,默认端口为"tcp://localhost:61616"
第二步:通过ConnectionFactory工厂对象,我们创建一个Connection连接,并且调用Connection的start方法开启连接,Connection默认是关闭的。
第三步:通过Connection对象创建Session会话(上下文环境对象),用于接收消息,参数1为是否启用事务(如果启用,发送完数据时,session.commit()提交事务),参数2为签收模式,一般我们设置自动签收(签收模式:Session.AUTO_ACKNOWLEDGE自动签收。Session.CLIENT_ACKNOWLEDGE需要手工签收消息,相当于另起一个线程去通知我们的MQ服务确认签收,在调用完receive方法后,msg.acknowledge())。
第四步:通过Session创建Destination对象,指的是一个客户端用来指定生产消息目标和消费消息来源的对象,在PTP模式中,Destination被称作Queue即队列;在Pub/Sub模式,Destination被称作Topic即主题。在程序中可以使用多个Queue和Toptic。
第五步:我们需要通过Session对象创建消息的发送和接收对象(生产者和消费者)MessageProducer/MessageConsumer
第六步:我们可以使用MessageProducer的setDeliveryMode方法为其设置持久化特性和非持久化特性。
第七步:最后我们使用JMS规范的TextMessage形式创建数据(通过Session对象),并用MessageProducer的send方法发送数据。同理,客户端使用receive方法进行接收数据,最后关闭Connection连接

4、ActiveMQ安全机制

ActiveMQ管控台使用jetty部署
conf\jetty-realm.properties:里面记录了账号密码
更改管理页面默认端口号:conf-->jetty.xml--><property name="port" value="8161"/>
ActiveMQ应该设置有安全机制,只有符合认证的用户才能进行发送和获取消息。在conf\activemq.xml的第123行之后添加配置:

<!-- 添加访问ActiveMQ的账号密码 -->
<plugins>
  <simpleAuthenticationPlugin>
    <users>
      <authenticationUser username="zhangsan" password="123" groups="users,admins"/>
    </users>
  </simpleAuthenticationPlugin>
</plugins>

5、常用的接口简介

①Connection方法的使用
当一个Connection被创建时,它的传输默认是关闭的,必须使用start方法开启。一个Connection可以创建一个或者多个Session。
当一个程序执行完后,必须关闭之前创建的Connection,否者ActiveMQ不能释放资源。

②Session方法使用
Session是一个发送或者接收消息的线程,可以使用Session创建MessageProducer,MessageConsumer和Message。
Session可以被事务化,也可以不被事务化。有3种签收模式。

③MessageProducer
是一个由Session创建的对象,用来向Destination发送消息。
有多个重载的send方法
其中,deliveryMode为传送模式,priority为消息优先级,timeToLive为消息过期时间,destination为消息目标,message为消息
ActiveMQ支持两种消息传送模式:PERSISTENT和NON_PERSISTENT,默认是持久性消息。如果容忍消息丢失,那么使用非持久性消息可以改善性能和减少存储的开销。
消息优先级从0-9十个级别,0-4是普通消息,5-9是加急消息。默认为4。JMS不要求严格按照这十个优先级发送消息,但必须保证加急消息要优于普通消息到达。不能保证按优先级消费。优先级不等同于顺序。
默认情况下,消息永不会过期。可以设置过期时间,单位为毫秒。

④MessageConsumer
是一个由Session创建的对象,用来向Destination接收消息。
Session有3个重载的MessageConsumer createConsumer()方法,2个重载的TopicSubscriber createDurableSubscriber()方法
其中,messageSelector为消息选择器;noLocal标志默认为false,当设置为true时限制消费者只能接收和自己相同的连接所发布的消息,此标志只适合主题,布适合队列。name标识订阅主题所对应的订阅名称,持久化订阅时需要设置此参数。
TopicSubscriber继承于MessageConsumer。
public final String SELECTOR="JMS_TYPE='MY_TAG1'"该选择器检查了传入消息的JMS_TYPE属性,并确定了这个属性的值是否等于MY_TAG1,如果相等,则消息被消费;如果不相等,那么消息会被忽略。
JMS_TYPE属性要通过setXxxProperty方法来设置,选择器才能发生作用,即选择器只作用setXxxProperty方法设置的属性。其中,Xxx是相应的数据类型。 并发量大的时候有问题。

⑤接收数据的操作:
    messageConsumer.setMessageListener(new MessageListener(){
           public void onMessage(Message message){
                //对接收到的数据进行操作  }
}

6、修改持久化数据的方式

①将连接Mysql数据库的jar文件,放到ActiveMQ的lib目录下
②修改conf/activemq.xml中<persistenceAdapter>标签:<jdbcPersistenceAdapter  dataSource="#mysql-ds"/>
③同一个配置文件中,在<broker>外面添加一个<bean>:

<!-- 用于持久化数据到Mysql数据库 -->
  <bean id="mysql-ds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
       <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
       <property name="url" value="jdbc:mysql://localhost:3306/databseName?relaxAutoCommit=true"/>
       <property name="username" value="root"/>
       <property name="password" value="1234"/>
       <property name="maxActive" value="200"/>
       <property name="poolPreparedStatements" value="true"/>
   </bean>

7、消息的同步和异步接收

消息的同步接收是指客户端主动去接收消息。客户端可以采用MessageConsumer的receive方法去接收下一个消息。
消息的异步接收是指当消息到达时,ActiveMQ主动通知客户端,可以通过注册一个实现MessageListener接口的对象到MessageConsumer,MessageListener只有一个必须实现的方法onMessage,它只接收一个参数Message。在为每个发送到Destination的消息实现onMessage时,将调用该方法。

8、示列代码

public class Sender {
	public static void main(String[] args) throws Exception {
		//第一步:建立ConnectionFactory工厂对象,需要填入用户名、密码、以及要连接的地址,均使用默认即可,默认端口为"tcp://localhost:61616"
		ConnectionFactory connectionFactory=new ActiveMQConnectionFactory("wyp", "123", "tcp://localhost:61616");
		
		//第二步:通过ConnectionFactory工厂对象,我们创建一个Connection连接,并且调用Connection的start方法开启连接,Connection默认是关闭的。
		Connection connection=connectionFactory.createConnection();
		connection.start();
				
		//第三步:通过Connection对象创建Session会话(上下文环境对象),用于接收消息,参数配置1为是否启用事物,参数配置2为签收模式,一般我们设置自动签收。
		Session session=connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
		
		//第四步:通过Session创建Destination对象,指的是一个客户端用来指定生产消息目标和消费消息来源的对象,在PTP模式中,Destination被称作Queue即队列;在Pub/Sub模式,Destination被称作Topic即主题。在程序中可以使用多个Queue和Toptic。
		Destination destination=session.createQueue("queue1");//queue1是队列名字
		Topic topic=session.createTopic("topic1");//topic1是话题名字
		
		//第五步:我们需要通过Session对象创建消息的发送和接收对象(生产者和消费者)MessageProducer/MessageConsumer
		MessageProducer messageProducer=session.createProducer(destination);
		
		//第六步:我们可以使用MessageProducer的setDeliveryMode方法为其设置持久化特性和非持久化特性。
		messageProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
		
		//第七步:最后我们使用JMS规范的TextMessage形式创建数据(通过Session对象),并用MessageProducer的send方法发送数据。同理,客户端使用receive方法进行接收数据,最后关闭Connection连接
		for(int i=0;i<5;i++) {
		TextMessage textMessage=session.createTextMessage();
		textMessage.setText("我"+i);
		messageProducer.send(textMessage);
		}
		
		if(connection != null) {
			connection.close();
		}
	}
}
public class Receiver {
	public static void main(String[] args) throws Exception {
		//第一步:建立ConnectionFactory工厂对象,需要填入用户名、密码、以及要连接的地址,均使用默认即可,默认端口为"tcp://localhost:61616"
		ConnectionFactory connectionFactory=new ActiveMQConnectionFactory("wyp", "123", "tcp://localhost:61616");
		
		//第二步:通过ConnectionFactory工厂对象,我们创建一个Connection连接,并且调用Connection的start方法开启连接,Connection默认是关闭的。
		Connection connection=connectionFactory.createConnection();
		connection.start();
				
		//第三步:通过Connection对象创建Session会话(上下文环境对象),用于接收消息,参数配置1为是否启用事物,参数配置2为签收模式,一般我们设置自动签收。
		Session session=connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
		
		//第四步:通过Session创建Destination对象,指的是一个客户端用来指定生产消息目标和消费消息来源的对象,在PTP模式中,Destination被称作Queue即队列;在Pub/Sub模式,Destination被称作Topic即主题。在程序中可以使用多个Queue和Toptic。
		Destination destination=session.createQueue("queue1");//queue1是队列名字
		Topic topic=session.createTopic("topic1");//topic1是话题名字
		
		//第五步:我们需要通过Session对象创建消息的发送和接收对象(生产者和消费者)MessageProducer/MessageConsumer
		MessageConsumer messageConsumer=session.createConsumer(destination);
		MessageConsumer topicmessageConsumer=session.createDurableSubscriber(topic,"topic1");
		
		while(true) {
			TextMessage textMessage=(TextMessage)messageConsumer.receive();
			if(textMessage==null) break;
			System.out.println(textMessage.getText());
		}
		
		if(connection != null) {
			connection.close();
		}
	}
}
发布了54 篇原创文章 · 获赞 66 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/qq_29837161/article/details/82391515
今日推荐