配置
关于ActiveMQ的配置有几个关键的文件,activemq.xml,jetty.xml和jetty-realm.properties,这几个文件全都在ActiveMQ文件的conf文件夹下
首先我们来看jetty-realm.properties文件
这两行分别代表的是admin和user两个用户,用户admin登录账号密码都是admin,user也一样,我们可以通过修改这个文件进行一些操作,增加用户,修改密码等,需要注意的是冒号后面的第一个是密码,第二个是账号,如我们想要把admin的密码改成123
修改之后保存重启我们的ActiveMQ,从http://localhost:8161登录我们管理界面输入新的账号密码才能进入。
jetty.xml文件中是控制台的安全配置
<bean id="securityConstraint" class="org.eclipse.jetty.util.security.Constraint">
<property name="name" value="BASIC" />
<property name="roles" value="user,admin" />
<!-- set authenticate=false to disable login -->
<property name="authenticate" value="true" />
</bean>
里面的<property name="authenticate" value="true" /> true代表需要认证,false不需要认证。
这上面两个文件中账号密码是我们登录http://localhost:8161/admin/时的安全配置。
如果需要配置连接的用户管理,可以在 activemq.xml中的broker内配置上这些内容
<plugins>
<simpleAuthenticationPlugin>
<users>
<authenticationUser username="admin" password="123" groups="admins,publishers,consumers"/>
<authenticationUser username="publisher" password="publisher" groups="publishers,consumers"/>
<authenticationUser username="consumer" password="consumer" groups="consumers"/>
<authenticationUser username="guest" password="guest" groups="guests"/>
</users>
</simpleAuthenticationPlugin>
</plugins>
在 activemq.xml中的broker标签里面有这么一段配置
<persistenceAdapter>
<kahaDB directory="${activemq.data}/kahadb"/>
</persistenceAdapter>
这里面说的是ActiveMQ消息存储方式,默认的是kahadb存储,KahaDB是默认的持久化策略,所有消息顺序添加到一个日志文件中,同时另外有一个索引文件记录指向这些日志的存储地址,还有一个事务日志用于消息回复操作。是一个专门针对消息持久化的解决方案,它对典型的消息使用模式进行了优化。
存储位置为ActiveMQ文件夹下的data->kahadb,这里面有四个文件
1.db.data 它是消息的索引文件,本质上是B-Tree(B树),使用B-Tree作为索引指向db-*.log里面存储的消息
2.db.redo 用来进行消息恢复
3.db-.log 存储消息内容。新的数据以APPEND的方式追加到日志文件末尾。属于顺序写入,因此消息存储是比较 快的。默认是32M,达到阀值会自动递增
4.lock文件 锁,写入当前获得kahadb读写权限的broker ,用于在集群环境下的竞争处理
修改持久化方式
在这里我们以jdbc为例进行持久化,首先在 activemq.xml中修改持久化方式
<persistenceAdapter>
<!-- <kahaDB directory="${activemq.data}/kahadb"/> -->
<jdbcPersistenceAdapter dataSource="#mysql-ds" createTablesOnStartup="true" />
</persistenceAdapter>
然后在beans中配置数据源
<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/activemq?relaxAutoCommit=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
<property name="maxActive" value="200"/>
<property name="poolPreparedStatements" value="true"/>
</bean>
然后需要在lib文件夹下添加这几个jar包
配置完成后重启ActiveMQ,启动上一节我们的Sender.java,打开数据库就可以看到有三个表已经创建了
1:activemq_acks:用于存储订阅关系。如果是持久化Topic,订阅者和服务器的订阅关系在这个表保存。
2:activemq_lock:在集群环境中才有用,只有一个Broker可以获得消息,称为Master Broker,其他的只能作为备份等待Master Broker不可用,才可能成为下一个Master Broker。这个表用于记录哪个Broker是当前的Master Broker。:
3:activemq_msgs:用于存储消息,Queue和Topic都存储在这个表中。
查询我们的activemq_msgs表就可以看到刚发送的100条消息了
然后我们运行消息接收者Receiver.java,运行成功之后再看我们的数据库就会发现这些数据已经被消费了
JMS事务
在一个JMS客户端,可以使用本地事务来组合消息的发送和接收。JMS Session接口提供了commit和 rollback方法。事务提交意味着生产的所有消息被发送,消费的所有消息被确认;事务回滚意味着生产
的所有消息被销毁,消费的所有消息被恢复并重新提交,除非它们已经过期。 事务性的会话总是牵涉到
事务处理中,commit或rollback方法一旦被调用,一个事务就结束了,而另一个事务被开始。关闭事务
性会话将回滚其中的事务。 需要注意的是,如果使用请求/回复机制,即发送一个消息,同时希望在同
一个事务中等待接收该消息的回复,那么程序将被挂起,因为知道事务提交,发送操作才会真正执行。
需要注意的还有一个,消息的生产和消费不能包含在同一个事务中。
session的【三种签收方式】
[ Session.AUTO_ACKNOWLEDGE ]
当客户端从receiver或onMessage成功返回时,Session自动签收客户端的这条消息的收条。
[ Session.CLIENT_ACKNOWLEDGE ]
客户端通过调用消息(Message)的acknowledge方法签收消息。在这种情况下,签收发生在Session层面:签收一个已经消费的消息会自动地签收这个Session所有已消费的收条。
[ Session.DUPS_OK_ACKNOWLEDGE ]
Session不必确保对传送消息的签收,这个模式可能会引起消息的重复,但是降低了Session的开销,所以只有客户端能容忍重复的消息,才可使用。
下面我们写个案例
/**
* 消息发送者
*/
public class Sender {
public static void main(String[] args) throws JMSException {
//1、获取连接工厂
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(
"admin",//用户名
"123",//密码
"tcp://localhost:61616"//连接地址
);
//2、获取一个向ActiveMQ的连接
Connection connection = activeMQConnectionFactory.createConnection();
//3、获取session
//参数true表示开启事务,false不开启
//对于第二个参数:
//AUTO_ACKNOWLEDGE = 1 自动确认
//CLIENT_ACKNOWLEDGE = 2 客户端手动确认
//DUPS_OK_ACKNOWLEDGE = 3 自动批量确认
//SESSION_TRANSACTED = 0 事务提交并确认//必须是事务模式
Session session = connection.createSession(true, Session.CLIENT_ACKNOWLEDGE);
//4、存到目的地
Queue queue = session.createQueue("queuename");
//5、向目的地写入消息
//producer-->创建者
//consumer-->消费者
//5.1、创建消息
MessageProducer producer = session.createProducer(queue);
//5.2、向目的地写入消息
for (int i = 0; i < 100; i++) {
TextMessage textMessage = session.createTextMessage("hello" + i);
producer.send(textMessage);
}
session.commit();
//6、关闭连接
session.close();
System.out.println("连接已关闭");
}
}
这个时候我们看数据库,数据已经发送成功了
然后我们看消息接收者
/**
* 消息接收者
*/
public class Receiver {
public static void main(String[] args) throws JMSException {
//1、获取连接工厂
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(
"admin",//用户名
"123",//密码
"tcp://localhost:61616"//连接地址
);
//2、获取一个向ActiveMQ的连接
Connection connection = activeMQConnectionFactory.createConnection();
//启动连接
connection.start();
//3、获取session
Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
//4、存到目的地
Destination queue = session.createQueue("queuename");
//5、获取消息
MessageConsumer consumer = session.createConsumer(queue);
while (true){
TextMessage receive = (TextMessage) consumer.receive();
// receive.acknowledge();
// session.commit();
System.out.println("message:"+receive.getText());
}
}
}
需要注意的是消费者的session与生产者的session签收模式保持一致,生产者启用事务了,但是消费者这里事务不要启用,不然会一直消费。
代码里的receive.acknowledge();现在是注释状态,这个时候idea的控制台已经输出了接收到的消息内容,但是数据库里面的数据还在。


这是因为消息接收者没有确认签收,把receive.acknowledge();的注释放开,重新跑一下接收者就可以了。
来源:CSDN
作者:刘通同童铜统砼
链接:https://blog.csdn.net/qq_38670578/article/details/104546494