一、在linux系统上搭建activemq单机版
1、下载activemq解压版包
2、在linux系统上安装单机版
二、结合spring+maven搭建activemq客户端
1、编写pom.xml文件
<dependencies>
<!-- spring-jms -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<!-- javax -jms -->
<dependency>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
<version>1.1</version>
</dependency>
<!-- active all -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.9.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-client</artifactId>
<version>5.9.0</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-core</artifactId>
<version>5.7.0</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-pool</artifactId>
<version>5.9.0</version>
</dependency>
<dependency>
<groupId>org.apache.activemq.protobuf</groupId>
<artifactId>activemq-protobuf</artifactId>
<version>1.1</version>
</dependency>
<!-- spring 上下文 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<!-- spring 测试包 项目上线后可以去掉 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.0.4.RELEASE</version>
</dependency>
<!-- spring web包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<!-- spring 核心包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<!-- spring bean包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<!-- spring aop包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
</dependencies>
导入的maven约束如上所示如果需要导入不一样版本的jar包约束 方式如下:访问http://mvnrepository.com/
或者直接在百度里面搜索 包名+maven 直接访问相应jar包的界面
2、编写spring配置文件
jms.broker.url=failover:(tcp://10.0.1.227:61616,tcp://10.0.1.227:61617,tcp://10.0.1.227:61618)
jms.queue.name=str.queue
jms.userName=admin
jms.password=admin
jms.connections.num=10
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:amq="http://activemq.apache.org/schema/core"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core-5.8.0.xsd">
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<!-- 表示加载的顺序 -->
<property name="order" value="1" />
<!-- 是否忽略不可以解析的Placeholder 当加载多个配置文件时就需要配置true-->
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="location">
<value>classpath:activemqconfig.properties</value>
</property>
</bean>
<!-- Activemq connection factory -->
<bean id="amqConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<constructor-arg index="0" value="${jms.useName}"/>
<constructor-arg index="1" value="${jms.password}"/>
<constructor-arg index="2" value="${jms.broker.url}"/>
<!-- 表示异步发送消息 -->
<property name="useAsyncSend" value="true"/>
</bean>
<!-- 配置连接池 -->
<bean id="pooledConnectionFactoryBean" class="org.apache.activemq.pool.PooledConnectionFactoryBean">
<property name="connectionFactory" ref="amqConnectionFactory"/>
<property name="maxConnections" value="${jms.connections.num}"/>
</bean>
<!-- ConnectionFactory Definition -->
<bean id="connectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="pooledConnectionFactoryBean"/>
</bean>
<!-- JmsTemplate Definition -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="defaultDestinationName" value="${jms.queue.name}"/>
</bean>
</beans>
配置监听类的方式的消息消费方配置文件的编写:activemqCustomer.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:amq="http://activemq.apache.org/schema/core"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core-5.8.0.xsd">
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="order" value="1" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="location">
<value>classpath:activemqconfig.properties</value>
</property>
</bean>
<!-- Activemq connection factory -->
<bean id="amqConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<constructor-arg index="0" value="${jms.useName}"/>
<constructor-arg index="1" value="${jms.password}"/>
<constructor-arg index="2" value="${jms.broker.url}"/>
<!-- 表示异步发送消息 -->
<property name="useAsyncSend" value="true"/>
</bean>
<!-- 配置连接池 -->
<bean id="pooledConnectionFactoryBean" class="org.apache.activemq.pool.PooledConnectionFactoryBean">
<property name="connectionFactory" ref="amqConnectionFactory"/>
<property name="maxConnections" value="${jms.connections.num}"/>
</bean>
<!-- ConnectionFactory Definition -->
<bean id="connectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="pooledConnectionFactoryBean"/>
</bean>
<!-- 自定义消息监听类 -->
<bean id="messageReceiver" class="com.xxxx.receiver.ActiveMqReceiverListener"/>
<!-- 配置消息监听器 -->
<bean class="org.springframework.jms.listener.SimpleMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destinationName" value="${jms.queue.name}"/>
<property name="messageListener" ref="messageReceiver"/>
</bean>
<!-- 自定义消息监听类 -->
<bean id="messageReceiverTwo" class="com.xxxx.receiver.ActiveMqReceiverListenerTwo"/>
<!-- 配置消息监听器 -->
<bean class="org.springframework.jms.listener.SimpleMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destinationName" value="${jms.queue.nameTwo}"/>
<property name="messageListener" ref="messageReceiverTwo"/>
</bean>
</beans>
MessageProducer producer = session.createrProducer("目标队列")
File file = new File("c:\\a.txt");
FileInputStream in = new FileInputStream(file);
byte[] bytes = new byte[1024];
int i = -1;
while((i=in.read(bytes))!=-1) {
message = session.createStreamMessage();
//这个方法实现在消息队列中存储键值对信息 文件名作为参数在消费方获取
message.setStringProperty("filename",filename);
//将messge中的信息进行清除
message.clearBody();
message.writeBytes(bytes);
producer.send(message);
}
3、消息发送方与消息消费方
- import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.Session;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
public class Sender {
private JmsTemplate jmsTemplate;
//getter and setter
public JmsTemplate getJmsTemplate() {
return jmsTemplate;
}
public void setJmsTemplate(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
public void sendInfo() {
jmsTemplate.send(new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
MapMessage message = session.createMapMessage();
message.setString("lastName", "ppp");
return message;
}
});
}
}
public class QueueMessageListener implements MessageListener {
//当收到消息时,自动调用该方法。
public void onMessage(Message message) {
TextMessage tm = (TextMessage) message;
try {
System.out.println("ConsumerMessageListener收到了文本消息:\t"
+ tm.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
- }
三、搭建activemq集群
1、搭建zookeeper集群
1.2、解压zookeeper安装包
在安装包的conf路径下的zoo_sample.cfg文件 复制一份到当前目录 改名为zoo.cfg
tickTime这个时间是作为zookeeper服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是说每个tickTime时间就会发送一个心跳。
initLimit这个配置项是用来配置zookeeper接受客户端(这里所说的客户端不是用户连接zookeeper服务器的客户端,而是zookeeper服务器集群中连接到leader的follower 服务器)初始化连接时最长能忍受多少个心跳时间间隔数。
当已经超过10个心跳的时间(也就是tickTime)长度后 zookeeper 服务器还没有收到客户端的返回信息,那么表明这个客户端连接失败。总的时间长度就是 10*2000=20秒。
syncLimit这个配置项标识leader与follower之间发送消息,请求和应答时间长度,最长不能超过多少个tickTime的时间长度,总的时间长度就是5*2000=10秒。
dataDir顾名思义就是zookeeper保存数据的目录,默认情况下zookeeper将写数据的日志文件也保存在这个目录里;
clientPort这个端口就是客户端连接Zookeeper服务器的端口,Zookeeper会监听这个端口接受客户端的访问请求;
----来自 linux就该这么学 用户 http://www.cnblogs.com/linuxprobe/p/5851699.html
注意在这里的clientPort的参数 如果在同一台虚拟机上配置三台zookeeper,需要修改端口号
1.4、创建data文件夹 log文件夹 myid唯一标志文件
在zookeeper主目录下创建data与log文件夹 在data文件夹下创建myid文件
touch myid 在每个文件下插入内容 echo 1 > myid 这个数字表示当前的zookeeper的编号
1.5、复制当前修改好的zookeeper到两个新的文件夹下
修改myid的唯一标识,echo 2 > myid 如果在同一台虚拟机上搭建的集群 需要修改clientPort参数
1.6、分别运行三台zookeeper
./zkServer.sh start
运行前两台zookeeper时会报错,但是不要担心,运行第三台后会发生这样的显示:
2、搭建activemq集群
1、下载activemq解压包 解压(单机版已经讲解)
2、修改conf文件夹下的activemq.xml文件
<persistenceAdapter>
<!--<kahaDB directory="${activemq.data}/kahadb"/>-->
<replicatedLevelDB
directory="${activemq.data}/leveldb"
replicas="3"
bind="tcp://10.0.1.227:61616"
hostname="localhost"
zkAddress="10.0.1.227:2181,10.0.1.227:2182,10.0.1.227:2183"
zkPath="/activemq/leveldb-stores"
sync="local_disk"
/>
</persistenceAdapter>
3、客户端的连接配置
四、activemq的优化配置
<!-- 配置消息监听器 -->
<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destinationName" value="${jms.queue.name}"/>
<property name="messageListener" ref="messageReceiver"/>
</bean>
多个队列之间用逗号隔开。
2、消息队列的垃圾队列管理
当我们频繁的创建了消息队列之后通过在activemq.xml文件中添加配置 实现定期删除不用的消息队列
也可以通过配置,使得broker可以自动探测到无用的队列(一定时间内为空的队列)并删除掉,回收响应资源。
配置如下:
<broker xmlns="http://activemq.apache.org/schema/core" schedulePeriodForDestinationPurge="10000">
<destinationPolicy>
<policyMap>
<policyEntries>
<policyEntry queue=">" gcInactiveDestinations="true" inactiveTimoutBeforeGC="30000"/>
</policyEntries>
</policyMap>
</destinationPolicy>
</broker>
schedulePeriodForDestinationPurge:10000 每十秒检查一次,默认为0,此功能关闭
gcInactiveDestinations: true 删除掉不活动队列,默认为false
inactiveTimoutBeforeGC:30000 不活动30秒后删除,默认为60秒
1、控制台安全配置,打开conf/jetty.xml文件
<bean id="securityConstraint" class="org.eclipse.jetty.http.security.Constraint">
<property name="name" value="BASIC" />
<property name="roles" value="admin" />
<property name="authenticate" value="false" />
</bean>
将“false”改为“true”即可。用户名和密码存放在conf/jetty-realm.properties文件中。
2、生产者和消费者连接MQ需要密码
打开conf/activemq.xml文件,在<broker>标签里的<systemUsage>标签前加入:
<plugins>
<simpleAuthenticationPlugin>
<users>
<authenticationUser username="${activemq.username}" password="${activemq.password}" groups="users,admins"/>
</users>
</simpleAuthenticationPlugin>
</plugins>
注意必须在<systemUsage>标签前,否则启动ActiveMQ会报错。
连接activemq生产者与消费者的账户与密码配置位置为
conf路径下的credentials.properties文件
1、KahaDb和AMQ Message Store两种持久方式如何选择?
官方:
From 5.3 onwards - we recommend you use KahaDB - which offers improved scalability and recoverability over the AMQ Message Store.
The AMQ Message Store which although faster than KahaDB - does not scales as well as KahaDB and recovery times take longer.
非官方:
kaha文件系统实际上上是一个文件索引系统,有两部分组成,一个是数据文件系统,由一个个独立的文件组成,缺省文件大小是32M大(可配置),另外一个是索引文件系统,记录消息在数据文件中的位置信息以及数据文件中的空闲块信息。数据文件是存储到硬盘上的,索引文件是缓存在内存中的。所以这个存储系统对大消息存储有利,象我们的memberId之类的文本消息,实际上是浪费,索引比消息还大,哈。
推荐: Amq持久方式
理由:虽然官方推荐使用KahaDB持久方式,但其提到的优势:可伸缩性和恢复性较好,对于我们实际的应用意义不大。从我们自己的使用经验来看,KahaDB持久方式,Data文件是一个大文件(感觉文件过大后,造成队列服务瘫死的可能性会增大),从官网的相关配置(附录1)也找不到哪里可以设置数据的文件的最大Size。)而Amq持久方式可以设置Data文件最大Size,这样可以保证即时消息积压很多,Data文件也不至于过大。
<!-- 消息持久化方式 -->
<persistenceAdapter>
<amqPersistenceAdapter directory="${<SPAN class=hilite1>activemq</SPAN>.base}/data"/>
</persistenceAdapter>
4、java main方法方式发送与接收
public static void main(String[] args) {
//初始化消息队列名字
String queueName = "";
String username = "";
String password = "";
String brokerURL = "";
//连接mq并发送数据
try {
//获取连接工厂 通过构造方法初始化 用户名 密码 连接地址
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(username,password,brokerURL);
//获取连接对象
Connection connection = connectionFactory.createConnection();
//开启连接对象
connection.start();
/* 获取session会话对象
connection.createSession(paramA,paramB);
paramA 取值有:
1、true:支持事务
为true时:paramB的值忽略, acknowledgment mode被jms服务器设置为SESSION_TRANSACTED 。
2、false:不支持事务
为false时:paramB的值可为Session.AUTO_ACKNOWLEDGE、Session.CLIENT_ACKNOWLEDGE、DUPS_OK_ACKNOWLEDGE其中一个。
paramB 取值有:
1、Session.AUTO_ACKNOWLEDGE:为自动确认,客户端发送和接收消息不需要做额外的工作。
2、Session.CLIENT_ACKNOWLEDGE:为客户端确认。客户端接收到消息后,必须调用javax.jms.Message的acknowledge方法。jms服务器才会删除消息。
3、DUPS_OK_ACKNOWLEDGE:允许副本的确认模式。一旦接收方应用程序的方法调用从处理消息处返回,会话对象就会确认消息的接收;而且允许重复确认。在需要考虑资源使用时,这种模式非常有效。
4、SESSION_TRANSACTED
*/
Session session = connection.createSession(Boolean.FALSE,Session.AUTO_ACKNOWLEDGE);
//设置订阅的目的地 String参数表示 通过名字进行区分
Destination destination = session.createQueue(queueName);
//获取消息发送者
MessageProducer producer = session.createProducer(destination);
//获取发送消息的载体 这里不用多态是因为 父类Message里面没有子类的特有方法setText
TextMessage message = session.createTextMessage();
//向消息里面添加key-value键值对 可以作为接收时的参数
message.setStringProperty("key","value");
//需要发送的消息文本
message.setText("sendActivemqMessageContent");
//发送消息
producer.send(message);
//流格式的消息 创建与发送
/*
创建StreamMessage流消息 通过向流消息里面写文件流发送消息
byte[] content = new byte[bytelength];
int i = 0;
InputStream ins = new FileInputStream(new File(filepath));
BufferedInputStream bins = new BufferedInputStream(ins);
while ((i = bins.read(content)) > 0) {
message = session.createStreamMessage();
message.setStringProperty("FILE_NAME", FILE_NAME);
message.setStringProperty("COMMAND", "sending");
message.clearBody();
message.writeBytes(content,0,i);
producer.send(message);
}
bins.close();
ins.close();
*/
} catch (Exception e) {
e.printStackTrace();
}
接收方代码:
public List<Object> receiveMessage(Message message) throws Exception {
List<Object> result = new ArrayList<Object>();
String file_name = "";
boolean appended = false;
try {
if (message == null) {
}
if (message instanceof StreamMessage) {
StreamMessage streamMessage = (StreamMessage) message;
String command = streamMessage.getStringProperty("COMMAND");
if ("start".equals(command)) {
appended = false;
result.add(appended);
//开始发消息时创建文件输出流
file_name = message.getStringProperty("FILE_NAME");
fos = new FileOutputStream(cfilepath + file_name, appended);
bos = new BufferedOutputStream(fos);
}
if ("sending".equals(command)) {
byte[] content = new byte[bytelength];
int i = 0;
try {
while ((i = streamMessage.readBytes(content)) > 0) {
bos.write(content,0,i);
bos.flush();
}
} catch (MessageEOFException e) {
throw e;
}
result.add(appended);
}
if ("end".equals(command)) {
file_name = message.getStringProperty("FILE_NAME");
String sign = message.getStringProperty("type");
appended = true;
result.add(appended);
result.add(file_name);
result.add(sign);
//关流
bos.close();
fos.close();
}
}
} catch (JMSException e) {
}
return result;
}
5、事务与消息确认与异步发送
//获取连接工厂 通过构造方法初始化 用户名 密码 连接地址
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(username,password,brokerURL);
//获取连接对象
Connection connection = connectionFactory.createConnection();
//开启连接对象
connection.start();
/* 获取session会话对象
connection.createSession(paramA,paramB);
paramA 取值有:
1、true:支持事务
为true时:paramB的值忽略, acknowledgment mode被jms服务器设置为SESSION_TRANSACTED 。
2、false:不支持事务
为false时:paramB的值可为Session.AUTO_ACKNOWLEDGE、Session.CLIENT_ACKNOWLEDGE、DUPS_OK_ACKNOWLEDGE其中一个。
paramB 取值有:
1、Session.AUTO_ACKNOWLEDGE:为自动确认,客户端发送和接收消息不需要做额外的工作。
2、Session.CLIENT_ACKNOWLEDGE:为客户端确认。客户端接收到消息后,必须调用javax.jms.Message的acknowledge方法。jms服务器才会删除消息。
3、DUPS_OK_ACKNOWLEDGE:允许副本的确认模式。一旦接收方应用程序的方法调用从处理消息处返回,会话对象就会确认消息的接收;而且允许重复确认。在需要考虑资源使用时,这种模式非常有效。
4、SESSION_TRANSACTED
*/
Session session = connection.createSession(Boolean.FALSE,Session.AUTO_ACKNOWLEDGE);
我们在发送消息时会创建一个上下文对象,在创建session会话对象时需要配置两个参数:
参数2:消息确认的方式
如果开启事务消息确认的方式会默认为是:SESSION_TRANSACTED。
如果在消费方不对异常捕获处理,就会导致消息不停的重发,导致消息消费缓慢。