1 前言
在JMS那篇文章中我有提过消息通信模式,主要的模式有两种点对点(Point-to-Point)、发布-订阅(Publish/Subscribe),在那篇文章中我只是描述了一些理论性的东西,并没有实际的案例,这篇文章我将用案例来讨论这两个模式。
2 点对点(P2P)
p2p的过程则理解起来比较简单。它好比是两个人打电话,这两个人是独享这一条通信链路的。一方发送消息,另外一方接收,就这么简单。它的通信场景如下图所示:
2.1 案例
生产端
class Publisher {
private ActiveMQConnectionFactory factory;
private Connection connection;
private Session session;
private MessageProducer producer;
private Destination destination;
public Publisher() throws JMSException {
//建立ConnectionFactory工厂对象,需要填入用户名、密码、以及要连接的地址,均使用默认即可,默认端囗为“tcp://localhost:61616”
factory = new ActiveMQConnectionFactory(
"hfbin",
"hfbin",
"tcp://localhost:61616");
//2通过ConnectionFactory工厂对象我们创建一个Connection连接,并且调用Connection的start方法开启连接,Connection默认是关闭的。
connection = factory.createConnection();
connection.start();
//3 过Connection对象创建Session会话(上下文环境对象),用于接收消息,参数配置1为是否启用是事务,参数配置2为签收模式,一般我们设置自动签收。
session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
4通过Session创建Destination对象,指的是一个客户端用来指定生产消息目标和消费消息来源的对象,在PTP模式中,Destination被称作Queue即队列:在Pub/Sub模式,Destination被称作Topic及主题。在程序中可以使用多个Queue和Topic。
destination = session.createQueue("queue2");
//5我们需要通过Session对象创建消息的发送和接收对象生产者 MessageProducer。
producer = session.createProducer(destination);
}
//6 该方法实现数据发送
public void sendMessage() throws JMSException {
for(int i = 0; i < 5; i++)
{
TextMessage message = session.createTextMessage("I Tell You >> "+i);
System.out.println("生产数据: " + "I Tell You >> "+i);
producer.send(destination, message);
}
connection.close();
}
public static void main(String[] args) throws JMSException {
Publisher publisher = new Publisher();
publisher.sendMessage();
}
}
这里我使用了构造函数初始化所有的资源,在这我不做过多说明,这里代码我上一篇文章都有详细的说明。
消费端
public class Consumer {
private ActiveMQConnectionFactory factory;
private Connection connection;
private Session session;
private MessageConsumer consumer;
private Destination destination;
public Consumer() throws JMSException {
factory = new ActiveMQConnectionFactory(
"hfbin",
"hfbin",
"tcp://localhost:61616"
);
connection = factory.createConnection();
connection.start();
session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
destination = session.createQueue("queue2");
//通过Session对象创建消息的发送和接收对象消费者 MessageConsumer。
consumer = session.createConsumer(destination);
}
//获取数据
public void getMessage() throws JMSException {
//使用监听方式获取数据 ,在这我定义了内部类Listener需要实现MessageListener接口然后实现里面的方法。
consumer.setMessageListener(new Listener());
}
//内部类 实现了MessageListener接口,里面的onMessage方法就是在接收到消息之后会被调用的方法
class Listener implements MessageListener{
@Override
public void onMessage(Message message) {
if (message instanceof TextMessage){
try {
System.out.println(((TextMessage) message).getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws JMSException {
Consumer consumer = new Consumer();
consumer.getMessage();
}
}
接收和处理消息的方法有两种,分为同步和异步的,一般同步的方式我们是通过MessageConsumer.receive()方法来处理接收到的消息。而异步的方法则是通过注册一个MessageListener的方法,使用MessageConsumer.setMessageListener()。这里我采用异步的方式实现。
这里我就不给出结果了,需要的可以自己到我的GitHub上下载代码,自己测试。
源代码:https://github.com/hfbin/Thread_Socket/tree/master/ActiveMQ/PTP
3 发布/订阅(Pub/Sub)
发布订阅模式有点类似于我们日常生活中订阅报纸。每年到年尾的时候,邮局就会发一本报纸集合让我们来选择订阅哪一个。在这个表里头列了所有出版发行的报纸,那么对于我们每一个订阅者来说,我们可以选择一份或者多份报纸。比如北京日报、潇湘晨报等。那么这些个我们订阅的报纸,就相当于发布订阅模式里的topic。有很多个人订阅报纸,也有人可能和我订阅了相同的报纸。那么,在这里,相当于我们在同一个topic里注册了。对于一份报纸发行方来说,它和所有的订阅者就构成了一个1对多的关系。这种关系如下图所示:
3.1 案例
其实这个案例跟PTP差不多就改一下创建类型而已,将session.createQueue()
改成session.createTopic()
而已,然后多几个消费端的类。
生产端
class Publisher {
private ActiveMQConnectionFactory factory;
private Connection connection;
private Session session;
private MessageProducer producer;
private Destination destination;
public Publisher() throws JMSException {
//建立ConnectionFactory工厂对象,需要填入用户名、密码、以及要连接的地址,均使用默认即可,默认端囗为“tcp://localhost:61616”
factory = new ActiveMQConnectionFactory(
"hfbin",
"hfbin",
"tcp://localhost:61616");
//2通过ConnectionFactory工厂对象我们创建一个Connection连接,并且调用Connection的start方法开启连接,Connection默认是关闭的。
connection = factory.createConnection();
connection.start();
//3 过Connection对象创建Session会话(上下文环境对象),用于接收消息,参数配置1为是否启用是事务,参数配置2为签收模式,一般我们设置自动签收。
session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
4通过Session创建Destination对象,指的是一个客户端用来指定生产消息目标和消费消息来源的对象,在PTP模式中,Destination被称作Queue即队列:在Pub/Sub模式,Destination被称作Topic及主题。在程序中可以使用多个Queue和Topic。
destination = session.createTopic("Topic");
//5我们需要通过Session对象创建消息的发送和接收对象生产者 MessageProducer。
producer = session.createProducer(destination);
}
//6 该方法实现数据发送
public void sendMessage() throws JMSException {
for(int i = 0; i < 5; i++)
{
TextMessage message = session.createTextMessage("I Tell You >> "+i);
System.out.println("生产数据: " + "I Tell You >> "+i);
producer.send(destination, message);
}
connection.close();
}
public static void main(String[] args) throws JMSException {
Publisher publisher = new Publisher();
publisher.sendMessage();
}
}
每一个消费端的代码都是一模一样的。
消费端1
public class Consumer1 {
private ActiveMQConnectionFactory factory;
private Connection connection;
private Session session;
private MessageConsumer consumer;
private Destination destination;
public Consumer1() throws JMSException {
factory = new ActiveMQConnectionFactory(
"hfbin",
"hfbin",
"tcp://localhost:61616"
);
connection = factory.createConnection();
connection.start();
session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
destination = session.createTopic("Topic");
consumer = session.createConsumer(destination);
}
public void getMessage() throws JMSException {
consumer.setMessageListener(new Listener());
}
class Listener implements MessageListener{
@Override
public void onMessage(Message message) {
if (message instanceof TextMessage){
try {
System.out.println(((TextMessage) message).getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws JMSException {
Consumer1 consumer1 = new Consumer1();
consumer1.getMessage();
}
}
消费端2
public class Consumer2 {
private ActiveMQConnectionFactory factory;
private Connection connection;
private Session session;
private MessageConsumer consumer;
private Destination destination;
public Consumer2() throws JMSException {
factory = new ActiveMQConnectionFactory(
"hfbin",
"hfbin",
"tcp://localhost:61616"
);
connection = factory.createConnection();
connection.start();
session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
destination = session.createTopic("Topic");
consumer = session.createConsumer(destination);
}
public void getMessage() throws JMSException {
consumer.setMessageListener(new Listener());
}
class Listener implements MessageListener{
@Override
public void onMessage(Message message) {
if (message instanceof TextMessage){
try {
System.out.println(((TextMessage) message).getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws JMSException {
Consumer2 Consumer2 = new Consumer2();
Consumer2.getMessage();
}
}
这里启动时候建议先启动消费端,然后再进行生产端的启动,等生产端运行完毕后我们查看消费端1、消费端2都打印了生产端发送的数据。
源代码:https://github.com/hfbin/Thread_Socket/tree/master/ActiveMQ/PubSub
现在如果我们比较一下pub-sub和p2p模式的具体实现步骤的话,我们会发现他们基本的处理流程都是类似的,除了在pub-sub中要通过createTopic来设置topic,而在p2p中要通过createQueue来创建通信队列。