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的 ...
随机推荐
- 编写高质量代码改善C#程序的157个建议——建议22:确保集合的线程安全
建议22:确保集合的线程安全 集合线程安全是指多个线程上添加或删除元素时,线程键必须保持同步. 下面代码模拟了一个线程在迭代过程中,另一个线程对元素进行了删除. class Program { sta ...
- word 2013如何从某一页开始插入页码
把光标移入要插入页面的最前面 插入分页符 在要插入页码的页脚双击打开页脚设计 取消页脚和前面页眉的链接 插入页码
- IOC AOP 设计模式
IOC AOP 不是什么技术而是一种设计模式 学习 IOC AOP 其实是在学习一种思想. 1.IOC IOC其实是 将对象的创建和获取提取到外部.由外部IOC容器提供需要的组件. 看下面代码: p ...
- Sql Server 日期格式化函數 Convert
Select CONVERT(varchar(100), GETDATE(), 0): 05 16 2015 10:57AM Select CONVERT(varchar(100), GETDATE( ...
- WPF中在摄像头视频上叠加控件的解决方案
一.视频呈现 前段时间,在一个wpf的项目中需要实时显示ip摄像头,对此的解决方案想必大家都应该知道很多.在winform中,我们可以将一个控件(一般用panel或者pictruebox)的句柄丢给摄 ...
- php代码审计4审计代码执行漏洞
代码执行漏洞代码执行漏洞是指应用程序本身过滤不严,用户可以通过请求将代码注入到应用中执行,当应用在调用一些能将字符串转化成代码的函数(如php中的eval)时,没有考虑到用户是否能控制这个字符串,造成 ...
- ASPxGridView编辑时弹出的editform值不是当前行值的原因
如下图所示(左边是红框是ASPxGridView编辑的行,右边是弹出的editform上显示的值). 这是因为ASPxGridView的KeyFieldName的值不是唯一的,需要修改或增加条件使键值 ...
- avascript的匿名函数
一.什么是匿名函数? 在Javascript定义一个函数一般有如下三种方式: 函数关键字(function)语句: function fnMethodName(x){alert(x);} 函数字面量( ...
- Python实例手册
在电脑中突然发现一个这么好的资料,雪松大神制作,不敢独享,特与大家共享.连他的广告也一并复制了吧! python实例手册 #encoding:utf8 # 设定编码-支持中文 0说明 手册制作: 雪松 ...
- python基础之内置函数(一)
内建函数都在 _builtins_ 里面 (1)abs() 取绝对值 adb(-10) 10 (2)bool()将参数转换成布尔型,返回值是True或False 参数是数字时,0返回False,其他任 ...