ActiveMQ 发布订阅(publish-subscribe)

publish-subscribe

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



  现在,假定我们用前面讨论的场景来写一个简单的示例。我们首先需要定义的是publisher.

publisher

     publisher是属于发布信息的一方,它通过定义一个或者多个topic,然后给这些topic发送消息。

    publisher的构造函数如下:


public Publisher() throws JMSException {
    	factory = new ActiveMQConnectionFactory(brokerURL);
    	connection = factory.createConnection();
    	try {
        connection.start();
    	} catch (JMSException jmse) {
    		connection.close();
    		throw jmse;
    	}
        session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        producer = session.createProducer(null);
    }


我们按照前面说的流程定义了基本的connectionFactory, connection, session, producer。这里代码就是主要实现初始化的效果。

    接着,我们需要定义一系列的topic让所有的consumer来订阅,设置topic的代码如下:



protected void setTopics(String[] stocks) throws JMSException {
	destinations = new Destination[stocks.length];
	for(int i = 0; i < stocks.length; i++) {
		destinations[i] = session.createTopic("STOCKS." + stocks[i]);
	}
}


这里destinations是一个内部定义的成员变量Destination[]。这里我们总共定义了的topic数取决于给定的参数stocks。

     在定义好topic之后我们要给这些指定的topic发消息,具体实现的代码如下:


protected void sendMessage(String[] stocks) throws JMSException {
	for(int i = 0; i < stocks.length; i++) {
		Message message = createStockMessage(stocks[i], session);
		System.out.println("Sending: " + ((ActiveMQMapMessage)message).getContentMap() + " on destination: " + destinations[i]);
		producer.send(destinations[i], message);
	}
}

protected Message createStockMessage(String stock, Session session) throws JMSException {
    MapMessage message = session.createMapMessage();
	message.setString("stock", stock);
	message.setDouble("price", 1.00);
	message.setDouble("offer", 0.01);
	message.setBoolean("up", true);
		
	return message;
}

  前面的代码很简单,在sendMessage方法里我们遍历每个topic,然后给每个topic发送定义的Message消息。

    在定义好前面发送消息的基础之后,我们调用他们的代码就很简单了:


public static void main(String[] args) throws JMSException {
	if(args.length < 1)
		throw new IllegalArgumentException();
	
        // Create publisher		
        Publisher publisher = new Publisher();
        
        // Set topics
	publisher.setTopics(args);
		
	for(int i = 0; i < 10; i++) {
		publisher.sendMessage(args);
		System.out.println("Publisher '" + i + " price messages");
		try {
			Thread.sleep(1000);
		} catch(InterruptedException e) {
			e.printStackTrace();
		}
    }
    // Close all resources
    publisher.close();
}

调用他们的代码就是我们遍历所有topic,然后通过sendMessage发送消息。在发送一个消息之后先sleep1秒钟。要注意的一个地方就是我们使用完资源之后必须要使用close方法将这些资源关闭释放。close方法关闭资源的具体实现如下:


public void close() throws JMSException {
    if (connection != null) {
        connection.close();
     }
}

consumer

    Consumer的代码也很类似,具体的步骤无非就是1.初始化资源。 2. 接收消息。 3. 必要的时候关闭资源。

    初始化资源可以放到构造函数里面:


public Consumer() throws JMSException {
    	factory = new ActiveMQConnectionFactory(brokerURL);
    	connection = factory.createConnection();
        connection.start();
        session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
    }

   接收和处理消息的方法有两种,分为同步和异步的,一般同步的方式我们是通过MessageConsumer.receive()方法来处理接收到的消息。而异步的方法则是通过注册一个MessageListener的方法,使用MessageConsumer.setMessageListener()。这里我们采用异步的方式实现:


public static void main(String[] args) throws JMSException {
    Consumer consumer = new Consumer();
    for (String stock : args) {
	Destination destination = consumer.getSession().createTopic("STOCKS." + stock);
	MessageConsumer messageConsumer = consumer.getSession().createConsumer(destination);
	messageConsumer.setMessageListener(new Listener());
    }
}
	
public Session getSession() {
	return session;
}


  在前面的代码里我们先找到同样的topic,然后遍历所有的topic去获得消息。对于消息的处理我们专门通过Listener对象来负责。

    Listener对象的职责很简单,主要就是处理接收到的消息:


public class Listener implements MessageListener {

	public void onMessage(Message message) {
		try {
			MapMessage map = (MapMessage)message;
			String stock = map.getString("stock");
			double price = map.getDouble("price");
			double offer = map.getDouble("offer");
			boolean up = map.getBoolean("up");
			DecimalFormat df = new DecimalFormat( "#,###,###,##0.00" );
			System.out.println(stock + "\t" + df.format(price) + "\t" + df.format(offer) + "\t" + (up?"up":"down"));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}


 它实现了MessageListener接口,里面的onMessage方法就是在接收到消息之后会被调用的方法。

    现在,通过实现前面的publisher和consumer我们已经实现了pub-sub模式的一个实例。仔细回想它的步骤的话,主要就是要两者设定一个共同的topic,有了这个topic之后他们可以实现一方发消息另外一方接收。另外,为了连接到具体的message server,这里是使用了连接tcp://localhost:16161作为定义ActiveMQConnectionFactory的路径。在publisher端通过session创建producer,根据指定的参数创建destination,然后将消息和destination作为producer.send()方法的参数发消息。在consumer端也要创建类似的connection, session。通过session得到destination,再通过session.createConsumer(destination)来得到一个MessageConsumer对象。有了这个MessageConsumer我们就可以自行选择是直接同步的receive消息还是注册listener了。





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值