ActiveMQ的使用与遇到的相关坑(点对点,发布与订阅,resreq)

1、介绍

ActiveMQ是Apache出品,最流行的,能力强劲的开源消息总线。ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现,尽管JMS规范出台已经是很久的事情了,但是JMS在当今的J2EE应用中间仍然扮演着特殊的地位。

MQ可以是不同应用之间进行沟通的桥梁,

支持多种语言和协议编写客户端,如: Java,C,C++,C#,Ruby,Perl,Python,PHP等;

应用协议: OpenWire,Stomp REST,WS Notification,XMPP,AMQP

简单的说,如果一个项目里分多个应用,每个应用分别处理着各自的事,MQ就是他们互相联系的工具,一个应用处理好某件事,向MQ发送一个消息,MQ通过点对点、发布订阅或request-response的方式来处理消息,将消息转发出去,另一个应用接收到消息,并作出相应的处理。

2、下载与安装

a、在ActiveMQ的官网下载最新版本,如图

ActiveMQ的使用与遇到的相关坑(点对点,发布与订阅,resreq)_第1张图片

b、解压文件,运行\apache-activemq-5.5.1\bin\activemq.bat,正常的话就会有这样的提示“Started [email protected]:8161”,出错了也不用担心,报的错通过google可以很轻易的解决。

c、打开网址http://localhost:8161/admin/,进入MQ的管理界面如下图

ActiveMQ的使用与遇到的相关坑(点对点,发布与订阅,resreq)_第2张图片

到此,MQ的下载与安装就完成了。

3、MQ的通信方式

MQ中需要用的一些类

  • ConnectionFactory :连接工厂,JMS 用它创建连接
  • Connection :JMS 客户端到JMS Provider 的连接
  • Session: 一个发送或接收消息的线程
  • Destination :消息的目的地;消息发送给谁.
  • MessageConsumer / MessageProducer: 消息接收者,消费者

a、发布-订阅

发布订阅模式有点类似于我们日常生活中订阅报纸。每年到年尾的时候,邮局就会发一本报纸集合让我们来选择订阅哪一个。在这个表里头列了所有出版发行的报纸,那么对于我们每一个订阅者来说,我们可以选择一份或者多份报纸。比如北京日报、潇湘晨报等。那么这些个我们订阅的报纸,就相当于发布订阅模式里的topic。有很多个人订阅报纸,也有人可能和我订阅了相同的报纸。那么,在这里,相当于我们在同一个topic里注册了。对于一份报纸发行方来说,它和所有的订阅者就构成了一个1对多的关系。

突破目的队列地理指向的限制,使消息按照特定的主题甚至内容进行分发,用户或应用程序可以根据主题或内容接收到所需要的消息。发布/订阅功能使得发送者和接收者之间的耦合关系变得更为松散,发送者不必关心接收者的目的地址,而接收者也不必关心消息的发送地址,而只是根据消息的主题进行消息的收发。

这种关系如下图所示:

ActiveMQ的使用与遇到的相关坑(点对点,发布与订阅,resreq)_第3张图片

java代码实现如下图,代码最后有分享

ActiveMQ的使用与遇到的相关坑(点对点,发布与订阅,resreq)_第4张图片

这里注意

i、session.createTopic(Stirng),参数要保持一致,原因你订阅了某个报纸,如果写错,当然邮局就不会认为你订阅了这个报纸;

ii、先运行receved,在运行sender,你必须先订阅报纸,邮局才会发给你,什么时候订阅,就从那个时间以后发报纸,也就是说,你在这个点启动receved,接收到的消息就是这个点以后的,之前的是收不到(这是与点对点的区别,很重要),你可以测试两者启动的顺序;

b、p2p(点对点)

p2p的过程则理解起来更加简单。它好比是两个人打电话,这两个人是独享这一条通信链路的。一方发送消息,另外一方接收,就这么简单。在实际应用中因为有多个用户对使用p2p的链路;

点对点方式是最为传统和常见的通讯方式,它支持一对一、一对多、多对多、多对一等多种配置方式,支持树状、网状等多种拓扑结构。

它的通信场景如下图所示:


ActiveMQ的使用与遇到的相关坑(点对点,发布与订阅,resreq)_第5张图片

java代码如下:

ActiveMQ的使用与遇到的相关坑(点对点,发布与订阅,resreq)_第6张图片

注意:

i、session.createQueue(String),和订阅发布不同,其他的都相同,也不要忘了参数要一直,相当于一个队列

ii、无论Receiver什么时候启动,只要队列里有消息,全部都可以接收到,可以更换两者的启动方式来测试

c、request-response

和前面两种方式比较起来,request-response的通信方式很常见,但是不是默认提供的一种模式。在前面的两种模式中都是一方负责发送消息而另外一方负责处理。而我们实际中的很多应用相当于一种一应一答的过程,需要双方都能给对方发送消息。于是请求-应答的这种通信方式也很重要。它也应用的很普遍。      请求-应答方式并不是JMS规范系统默认提供的一种通信方式,而是通过在现有通信方式的基础上稍微运用一点技巧实现的。

个人觉得这样比较合理,就像tcp/ip协议一样,有请求就要有响应,我发送给你消息,你至少给我个反馈吧,收到还是没收到,出现了什么问题,都说一声,我这边可以继续下一个消息的转发

ActiveMQ的使用与遇到的相关坑(点对点,发布与订阅,resreq)_第7张图片

相关代码:

Client:

package com.mq.reqres;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;
import java.util.Random;
 
public class Client implements MessageListener {
    private static int ackMode;
    private static String clientQueueName;
 
    private boolean transacted = false;
    private MessageProducer producer;
 
    static {
        clientQueueName = "client.messages";
        ackMode = Session.AUTO_ACKNOWLEDGE;
    }
 
    public Client() {
        ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
        Connection connection;
        try {
            connection = connectionFactory.createConnection();
            connection.start();
            Session session = connection.createSession(transacted, ackMode);
            Destination adminQueue = session.createQueue(clientQueueName);
 
            //Setup a message producer to send message to the queue the server is consuming from
            this.producer = session.createProducer(adminQueue);
            this.producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
 
            //Create a temporary queue that this client will listen for responses on then create a consumer
            //that consumes message from this temporary queue...for a real application a client should reuse
            //the same temp queue for each message to the server...one temp queue per client
            Destination tempDest = session.createTemporaryQueue();
            MessageConsumer responseConsumer = session.createConsumer(tempDest);
 
            //This class will handle the messages to the temp queue as well
            responseConsumer.setMessageListener(this);
 
            //Now create the actual message you want to send
            TextMessage txtMessage = session.createTextMessage();
            // 设置信息
            txtMessage.setText("MyProtocolMessage");
 
            //Set the reply to field to the temp queue you created above, this is the queue the server
            //will respond to
            txtMessage.setJMSReplyTo(tempDest);
 
            //Set a correlation ID so when you get a response you know which sent message the response is for
            //If there is never more than one outstanding message to the server then the
            //same correlation ID can be used for all the messages...if there is more than one outstanding
            //message to the server you would presumably want to associate the correlation ID with this
            //message somehow...a Map works good
            String correlationId = this.createRandomString();
            txtMessage.setJMSCorrelationID(correlationId);
            this.producer.send(txtMessage);
        } catch (JMSException e) {
            //Handle the exception appropriately
        }
    }
 
    private String createRandomString() {
        Random random = new Random(System.currentTimeMillis());
        long randomLong = random.nextLong();
        return Long.toHexString(randomLong);
    }
 
    public void onMessage(Message message) {
        String messageText = null;
        try {
            if (message instanceof TextMessage) {
                TextMessage textMessage = (TextMessage) message;
                messageText = textMessage.getText();
                System.out.println("响应内容 = " + messageText);
            }
        } catch (JMSException e) {
            //Handle the exception appropriately
        }
    }
 
    public static void main(String[] args) {
        new Client();
    }
}
Server

package com.mq.reqres;

import org.apache.activemq.broker.BrokerService;
import org.apache.activemq.ActiveMQConnectionFactory;
 
import javax.jms.*;
 
public class Server implements MessageListener {
    private static int ackMode;
    private static String messageQueueName;
    private static String messageBrokerUrl;
 
    private Session session;
    private boolean transacted = false;
    private MessageProducer replyProducer;
    private MessageProtocol messageProtocol;
 
    static {
        messageBrokerUrl = "tcp://localhost:61616";
        messageQueueName = "client.messages";
        ackMode = Session.AUTO_ACKNOWLEDGE;
    }
 
    public Server() {
        try {
            //This message broker is embedded
            BrokerService broker = new BrokerService();
            broker.setPersistent(false);
            broker.setUseJmx(false);
            broker.addConnector(messageBrokerUrl);
            broker.start();
        } catch (Exception e) {
            //Handle the exception appropriately
        }
 
        //Delegating the handling of messages to another class, instantiate it before setting up JMS so it
        //is ready to handle messages
        this.messageProtocol = new MessageProtocol();
        this.setupMessageQueueConsumer();
    }
 
    private void setupMessageQueueConsumer() {
        ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(messageBrokerUrl);
        Connection connection;
        try {
            connection = connectionFactory.createConnection();
            connection.start();
            this.session = connection.createSession(this.transacted, ackMode);
            Destination adminQueue = this.session.createQueue(messageQueueName);
 
            //Setup a message producer to respond to messages from clients, we will get the destination
            //to send to from the JMSReplyTo header field from a Message
            this.replyProducer = this.session.createProducer(null);
            this.replyProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
 
            //Set up a consumer to consume messages off of the admin queue
            MessageConsumer consumer = this.session.createConsumer(adminQueue);
            consumer.setMessageListener(this);
        } catch (JMSException e) {
            //Handle the exception appropriately
        }
    }
 
    public void onMessage(Message message) {
        try {
            TextMessage response = this.session.createTextMessage();
            if (message instanceof TextMessage) {
                TextMessage txtMsg = (TextMessage) message;
                String messageText = txtMsg.getText();
                response.setText(this.messageProtocol.handleProtocolMessage(messageText));
            }
 
            //Set the correlation ID from the received message to be the correlation id of the response message
            //this lets the client identify which message this is a response to if it has more than
            //one outstanding message to the server
            response.setJMSCorrelationID(message.getJMSCorrelationID());
 
            //Send the response to the Destination specified by the JMSReplyTo field of the received message,
            //this is presumably a temporary queue created by the client
            this.replyProducer.send(message.getJMSReplyTo(), response);
        } catch (JMSException e) {
            //Handle the exception appropriately
        }
    }
 
    public static void main(String[] args) {
        new Server();
    }
}
MessageProtocol

package com.mq.reqres;

public class MessageProtocol {
    public String handleProtocolMessage(String messageText) {
        String responseText;
        // 判断是否是client传过来的信息,在这里就可以做些解析
        if ("MyProtocolMessage".equalsIgnoreCase(messageText)) {
            responseText = "我收到了信息";
        } else {
            responseText = "我不知道你传的是什么: " + messageText;
        }
         
        return responseText;
    }
}

结果

响应内容 = 我收到了信息


相信大家都能看得懂,很容易理解。


这是我对MQ的理解,像Spinrg配置和负载均衡配置或者断线重连等等问题,我会在以后遇到解决并跟新blog...


这里说下我遇到的大坑,我们这边的项目用的是Topic来处理信号,然而外包那边用的是Queue,结果就是信号怎么也过不来,我对MQ也不是很了解,我花了半天的时间检查双方的代码,确定是外包那边的问题。外包一句感谢的话也没说!!!当时就不开心了

我这里想说的是,双方之间通信的方式要统一,一定要统一,一定一定要统一


代码:GitHub


参考Blog:http://www.cnblogs.com/xwdreamer/archive/2012/02/21/2360818.html

你可能感兴趣的:(JAVA)