ActiveMQ学习文档

MQ(Message Queue)=消息中间件,是理念(规范)activeMq是其中一个具体的产品

MQ学习前言

消息

微信消息,短信消息,邮件消息,语音留言消息…

双方约定好的格式作为一种信息载体的传输媒介,在网上发布然后让大家获得,实现信息的同步和信息			      	         的交换

中间件

既然为中间件,那么一定会分为左中右

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dyYqE2d9-1585967767084)(image/2019-06-26_100759.png)]


MQ的分类

消息队列已经逐渐成为企业IT系统内部通信的核心手段。它具有低耦合、可靠投递、广播、流量控制、最终一致性等一系列功能,成为异步RPC的主要手段之一。当今市面上有很多主流的消息中间件,如老牌的ActiveMQ、RabbitMQ,炙手可热的Kafka,阿里巴巴自主开发RocketMQ等

Kafka

RabbitMQ

RocketMQ

ActiveMQ

1.什么是消息中间件

可与OA、ERP集成的免费消息中间件Active Messenger(简称AM)是一款非常实用的企业即时通讯软件。系统提供免费的消息中间件,开放给第三方程序使用。

2.简介

消息中间件利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。

3.**JMS(Java Message Service)**的概念

它是一种与厂商无关的API,用来访问消息收发系统消息。它类似于JDBC,JDBC是可以用来访问不同关系数据库的API,而JMS则提供同样与厂商无关的访问消息收发服务的方法,这样就可以通过消息收发服务实现从一个JMS客户机向另一个JMS客户机发送消息,所需要的是厂商支持JMS。换句话说,JMS是Java平台上有关面向消息中间件的技术规范。

4.jms中关键的对象

  • ConnectionFactory 连接工厂
  • Connection
  • session,是发送和接收消息的上下文,用于创建消息生产者,消息消费者,相比rocketMQ会话session是提供事务性的;
  • destination,指定生产消息的目的地和消费消息的来源对象;生产者、消费者,由会话创建的对象,顾名思义。

5.消息机制

5.1.p2p(点对点)

  • 一个消息只有一个消费者(消息消费即删除)
  • 发送者和消费者时间上没有依赖性
  • 接收消息之后需向队列发送回执信息
  • 目的地必须是队列

契合应用场景:订单和商品,应用解耦

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CAKwR6Qy-1585967767086)(image\2.png)]

5.2.pub and sub(发布订阅)

  • 一个消息有多个消费者
  • 时间上有依赖性

契合应用场景:app的消息推送

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wIqvqCCr-1585967767087)(image\3.png)]

6.消息中间件的应用场景

  • 应用解耦
  • 异步处理
  • 流量削峰
  • 消息推送

7.ActiveMQ的引言

ActiveMQ是由Apache出品的,一款最流行的,能力强劲的消息中间件(MOM:Message Orient middleware)。并且是对消息通信规范JMS的一种具体实现

8.安装ActiveMQ

8.1.搭建java环境 安装jdk
8.1.a.安装jdk(1) yum一键安装

8.1.1.首先执行以下命令查看可安装的jdk版本:

yum -y list java*

8.1.2.选择自己需要的jdk版本进行安装,比如这里安装1.8,执行以下命令:

yum install -y java-1.8.0-openjdk-devel.x86_64

等待安装完成即可。

8.1.3.安装完成之后,查看安装的jdk版本,输入以下指令:

java -version
8.1.b.安装jdk(2) 手动安装 使用过

8.1.1.将jdk导入SFTP

8.1.2.解压压缩包

tar -zxvf jdk-8u171-linux-x64.tar.gz

8.1.3.将解压文件移动到 /usr/local/src/ 目录

mv jdk1.8.0_171 /usr/local/src/

8.1.4.修改配置文件

vim /root/.bashrc

8.1.5.配置环境变量,输入以下指令进行配置:

export JAVA_HOME=/usr/local/src/jdk1.8.0_171
export PATH=JAVA_HOME/bin:PATH
export CLASSPATH=.:JAVA_HOME/lib/dt.jar:JAVA_HOME/lib/tools.jar

8.1.6.重新加载文件

source /root/.bashrc
8.1.c.安装jdk(3) 手动安装

8.1.1.将jdk导入SFTP

8.1.2.解压压缩包

tar -zxvf jdk-8u171-linux-x64.tar.gz

8.1.3.将解压文件移动到 /usr/local/jdk/ 目录,jdk目录需要自己手动创建

mv jdk1.8.0_171 /usr/local/jdk/

8.1.4.修改配置文件

vim /etc/profile

8.1.5.配置环境变量,输入以下指令进行配置:

export JAVA_HOME=/usr/local/jdk/jdk1.8.0_171
export CLASSPATH=$:CLASSPATH:$JAVA_HOME/lib/ 
export PATH=$PATH:$JAVA_HOME/bin

8.1.6.重新加载文件

source /etc/profile
8.2.安装ActiveMq

1.官网下载最新版本

http://activemq.apache.org/

2…将ActiveMQ导入SFTP

3.解压activemq压缩包

tar -zxvf apache-activemq-5.15.3-bin.tar.gz

4.将解压文件移动到 /usr/local/src/ 目录

mv apache-activemq-5.15.3 /usr/local/src/

5.修改映射文件 将主机名映射本机的ip 默认不用改

vi /etc/sysconfig/network

vi /etc/hosts
加入 主机名映射 192.168.241.142 localhost

重启完成后用,命令查询系统主机名
hostname

6.启动ActiveMQ 进入ActiveMQ安装的bin目录

./activemq start

进入 data 目录

tail -f activemq.log   查看启动日志

7.访问web的控制界面

http://192.168.31.169:8161/admin

默认账号:admin 密码: admin

修改登录用户名密码:

#修改conf目录下的 users.properties
vim users.properties

注意:

  1. 在外部访问tomcat需要关闭防火墙 service iptables stop(centos6)
  2. 在外部访问tomcat需要关闭防火墙 (centos7)

临时关闭 systemctl stop firewalld

禁止开机启动 systemctl disable firewalld

8.3.测试访问web的控制界面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n0CzvGxe-1585967767088)(image/4.png)]

9.java操作ActiveMQ

9.1.点对点(P2P)

9.1.1.生产者

1.相关依赖

<dependency>
    <groupId>org.apache.activemq</groupId>
    <artifactId>activemq-core</artifactId>
    <version>5.7.0</version>
</dependency>

2.开发步骤

@Test
    public void testProduct() throws Exception {

        String brokeURL="tcp://192.168.31.169:61616";

        //1.连接工厂
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(brokeURL);

        //2.创建连接
        Connection connection = activeMQConnectionFactory.createConnection();

        //3.创建会话
        //第一个参数 代表第二参数是否生效开启事务  参数二 自动发送回执
        Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);

        //4.创建生产者
        Destination destination=new ActiveMQQueue("javaQueue");
        MessageProducer producer = session.createProducer(destination);

        //5.创建消息
        TextMessage textMessage = session.createTextMessage();
        textMessage.setText("hello aaa");

        //6.使用生产者发生消息
        producer.send(textMessage);

        //7.提交
        session.commit();

        //8.关闭
        producer.close();
        session.close();
        connection.close();

    }
9.1.2.消费者

开发步骤

//消费者
@Test
public void testConsumer() throws JMSException {

    String brokeURL="tcp://192.168.31.169:61616";

    //1.连接工厂
    ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(brokeURL);

    //2.创建连接
    Connection connection = activeMQConnectionFactory.createConnection();
    connection.start();  //启动连接

    //3.创建会话
    //第一个参数 代表第二参数是否生效开启事务  参数二 自动发送回执
    Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);

    //4.创建消费者
    Destination destination=new ActiveMQQueue("javaQueue");
    MessageConsumer consumer = session.createConsumer(destination);

    //6.使用消费者发消息
    TextMessage message = (TextMessage)consumer.receive();
    String text = message.getText();
    System.out.println(text);

    //7.提交
    session.commit();

    //8.关闭
    consumer.close();
    session.close();
    connection.close();
}

//消费者
@Test
public void testConsumer() throws JMSException {

    String brokeURL="tcp://192.168.31.169:61616";

    //1.连接工厂
    ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(brokeURL);

    //2.创建连接
    Connection connection = activeMQConnectionFactory.createConnection();
    connection.start();  //启动连接

    //3.创建会话
    //第一个参数 代表第二参数是否生效开启事务  参数二 自动发送回执
    Session session = connection.cr 
    //4.创建生产者
    Destination destination=new ActiveMQQueue("javaQueue");
    MessageConsumer consumer = session.createConsumer(destination);

    //6.使用生产者发生消息
    while(true){
        TextMessage message = (TextMessage)consumer.receive();
        String text = message.getText();
        if(message!=null){
            System.out.println(text);

            //7.提交
            session.commit();
        }else{
            break;
        }
    }

}

注意:当生产者生产消息之后,不管何时,只要消费者启动,就会接收到消息(非时间依赖性)

9.1.3.发送复杂数据

5种消息体格式

TextMessage: 普通字符转消息,包含一个String (重点)

MapMessage: 一个Map类型的消息,key为String类型,而值为java的基本类型 (重点)

BytesMessage: 二进制数组消息,包含一个byte[]

StreamMessage: Java数据流消息,用标准流操作顺序的填充和读取

ObjectMessage: 对象消息,包含一个可序列化的Java对象

发送和接受的消息体类型必须一致对应

生产者

创建对象

//注意:对象一定要实现Serializable接口
public class User implements Serializable {

    private String name;
    private String password;
    private Integer age;
    private Date bir;
}

消息类型

JAVA消息服务定义了6种JMS提供者必须支持的Message接口类型。尽管JMS定义了Message接口,但它并未定义它们的实现方式。这就允许提供者以它们自己的方式实现和传送消息,同时为JMS应用程序开发者维护了一个兼容的标准接口。这6个消息接口是Message和它的5个子接口:TextMessage、StreamMessage、MapMessage、ObjectMessage和ByteMessage。

Message

最简单的消息类型是javax.jms.Message,它担当了其他消息类型基接口(base interface)的角色。可按一下方式创建Message类型,并将它用作一条不含有效负载的JMS消息:

//使用者发布者创建并发送消息
Message message = pubSession .createTextMessage();
publisher.publish(message );

//接受消息
@Override
public void onMessage(Message message) {}

这种类型的消息仅仅包含了JMS消息头和消息属性,而且,它仅限用于事件通知,它仅用于事件通知。一个事件通知就是出现某些情况的一个广播、告警或者通知。如果业务场景需要一个不含有效负载的简单通知,轻量级消息类型就是实现它的最有效方式。
例如为了广播通知某个特定类中的一个异常,你可以发布一条不含有效负载的异常文本消息,如下所示:

//发送异常
try{

}catch(Exception up){
    Message message = Sesstion.createMessage();
    message.setStringProperty("Exception",up.getMessage());
    publisher.publish(message);
    throw up;
}

//接受异常
public void onMessage(Message message){
	System.out.println("Exception:"+message.getStringProperty());
}

发送消息对象

ObjectMessage

对象消息可以使用Session接口中的两个工厂方法中的一个来创建。

//对象类型的数据
ObjectMessage objectMessage = session.createObjectMessage();
	User user = new User("1","xiaohei",23,new Date());  
	objectMessage.setObject(user);
producer.send(objectMessage);

消息响应

ObjectMessage message = (ObjectMessage) consumer.receive();
User user = (User) message.getObject();
System.out.println(user);

BytesMessage

这种类型懈怠了一组原始类型字节流作为有效负载。

它可以使用应用程序的本机格式来交换数据,这种格式可能不兼容其他现有的Message类型。当JMS纯粹用于两个系统之间的消息传送时,也可以使用这种类型,而且该消息的有效负载对JMS客户端来说是透明的。
如果你使用java.io.DataInputStream和java.io.DataOutputStream,那么以这些I/O类为基础的ByteMessage接口方法,就会让你觉得熟悉。

//Byte类型的数据
BytesMessage bytesMessage = session.createBytesMessage();
    bytesMessage.writeByte((byte)12);
    bytesMessage.writeShort((short)12);
    bytesMessage.writeInt(12);
    bytesMessage.writeLong(1212);
    bytesMessage.writeFloat(12.12f);
    bytesMessage.writeDouble(8.88);
    bytesMessage.writeBoolean(true);
    bytesMessage.writeChar('a');
    bytesMessage.writeUTF("嘿嘿");
producer.send(bytesMessage);

ByteMessage是可移植性最好的而一种消息类型,因此他在于非JMS消息类型传送客户端通信时非常有用。有些情况下,一个JMS客户端可以是一类路由器,它消费来自某个消息源的消息,并将他们传送给一个目的地。路由选择应用程序可能不需要知道它们传输的数据内容,并因此可以选择使用ByteMessage将有效负载作为二进制数据,从一个位置传送到其他位置。

消息响应

BytesMessage  message = (BytesMessage ) consumer.receive();
    byte b = message.readByte();
    short i = message.readShort();
    int i1 = message.readInt();
    long l = message.readLong();
    float v1 = message.readFloat();
    double v = message.readDouble();
    boolean aBoolean = message.readBoolean();
    char c = message.readChar();
    String s = message.readUTF();

MapMessage
这种类型携带了一组键/值对作为有效负载。有效负载类似与一个Java.util.Properties对象,除了有效负载值必须是Java原始数据类型和Strings型之外。
本质上,MapMessage的工作方式类似于JMS属性:任何键/值对都可以写入有效负载。其中名称必须是一个String对象,而值则可以是String类型或原始数据类型。

//Map类型的数据
MapMessage mapMessage = session.createMapMessage();
	mapMessage.setByte("a",(byte)15);
	mapMessage.setBoolean("b",false);
	mapMessage.setString("c","哈哈");
	mapMessage.setDouble("d",89.9);
	mapMessage.setInt("e",23);
producer.send(mapMessage);

消息响应

MapMessage  message = (MapMessage ) consumer.receive();
int age = message.getInt("e");
float weight = message.getFloat("Weight");
String name = message.getString("c");
Double height = (Double)message.getObject("d");
System.out.println(message);

StreamMessage

StreamMesage携带了一个Java原始数据类型流作为有效负载。它提供了一套将格式化字节流映射为Java原始数据类型的简便方法。
StreamMessage保持了写入流时的顺序和原始数据类型,因此它适用于形式转换规则。

9.2.发布订阅(pub& sub)

9.2.1.发布者(publisher)
//发布者1
@Test
public void testPub() throws Exception {

    String brokeURL="tcp://192.168.31.169:61616";

    //1.连接工厂
    ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(brokeURL);

    //2.创建连接
    TopicConnection topicConnection = connectionFactory.createTopicConnection();
    //3.创建会话
    //第一个参数 代表第二参数是否生效开启事务  参数二 自动发送回执
    TopicSession topicSession = topicConnection.createTopicSession(true, Session.AUTO_ACKNOWLEDGE);
    //4.创建生产者
    ActiveMQTopic javaTopic = new ActiveMQTopic("javaTopic");
    TopicPublisher publisher = topicSession.createPublisher(javaTopic);

    //5.创建消息
    TextMessage textMessage = topicSession.createTextMessage();
    textMessage.setText("hello subscriber");

    //6.使用生产者发生消息
    publisher.send(textMessage);

    //7.提交
    topicSession.commit();

    //8.关闭
    publisher.close();
    topicSession.close();
    topicConnection.close();

}
9.2.2.订阅者(subscriber)
//订阅者1
@Test
public void testsub1() throws Exception {

    String brokeURL="tcp://192.168.31.169:61616";

    //1.连接工厂
    ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(brokeURL);

    //2.创建连接
    TopicConnection topicConnection = activeMQConnectionFactory.createTopicConnection();
    topicConnection.start(); //启动连接

    //3.创建会话
    //第一个参数 代表第二参数是否生效开启事务  参数二 自动发送回执
    TopicSession topicSession = topicConnection.createTopicSession(true, Session.AUTO_ACKNOWLEDGE);

    //4.创建消费者
    ActiveMQTopic javaTopic = new ActiveMQTopic("javaTopic");
    TopicSubscriber durableSubscriber = topicSession.createSubscriber(javaTopic);

    while(true){
        TextMessage message = (TextMessage)durableSubscriber.receive();
        String text = message.getText();
        if(message!=null){
            System.out.println("订阅者1=="+text);
            //7.提交
            topicSession.commit();
        }else{
            break;
        }
    }
}

//订阅者2
@Test
public void testsub2() throws JMSException {

    String brokeURL="tcp://192.168.31.169:61616";

    //1.连接工厂
    ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(brokeURL);

    //2.创建连接
    TopicConnection topicConnection = activeMQConnectionFactory.createTopicConnection();
    topicConnection.start(); //启动连接

    //3.创建会话
    //第一个参数 代表第二参数是否生效开启事务  参数二 自动发送回执
    TopicSession topicSession = topicConnection.createTopicSession(true, Session.AUTO_ACKNOWLEDGE);

    //4.创建消费者
    ActiveMQTopic javaTopic = new ActiveMQTopic("javaTopic");
    TopicSubscriber durableSubscriber = topicSession.createSubscriber(javaTopic);

    while(true){
        TextMessage message = (TextMessage)durableSubscriber.receive();
        String text = message.getText();
        if(message!=null){
            System.out.println("订阅者2=="+text);
            //7.提交
            topicSession.commit();
        }else{
            break;
        }
    }
}

注意:订阅者要在发布者之前启动,才会接收到发布者发布的消息(时间依赖性)

10.springboot集成ActiveMQ

10.1.点对点点对点(P2P)

导入相关依赖

<!--ActiveMQ相关jar包-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.activemq</groupId>
    <artifactId>activemq-pool</artifactId>
</dependency>

配置文件

server.port=8989
server.servlet.context-path=springbootamq
spring.activemq.broker-url=tcp://192.168.31.169:61616
spring.activemq.user=admin
spring.activemq.password=admin
#如果是点对点(queue),那么此处默认应该是false,如果发布订阅,那么一定设置为true
spring.jms.pub-sub-domain=false
1.生产者
@Component
public class Product {

    @Resource
    JmsTemplate jmsTemplate;

    public void productSend(String msg){
        ActiveMQQueue springbootQueue = new ActiveMQQueue("SpringbootQUEUE");
        jmsTemplate.convertAndSend(springbootQueue,msg);
    }
}
2.消费者
@Component
public class Consumer {

    @JmsListener(destination = "SpringbootQUEUE")
    public void consumers(TextMessage msg){
        try {
            System.out.println(msg.getText());
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}
3.Controller测试
@RestController
@RequestMapping("test")
public class ProductController {

    @Autowired
    private Product product;
    

    @RequestMapping("send")
    public void send(String msg){
        product.sendMessage(msg);
    }
}
4.发送其他数据

生产者

创建对象

//注意:对象一定要实现Serializable接口
public class User implements Serializable {

    private String name;
    private String password;
    private Integer age;
    private Date bir;
}
#如果使用ObjectMessage传输对象,必须要加上这个信任包,否则会报ClassNotFound异常
spring.activemq.packages.trust-all: true
@JmsListener(destination = "UserTopic")
public void consumer(ObjectMessage msg){
    try {
        System.out.println("订阅者1-消费消息 "+msg.getObject());
    } catch (JMSException e) {
        e.printStackTrace();
    }
}

10.2.发布订阅(pub& sub)

1.修改配置
#如果是点对点(queue),那么此处默认应该是false,如果发布订阅,那么一定设置为true
spring.jms.pub-sub-domain=true
2.生产者
@Component
public class Product {

    @Resource
    JmsTemplate jmsTemplate;

    public void ProductSend(String msg){
        ActiveMQTopic springbootTopic = new ActiveMQTopic("SpringbootTopic");
        jmsTemplate.convertAndSend(springbootTopic,msg);
    }
}
3.消费者1
@JmsListener( destination= "SpringbootTopic")
public void Consumers(TextMessage msg){
    try {
        System.out.println(msg.getText());
    } catch (JMSException e) {
        e.printStackTrace();
    }
}
4.消费者2
@JmsListener( destination= "SpringbootTopic")
public void Consumers(TextMessage msg){
    try {
        System.out.println(msg.getText());
    } catch (JMSException e) {
        e.printStackTrace();
    }
}

设置为true
spring.jms.pub-sub-domain=true

#### 2.生产者

~~~java
@Component
public class Product {

    @Resource
    JmsTemplate jmsTemplate;

    public void ProductSend(String msg){
        ActiveMQTopic springbootTopic = new ActiveMQTopic("SpringbootTopic");
        jmsTemplate.convertAndSend(springbootTopic,msg);
    }
}
3.消费者1
@JmsListener( destination= "SpringbootTopic")
public void Consumers(TextMessage msg){
    try {
        System.out.println(msg.getText());
    } catch (JMSException e) {
        e.printStackTrace();
    }
}
4.消费者2
@JmsListener( destination= "SpringbootTopic")
public void Consumers(TextMessage msg){
    try {
        System.out.println(msg.getText());
    } catch (JMSException e) {
        e.printStackTrace();
    }
}