ActiveMQ入门

前言:最近在公司用到了activeMq接收消息,从网上查了些资料,形成了一个大概的理解,所以总结一下,因为知识都是网上学习的难免有疏漏之处,望理解。

参考:

https://blog.csdn.net/qwqw3333333/article/details/89337277

https://www.jianshu.com/p/cd8e037e11ff

https://www.jianshu.com/p/2b7dff270cfd

https://blog.csdn.net/weixin_40904369/article/details/80403601

https://www.cnblogs.com/bohexiongmao/p/9699778.html

https://blog.csdn.net/chy19911123/article/details/80457904

https://blog.csdn.net/joy1211/article/details/79776733

目录

第一章 ActiveMQ简介

第二章 JMS简介

2.1 JMS简介

2.2 JMS消息类型与相关类

2.3 JMS消息传递模型

2.3.1 点对点模式

2.3.2 订阅模式

2.4 JMS的接收方式

2.5 JMS实践

2.6 扩展

第三章 ActiveMQ的信息交换模型

第四章 ActiveMQ结合SpringBoot

第五章 ActiveMQ与其它MQ的区别


第一章 ActiveMQ简介

ActiveMQ是Apache提供的开源的消息系统,纯Java实现,因此很好的支持J2EE提出的JMS规范。

主要作用:消息发送异步进行,无须等待接受者立即响应。

使用场景:异步处理、应用解耦、流量削峰、日志处理、消息通讯

消息通讯流程简图(大概扫一下就好,可以熟悉了再翻回来看):

更详细一点的,最下面是两种不同的接收方式,左边是同步接收,右边是异步接收:

 

 

第二章 JMS简介

在进行ActiveMQ进行编程的时候,经常会用到jms包下的类,如下面所示的类,于是了解了一下jms的作用。

import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;

2.1 JMS简介

JMS(Java Message Service,即Java消息服务)是一组Java应用程序接口和相应的语法,类似于JDBC,是一组与厂商无关的API,使Java程序能与不同的厂商消息组件进行通信。也就是说JMS是一个用于提供消息服务的技术规范,它制定了在整个消息服务提供过程中的所有数据结构和交互流程。

比如mysql给出了jdbc的实现,ActiveMQ给出了JMS的实现。

2.2 JMS消息类型与相关类

消息是 JMS 中的一种类型对象,由两部分组成:报头和消息主体。报头由路由信息以及有关该消息的元数据组成。消息主体则携带着应用程序的数据或有效负载。

根据有效负载的类型来划分,可以将消息分为几种类型,它们分别携带:

简单文本(TextMessage)、可序列化的对象 (ObjectMessage)、属性集合 (MapMessage)、字节流 (BytesMessage)、原始值流 (StreamMessage),还有无有效负载的消息 (Message)。

JMS相关类:

  • 连接工厂。连接工厂(ConnectionFactory)是由管理员创建,并绑定到JNDI树中。客户端使用JNDI查找连接工厂,然后利用连接工厂创建一个JMS连接。

  • JMS连接。JMS连接(Connection)表示JMS客户端和服务器端之间的一个活动的连接,是由客户端通过调用连接工厂的方法建立的。

  • JMS会话。JMS会话(Session)表示JMS客户与JMS服务器之间的会话状态。JMS会话建立在JMS连接上,表示客户与服务器之间的一个会话线程。

  • JMS目的。JMS目的(Destination),又称为消息队列,是实际的消息源。

  • JMS生产者和消费者。生产者(Message Producer)和消费者(Message Consumer)对象由Session对象创建,用于发送和接收消息

步骤概要(概要代码在2.5):

生产者:

第一步:创建ConnectionFactory对象,需要指定服务端ip及端口号。

第二步:使用ConnectionFactory对象创建一个Connection对象。

第三步:开启连接,调用Connection对象的start方法。

第四步:使用Connection对象创建一个Session对象。

第五步:使用Session对象创建一个Destination对象(topic、queue),此处创建一个Queue对象。

第六步:使用Session对象创建一个Producer对象。

第七步:创建一个Message对象,创建一个TextMessage对象。

第八步:使用Producer对象发送消息。

第九步:关闭资源。


消费者:

消费者:接收消息。

第一步:创建一个ConnectionFactory对象。

第二步:从ConnectionFactory对象中获得一个Connection对象。

第三步:开启连接。调用Connection对象的start方法。

第四步:使用Connection对象创建一个Session对象。

第五步:使用Session对象创建一个Destination对象。和发送端保持一致queue,并且队列的名称一致。

第六步:使用Session对象创建一个Consumer对象。

第七步:接收消息。

第八步:关闭资源

2.3 JMS消息传递模型

2.3.1 点对点模式

消息生产者生产消息发送到queue中,然后消息消费者从queue中取出并且消费消息。
消息被消费以后(消费者ack应答确认/事务模式),queue中不再有存储,所以消息消费者不可能消费到已经被消费的消息。
Queue支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费、其它的则不能消费此消息了,即只有一个consumer能消费此消息。
当消费者不存在时,消息会一直保存,直到有消费消费

2.3.2 订阅模式

消息生产者(发布)将消息发布到Topic中,同时有多个消息消费者(订阅该Topic)消费该消息。
和点对点方式不同,发布到topic的消息会被所有订阅者消费。
当生产者发布消息,不管是否有消费者。都不会保存消息。如果生产者向队列发送消息时,没有消费者订阅该队列,则消息全部丢失。否则向所有订阅了该Topic的消费者发送同样的消息(即:消费者必须在线)

2.4 JMS的接收方式

在JMS中,消息的产生和消费都是异步的。对于消费来说,JMS的消息者可以通过两种方式来消费消息。

(1)同步:订阅者或接收者通过receive方法来接收消息,receive方法在接收到消息之前(或超时之前)将一直阻塞;

/* 创建消息消费者*/
messageConsumer = session.createConsumer(destination);
Message message;
while ((message = messageConsumer.receive()) != null) {
      System.out.println(((TextMessage) message).getText());
}

(2)异步:订阅者或接收者可以注册为一个消息监听器。当消息到达之后,系统自动调用监听器的onMessage方法。

/* 创建消息消费者*/
messageConsumer = session.createConsumer(destination);
/* 设置消费者监听器,监听消息*/
messageConsumer.setMessageListener(new MessageListener() {
     public void onMessage(Message message) {
         try {
              System.out.println(((TextMessage) message).getText());
         } catch (JMSException e) {
              e.printStackTrace();
         }
     }
});

2.5 JMS实践

点对点模式:

/**
 * 消息生产者
 */
public class Producer {
    public static void main(String[] args) throws InterruptedException {
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
                ActiveMQConnectionFactory.DEFAULT_USER,
                ActiveMQConnectionFactory.DEFAULT_PASSWORD,
                MQProperties.IP
        );
        try {
            //通过 connection创建连接
            Connection connection=connectionFactory.createConnection();
            connection.start();
            //通过连接创建 session
            Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);

            Destination destination= session.createQueue(MQProperties.QUEUE_NAME);
            MessageProducer producer = session.createProducer(destination);
            producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
            for (int i = 0; i < 10; i++) {
                ObjectMessage message = session.createObjectMessage("QUEUE计数器:【"+i+"】");
                producer.send(message);
                Thread.sleep(1000);
                session.commit();
            }

        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}
/**
 * 消息消费者
 */
public class Consumer {
    public static void main(String[] args) {
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
                ActiveMQConnectionFactory.DEFAULT_USER,
                ActiveMQConnectionFactory.DEFAULT_PASSWORD,
                MQProperties.IP
        );
        Connection connection;
        try {
            connection = connectionFactory.createConnection();
            connection.start();
            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            Destination destination = session.createQueue(MQProperties.QUEUE_NAME);
            MessageConsumer consumer = session.createConsumer(destination);
            while (true) {
                ObjectMessage message = (ObjectMessage) consumer.receive(10000);
                if (null != message) {
                    System.out.println(message.getObject().toString());
                }
            }
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

订阅模式:

/**
 * 消息发布者
 */
public class Publisher {
    public static void main(String[] args) throws InterruptedException {
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
                ActiveMQConnectionFactory.DEFAULT_USER,
                ActiveMQConnectionFactory.DEFAULT_PASSWORD,
                MQProperties.IP
        );
        try {
            //通过 connection创建连接
            Connection connection=connectionFactory.createConnection();
            connection.start();
            //通过连接创建 session
            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            Topic topic = session.createTopic(MQProperties.TOPIC_NAME);
            MessageProducer producer = session.createProducer(topic);
            producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
            for (int i = 0; i < 10; i++) {
                TextMessage message = session.createTextMessage();
                message.setText("TOPIC计数器:【"+i+"】");
                producer.send(message);
                Thread.sleep(1000);
            }

        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}
/**
 *  消息订阅者
 */
public class Subscriber {
    public static void main(String[] args) {
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
                ActiveMQConnectionFactory.DEFAULT_USER,
                ActiveMQConnectionFactory.DEFAULT_PASSWORD,
                MQProperties.IP
        );
        Connection connection;
        try {
            connection = connectionFactory.createConnection();
            connection.start();
            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            Topic topic = session.createTopic(MQProperties.TOPIC_NAME);
            MessageConsumer consumer = session.createConsumer(topic);
            consumer.setMessageListener(new MessageListener() {
                public void onMessage(Message message) {
                    TextMessage tm = (TextMessage) message;
                    try {
                        System.out.println(tm.getText());
                    } catch (JMSException e) {
                        e.printStackTrace();
                    }
                }
            });
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

 

2.6 扩展

ActiveMQ基于JMS API开发的。

RabbitMQ是基于AMQP开发的。

 AMQP(advanced message queuing protocol)在2003年时被提出,最早用于解决金融领不同平台之间的消息传递交互问题。顾名思义,AMQP是一种协议,更准确的说是一种binary wire-level protocol(链接协议)。这是其和JMS的本质差别,AMQP不从API层进行限定,而是直接定义网络交换的数据格式。这使得实现了AMQP的provider天然性就是跨平台的。意味着我们可以使用Java的AMQP provider,同时使用一个python的producer加一个rubby的consumer。从这一点看,AQMP可以用http来进行类比,不关心实现的语言,只要大家都按照相应的数据格式去发送报文请求,不同语言的client均可以和不同语言的server链接。

Kafka提供了类JMS的特性,但在设计实现上并不遵循JMS规范。

 

第三章 ActiveMQ的信息交换模型

通过上面对JMS的分析,知道了JMS有两种信息交传递模型,点对点和发布/订阅模式,因为ActiveMQ实现了JMS接口,所以ActiveMQ也有这两种信息传递模型。

除了这两种模式,ActiveMQ还有种信息交换模型,即虚拟主题。

假设我们有三个消费者A、B、C都订阅了Z主题,每次生产者发消息三个消费者都会接收到,突然有一天,B消费者想要进行负载均衡或者为了防止挂掉,要增加一个消费者D,每次Z主题发布的消息由A,C,B与D中的一个接收,这时,JMS原本的功能就不够用了,JMS要不就是订阅模式的消费者全部接收,要不就是点对点模式的所有消费者只接收一个,不能做到让A与C都收到,B与D其中一个收到,这时就用到了虚拟主题。

对于消息发布者来说,就是一个正常的Topic,名称以VirtualTopic.开头。例如VirtualTopic.TEST。
对于消息接收端来说,是个队列,不同应用里使用不同的前缀作为队列的名称,即可表明自己的身份即可实现消费端应用分组。例如Consumer.A.VirtualTopic.TEST,说明它是名称为A的消费端,同理Consumer.C.VirtualTopic.TEST说明是一个名称为C的客户端。可以在同一个应用里使用多个consumer消费此queue,则可以实现上面两个功能。又因为不同应用使用的queue名称不同(前缀不同),所以不同的应用中都可以接收到全部的消息。每个客户端相当于一个持久订阅者,而且这个客户端可以使用多个消费者共同来承担消费任务。

针对上面的情况,我们就可以

消息发布者的Topic:

VirtualTopic.TEST

消息消费者的Topic:

A消费者:Consumer.A.VirtualTopic.TEST

C消费者:Consumer.C.VirtualTopic.TEST

B消费者:Consumer.BD.VirtualTopic.TEST

D消费者:Consumer.BD.VirtualTopic.TEST

这样就能做到A,C都接到消息,B,D每次一个接到消息。

第四章 ActiveMQ结合SpringBoot

因为项目是现成的,我就根据推断直接复制过来了,下面只有消费者的配置,因为这篇主要是说概念,代码主要是提供个思路,参考一下就好,可能有冗余的地方,想要具体代码的建议去找网上一些其它的帖子

引入依赖:

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

消费者配置:

监听器配置:

@Component
public class HouyiMessageListener implements SessionAwareMessageListener {
    private Logger log = LoggerFactory.getLogger(HouyiMessageListener.class);
    @Override
    public void onMessage(Message message, Session session) {
        String result ="";
        try {
            ActiveMQTextMessage mes = (ActiveMQTextMessage) message;
            result = mes.getText();
            log.info("接收到消息"+result);
            /*
                处理消息
            */
            message.acknowledge();
        } catch (Exception e) {
            log.info("消息保存错误{},等待重发",result);
            try {
                session.recover();
            } catch (JMSException e1) {
                log.info("消息重发失败{}",result);
            }
        }
    }
}

 

系统属性配置:

@Configuration
public class MqConfig {
    @Autowired
    private HouyiMessageListener houyiMessageListener;
 
	@Value("${activemq.url}")
    private String url;
	
	@Value("${activemq.topic}")
    private String topic;
	
	@Value("${activemq.userName}")
    private String userName;
	
	@Value("${activemq.password}")
    private String password;
	
	@Value("${activemq.sendTimeout}")
    private String sendTimeout;
	
	@Value("${activemq.sessionCacheSize}")
    private String sessionCacheSize;

    
    private ActiveMQConnectionFactory activeMQConnectionFactory() {
    	
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory();
        activeMQConnectionFactory.setBrokerURL(url);
        activeMQConnectionFactory.setUserName(userName);
        activeMQConnectionFactory.setPassword(password);
        activeMQConnectionFactory.setUseAsyncSend(true);
        activeMQConnectionFactory.setSendTimeout(Integer.parseInt(sendTimeout));
        activeMQConnectionFactory.setTrustAllPackages(true);
        activeMQConnectionFactory.setRedeliveryPolicy(redeliveryPolicy());
        return activeMQConnectionFactory;
    }

    @Bean
    public CachingConnectionFactory cachingConnectionFactory() {

        CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();
        cachingConnectionFactory.setTargetConnectionFactory(activeMQConnectionFactory());
        cachingConnectionFactory.setSessionCacheSize(Integer.parseInt(sessionCacheSize));
        return cachingConnectionFactory;
    }

    public Destination destination() {

        ActiveMQQueue activeMQQueue = new ActiveMQQueue();
        activeMQQueue.setPhysicalName(topic);
        return activeMQQueue;
    }

    @Bean
    public JmsTemplate jmsTemplate() {

        JmsTemplate jmsTemplate = new JmsTemplate();
        jmsTemplate.setConnectionFactory(cachingConnectionFactory());
        jmsTemplate.setExplicitQosEnabled(true);
        jmsTemplate.setDeliveryPersistent(true);
        jmsTemplate.setDefaultDestination(destination());
        return jmsTemplate;
    }

    @Bean
    public DefaultMessageListenerContainer defaultMessageListenerContainer() {

        DefaultMessageListenerContainer messageListenerContainer = new DefaultMessageListenerContainer();
        messageListenerContainer.setMessageListener(houyiMessageListener);
        messageListenerContainer.setSessionAcknowledgeMode(4);
        messageListenerContainer.setDestination(destination());
        messageListenerContainer.setConnectionFactory(activeMQConnectionFactory());
        //关闭事物,否则消息重发不生效
        messageListenerContainer.setSessionTransacted(false);
        return messageListenerContainer;
    }
    /**
     * 消息失败重发
     **/
    @Bean
    public RedeliveryPolicy redeliveryPolicy(){
        RedeliveryPolicy  redeliveryPolicy=   new RedeliveryPolicy();
        //是否在每次尝试重新发送失败后,增长这个等待时间
        redeliveryPolicy.setUseExponentialBackOff(true);
        //重发次数,默认为6次   这里设置为10次
        redeliveryPolicy.setMaximumRedeliveries(10);
        //重发时间间隔,默认为1秒
        redeliveryPolicy.setInitialRedeliveryDelay(1);
        //第一次失败后重新发送之前等待500毫秒,第二次失败再等待500 * 2毫秒,这里的2就是value
        redeliveryPolicy.setBackOffMultiplier(2);
        //是否避免消息碰撞
        redeliveryPolicy.setUseCollisionAvoidance(false);
        //设置重发最大拖延时间-1 表示没有拖延只有UseExponentialBackOff(true)为true时生效
        redeliveryPolicy.setMaximumRedeliveryDelay(-1);
        return redeliveryPolicy;
    }
}

 

第五章 ActiveMQ与其它MQ的区别

从网上随便复制了一个过来,等以后熟悉了别的或者有空了再来详细写写。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值