JMS订阅模式消息

  发布/订阅模型,消息会发送到一个名为主题(Topic)的虚拟通道中,消息生产者成为发布者(Publisher),消息的消费者成为订阅者(Subscriber);与点对点模型的最大不同,就是发布到主题的消息,能够被多个订阅者接收,类似于广播.发布/订阅模型的消息传输机制是一个基于推送(push)的方式,消息将有JMS Provider主动的向消息消费者广播,消费者客户端无需请求或者轮询,只需要保持Connection的活跃性即可.

    不过在发布/订阅消息传送模型的内部,有多种不同类型的订阅者(比如,受托管订阅者,耐久订阅者,临时订阅者,动态订阅者);临时订阅者(TemporarySubscriber)为只有它主动侦听主题时才能收到消息,JMS Provider不会为临时订阅者持久存储"离线时"的任何消息的副本;持久订阅者将接收到发布的每条消息的一个副本,即使发布消息时,订阅者处于"离线"状态.

 

Java代码   收藏代码
  1. ###contextFactory  
  2. java.naming.factory.initial = org.apache.activemq.jndi.ActiveMQInitialContextFactory  
  3. ###brokerUrl,any protocol  
  4. java.naming.provider.url = tcp://localhost:61616  
  5. ##username  
  6. ##java.naming.security.principal=  
  7. ##password  
  8. ##java.naming.security.credentials=  
  9. ##connectionFactory,for building sessions  
  10. connectionFactoryNames = QueueCF,TopicCF  
  11. ##topic.<topicName> = <physicalName-of-topic>  
  12. ##your application should use <topicName>,such as:  
  13. ## context.lookup("topic1");  
  14. ##It can be more than once  
  15. topic.topic1 = jms.topic1  
  16. ##queue.<topicName> = <physicalName-of-queue>  
  17. queue.queue1 = jms.queue1  

 

Java代码   收藏代码
  1. //Topic发布者  
  2. package com.test.jms.simple.topic;  
  3.   
  4. import javax.jms.DeliveryMode;  
  5. import javax.jms.Message;  
  6. import javax.jms.MessageConsumer;  
  7. import javax.jms.Session;  
  8. import javax.jms.Topic;  
  9. import javax.jms.TopicConnection;  
  10. import javax.jms.TopicConnectionFactory;  
  11. import javax.jms.TopicPublisher;  
  12. import javax.jms.TopicSession;  
  13. import javax.jms.TopicSubscriber;  
  14. import javax.naming.Context;  
  15. import javax.naming.InitialContext;  
  16.   
  17. public class SimplePublisher {  
  18.   
  19.     private TopicPublisher producer;  
  20.     private TopicSession session;  
  21.     private TopicConnection connection;  
  22.     private boolean isOpen = true;  
  23.       
  24.     public SimplePublisher() throws Exception{  
  25.         Context context = new InitialContext();  
  26.         TopicConnectionFactory connectionFactory = (TopicConnectionFactory)context.lookup("TopicCF");  
  27.         connection = connectionFactory.createTopicConnection();  
  28.         connection.setClientID("OK111");  
  29.         session = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);  
  30.         Topic topic = (Topic)context.lookup("topic1");  
  31.         producer = session.createPublisher(topic);//non durable  
  32.         producer.setDeliveryMode(DeliveryMode.PERSISTENT);  
  33.         connection.start();  
  34.           
  35.     }  
  36.       
  37.       
  38.     public boolean send(String text) {  
  39.         if(!isOpen){  
  40.             throw new RuntimeException("session has been closed!");  
  41.         }  
  42.         try{  
  43.             Message message = session.createTextMessage(text);  
  44.             producer.send(message);  
  45.             return true;  
  46.         }catch(Exception e){  
  47.             return false;  
  48.         }  
  49.     }  
  50.       
  51.     public synchronized void close(){  
  52.         try{  
  53.             if(isOpen){  
  54.                 isOpen = false;  
  55.             }  
  56.             session.close();  
  57.             connection.close();  
  58.         }catch (Exception e) {  
  59.             //  
  60.         }  
  61.     }  
  62.       
  63. }  

 

Java代码   收藏代码
  1. //Topic订阅者  
  2. package com.test.jms.simple.topic;  
  3.   
  4. import javax.jms.Session;  
  5. import javax.jms.Topic;  
  6. import javax.jms.TopicConnection;  
  7. import javax.jms.TopicConnectionFactory;  
  8. import javax.jms.TopicSession;  
  9. import javax.jms.TopicSubscriber;  
  10. import javax.naming.Context;  
  11. import javax.naming.InitialContext;  
  12.   
  13. import com.test.jms.object.TopicMessageListener;  
  14.   
  15. public class SimpleSubscriber {  
  16.   
  17.     private TopicConnection connection;  
  18.     private TopicSession session;  
  19.     private TopicSubscriber consumer;  
  20.       
  21.     private boolean isStarted;  
  22.       
  23.     public SimpleSubscriber(String clientId) throws Exception{  
  24.         Context context = new InitialContext();  
  25.         TopicConnectionFactory connectionFactory = (TopicConnectionFactory)context.lookup("TopicCF");  
  26.         connection = connectionFactory.createTopicConnection();  
  27.         connection.setClientID(clientId);  
  28.         session = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);  
  29.         Topic topic = (Topic)context.lookup("topic1");  
  30.         consumer = session.createDurableSubscriber(topic, "Test-subscriber");  
  31.         consumer.setMessageListener(new TopicMessageListener());  
  32.     }  
  33.       
  34.       
  35.     public synchronized boolean start(){  
  36.         if(isStarted){  
  37.             return true;  
  38.         }  
  39.         try{  
  40.             connection.start();  
  41.             isStarted = true;  
  42.             return true;  
  43.         }catch(Exception e){  
  44.             return false;  
  45.         }  
  46.     }  
  47.       
  48.     public synchronized void close(){  
  49.         isStarted = false;  
  50.         try{  
  51.             session.close();  
  52.             connection.close();  
  53.         }catch(Exception e){  
  54.             //  
  55.         }  
  56.     }  
  57. }  

 

Java代码   收藏代码
  1. //测试类  
  2. package com.test.jms.simple.topic;  
  3.   
  4.   
  5.   
  6.   
  7. public class SimpleTestMain {  
  8.   
  9.     /** 
  10.      * @param args 
  11.      */  
  12.     public static void main(String[] args) throws Exception{  
  13.         SimpleSubscriber consumer = new SimpleSubscriber("TestClientId");  
  14.         consumer.start();  
  15.           
  16.         SimplePublisher productor = new SimplePublisher();  
  17.         for(int i=0; i<10; i++){  
  18.             productor.send("message content:" + i);  
  19.         }  
  20.         productor.close();  
  21.         //consumer.close();  
  22.     }  
  23.       
  24.   
  25. }  

 

    在session.createSubscriber(Topic topic)方法中,将会创建一个"非耐久性"主题,即只有subscriber侦听时才会收到消息,当subscriber离线时,它将错过消息.session.createDurableSubscriber(Topic topic,String name)用来创建一个耐久性订阅者,这种订阅者不会错过离线时的消息,JMS Provider将会为它保留所有的消息副本(必须符合相应的消息选择器).其中参数"name"用来表示此订阅者的名字,name的值可以任意,也不允许重复.

    其中耐久性订阅者,必须对connection设定ClientId且此ID全局不能重复,否则将会抛出:javax.jms.JMSException: You cannot create a durable subscriber without specifying a unique clientID on a Connection.

    一个session中只能创建一个耐久性订阅者,否则将抛出异常:javax.jms.JMSException: Durable consumer is in use for client;不过一个connection下可以有多个耐久性订阅者.

    如果一个connection下有多个耐久订阅者时,此时订阅者的name不能重复,否则抛出: javax.jms.JMSException: Durable consumer is in use for client: TestClientId and subscriptionName: ..

    session.unsubscribe(String name)方法为取消订阅,取消当前connection下指定name的订阅者.此后JMS Provider将不会为其保存消息副本.如果你确定一个耐久订阅者不会再次激活时,你需要"取消订阅",否则JMS Provider将会一直为它保存消息副本,而且极有可能带来存储上的风险,如果磁盘或者内存消耗完毕,将会导致JMS Provider故障.

 

    Topic中消息副本的存储模式(数据库描述):

 

Java代码   收藏代码
  1. ++++++++++++Consumers table+++++++  
  2. ++id      |       Name                      |       destinationId         |     created  
  3. 1                  clientID1::name1                  testTopoc               122222222  
  4. 2                  clientID1::name2                  testTopoc               122323232  
  5. //其中Name + destinationId为唯一索引.  
  6.   
  7.   
  8. ++++++++++++Messages_handles+++++++  
  9. ++id      |    messagId    |     destinationId    |    consumerId  |   delivered  
  10.   
  11. 1                 10010                 testTopic                      1                0  
  12. 2                 10010                 testTopic                      2                0  
  13. //消息的实体将会保存在其他表中,通过messageId与其关联.  
  14. //此表中messageId + destinationId + consumerId为唯一索引.  

  

    通过这个存储模式,我们能够理解出JMS Provider是如何创建消息副本的:每创建一个耐久订阅者都将会在Consumer表中新增一条记录,当"取消订阅"之后,相应的consumer记录也会被删除;当一个Topic中新的消息生成时,将会检索consumer表中此destinationId下的所有consumer,然后为每个conusmer生成消息副本--在Message_handles表中插入一条数据;如果某个consumer消费了一条消息,将会在message_handles表中删除消息副本记录.(某些JMS 实现,可能是记录每个订阅者已经消费的最后一个消息的ID,而不是消息的副本)

 

    其中createDurableSubscriber(Topic topic,String name,String selector,boolean noLocal)方法中还有一个重要的参数--noLocal,此参数主要用来控制此订阅者是否接受本地消息,所谓本地消息就是当前Connection下其他publisher发送的消息(对于JMS Provider而言,就是ClientID标识),如果noLocal = true,那么意味着将只能收到其他Client发布的消息.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值