activeMQ详解

目标1:理解消息中间件、JMS等概念

目标2:掌握JMS点对点与发布订阅模式的收发消息

目标3:掌握SpringJms ActiveMQ

JMS入门

一、消息中间件

什么是消息中间件?

消息中间件利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行*分布式系统*的集成。
通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。
对于消息中间件,常见的角色大致也就有*Producer(生产者)、Consumer(消费者)*
Controller和Service解耦通过JMS建立连接

常见的消息中间件产品:

  • ActiveMQ
  • RabbitMQ
  • ZeroMQ
  • Kafka

二、JMS简介

什么是JMS?

JMS(Java Messaging Service)是Java平台上有关面向消息中间件的技术规范,它便于消息系统中的Java应用程序进行消息交换,并且通过提供标准的产生、发送、接收消息的接口简化企业应用的开发。
JMS本身只定义了一系列的接口规范,是一种与厂商无关的 API,用来访问消息收发系统。

JMS 定义了五种不同的消息正文格式,以及调用的消息类型

  • TextMessage–一个字符串对象
  • MapMessage–一系列key-value对
  • ObjectMessage–一个序列化的Java对象
  • BytesMessage–一个字节的数据流
  • StreamMessage–Java原始值的数据流

JMS消息传递类型

对于消息的传递有两种类型(两种模型):

1. **点对点**:即一个生产者和一个消费者一一对应;
    PTP(Point-to-point)无需订阅直接获取消息并消费掉,之后的consumer无法获取消息 
    即一个消息传递 多个消费者随机一个消费者处理一个消息
graph LR
    p1[Producer]-->Queue;
    p2[Producer]-->Queue;
    Consumer--监听Listener-->Queue;
    Queue--获取消息-->Consumer;
2. 另一种是**发布/订阅模式**:即一个生产者产生消息进行发送后,可以由多个消费者进行接收
    Publish/subscribe(pub/sub)需要先订阅才能获取发布的消息
    即一个消息发布 多个订阅的消费者全部接收到这个消息,每个消费者都处理这个消息
graph LR
    p1[Producer]-->Topic;
    p2[Producer]-->Topic;
    Topic--监听获取消息-->c1[Consumer];
    Topic--监听获取消息-->c2[Consumer];

三、ActiveMQ

ActiveMQ简介

ActiveMQ可以持久化,消息存储在本地的data文件夹,也可以设置存储在数据库
ActiveMQ管理软件端口号8161  
TCP端口号61616,远程连接ActiveMQ服务器走端口号61616
建议一个功能一个消息queue/topics

生产者producer

graph LR
    ActiveMQConnectionFactory-->Connection;
    Connection-->Session;
    Session-->Queue/Topics;
    Queue/Topics-->producer;
    Session-->producer;
    Session-->Message;
    Message-->producer;
    producer--send-->发送消息待发;
    
    s[Session]--commit-->手动提交消息;

消费者consumer

graph LR
    ActiveMQConnectionFactory-->Connection;
    Connection-->Session;
    Session-->Queue/Topics;
    Queue/Topics-->consumer;
    Session-->consumer;
    consumer--listener监听-->Message;
    Message-->consumer;

api:

ActiveMQConnectionFactory

Connection createConnection();

Connection

void start();
Session createSession(Boolean boolean, int acknowledgeMode);
    第1个参数 是否使用事务
    第2个参数 消息的确认模式
    •	AUTO_ACKNOWLEDGE = 1    自动确认
    •	CLIENT_ACKNOWLEDGE = 2    客户端手动确认   
    •	DUPS_OK_ACKNOWLEDGE = 3    自动批量确认
    •	SESSION_TRANSACTED = 0    事务提交并确认

Session

1.创建传递消息类型  分为点对点队列
Queue createQueue(String name);//队列名称
Topics createTopics(String name);//发布/订阅名称,生产者和消费者通过消息传递类型的名称绑定消息
2.创建消息
createTextMessage();
    void setText(string)//设置字符串
    String getText();//获取字符串
createTextMessage(String text);//字符串 构造传参
createObjectMessage();
    void setObject(Serializable object);//设置对象
    Object getObject();//获取对象
createObjectMessage(Serializable object);//对象需要实现序列化接口 构造传参
createBytesMessage();//字节
    void writerByte(Byte byte);//写 字节
    Byte readByte();//读 字节
createMapMessage();//map集合
    void setObject(String name,Object value);//设置键值对
    Object getObject(String name);//获取指定键的值
createStreamMessage();//数据流
    void writer*(* t);//写 字节
    * read*();//读 字节

Queue extends Destination

String getQueueName();//获得队列名

Topics extends Destination

String getTopicName();//获得主题名

MessageProducer

void send(Message message);//发送消息 默认使用创建时指定的传递消息
void send(Destination destination, Message message);//指定传递消息(队列,发布/订阅) 传递Message消息 

MessageConsumer

void setMessageListener(MessageListener listener)
    //MessageListener接口
         void onMessage(Message message)//抽象方法
Message receive();//只接收一次消息

ActiveMQ的Linux的安装

  • 解压缩tar zxvf apache-activemq-5.12.0-bin.tar.gz
  • 启动服务./activemq start
  • 访问192.168.25.135:8161查看后台界面,用户名和密码都是admin
```
后台界面介绍
Queues:队列消息,也叫点对点消息
	Number of Pending Message:没有消费的消息数量
	Number of Consumers:连接得消费者
    Messages Enqueued:进入到队列中得消息数量
    Messages Dequeued:已经出列得消息数量
Topics:订阅消息, 类似广播的消息
```

JMS使用流程

pom坐标依赖

<dependencies>
	<dependency>
		<groupId>org.apache.activemq</groupId>
		<artifactId>activemq-all</artifactId>
		<version>5.13.4</version>
	</dependency>
</dependencies>

一、点对点(队列)模式

发送&接收消息的9个步骤
//1.创建连接工厂
//2.获取连接
//3.启动连接
//4.获取session
//5.创建队列对象    //**点对点和发布订阅此处创建的对象为Queue或Topic**
//6.创建消息生产者对象或消费者
//7.创建消息   				 //此处consumer接受消息setMessageListener
//8.使用生成者发送消息  		 //等待键盘输入System.in.read();
//9.关闭资源

点对点生产者Producer示例:

package cn.itcast.test;
public class QueueProducer {
	
	public static void main(String[] args) throws JMSException {
		//1.创建连接工厂  若连接远程服务器需要写远程地址加tcp端口号,本地无需写参数
		ActiveMQConnectionFactory connectionFactory=new ActiveMQConnectionFactory("tcp://192.168.25.130:61616");
		//2.获取连接
		Connection connection = connectionFactory.createConnection();
		//3.启动连接
		connection.start();
		//4.获取session
		Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
		//5.创建队列对象
		Queue queue = session.createQueue("test-queue");
		//6.创建消息生产者对象
		MessageProducer producer = session.createProducer(queue);
		//7.创建消息
		TextMessage textMessage = session.createTextMessage("欢迎来到神奇的品优购世界!");
		//8.使用生成者发送消息
		producer.send(textMessage);
		//9.关闭资源
		producer.close();
		session.close();
		connection.close();
	}
}
上述代码中第4步创建session  的两个参数:
第1个参数 是否使用事务
第2个参数 消息的确认模式
•	AUTO_ACKNOWLEDGE = 1    自动确认
•	CLIENT_ACKNOWLEDGE = 2    客户端手动确认   
•	DUPS_OK_ACKNOWLEDGE = 3    自动批量确认
•	SESSION_TRANSACTED = 0    事务提交并确认
运行后通过ActiveMQ管理界面查询

点对点消费者Consumer示例:

package cn.itcast.test;
public class QueueConsumer {
	public static void main(String[] args) throws JMSException, IOException {
		// 1.创建连接工厂
		ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://192.168.25.130:61616");
		// 2.获取连接
		Connection connection = connectionFactory.createConnection();
		// 3.启动连接
		connection.start();
		// 4.获取session
		Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
		// 5.创建队列对象
		Queue queue = session.createQueue("test-queue");
		// 6.创建消息消费者
		MessageConsumer consumer = session.createConsumer(queue);
		// 7.接受消息
		consumer.setMessageListener(new MessageListener() {

			public void onMessage(Message message) {
				// 获取文本消息对象
				TextMessage textMessage = (TextMessage) message;
				try {
					String text = textMessage.getText();// 提取文本
					System.out.println(text);
				} catch (JMSException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		});
		// 8.等待键盘输入
		System.in.read();
		// 9.关闭资源
		consumer.close();
		session.close();
		connection.close();
	}
}

二、发布/订阅模式

发送&接收消息的9个步骤
//1.创建连接工厂
//2.获取连接
//3.启动连接
//4.获取session
//5.创建一个订阅 Topic
//6.创建消息生产者对象或**消费者**
//7.创建消息   				 //**接受消息setMessageListener**
//8.使用生成者发送消息  		 //**等待键盘输入System.in.read();**
//9.关闭资源

​ 创建订阅生产者TopicProducer

package cn.itcast.test;
public class TopicProducer {
	public static void main(String[] args) throws JMSException {		
		//1.创建连接工厂
		ActiveMQConnectionFactory connectionFactory=new ActiveMQConnectionFactory("tcp://192.168.25.130:61616");		
		//2.创建连接
		Connection connection = connectionFactory.createConnection();		
		//3.启动连接
		connection.start();
		//4.创建会话
		Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
		//5.创建一个订阅
		Topic topic = session.createTopic("test-topic");
		//6.创建消息生产者		
		MessageProducer producer = session.createProducer(topic);
		//7.创建消息
		TextMessage textMessage = session.createTextMessage("您的手机该续费了!");
		//8.发送消息		
		producer.send(textMessage);
		//9.关闭资源
		producer.close();
		session.close();
		connection.close();
	}
}

​ 创建订阅消费者TopicConsumer

package cn.itcast.test;
public class TopicConsumer {

	public static void main(String[] args) throws JMSException, IOException {
		ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("tcp://192.168.25.131:61616");
		Connection connection = factory.createConnection();
		connection.start();
		Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
		Topic topic = session.createTopic("test-topic");
		MessageConsumer consumer = session.createConsumer(topic);
		consumer.setMessageListener(new MessageListener() {
			
			public void onMessage(Message arg0) {
				// TODO Auto-generated method stub
				TextMessage message = (TextMessage) arg0;
				try {
					System.out.println("收到消息"+message.getText());
				} catch (JMSException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		});
		System.in.read();
		consumer.close();
		session.close();
		connection.close();
	}
}

Spring整合JMS

SpringJms整合JMS消息中间件

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-jms</artifactId>
	<version>${spring.version}</version>
</dependency>
消息中间件使用ActiveMQ
<dependency>
	<groupId>org.apache.activemq</groupId>
	<artifactId>activemq-all</artifactId>
	<version>5.11.2</version>
</dependency>

JmsTemplate:

    send(Destination dest,MessageCreator creator)
    //MessageCreator接口 
    //抽象方法
        Message createMessage(Session session);
    convertAndSend(Destination dest,Object obj)

Spring整合生产者配置:

通过JmsTemplate操作ActiveMQ

    <context:component-scan base-package="cn.itcast.demo"></context:component-scan>        
    <!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供-->  
	<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">  
	    <property name="brokerURL" value="tcp://192.168.25.135:61616"/>  
	</bean>          
    <!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->  
	<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">  
	<!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->  
	    <property name="targetConnectionFactory" ref="targetConnectionFactory"/>  
	</bean>  	   
    <!-- Spring提供的JMS工具类,它可以进行消息发送、接收等 -->  
	<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">  
	    <!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 -->  
	    <property name="connectionFactory" ref="connectionFactory"/>  
	</bean>      
    <!--这个是队列目的地,点对点的  文本信息 可以写在代码  写在配置文件方便管理
        ActiveMQQueue实现了Destination
    -->  
	<bean id="queueTextDestination" class="org.apache.activemq.command.ActiveMQQueue">  
	    <constructor-arg value="queue_text"/>  
	</bean>    
	
	<!--这个是订阅模式  文本信息-->  
	<bean id="topicTextDestination" class="org.apache.activemq.command.ActiveMQTopic">  
	    <constructor-arg value="topic_text"/>  
	</bean>  



Spring整合消费者配置:

consumer监听类需要实现MessageListener接口 实现其抽象方法onMessage(Message message);//message为要监听获取的消息
    <!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供-->  
	<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">  
	    <property name="brokerURL" value="tcp://192.168.25.135:61616"/>  
	</bean>
    <!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->  
	<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">  
	<!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->  
	    <property name="targetConnectionFactory" ref="targetConnectionFactory"/>  
	</bean>  
	
	 <!--这个是队列目的地,点对点的  文本信息 可以写在代码  写在配置文件方便管理
        ActiveMQQueue实现了Destination
    -->  
	<bean id="queueTextDestination" class="org.apache.activemq.command.ActiveMQQueue">  
	    <constructor-arg value="queue_text"/>  
	</bean>    
	
	<!--这个是订阅模式  文本信息-->  
	<bean id="topicTextDestination" class="org.apache.activemq.command.ActiveMQTopic">  
	    <constructor-arg value="topic_text"/>  
	</bean>  
	
	<!-- 以下不同于producer 且没有template配置 -->
	<!-- 我的监听类 配置consumemr实现MessageListener的监听类 -->
	<bean id="myMessageListener" class="cn.itcast.demo.MyMessageListener"></bean>
	<!-- 消息监听容器 -->
	<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
	    <!-- 第一个连接,第二个监听目标,第三个consumer监听类配置到监听容器中 -->
		<property name="connectionFactory" ref="connectionFactory" />
		<property name="destination" ref="topicTextDestination" />
		<property name="messageListener" ref="myMessageListener" />
	</bean>

发布/订阅模式与队列模式

web层发送消息
service层监听消息

producer发送消息:常用于Controller的处理器

@Autowired
private JmsTemplate jmsTemplate;

@Autowired//发布订阅
@Qualifier("queueSolrCreateDestination")
private Destination queueSolrCreateDestination;

@Autowired//点对点队列
@Qualifier("topicPageCreateDestination")
private Destination topicPageCreateDestination;
//发送solr库消息,对solr库进行数据新增		
//通过JmsTemplate完成producer发送消息步骤
//调用方法send(Destination desc,MessageCreator) 第一个参数是采用哪个消息目标,第二个参数创建消息对象Message的一个接口
jmsTemplate.send(queueSolrCreateDestination,new MessageCreator() {
	public Message createMessage(Session session) throws JMSException {
	    //此被实现的抽象方法参数Session 可以创建Message类型
		return session.createObjectMessage(ids);//传递对象ObjectMessage
	}
});
//改为发送生成页面消息
for (final Long id : ids) {
	jmsTemplate.send(topicPageCreateDestination, new MessageCreator() {
		
		@Override
		public Message createMessage(Session session) throws JMSException {
			// TODO Auto-generated method stub
			return session.createTextMessage(id.toString());
		}
	});
	*OR*
	jmsTemplate.send(topicPageCreateDestination,x->x.createTextMessage(id.toString()));

consumer监听:常用于Service层 单独写一个类实现MessageListener


//队列的监听器
@Component
public class ItemSearchCreateListener implements MessageListener{//监听器需要实现MessageListener接口
	@Autowired
	private ItemSearchService itemSearchService;//监听器监听到id执行对应功能所需要调用的service层对象
	
	@Override
	public void onMessage(Message message) {//onMessage(Message message) 监听方法 实现的抽象方法,参数为监听获取的消息对象Message
		try {
			ObjectMessage objectMessage = (ObjectMessage)message;//监听到的消息为对象ObjectMessage 注意发送什么类型的Message就接收什么类型的Message
			Long[] ids = (Long[]) objectMessage.getObject();
			itemSearchService.importItems(ids);
		} catch (JMSException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
//发布订阅的监听器
@Component
public class ItemPageCreateListener implements MessageListener{
	@Autowired
	private ItemPageService itemPageService;
	
	@Override
	public void onMessage(Message message) {
		TextMessage ms = (TextMessage) message;
		try {
			String goodsId = ms.getText();
			itemPageService.createItemHtml(Long.parseLong(goodsId));
			System.out.println("创建页面消息=====");
		} catch (JMSException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

其他

tcc分段式补偿

message的大小有限制 5000字符

message只发id由service和controller根据id查库 防止message太大出错

上(发)下(收)游

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值