消息中间件之ActiveMQ(2)配置和事务

烈酒焚心 提交于 2020-02-28 22:13:12

配置

关于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条消息了
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200228112119404.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4NjcwNTc4,size_16,color_FFFFFF,t_70
然后我们运行消息接收者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();的注释放开,重新跑一下接收者就可以了。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!