JMS之初步学习

来源:互联网 发布:眼睛 风格 知乎 编辑:程序博客网 时间:2024/06/03 22:26

消息传送模型

 JMS支持两类消息传送模型:点对点模型和发布/订阅模型。

  发布/订阅模型设计用于一对多消息广播,而点对点模型则设计用于一对于消息传送。

 

  从JMS的视角来看,消息传送客户端称为JMS客户端,而消息传送系统则称为JMS提供者。一个JMS应用程序是由多个JMS客户端和(通常是)一个JMS提供者所组成的业务系统。此外,生成消息的JMS客户端称为消息生产者(message producer),而接收消息的JMS客户端则称为消息消费者(message consumer)。一个JMS客户端可以既是消息生产者又是消息消费者。

(一)点对点模型

      点对点消息传送模型允许JMS客户端通过队列这个虚拟通道来同步和异步发送、接收消息。在点对点模型中,消息生成者称为发送者,而消息消费者则称为接收者。传统上,点对点模型是一个基础拉取(Pull)或基于轮询(polling)的消息传送模型,这种模型从队列中请求消息,而不是自动地将消息推送到客户端。点对点消息传送模型的一个突出特点就是:发送到队列的消息被一个而且仅仅一个接收者所接收,即使可能有多个接收者在一个队列中侦听同一消息,也是如此。

     点对点消息传送模型既支持异步“即发即弃”消息传送方式,又支持同步请求/应答消息传送方式。点对点消息传送模型比发布/订阅模型具有更强的耦合性,发送者通常会知道消息将被如何使用,而且也会知道谁将接收该消息。

     点对点模型支持负载均衡,它允许多个接收者侦听同一队列,并以此来分配负载。JMS提供者负责管理队列,确保每条消息被组内下一可用的接收者消费一次,而且仅仅一次。

(二)发布/订阅模型

      在发布/订阅模型中,消息会被发布到一个名为主题(topic)的虚拟通道中。消息生产者称为发布者(publisher),而消息消费者则称为订阅者(subscriber)。与点对点模型不同,使用发布/订阅模型发布到一个主题的消息,能够由多个订阅者所接收。有时候,这项技术也称为广播消息。每个订阅者都会接收到每条消息的一个副本。总地来说,发布/订阅消息传送模型基本上是一个基于推送的模型,其中消息自动地向消费者广播,它们无须请求或轮询主题来获得新消息。

     发布/订阅模型的去耦能力要比P2P模型更强,消息发布者通常不会意识到有多少订阅者或那些订阅者如何处理这些消息。在发布/订阅消息传送模型内部,有多钟不同类型的订阅者。非持久订阅者是临时订阅类型,它们只是在主动侦听主题时才接收消息。另一方面,持久订阅者将接收到发布的每条消息的一个副本,即便在发布消息。它们处于“离线”状态时也是如此。另外还有动态持久订阅者和受管的持久订阅者等类型。 


JMS API

      JMS是一种企业消息传送API。JMS自身并不是一种消息传送系统;它是消息传送客户端和消息传送系统通信时所需接口和类的一个抽象。与JDBC抽象访问关系数据库、JNDI抽象访问命名和目录服务的方式一样,JMS抽象可以访问消息提供者。使用JMS,应用程序的消息传送客户端可以实现跨消息服务器产品的移植。

      JMS API可以分为3个主要部分:公共API、点对点API和发布/订阅API。

      在JMS公共API内部,和发送和接收JMS消息有关的JMS API接口主要有7个:

      ConnectionFactory      Destination    Connection   Session   Message    MessageProducer    MessageConsumer

      在这些公共接口中,ConnectionFactory和Destination必须使用JNDI(遵照JMS规范要求)从提供者处获得。其他接口则可以通过工厂方法在不同的API接口中创建。例如,一旦有了一个ConnectionFactory,就可以创建一个Connection。一旦有了一个Connection,就可以创建一个Session。而一旦有了一个Session,就可以创建一个Message、MessageProducer和MessageConsumer。

      在JMS中,是Session对象保存着用于消息传送的事务性工作单元,而不是Connection对象。这和JDBC不同,JDBC中是Connection对象保存事务性工作单元。这就意味着在使用JMS时,一个应用程序通常只会有一个Connection对象,但是它可以有一个Session对象池。

  JMS公共API核心接口如下图所示:


   

    点对点API

   点对点消息传送模型API特指JMS API之内基于队列的接口。下面是用于向一个队列发送和从一个队列接收消息的接口:

   QueueConnectionFactory   Queue    QueueConnection    Message   QueueSender   QueueReceiver

   JMS点对点API核心接口 如下图所示:

  

  

     发布/订阅API

     发布/订阅消息传送模型内部使用的接口如下:

      TopicConnectionFactory   Topic   TopicConnection   Message   TopicPublisher    TopicSubscriber

      其核心接口的关系和流程如下图所示:


 Chat实例

  导入activemq-all.jar 包并启动activemq服务。

  创建jndi.properties,将配置信息写在该文件中:

 

java.naming.factory.initial = org.apache.activemq.jndi.ActiveMQInitialContextFactoryjava.naming.provider.url = tcp://localhost:61616java.naming.security.principal=systemjava.naming.security.credentials=managerconnectionFactoryNames=TopicCFtopic.topic1 = jms.topic1

创建Chat类

public class Chat implements javax.jms.MessageListener{    private TopicSession pubSession;    private TopicPublisher publisher;    private TopicConnection connection;    private String username;    /* 用于初始化Chat(聊天)的构造函数 */    public Chat(String topicFactory, String topicName, String username)            throws Exception {        // 使用jndi.properties文件获得一个JNDI连接        InitialContext ctx = new InitialContext();        // 查找一个JMS连接工厂并创建连接        TopicConnectionFactory conFactory =                (TopicConnectionFactory)ctx.lookup(topicFactory);        TopicConnection connection = conFactory.createTopicConnection();        // 创建两个JMS会话对象        TopicSession pubSession = connection.createTopicSession(                false, Session.AUTO_ACKNOWLEDGE);        TopicSession subSession = connection.createTopicSession(                false, Session.AUTO_ACKNOWLEDGE);        // 查找一个JMS主题        Topic chatTopic = (Topic)ctx.lookup(topicName);        // 创建一个JMS发布者和订阅者。createSubscriber中附加的参数是一个消息选择器(null)        //和noLocal标记的一个真值,它表明这个发布者生成的消息不应被它自己所消费        TopicPublisher publisher =                pubSession.createPublisher(chatTopic);        TopicSubscriber subscriber =                subSession.createSubscriber(chatTopic, null, true);        // 设置一个JMS消息侦听器        subscriber.setMessageListener(this);        // 初始化Chat应用程序变量        this.connection = connection;        this.pubSession = pubSession;        this.publisher = publisher;        this.username = username;        // 启动JMS连接;允许传送消息        connection.start( );    }    /* 接收来自TopicSubscriber的消息 */    public void onMessage(Message message) {        try {            TextMessage textMessage = (TextMessage) message;            String text = textMessage.getText( );            System.out.println(text);        } catch (JMSException jmse){ jmse.printStackTrace( ); }    }    /* 使用发布者创建并发送消息 */    public void writeMessage(String text) throws JMSException {        TextMessage message = pubSession.createTextMessage( );        message.setText(username+": "+text);        publisher.publish(message);    }    /* 关闭JMS连接 */    public void close( ) throws JMSException {        connection.close( );    }    /* 运行聊天客户端 */    public static void main(String [] args){        try{            if (args.length!=3)                System.out.println("Factory, Topic, or username missing");            // args[0]=topicFactory; args[1]=topicName; args[2]=username            Chat chat = new Chat(args[0],args[1],args[2]);            // 输入文本            BufferedReader commandLine = new                    java.io.BufferedReader(new InputStreamReader(System.in));            // 循环,直到键入“exit”为止            while(true){                String s = commandLine.readLine( );                if (s.equalsIgnoreCase("exit")){                    chat.close( ); // close down connection                    System.exit(0);// exit program                } else                    chat.writeMessage(s);            }        } catch (Exception e){ e.printStackTrace( ); }    }}
这个实例需要往main函数传入三个参数   Program arguments   传入参数TopicCF  topic1  zhai

运行之后   可以再修改参数再进行运行 传入参数TopicCF  topic1  Zhang   ,同理可以运行多个窗口。然后在一个窗口输入信息,另外的所有订阅者都可以接收到。

 

分析源代码

    聊天客户端为一个特定的主题创建一个JMS发布者和订阅者。该主题就代表了聊天室。JMS服务器注册了所有想要发布或订阅一个特定主题的JMS客户端。在一个聊天客户端的命令行中输入文本时,它会发布给消息传送服务器。消息传送服务器会识别出和该发布者有关的主题,并将消息传送给已经订阅该主题的所有JMS客户端。由任何一个JMS客户端发布的消息,都会被传送给该主题的所有JMS订阅者。

 

    

    其中值得注意的两点:

   1.  subscriber.setMessageListener(this);

    JMS中的发布/订阅消息传送模型包括了一个用于处理输入消息的进程内Java事件模型。一个对象非常简单地实现了侦听器接口(MessageListener),然后使用TopSubscriber进行注册。一个TopicSubscriber只能有一个MessageListener对象。当TopicSubscriber从它的主题接收一个消息时,调用了它的MessageListener对象的onMessage()方法。在Chat类中自身实现了MessageListener接口和onMessage()方法。Chat类是MessageListener类型,因此,使用TopicSubscriber在构造函数中进行自我注册。当消息服务器将一条消息推向TopicSubscriber时,Topicsubscriber将调用Chat对象的onMessage()方法。

     java事件模型用于通过调用一个或多个对象上的方法来传送事件,这些对象处于同一过程之中,而且已经注册为侦听器。JMS发布/订阅模型使用Java事件模型,以便TopicSubscriber通知它在同一过程中的MessageListener对象:一条来自消息服务器的消息已经到达。

    2.pubSession和subSession

      Chat应用程序分别为发布者和订阅者设置了两个单独的会话:pubSession和subSession。其原因在于JMS强制实行的线程限制。按照JMS规范的规定:一个会话不能同时在一个以上的线程中运行。

       JMS规范的一个目标就是要避免将一个内部体系结构强加在JMS提供者上。特别是要避免要求JMS提供者对Session对象的实现具有安全处理对个线程的能力。这主要是考虑了JMS的预定用途——JMS API是现有消息传送系统的一个包装器,它可能在客户端不具有多线程传送的能力。

0 0
原创粉丝点击