ActiveMq笔记

消息队列:

作用:

消息队列中间件是分布式系统中的重要组件,主要解决异步消息,应用解耦,流量削峰等问题,从而实现高性能,高可用,可伸缩和最终一致性的架构

今天就来分享一个ActiveMq(apache开源项目)的demo
ActiveMq的下载地址:http://activemq.apache.org/ 解压即能用,运行批处理文件即运行(activemq.bat),
运行ActiveMq后可以访问到web界面:http://localhost:8161/
在这里插入图片描述
这里的登陆账号,是activemq默认的,可以在配置文件里找到(jetty-realm.properties)

# Defines users that can access the web (console, demo, etc.)
# username: password [,rolename ...]
admin: admin, admin
user: user, user

在这里插入图片描述
在这里插入图片描述

入门Demo:

记得要先打开ActiveMq服务,同时先用消息消费者订阅broker,然后再运行消息生产者

package com.chenfu;

import org.apache.activemq.ActiveMQConnectionFactory;
import org.junit.Test;

import javax.jms.*;

/**
 * @Author: romantic_ke@163.com
 * @Description:
 * @Date: 2019/1/9 22:26
 */
public class ActiveMqTest {

    //        消息发送端
    @Test
    public void msgProdecer() throws JMSException {
//        获得工厂
        ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");
//        拿到连接
        Connection connection = factory.createConnection();
//        打开连接
        connection.start();
//        是否开启事务,设置应答方式
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//        设置中间件
        Topic topic = session.createTopic("chenfuTop");
//        创建生产者
        MessageProducer producer = session.createProducer(topic);
//        创建消息
        TextMessage textMessage = session.createTextMessage();
//        设置消息
        textMessage.setText("ping");
//        发送消息
        producer.send(textMessage);

//        关闭连接
        producer.close();
        session.close();
        connection.close();
    }

    //     消息接收端
    @Test
    public void msgConsumer() throws JMSException {

//        获得工厂
        ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory();
        factory.setBrokerURL("tcp://localhost:61616");
//        获得连接
        Connection connection = factory.createConnection();
//        打开连接
        connection.start();
//        获得连接
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//        创建主题
        Topic topic = session.createTopic("chenfuTop");
//        创建消费者
        MessageConsumer consumer = session.createConsumer(topic);

//            指定消息监听器
        consumer.setMessageListener(new MessageListener() {
            public void onMessage(Message message) {
                TextMessage textMessage = (TextMessage) message;
                System.out.println("message:" + textMessage);
                try {
                    System.out.println("连接到客户端:" + textMessage.getText());
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
        });
        while (true) {

        }
    }
}

持久化

默认的持久化(到文件)

activemq默认的持久化操作是讲消息保存在文件里,在activemq.xml配置文件里可以看到

<persistenceAdapter>
    <kahaDB directory="${activemq.data}/kahadb"/>
</persistenceAdapter>

在这里插入图片描述
采用默认持久化方式的案例:

package com.chenfu;

import org.apache.activemq.ActiveMQConnectionFactory;
import org.junit.Test;

import javax.jms.*;

/**
 * @Author: romantic_ke@163.com
 * @Description:
 * @Date: 2019/1/10 22:51
 */
public class PersistenceMqTest {

    @Test
    public void createProducer() throws JMSException {
//        创建获得connection的工厂
        ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory();
//        设置工厂的连接参数
        factory.setBrokerURL("tcp://localhost:61616");
//        获得activeMq的连接
        Connection connection = factory.createConnection();
//        打开连接
        connection.start();
//        创建会话
//        第一个参数是是否是开启事务,第二个参数是设置消息的应答机制
        Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
//        创建主题
        Topic chenfuTopic = session.createTopic("chenfuTopic");
//        创建生产者
        MessageProducer producer = session.createProducer(chenfuTopic);
//        创建消息
        TextMessage textMessage = session.createTextMessage();
//        设置消息内容
        textMessage.setText("兄弟,借个火");
//        生产者发送消息
//        第一个参数是消息,第二个参数是是否启用持久化,第三个是设置优先级,第四个参数是持久化时间
        producer.send(textMessage,DeliveryMode.PERSISTENT,1,1000 * 60 * 60 * 24);
//        关闭连接
        producer.close();
        session.close();
        connection.close();
    }

    @Test
    public void createConsumer() throws JMSException {
        ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory();
        factory.setBrokerURL("tcp://localhost:61616");
        Connection connection = factory.createConnection();
        String clientId = "chenfu-01";
        connection.setClientID(clientId);
        connection.start();
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        Topic chenfuTopic = session.createTopic("chenfuTopic");
        TopicSubscriber subscriber = session.createDurableSubscriber(chenfuTopic, clientId);
        subscriber.setMessageListener(new MessageListener() {
            public void onMessage(Message message) {
                TextMessage textMessage = (TextMessage) message;
                try {
                    System.out.println(textMessage.getText());
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
        });
        while (true){

        }
    }
}

以上两种的区别:
采用了持久化方案后,消息生产件在发送消息的时候,需要调用能设置持久化方案的发送方法

// 第一个参数是消息,第二个参数是是否启用持久化,第三个是设置优先级,第四个参数是持久化时间
producer.send(textMessage,DeliveryMode.PERSISTENT,1,1000 * 60 * 60 * 24);

消息的消费者是支持事务的消费者类型
同时这种消费者被创建的时候需要一个客户端ID

String clientId = "chenfu-01";
connection.setClientID(clientId);
		...
TopicSubscriber subscriber = session.createDurableSubscriber(chenfuTopic, clientId);

持久化到数据库

1.将数据库的驱动jar放在ActiveMq的lib目录下(我这里用的mysql数据库),因为我在这里用的druid连接池,所以需要将druid的jar也放入【PS:注意mysql驱动jar包的版本,我第一次用了一个8.0的版本一直连接失败,反复建立连接】
2.修改ActiveMq的配置文件(activemq.xml),将默认的持久化规则修改为持久化到数据库

<persistenceAdapter>
	<jdbcPersistenceAdapter  dataDirectory="${activemq.base}/data" dataSource="#druid-ds"/>
</persistenceAdapter>
<bean id="druid-ds" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
	<property name="driverClassName" value="com.mysql.jdbc.Driver" />
	<property name="url" value="jdbc:mysql:///activemq" />
	<!-- <property name="url" value="jdbc:mysql://localhost:3306/activemq?relaxAutoCommit=true" /> -->
	<property name="username" value="root" />
	<property name="password" value="root" />
	<property name="poolPreparedStatements" value="false" />
</bean>

此时开启ActiveMq服务后,可以看到生成了三张表
在这里插入图片描述
在这里插入图片描述
此时的消息表是空的
在这里插入图片描述
启动消息订阅者后,acks表多了一条记录
在这里插入图片描述
关闭消息订阅者,运行消息发布者,msg表生成一条记录
在这里插入图片描述
再次运行消息订阅者,控制台输出了消息
在这里插入图片描述
持久化到MySQL数据库的Demo:

package com.chenfu;

import org.apache.activemq.ActiveMQConnectionFactory;
import org.junit.Test;

import javax.jms.*;

/**
 * @Author: romantic_ke@163.com
 * @Description:
 * @Date: 2019/1/11 22:49
 */
public class PersistenceMqToMysqlTest {

    @Test
    public void msgProducer() throws JMSException {
        ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory();
        factory.setBrokerURL("tcp://localhost:61616");
        Connection connection = factory.createConnection();
        connection.start();
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        Topic topic = session.createTopic("chenfuTopic");
        MessageProducer producer = session.createProducer(topic);
        TextMessage msg = session.createTextMessage();
        msg.setText("啊哈!very nice!");
        producer.send(msg,DeliveryMode.PERSISTENT,1,1000 * 60 *60 *24);
        producer.close();
        session.close();
        connection.close();
    }

    @Test
    public void msgConsumer() throws JMSException {
        ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory();
        factory.setBrokerURL("tcp://localhost:61616");
        Connection connection = factory.createConnection();
        String chenfuClient = "chenfu-02";
        connection.setClientID(chenfuClient);
        connection.start();
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        Topic topic = session.createTopic("chenfuTopic");
        TopicSubscriber subscriber = session.createDurableSubscriber(topic, chenfuClient);
        sout(subscriber);
        while (true){

        }
    }

    static void sout(TopicSubscriber subscriber) throws JMSException {
        subscriber.setMessageListener(new MessageListener() {
            public void onMessage(Message message) {
                TextMessage textMessage = (TextMessage)message;
                try {
                    System.out.println(textMessage.getText());
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

zookeeper+activemq集群实现高可用

使用ZooKeeper实现的Master-Slave实现方式,是对ActiveMQ进行高可用的一种有效的解决方案。高可用的原理:使用ZooKeeper(集群)注册所有的ActiveMQ Broker。只有其中的一个Broker可以对外提供服务(也就是Master节点),其他的Broker处于待机状态,被视为Slave。如果Master因故障而不能提供服务,则利用ZooKeeper的内部选举机制从Slave中选举出一个Broker充当Master节点,继续对外提供服务。通过zookeeper+activeMQ实现的集群,可以有效的排除单点故障引起的服务中断
1、准备三台虚拟机,IP分别为:
192.168.106.129
192.168.106.130
192.168.106.131
2.需要预装好jdk环境
3.准备好zookeeper和activema的安装包

安装zookeeper

第一步:将下载的zookeeper安装包分别上传到三台虚拟机,分别进行下面操作
第二步:解压安装包
tar –zxvf zookeeper-3.4.10.tar.gz –C /usr/local
第三步:将/usr/local目录下的zookeeper-3.4.10改名
mv zookeeper-3.4.10 zookeeper
第四步:在zookeeper目录下创建两个目录data和logs,分别存放数据和日志
cd /usr/local/zookeeper/
mkdir data
mkdir logs
第五步:将zookeeper/conf目录下的zoo_sample.cfg文件名改为zoo.cfg
cd /usr/local/zookeeper/conf/
mv zoo_sample.cfg zoo.cfg

安装activeMQ

第一步:将下载的activemq安装包分别上传到三台虚拟机,分别进行下面操作
第二步:解压安装包
tar –zxvf apache-activemq-5.14.0-bin.tar.gz –C /usr/local
第三步:将/usr/local目录下的apache-activemq-5.14.0改名
mv apache-activemq-5.14.0 activemq

集群配置

第一步:在/usr/local/zookeeper/data下创建myid文件,文件内容为1。同理,其他虚拟机中也创建myid文件,内容分别为2和3
第二步:修改/usr/local/zookeeper/conf/zoo.cfg文件,加入以下内容

dataDir=/usr/local/zookeeper/data/
dataLogDir=/usr/local/zookeeper/logs
server.1=192.168.106.129:2888:3888 
server.2=192.168.106.130:2888:3888 
server.3=192.168.106.131:2888:3888 

第三步:分别启动三台zookeeper(需要关闭防火墙)
关闭防火墙(centos7关闭防火墙的指令如下)
systemctl stop firewalld.service
查看防火墙
firewall-cmd --state
在这里插入图片描述
启动zookeeper
/usr/local/zookeeper/bin/zkServer.sh start
第四步:修改/usr/local/activemq/conf目录下的activemq.xml
vim /usr/local/activemq/conf/activemq.xml
修改brokerName=”activemq-cluster”
将文件中持久化适配器改为

<persistenceAdapter>  
	  <replicatedLevelDB
	    directory="${activemq.data}/leveldb"
	    replicas="3"
	    bind="tcp://0.0.0.0:0"
	    zkAddress="192.168.106.129:2181,192.168.106.130:2181,192.168.106.131:2181"
	    hostname="192.168.106.129"  
		zkPath="/activemq/leveldb-stores"/>
</persistenceAdapter>

注意:其他虚拟机中修改时hostname需要改为对应的ip
第五步:启动mq服务
./activemq start
从外部访问
在这里插入图片描述
可以看到,此时的130节点是不提供服务的,131也是
在这里插入图片描述
在这里插入图片描述
此时,停掉master节点(129)
在这里插入图片描述
130节点开始提供服务
在这里插入图片描述
最后抛一个疑问:
博主发现,当有两台节点都停掉(activemq)之后,还剩一台机器存活的时候,此时的最后一台机器没有独自去提供服务,当再开启一台节点后,才会重新去提供服务,即至少要有两台节点存活(activemq)才能正常提供服务,不知道是什么原因导致的,欢迎路过的高手指点解惑

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值