Activemq API使用(不整合spring)
首先需要引入activemq的jar包,这里用的是5.14.4版本的
<!-- https://mvnrepository.com/artifact/org.apache.activemq/activemq-all --> <dependency> <groupId>org.apache.activemq</groupId> <artifactId>activemq-all</artifactId> <version>5.14.4</version> </dependency>
P2P消息传递模型:
发送者:
import javax.jms.DeliveryMode; import javax.jms.JMSException; import javax.jms.Queue; import javax.jms.QueueConnection; import javax.jms.QueueSender; import javax.jms.QueueSession; import javax.jms.Session; import javax.jms.TextMessage; import org.apache.activemq.ActiveMQConnection; import org.apache.activemq.ActiveMQConnectionFactory; import org.apache.log4j.Logger; public class FirstSender { private static Logger LOGGER = Logger.getLogger(FirstSender.class); private static final int SEND_NUMBER = 100; public static void main(String[] args) { ActiveMQConnectionFactory connectionFactory = null; QueueConnection connection = null; QueueSession session = null; Queue queue = null; QueueSender sender = null; TextMessage message = null; try { connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER, ActiveMQConnection.DEFAULT_PASSWORD, "tcp://192.168.220.128:61616"); connection = connectionFactory.createQueueConnection(); connection.start(); session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); queue = session.createQueue("firstQueue"); sender = session.createSender(queue); //默认是持久化模式的 sender.setDeliveryMode(DeliveryMode.NON_PERSISTENT); for (int i = 0; i < SEND_NUMBER; i++) { message = session.createTextMessage("你好吗!!"); sender.send(message); } } catch (JMSException e) { LOGGER.error("向消息队列发送消息失败", e); } finally { if (connection != null) { try { connection.close(); } catch (JMSException e) { LOGGER.error("connection关闭失败", e); } } } } }
本例中创建的是非事务性的session,如果创建的是事务性的session的话(第一个参数传true),那么在sender.send()方法之后,还需要调用session.commit()方法,否则事务不会提交,消息不会被丢到消息队列中去。此外,send()方法默认是同步的,如果想要异步发送,则需要调用ConnectionFactory对象的setUseAsyncSend(boolean useAsyncSend)方法或者setAlwaysSyncSend(boolean alwaysSyncSend)方法。
接收者:
import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.Queue; import javax.jms.QueueConnection; import javax.jms.QueueReceiver; import javax.jms.QueueSession; import javax.jms.Session; import javax.jms.TextMessage; import org.apache.activemq.ActiveMQConnection; import org.apache.activemq.ActiveMQConnectionFactory; import org.apache.log4j.Logger; public class FirstReceiver { private static Logger LOGGER = Logger.getLogger(FirstReceiver.class); public static void main(String[] args) { ActiveMQConnectionFactory connectionFactory = null; QueueConnection connection = null; QueueSession session = null; Queue queue = null; QueueReceiver receiver = null; try { connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER, ActiveMQConnection.DEFAULT_PASSWORD, "tcp://192.168.220.128:61616"); connection = connectionFactory.createQueueConnection(); connection.start(); session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); queue = session.createQueue("firstQueue"); receiver = session.createReceiver(queue); receiver.setMessageListener(new MessageListener() { @Override public void onMessage(Message message) { try { TextMessage text = (TextMessage) message; LOGGER.info(text.getText()); } catch (InterruptedException e) { LOGGER.error("线程休眠异常", e); } catch (JMSException e) { LOGGER.error("接收消息异常", e); } } }); } catch (Exception e) { LOGGER.error("接收消息失败", e); } } }
在本例中,接收者接收消息的方式采用的是异步的方式,即注册了一个监听器,即使队列中没有消息可以消费了,代码还会继续往下走,不会在这里阻塞的。实际上,正常情况下都应该用此异步方式来接收消息。
jms规范提供了javax.jms.QueueBrowser接口来看队列中的元素,activemq对应的实现类是org.apache.activemq.ActiveMQQueueBrowser,这个类实现了QueueBrowser和Enumeration接口。QueueBrowser对象可以通过Session对象的createBrowser(Queue queue)方法得到,利用其Enumeration getEnumeration()方法、boolean hasMoreElements()方法及Object nextElement()方法可以查看队列中的元素。
Pub/Sub消息传递模型:
发布者:
import javax.jms.JMSException; import javax.jms.Session; import javax.jms.Topic; import javax.jms.TopicConnection; import javax.jms.TopicPublisher; import javax.jms.TopicSession; import org.apache.activemq.ActiveMQConnection; import org.apache.activemq.ActiveMQConnectionFactory; import org.apache.log4j.Logger; public class FirstPublisher { private static Logger LOGGER = Logger.getLogger(FirstPublisher.class); private static final int PUBLISH_NUMBER = 100; public static void main(String[] args) { ActiveMQConnectionFactory connectionFactory = null; TopicConnection connection = null; TopicSession session = null; Topic topic = null; TopicPublisher publisher = null; try { connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER, ActiveMQConnection.DEFAULT_PASSWORD, "tcp://192.168.220.128:61616"); connection = connectionFactory.createTopicConnection(); connection.start(); session = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); topic = session.createTopic("firstTopic"); publisher = session.createPublisher(topic); for (int i = 1; i <= PUBLISH_NUMBER; i++) { publisher.send(session.createTextMessage("我爱你哦,firstTopic!")); } } catch (Exception e) { LOGGER.error("向firstTopic发布消息异常", e); } finally { if (connection != null) { try { connection.close(); } catch (JMSException e) { LOGGER.error("connection异常", e); } } } } }
其实,Pub/Sub消息传递模型的编程思路与P2P消息传递模型的编程思路差不多,就是把queue换成topic而已。
订阅者:
import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.Session; import javax.jms.TextMessage; import javax.jms.Topic; import javax.jms.TopicConnection; import javax.jms.TopicSession; import javax.jms.TopicSubscriber; import org.apache.activemq.ActiveMQConnection; import org.apache.activemq.ActiveMQConnectionFactory; import org.apache.log4j.Logger; public class FirstSubscriber { private static Logger LOGGER = Logger.getLogger(FirstSubscriber.class); public static void main(String[] args) { ActiveMQConnectionFactory connectionFactory = null; TopicConnection connection = null; TopicSession session = null; Topic topic = null; TopicSubscriber subscriber = null; try { connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER, ActiveMQConnection.DEFAULT_PASSWORD, "tcp://192.168.220.128:61616"); connection = connectionFactory.createTopicConnection(); connection.start(); session = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); topic = session.createTopic("firstTopic"); subscriber = session.createSubscriber(topic); subscriber.setMessageListener(new MessageListener() { @Override public void onMessage(Message message) { try { Thread.sleep(1000); if (message instanceof TextMessage) { TextMessage text = (TextMessage) message; LOGGER.info(text.getJMSMessageID() + ": " + text.getText()); } } catch (InterruptedException e) { LOGGER.error("线程休眠异常", e); } catch (JMSException e) { LOGGER.error("firstSubscriber消費消息异常", e); } } }); } catch (Exception e) { LOGGER.error("firstSubscriber异常", e); } } }
在本例中创建的订阅者不是持久化的,也就是说只能收到之后发布者发布的消息,接收不到之前发布者发布的消息。如果想要创建持久化的订阅者,需要用
session.createDurableSubscriber(Topic topic, String name),而且在此之前还必须要给session设置clientID,否则会报错。
session.setClientID(String clientID),这个有点疑惑,如果传一个常量字符串是可以的,如果传一个变量字符串,例如传System.currentTimeMillis()+""的话,就不起作用(只能接受到之后发布者发布的消息),这有点奇怪,不知是bug还是有其他什么原因!!!
优化版:
引入连接池。上面例子中与activemq服务端的连接都是调用createConnection()直接获得,用多少次连接就要创建多少次新的连接对象,所以可以引入连接池减少资源浪费。
需要导入commons-pool-xxx.jar包,pom.xml添加如下:
<!-- https://mvnrepository.com/artifact/commons-pool/commons-pool --> <dependency> <groupId>commons-pool</groupId> <artifactId>commons-pool2</artifactId> <version>2.4.2</version> </dependency>
用org.apache.activemq.pool.PooledConnectionFactory包装ActiveMQConnectionFactory实例,随后可以设置PooledConnectionFactory实例的属性来实现连接池。如
setCreateConnectionOnStartup(boolean createConnectionOnStartup);//是否创建PooledConnectionFactory的时候就创建连接,默认是
setMaxConnections(int maxConnections);//设置最大连接数,默认值为8
setMaximumActiveSessionPerConnection(int maximumActiveSessionPerConnection) //设置每个连接可创建的session的最大数量,默认值为500
其实上面的setter方法都是继承自org.apache.activemq.pool.PooledConnectionFactory的父类org.apache.activemq.jms.pool.PooledConnectionFactory(org.apache.activemq.jms.pool.PooledConnectionFactory引用了commons-pool2-xxx.jar中的类,所以才需要导入commons-pool2-xxx.jar包)。
需要注意的是,5.12.0(不包括)以前版本的activemq-all-xxx.jar引用的是commons-pool-xxx.jar包中的类,这时候就需要导入commons-pool-xxx.jar包了。
更进一步,还可以引入线程池,使用多线程发送消息(用多线程操作send(Message message)方法)和消费消息(监听器的onMessage(Message message)方法中用多线程操作)。
具体代码示例:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javax.jms.Connection; import javax.jms.Destination; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageProducer; import javax.jms.Session; import org.apache.activemq.ActiveMQConnectionFactory; import org.apache.activemq.pool.PooledConnectionFactory; import org.apache.log4j.Logger; public class JMSProducer { Logger LOGGER = Logger.getLogger(JMSProducer.class); // 设置连接的最大连接数 public final static int DEFAULT_MAX_CONNECTIONS = 2; private int maxConnections; // 线程池数量 private int threadPoolSize; public final static int DEFAULT_THREAD_POOL_SIZE = 4; // 强制使用异步返回数据的格式 private boolean useAsyncSend; public final static boolean DEFAULT_USE_ASYNC_SEND_FOR_JMS = true; // 连接地址 private String brokerUrl; private String userName; private String password; private ExecutorService threadPool; private PooledConnectionFactory connectionFactory; public JMSProducer(String brokerUrl, String userName, String password) { this(brokerUrl, userName, password, DEFAULT_MAX_CONNECTIONS, DEFAULT_THREAD_POOL_SIZE, DEFAULT_USE_ASYNC_SEND_FOR_JMS); } public JMSProducer(String brokerUrl, String userName, String password, int maxConnections, int threadPoolSize, boolean useAsyncSendForJMS) { this.useAsyncSend = useAsyncSendForJMS; this.brokerUrl = brokerUrl; this.userName = userName; this.password = password; this.maxConnections = maxConnections; this.threadPoolSize = threadPoolSize; init(); } private void init() { // 设置JAVA线程池 this.threadPool = Executors.newFixedThreadPool(this.threadPoolSize); // ActiveMQ的连接工厂 ActiveMQConnectionFactory actualConnectionFactory = new ActiveMQConnectionFactory(this.userName, this.password, this.brokerUrl); actualConnectionFactory.setUseAsyncSend(this.useAsyncSend); this.connectionFactory = new PooledConnectionFactory(actualConnectionFactory);// org.apache.activemq.pool.PooledConnectionFactory // 父类org.apache.activemq.jms.pool.PooledConnectionFactory的setter方法 this.connectionFactory.setMaxConnections(this.maxConnections); } public void send(final String queue, final String text) { // 直接使用线程池来执行具体的调用 this.threadPool.execute(new Runnable() { @Override public void run() { try { Thread.sleep(2000); LOGGER.info(Thread.currentThread().getName()); sendMsg(queue, text); } catch (Exception e) { LOGGER.error("发送失败", e); } } }); } private void sendMsg(String queue, String text) throws Exception { Connection connection = null; Session session = null; try { // 从连接池工厂中获取一个连接 connection = this.connectionFactory.createConnection(); LOGGER.info("connection: " + connection); session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE); Destination destination = session.createQueue(queue); MessageProducer producer = session.createProducer(destination); Message message = getMessage(session, text); producer.send(message); } finally { closeSession(session); closeConnection(connection); } } private Message getMessage(Session session, String text) throws JMSException { return session.createTextMessage(text); } private void closeSession(Session session) { try { if (session != null) { session.close(); } } catch (Exception e) { e.printStackTrace(); } } private void closeConnection(Connection connection) { try { if (connection != null) { connection.close(); } } catch (Exception e) { e.printStackTrace(); } } }
写一个测试类测试上面的发送者:
import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.UUID; import org.apache.log4j.Logger; public class JMSProducerTest { private static Logger LOGGER = Logger.getLogger(JMSProducerTest.class); public static void main(String[] args) { String brokerURL = "tcp://192.168.220.128:61616"; JMSProducer producer = new JMSProducer(brokerURL, "admin", "admin"); for (int i = 0; i < 6; i++) { LOGGER.info(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").format(LocalDateTime.now())); producer.send("firstQueue", UUID.randomUUID().toString().replaceAll("-", "")); } } }
Activemq API使用(不整合spring)的更多相关文章
- Activemq API使用(整合spring)
整合spring之后,主要用的就是org.springframework.jms.core.JmsTemplate的API了,在spring-jms-xxx.jar中. 引入整合需要的jar包: &l ...
- ActiveMQ 入门和与 Spring 整合
ActiveMQ 入门演示 activemq 依赖 <dependency> <groupId>org.apache.activemq</groupId> < ...
- ActiveMQ第二弹:使用Spring JMS与ActiveMQ通讯
本文章的完整代码可从我的github中下载:https://github.com/huangbowen521/SpringJMSSample.git 上一篇文章中介绍了如何安装和运行ActiveMQ. ...
- ActiveMQ(5.10.0) - Spring Support
Maven Dependency: <dependencies> <dependency> <groupId>org.apache.activemq</gro ...
- activemq api的封装
今天无聊写段代码..学习一下activemq,简单封装了一下activemq 的topic api.跟jdbc很类似 主要代码: import java.io.Serializable; import ...
- api网关揭秘--spring cloud gateway源码解析
要想了解spring cloud gateway的源码,要熟悉spring webflux,我的上篇文章介绍了spring webflux. 1.gateway 和zuul对比 I am the au ...
- 框架源码系列十一:事务管理(Spring事务管理的特点、事务概念学习、Spring事务使用学习、Spring事务管理API学习、Spring事务源码学习)
一.Spring事务管理的特点 Spring框架为事务管理提供一套统一的抽象,带来的好处有:1. 跨不同事务API的统一的编程模型,无论你使用的是jdbc.jta.jpa.hibernate.2. 支 ...
- 86. Spring Boot集成ActiveMQ【从零开始学Spring Boot】
在Spring Boot中集成ActiveMQ相对还是比较简单的,都不需要安装什么服务,默认使用内存的activeMQ,当然配合ActiveMQ Server会更好.在这里我们简单介绍怎么使用,本节主 ...
- 消息中间件-activemq实战之整合Spring(四)
前面的理论准备已经很充分,这一节我们来实战:将activemq整合到Spring框架才行中,因为Spring已经集成了JMS,这也为我们配置activermq带来了方便. 1. Spring对jms的 ...
随机推荐
- (转)MongoDB入门分享-笔记整理精选
原文地址:http://www.cnblogs.com/Kummy/p/3372729.html 最近在学习MongoDB,怕以后忘记,自己做了一个整理,给不知道的小伙伴一起分享学习一下. 第一步&g ...
- 编写高质量代码改善C#程序的157个建议——建议40:使用event关键字为委托施加保护
建议40:使用event关键字为委托施加保护 在建议中我们实现了一个具有通知功能的文件传输类,如下: class FileUploader { public delegate void FileUpl ...
- 关于bootstrap模态框的初始化事件
转:https://blog.csdn.net/u010181136/article/details/77579823
- 接上一篇,Springcloud使用feignclient远程调用服务404 ,为什么去掉context-path后,就能够调通
一.问题回顾 如果application.properties文件中配置了 #项目路径 server.servlet.context-path=/pear-cache-service 则feigncl ...
- gets()scanf()有害------c++程序设计原理与实践(进阶篇)
最简单的读取字符串的方式是使用gets(),例如: char a[12]; gets(a); 但gets()和scanf()是有害的,曾经有大约1/4的成功黑客攻击是由于gets()和它的近亲scan ...
- 码云&Github 个人代码资源快速查找
1.Siri SiriShortCut
- 一些c++多线程习题
题目1:子线程循环 10 次,接着主线程循环 100 次,接着又回到子线程循环 10 次,接着再回到主线程又循环 100 次,如此循环50次,试写出代码 代码1: #include <iostr ...
- Django之跨域请求同源策略
同源策略: 首先基于安全的原因,浏览器是存在同源策略这个机制的,同源策略阻止从一个源加载的文档或脚本获取或设置另一个源加载的文档的属性. 而如果我们要跳过这个策略,也就是说非要跨域请求,那么就需要通过 ...
- MySQL 学习笔记(二):数据库更新、视图和数据控制
基础准备: 在 school 数据库下建立student.course.sc 三个表: create table student( Sno ) primary key, Sname ) unique, ...
- 递归-归并排序 思想 JAVA实现
已知一个数组 15.58.61.75.21.32.89.4.78.83.采用递归实现的归并排序将数组有序. 分治策略:(摘自<算法导论>) 在分治策略中,我们采用递归解决问题 分解:将 ...