ActiveMQ从安装到springboot实例

ActiveMQ入门(一)

1. 为什么要学习MQ

因为我们在发送接收消息时,如果采用同步的话,一个个等待效率十分低下,但是如果一下子蜂拥而至,对于高并发的情况处理是削峰防止崩溃,更重要的是解耦,避免了RPC式同步调用等待

以上的原因导致了MQ消息中间件的出现,用异步的方式,以及削峰手段,还有良好的解耦性,使项目更对于消息的处理更为完善

2. MQ的作用

image-20200428164449412

A不用和B有强耦合关系,只需要通过消息队列确认收发

而且他们不用同时在线,收发异步,很方便就像qq一样

image-20200428164715718

官网下载:传送门

  1. 传到/opt目录下解压 tar -zxvf 名字

  2. cp -r apache-activemq-5.15.12 /myactiveMQ/

  3. 复制到自己的文件夹

  4. 进入bin目录然后 ./activemq start启动

发现没有启动起来 ./activemq status查看状态

下面这个博客解决了无法启动的一个问题

https://blog.csdn.net/xiewenfeng520/article/details/85236304?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1

启动成功

image-20200428175445732

image-20200428180347226

查看tomcat也启动了

netstat -anp|grep 61615查看端口使用情况

./activemq stop 停止运行

带日志的启动

./activemq start > /myactiveMQ/日志文件名

可以去vim这个log看日志

3.控制台查看

报错访问不到之后查看自己hostname是否是绑定了127.0.0.1 可以通过ping 主机名成功就是绑定了

image-20200428183656197

史前踩坑

我用的阿里云服务器,发现安全组规则没有打开,后来用ip:8161一直不行,打开了端口还是不行,后来我去宝塔面板放行了8161,才明白过来,原来是我之前域名绑定过,并且解析了,基本用宝塔放行后我用域名:8161就可以成功了,

留个图纪念一下

image-20200428184832815

点击broker登录,账号密码都是admin

image-20200428184930148

4.创建Maven项目

pom依赖

 <dependencies>
<!--       activemq所需要的jar-->
       <dependency>
           <groupId>org.apache.activemq</groupId>
           <artifactId>activemq-all</artifactId>
           <version>5.15.12</version>
       </dependency>
       <dependency>
           <groupId>org.apache.xbean</groupId>
           <artifactId>xbean-spring</artifactId>
           <version>3.16</version>
       </dependency>

       <dependency>
           <groupId>org.projectlombok</groupId>
           <artifactId>lombok</artifactId>
           <version>1.18.12</version>
       </dependency>
       <dependency>
           <groupId>ch.qos.logback</groupId>
           <artifactId>logback-classic</artifactId>
           <version>1.2.3</version>
       </dependency>
       <dependency>
           <groupId>junit</groupId>
           <artifactId>junit</artifactId>
           <version>4.12}</version>
       </dependency>
   </dependencies>

4.1 JMS总体规范

image-20200428191209545

和jdbc编码有点像

image-20200428191347409

Destination:发送消息到的目的地,就比如new book(),这个会去堆里面一样,消息也会去目的地,里面有队列和主题

一对一:去私聊 目的地队列

一对多:微信推送公众号 目的地主题

image-20200428193626685

image-20200428200801822

4.2 生产者入消息队列代码

package com.example;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;


public class JmsProduce {
    public static  final String URL="tcp://www.cqfbest.ltd:61615";
    public static  final String Queue_name="queue01";
    public static void main(String[] args) {
        //1.创建连接工厂
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(URL);
        //2.通过连接工厂,获得连接connection
        try {
            Connection connection=activeMQConnectionFactory.createConnection();
            connection.start();

            //3.创建Session  两个参数:事务和签收
            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);//先默认
           //4。创建目的地(队列或者主题)
            Queue queue = session.createQueue(Queue_name);
            Destination destination= session.createQueue(Queue_name);
            //5.创建消息的生产者
            MessageProducer producer = session.createProducer(queue);
            //6.通过使用消息生产者生产3条消息到队列里面
            for (int i = 0; i < 3; i++) {
                //7.创建消息
                TextMessage textMessage = session.createTextMessage("msg---" + (i + 1));//一个消息字符串
                //8. 通过生产者发送给MQ
                producer.send(textMessage);
                //9.倒着关闭
            }
            producer.close();
            session.close();
            connection.close();
            System.out.println("发送完成!");
        } catch (JMSException e) {
            e.printStackTrace();
        }


    }
}

image-20200428202031130

辅助识记

image-20200428202318909

4.3 消费者消费队列消息代码

package com.example;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class JmsConsumer {
    public static  final String URL="tcp://www.cqfbest.ltd:61615";
    public static  final String Queue_name="queue01";
    public static void main(String[] args) {
        //1.创建连接工厂
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(URL);
        //2.通过连接工厂,获得连接connection
        try {
            Connection connection=activeMQConnectionFactory.createConnection();
            connection.start();

            //3.创建Session  两个参数:事务和签收
            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);//先默认
            //4。创建目的地(队列或者主题)
            Queue queue = session.createQueue(Queue_name);
            Destination destination= session.createQueue(Queue_name);
            //5.创建消费者
            MessageConsumer consumer = session.createConsumer(queue);
            //6.通过使用消息生产者生产3条消息到队列里面
            while (true){
                //7.接收消息  类型一样和生产的消息格式
                TextMessage textMessage = (TextMessage) consumer.receive();
                 if(textMessage!=null){
                     System.out.println("消费者接收到消息:"+textMessage.getText());

                 }else {
                     break;
                 }
            }
            //8.倒着关闭
            consumer.close();
            session.close();
            connection.close();
            System.out.println("接收完毕!");
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

打印出来的消息

image-20200428205921118

image-20200428210440238

recive(4000L); 4秒以后没收到消息,消费者自己退出

4.3.1 利用监听器的消费者

接口监听

public interface MessageListener {
    void onMessage(Message message);
}
package com.example;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;
import javax.jms.MessageListener;
import java.io.IOException;

public class JmsConsumer2 {
    public static  final String URL="tcp://www.cqfbest.ltd:61615";
    public static  final String Queue_name="queue01";
    public static void main(String[] args) throws IOException {
        //1.创建连接工厂
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(URL);
        //2.通过连接工厂,获得连接connection
        try {
            Connection connection=activeMQConnectionFactory.createConnection();
            connection.start();

            //3.创建Session  两个参数:事务和签收
            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);//先默认
            //4。创建目的地(队列或者主题)
            Queue queue = session.createQueue(Queue_name);
            Destination destination= session.createQueue(Queue_name);
            //5.创建消费者
            MessageConsumer consumer = session.createConsumer(queue);
            //6.监听消息MessageListener
            consumer.setMessageListener(new MessageListener() {
                public void onMessage(Message message) {
                    if(null!=message&& message instanceof TextMessage){
                        TextMessage textMessage= (TextMessage) message;
                        try {
                            System.out.println("消费者接收到消息:"+textMessage.getText());
                        } catch (JMSException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
            System.in.read(); //保证控制台不灭  doubbo源码有
            consumer.close();
            session.close();
            connection.close();
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

一样的效果但是这个需要后面有read()缓冲时间再关闭

image-20200428215356529

4.3.2 总结流程

image-20200428215137794

5. 消费者的几种情况

  1. 一个生产者然后开启两个消费者,1号消费完,2号能消费吗?2号消费者消费不到,因为1号直接全部接收完了消息
  2. 一个生产者对应一个消费者,最好的情况
  3. 先启动2个消费者,然后再生产6条消息,结果怎么样? 答案:有点像轮询 1号接收1,3,5,二号接收2,4,6

6.生产者发布消息主题

主题是不保存消息的,发布出来无人订阅都浪费资源空间了,=所以一般先启动消费者后启动生产者

image-20200428220132675

和队列一样,就改queue为topic

package com.example.topic;
import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

import static com.example.JmsProduce.Queue_name;
public class JmsProduce {
    public static  final String URL="tcp://www.cqfbest.ltd:61615";
    public static  final String topic_name="topic01";
    public static void main(String[] args) {
        //1.创建连接工厂
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(URL);
        //2.通过连接工厂,获得连接connection
        try {
            Connection connection=activeMQConnectionFactory.createConnection();
            connection.start();

            //3.创建Session  两个参数:事务和签收
            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);//先默认
           //4。创建目的地(队列或者主题)
            Topic topic = session.createTopic(topic_name);
            Destination destination= session.createTopic(topic_name);
            //5.创建消息的生产者
            MessageProducer producer = session.createProducer(topic);
            //6.通过使用消息生产者生产3条消息到队列里面
            for (int i = 0; i < 3; i++) {
                //7.创建消息
                TextMessage textMessage = session.createTextMessage("topic消息---" + (i + 1));//一个消息字符串
                //8. 通过生产者发送给MQ
                producer.send(textMessage);
                //9.倒着关闭
            }
            producer.close();
            session.close();
            connection.close();
            System.out.println("主题消息发送完成!");
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

7. 消费者订阅消息主题

package com.example.topic;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.MessageListener;
import javax.jms.*;
import java.io.IOException;

public class JmsConsumer2 {
    public static  final String URL="tcp://www.cqfbest.ltd:61615";
    public static  final String Topic_name="topic01";
    public static void main(String[] args) throws IOException {
        //1.创建连接工厂
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(URL);
        //2.通过连接工厂,获得连接connection
        try {
            Connection connection=activeMQConnectionFactory.createConnection();
            connection.start();

            //3.创建Session  两个参数:事务和签收
            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);//先默认
            //4。创建目的地(队列或者主题)
            Topic topic = session.createTopic(Topic_name);
            Destination destination= session.createTopic(Topic_name);
            //5.创建消费者
            MessageConsumer consumer = session.createConsumer(topic);
            //6.监听消息MessageListener
//            consumer.setMessageListener(new MessageListener() {
//                public void onMessage(Message message) {
//                    if(null!=message&& message instanceof TextMessage){
//                        TextMessage textMessage= (TextMessage) message;
//                        try {
//                            System.out.println("消费者接收到消息:"+textMessage.getText());
//                        } catch (JMSException e) {
//                            e.printStackTrace();
//                        }
//                    }
//                }
//            });
            consumer.setMessageListener((Message message)->{
                   if(null!=message&& message instanceof TextMessage){
                        TextMessage textMessage= (TextMessage) message;
                        try {
                            System.out.println("消费者接收到消息:"+textMessage.getText());
                        } catch (JMSException e) {
                            e.printStackTrace();
                        }
                    }
            });
            System.in.read(); //保证控制台不灭  doubbo源码有
            consumer.close();
            session.close();
            connection.close();
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

接口的话和队列里写的那个一样,拿来复用

image-20200428221647892

启动完生产者会发现每个消费者全收到了,不像队列一样负载均衡

image-20200428221932568

image-20200428221945143

试试先启动生产发布者,再启动订阅者

image-20200428222245758

image-20200428222347350

image-20200428222412804

image-20200428222459683

再生产的话,消费者可以接收,之前先生产的是3条废消息

7.1 两大模式比较

image-20200428222802493

JMS到底是什么?

JMS是JAVAEE的一个子模块

两个程序之间异步通信的API,定义了消息的规范

image-20200429104854382

消息的可靠性之持久化和非持久化

image-20200429130118536

消息不持久,服务崩了不会保存消息,持久化后可以保存,再发送一次

队列默认是持久的,服务崩掉再开启也是可以再接收消息的,它保证接收和传送都一次

1.持久化发布订阅

package com.example.db_topic;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;
import java.io.IOException;

public class JmsConsumer1 {
    public static  final String URL="tcp://www.cqfbest.ltd:61615";
    public static  final String Topic_name="topic01";
    public static void main(String[] args) throws IOException {
        System.out.println("我是一号消费者");
        //1.创建连接工厂
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(URL);
        //2.通过连接工厂,获得连接connection
        try {
            Connection connection=activeMQConnectionFactory.createConnection();
            connection.setClientID("z3");

            //3.创建Session  两个参数:事务和签收
            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);//先默认
            //4。创建目的地(队列或者主题)
            Topic topic = session.createTopic(Topic_name);
            TopicSubscriber topicSubscriber=session.createDurableSubscriber(topic,"remark...");

            connection.start();
            Message message=topicSubscriber.receive();
            while (null!=message){
                TextMessage textMessage= (TextMessage) message;
                System.out.println("收到的持久化topic:"+textMessage.getText());
                message=topicSubscriber.receive(5000L);
            }


            session.close();
            connection.close();
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}
package com.example.db_topic;
import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class JmsProduce {
    public static  final String URL="tcp://www.cqfbest.ltd:61615";
    public static  final String topic_name="topic01";
    public static void main(String[] args) {
        //1.创建连接工厂
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(URL);
        //2.通过连接工厂,获得连接connection
        try {
            Connection connection=activeMQConnectionFactory.createConnection();
            //3.创建Session  两个参数:事务和签收
            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);//先默认
           //4。创建目的地(队列或者主题)
            Topic topic = session.createTopic(topic_name);
            Destination destination= session.createTopic(topic_name);
            //5.创建消息的生产者
            MessageProducer producer = session.createProducer(topic);
            producer.setDeliveryMode(DeliveryMode.PERSISTENT);
            //6.通过使用消息生产者生产3条消息到队列里面
            for (int i = 0; i < 3; i++) {
                //7.创建消息
                TextMessage textMessage = session.createTextMessage("topic消息---" + (i + 1));//一个消息字符串
                //8. 通过生产者发送给MQ
                producer.send(textMessage);
                //9.倒着关闭
            }
            producer.close();
            session.close();
            connection.close();
            System.out.println("主题消息发送完成!");
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

会一直等待主题发布消息,除非自己设置了超时取关

等消费者再重新连接依然收得到之前的消息

2. 事务

开启了事务,一定要commit提交

session.commit();

3.签收

上面讲的都是自动签收

手动签收

image-20200429163029695

允许重复签收(不常用)

image-20200429163234677

生产事务开启只有commit才能把消息转为已签收,事务权限大于签收

没有commit即使写了ack也没用,写了事务提交了即使没有ack也以消费

4. JavA代码操作Broker

复制一份activemq.xml

cp  activemq.xml activemq02.xml

启动新的复制的xml

./activemq start xbean:file:/myactiveMQ/apache-activemq-5.15.12/conf/activemq02.xml

Broker是什么? 它就是嵌入java代码的形式启动mq,随时用随时启动

pom依赖

<!--       borker-->
       <dependency>
           <groupId>com.fasterxml.jackson.core</groupId>
           <artifactId>jackson-databind</artifactId>
           <version>2.10.3</version>
       </dependency>
public class EmbedBorker {
    public static void main(String[] args) throws Exception {
        //嵌入式代码执行
        BrokerService brokerService=new BrokerService();
        brokerService.setUseJmx(true);
        brokerService.addConnector("tcp://www.cqfbest.ltd:61615");
        brokerService.start();
    }
}

启动后,原来的生产者和消费者只要把地址改成本地localhost就可以,在本地收发消息,broker相当于启动new了一个消息中间件

5. springboot实现队列消息发送

第一步创建springbbot项目,导入activemq依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-activemq</artifactId>
        </dependency>

image-20200429190625573

5.1 ConfigBean.java
package com.example.config;

import org.apache.activemq.command.ActiveMQQueue;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.stereotype.Component;

import javax.jms.Queue;


@Component
@EnableJms   //开启jms
public class ConfigBean {
      @Value("${myqueue}")
      private String myqueue;

      @Bean
      public Queue queue(){
          return new ActiveMQQueue(myqueue);
      }


}
5.2 Queue_Produce.java
package com.example.produce;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Component;

import javax.jms.Queue;
import java.util.UUID;

@Component
public class Queue_Produce {
       @Autowired
       private JmsMessagingTemplate jmsMessagingTemplate;

       @Autowired
       private Queue queue;

       public void produceMsg(){
           jmsMessagingTemplate.convertAndSend(queue,"***:"+ UUID.randomUUID().toString().substring(0,6));
       }


}
5.3 application.yml
server:
  port: 7777

spring:
  activemq:
    broker-url: tcp://www.cqfbest.ltd:61615
    user: admin
    password: admin
  jms:
    pub-sub-domain: false   #默认false 队列  true 为topic
myqueue: boot-activemq-queue    #自定义队列名字
5.4 Test.java
package com.example;

import com.example.produce.Queue_Produce;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;

@SpringBootTest
public class testActiveMQ {
    @Resource
    private Queue_Produce queue_produce;

    @Test
    public void testProduce(){

        queue_produce.produceMsg();
    }
}

image-20200429192937032

上传成功

6.springboot实现队列消费者

application.yml还有pom文件都和生产者一样,没什么区别

image-20200429195745897

7. springboot实现主题消息发布

7.1 ConfigBean.java

package com.example.config;

import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.command.ActiveMQTopic;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.stereotype.Component;

import javax.jms.Queue;
import javax.jms.Topic;


@Component
@EnableJms   //开启jms
public class ConfigBean {
      @Value("${mytopic}")
      private String mytopic;

      @Bean
      public Topic topic(){
          return new ActiveMQTopic(mytopic);
      }


}

7.2 Topic_Produce.java

package com.example.produce;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.jms.Queue;
import javax.jms.Topic;
import java.util.UUID;

@Component
public class Topic_Produce {
       @Autowired
       private JmsMessagingTemplate jmsMessagingTemplate;

       @Autowired
       private Topic topic;

       public void produceMsg(){
           jmsMessagingTemplate.convertAndSend(topic,"***:"+ UUID.randomUUID().toString().substring(0,6));
       }

       //间隔时间3秒自动发消息
       @Scheduled(fixedDelay = 3000)
       public void prosend(){
              jmsMessagingTemplate.convertAndSend(topic,"***:"+ UUID.randomUUID().toString().substring(0,6));
              System.out.println("****定时3秒投递完成 send ok");
       }

}

7.3 application.yml

server:
  port: 6665

spring:
  activemq:
    broker-url: tcp://www.cqfbest.ltd:61615
    user: admin
    password: admin
  jms:
    pub-sub-domain: true   #默认false 队列  true 为topic
mytopic: boot-activemq-topic    #自定义队列名字

8springboot实现消费短信

package com.example.consumer;

import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;

import javax.jms.TextMessage;

@Component
public class Topic_Consumer {
    @JmsListener(destination = "${mytopic}")   //消息监听myqueue队列
    public void ReciveMsg(TextMessage textMessage)throws Exception{
        System.out.println("****丢丢发现手机收到了 "+textMessage.getText()+" 这样一条短信");
    }
}
       

7.3 application.yml

server:
  port: 6665

spring:
  activemq:
    broker-url: tcp://www.cqfbest.ltd:61615
    user: admin
    password: admin
  jms:
    pub-sub-domain: true   #默认false 队列  true 为topic
mytopic: boot-activemq-topic    #自定义队列名字

8springboot实现消费短信

package com.example.consumer;

import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;

import javax.jms.TextMessage;

@Component
public class Topic_Consumer {
    @JmsListener(destination = "${mytopic}")   //消息监听myqueue队列
    public void ReciveMsg(TextMessage textMessage)throws Exception{
        System.out.println("****丢丢发现手机收到了 "+textMessage.getText()+" 这样一条短信");
    }
}

image-20200429210957564

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值