挖坑记录~ActiveMQ基础

作用

  1. 解决系统间的耦合性
  2. 异步调用
  3. 减轻服务提供方在流量高峰的压力,削峰

环境

./activemq start > /myactiveMQ/myrunmq.log 带有日志启动
http://192.168.56.10:8162/admin admin/admin

概念

JMS

java message service java消息服务的规范, 类似jdbc, 有各种实现的规范软件, 规范中定义了jms有4部分组成
1.生产者
2.消费者
3.目的地Destination 父类消息容器 -> 实现类 队列queue/主题topic
4.服务端

消息中间件

rabbitMQ(高并发) | ActiveMQ (老牌用户多) |
RocketMQ(阿里产品) | kafka(高吞吐,大数据)

RabbitMQ由基于erlang, 使用amqp协议
*AMQP与JMS区别

JMS AMQP
操作的规范接口 传输协议
必须使用java 跨语言
2种消息模型 多种消息模型

java操作

pom

 <dependencies>
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-broker</artifactId>
            <version>5.14.5</version>
        </dependency>
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-pool</artifactId>
            <version>5.14.5</version>
        </dependency>
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-spring</artifactId>
            <version>5.14.5</version>
        </dependency>
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>artemis-jms-client</artifactId>
            <version>2.3.0</version>
        </dependency>
    </dependencies>

队列点对点模式

生产者


import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;

//生产者
public class ProducerTest {
    public static void main(String[] args) throws JMSException {
        //创建连接工厂
        ActiveMQConnectionFactory activeMQConnectionFactory =
                new ActiveMQConnectionFactory(
                        "admin",
                        "admin",
                        "tcp://192.168.56.10:61616");
//         获取connection
        Connection connection = activeMQConnectionFactory.createConnection();
   		connection.start();
        //创建session 事务,接收模式
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//        创建队列
        Destination destination = session.createQueue("queue01");
//        创建生产者
        MessageProducer producer = session.createProducer(destination);
        for (int i = 0; i < 3; i++) {
            TextMessage textMessage = session.createTextMessage("msg from queue" + i);
            producer.send(textMessage);
        }
        producer.close();
        session.close();
        connection.close();
    }
}

消费者

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class ConsumerTest {
    public static void main(String[] args) {
        //创建连接工厂
        ActiveMQConnectionFactory activeMQConnectionFactory =
                new ActiveMQConnectionFactory(
                        "admin",
                        "admin",
                        "tcp://192.168.56.10:61616");
//         获取connection
        Connection connection = null;
        Session session = null;
        MessageConsumer consumer = null;
        try {
            connection = activeMQConnectionFactory.createConnection();
            //
            connection.start();
            //创建session 事务,接收模式
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//        创建队列
            Destination destination = session.createQueue("queue01");
            consumer = session.createConsumer(destination);
            while(true){
                TextMessage receive = (TextMessage) consumer.receive(4000);
                if (null!=receive){
                    System.out.println("消费者收到的消息为:"+receive.getText());
                }else{
                    break;
                }
            }
        } catch (JMSException e) {
            e.printStackTrace();
        } finally {
            try {
                consumer.close();
                session.close();
                connection.close();
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }
    }
}

执行完代码后观察mq控制台变化
在这里插入图片描述

connection.start();
内部用了原子类自旋检查 , 如果调用这个方法, 消费者的excutor不会开始
在这里插入图片描述
在这里插入图片描述

consumer.receive()
参数中可以设置阻塞时间, 默认不超时, 一直接受消息

消费者也可以使用监听器模式

//消费者也可以使用监听器模式
  consumer.setMessageListener(message -> {
                if(null!=message&&message instanceof TextMessage){
                    TextMessage message1 = (TextMessage) message;
                    try {
                        System.out.println("消费者收到的消息为:"+message1.getText());
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
            //阻塞 等待控制台输入
            System.in.read();

特点:

消息会被保存
消息阻塞接收/异步获取,消息生产后被消费的时间不确定
同一消息只会被消费一次
多个消费者时,采用负载均衡模式,不影响性能

主题topic一对多模式

消费者和生产者的队列的代码中, 改动此行创建队列即可

Destination destination = session.createTopic("topic01");

特点:

消息不会存储,消息生产即消费
一条消息被多个消费者消息
消费者数量多时,影响性能

配置

消息头

生产者发送消息时的参数代表了消息头设置
在这里插入图片描述

1.deliveryMode 持久非持久

  1. 非持久的消息在服务关闭后即清除, 持久化的消息会在服务器上产生临时文件保存
  2. 默认持久
    在这里插入图片描述

2.priority 优先级 高级/普通级 消费者优先消费 默认普通级别
3.timeToLive 发送过期时间, 有效期内未发送到服务端将被丢弃
4.JMSMessageID MQ创建的唯一标识 uuid
在这里插入图片描述

消息体

在这里插入图片描述
textMessage 文本
MapMessage 键值对
byte 字节
object 对象
stream 流消息

属性

在这里插入图片描述
键值对, 增加对消息的标识度

持久化

生产者

producer.setDeliveryMode(DeliveryMode.PERSISTENT);

消费者

 connection = activeMQConnectionFactory.createConnection();
            connection.setClientID("shadow");
            //创建session 事务,接收模式
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//        创建队列
            Topic topic = session.createTopic("topic_Persist01");
            TopicSubscriber durableSubscriber = session.createDurableSubscriber(topic, "topic_Persist01_durableSubscriber01");
            connection.start();
           while(true){
               TextMessage message = (TextMessage)durableSubscriber.receive();
                if (null!=message){
                    System.out.println("消费者收到的消息为:"+message.getText());
                }else{
                    break;
                }
            }

在这里插入图片描述
生产者持久化消息, 消费者订阅topic后, 即使消费者关闭, 生产者发布到话题的消息仍会保存, 等到消费者上线, 会接收到话题中的消息

事务

生产者开启事务后要手动提交, 类似jdbc

 session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
 ...
 session.commit();

消费者开启事务并获取到消息后, 如果不提交, 将会出现重复消费的情况, 及时消息获取到, 队列中的消息仍然没有出列

签收

在这里插入图片描述
1.自动签收 AUTO_ACKNOWLEDGE
2.客户端签收 CLIENT_ACKNOWLEDGE

 message.acknowledge();

如果客户端开启了手动签收, 但是没有手动签收, 消息将不会出列, 仍会在队列中
3. 重复签收 DUPS_OK_ACKNOWLEDGE

事务与签收

当有事务的情况下, 一旦提交了, 即使开启了客户端签收, 客户端没有签收, 仍然能保证不会重复消费
事务的优先度大于签收

spring整合

pom

<dependencies>
   <!-- activemq核心依赖包  -->
    <dependency>
        <groupId>org.apache.activemq</groupId>
        <artifactId>activemq-all</artifactId>
        <version>5.10.0</version>
    </dependency>
    <!--  嵌入式activemq的broker所需要的依赖包   -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.10.1</version>
    </dependency>
    <!-- activemq连接池 -->
    <dependency>
        <groupId>org.apache.activemq</groupId>
        <artifactId>activemq-pool</artifactId>
        <version>5.15.10</version>
    </dependency>
    <!-- spring支持jms的包 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jms</artifactId>
        <version>5.2.1.RELEASE</version>
    </dependency>
    <!--spring相关依赖包-->
    <dependency>
        <groupId>org.apache.xbean</groupId>
        <artifactId>xbean-spring</artifactId>
        <version>4.15</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>5.2.1.RELEASE</version>
    </dependency>
    <!-- Spring核心依赖 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>4.3.23.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.3.23.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>4.3.23.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>4.3.23.RELEASE</version>
    </dependency>
</dependencies>

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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!--  开启包的自动扫描  -->
    <context:component-scan base-package="com.activemq.demo"/>
    <!--  配置生产者  -->
    <bean id="connectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop">
        <property name="connectionFactory">
            <!--      正真可以生产Connection的ConnectionFactory,由对应的JMS服务商提供      -->
            <bean class="org.apache.activemq.spring.ActiveMQConnectionFactory">
                <property name="brokerURL" value="tcp://192.168.10.130:61616"/>
            </bean>
        </property>
        <property name="maxConnections" value="100"/>
    </bean>

    <!--  这个是队列目的地,点对点的Queue  -->
    <bean id="destinationQueue" class="org.apache.activemq.command.ActiveMQQueue">
        <!--    通过构造注入Queue名    -->
        <constructor-arg index="0" value="spring-active-queue"/>
    </bean>

    <!--  这个是队列目的地,  发布订阅的主题Topic-->
    <bean id="destinationTopic" class="org.apache.activemq.command.ActiveMQTopic">
        <constructor-arg index="0" value="spring-active-topic"/>
    </bean>

    <!--  Spring提供的JMS工具类,他可以进行消息发送,接收等  -->
    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <!--    传入连接工厂    -->
        <property name="connectionFactory" ref="connectionFactory"/>
        <!--    传入目的地    -->
        <property name="defaultDestination" ref="destinationQueue"/>
        <!--    消息自动转换器    -->
        <property name="messageConverter">
            <bean class="org.springframework.jms.support.converter.SimpleMessageConverter"/>
        </property>
    </bean>
</beans>

生产者

@Service
public class SpringProduce {

    @Autowired
    private JmsTemplate jmsTemplate;

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-activemq.xml");
        SpringProduce springMQ_producer = applicationContext.getBean(SpringProduce.class);
        springMQ_producer.jmsTemplate.send(
                new MessageCreator() {
                    public Message createMessage(Session session) throws JMSException {
                        return session.createTextMessage("***Spring和ActiveMQ的整合case111.....");
                    }
                }
        );
        System.out.println("********send task over");
    }
}

消费者

SpringConsumer springConsumer = applicationContext.getBean(SpringConsumer.class);
        String  o = (String)springConsumer.jmsTemplate.receiveAndConvert();
        System.out.println(o);

topic生产消费者

<property name="defaultDestination" ref="destinationQueue"/>

消费者监听器

<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
        <property name="connectionFactory" ref="connectionFactory"/>
        <property name="destination" ref="destinationTopic"></property>
        <property name="messageListener" ref="myMessageListener"></property>
    </bean>

监听器

@Component
public class MyMessageListener implements MessageListener {
    @Override
    public void onMessage(Message message) {
        if (null!=message&& message instanceof TextMessage){
            TextMessage textMessage = (TextMessage) message;
            try {
                String text = textMessage.getText();
                System.out.println(text);
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }
    }
}

Springboot整合

queue

生产者工程
pom

 <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!--spring boot整合activemq的jar包-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-activemq</artifactId>
            <version>2.1.5.RELEASE</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

xml

server:
  port: 8080
#工厂
spring:
  activemq:
    broker-url: tcp://192.168.56.10:61616
    password: admin
    user: admin
  jms:
#    false 队列 true 主题
    pub-sub-domain: false
myqueue: springboot-acmq

配置类

@Component
@EnableJms
public class ConfigBean {
    @Value("${myqueue}")
    private String myqueue;
    @Bean   // bean id=""  class="…"
    public Queue queue(){
        return  new ActiveMQQueue(myqueue);
    }
}
/*
@WebAppConfiguration("src/main/resources") : 注解在类上,
用来声明加载的ApplicationContex 是一个WebApplicationContext ,
它的属性指定的是Web资源的位置,默认为 src/main/webapp ,
自定义修改为 resource
* */
@SpringBootTest(classes = AcmqSpringbootApplication.class)
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
public class TestMQ {
    @Resource
    private QueueProduce queueProduce;
    @Test
    public void send(){
        queueProduce.send();
    }
     @Scheduled(fixedDelay = 3000)
    public void scheduleSend(){
        jmsMessagingTemplate.convertAndSend(queue,"schedule mes:"+ UUID.randomUUID().toString().substring(0,6));
    }
}
@SpringBootApplication
@EnableScheduling
public class AcmqSpringbootApplication {

    public static void main(String[] args) {
        SpringApplication.run(AcmqSpringbootApplication.class, args);
    }
}

queue消费者工程同生产者 配置类不要了
在这里插入图片描述

@Component
public class QueueConsumer {
	//jmstemplate监听器 
    @JmsListener(destination = "${myqueue}")
    public void receive(TextMessage textMessage){
        try {
            System.out.println(textMessage.getText());
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

topic

与队列类似 , 只贴改动的地方
生产者

server:
  port: 8080
#工厂
spring:
  activemq:
    broker-url: tcp://192.168.56.10:61616
    password: admin
    user: admin
  jms:
#    false 队列 true 主题
    pub-sub-domain: true
mytopic: springboot-acmq-topic
@Component
public class TopicProduce {
    @Autowired
    private JmsMessagingTemplate jmsMessagingTemplate;

    @Autowired
    private Topic topic;

    public void send(){
        jmsMessagingTemplate.convertAndSend(topic,"mes:"+ UUID.randomUUID().toString().substring(0,6));
    }
    @Scheduled(fixedDelay = 3000)
    public void scheduleSend(){
        jmsMessagingTemplate.convertAndSend(topic,"schedule mes:"+ UUID.randomUUID().toString().substring(0,6));
    }
}
@Component
@EnableJms
public class ConfigBean {
    @Value("${mytopic}")
    private String myTopic;
    @Bean   // bean id=""  class="…"
    public Topic topic(){
        return  new ActiveMQTopic(myTopic);
    }
}

消费者
将读取文件的部分改为topic

${mytopic}

猜你喜欢

转载自blog.csdn.net/weixin_43453109/article/details/107744167
今日推荐