JMS学习(四)-一个简单的聊天应用程序分析

简介:

一,介绍

本文介绍一个简单的聊天应用程序:生产者将消息发送到Topic上,然后由ActiveMQ将该消息Push给订阅了该Topic的消费者。示例程序来自于《JAVA 消息服务--第二版 Mark Richards著》

 

二,项目开发环境搭建

①使用Eclipse新建一个JAVA工程:jms_activemq

②添加依赖包:activemq-all-5.13.2.jar

③创建JNDI文件: jndi.properties,并将之放到classpath下(即eclipse项目的 src 目录)。   文件内容如下:

复制代码
java.naming.factory.initial = org.apache.activemq.jndi.ActiveMQInitialContextFactory
java.naming.provider.url=tcp://192.168.121.35:61616
java.naming.security.principal=system
java.naming.security.credentials=manager

# use the following property to specify the JNDI name the connection factory
# should appear as. 
#connectionFactoryNames = connectionFactory, queueConnectionFactory, topicConnectionFactry
connectionFactoryNames = TopicCF  
#TopicConnectionFactory conFactory = (TopicConnectionFactory)ctx.lookup(topicFactory)

# register some queues in JNDI using the form
# queue.[jndiName] = [physicalName]
## queue.MyQueue = example.MyQueue

# register some topics in JNDI using the form
# topic.[jndiName] = [physicalName]
## topic.MyTopic = example.MyTopic
topic.topic1=jms.topic1
#Topic chatTopic = (Topic)ctx.lookup(topicName);
复制代码

注意:jndi.properties文件中的值不要拼写错误了(此外,等号右边的值的末尾,不能带有空格!!!)。否则会抛出:java.lang.ClassNotFoundException

④新建源文件。编写程序。项目截图如下:

 

⑤配置activemq.xml文件,在broker下 添加一个 destinations,并启动ActiveMQ

配置如下:

这里详细说下如下启动ActiveMQ,一种方式是在前台启动,另一个方式是在后台启动。如果在前台终端中启动的话,当关闭终端后程序也就终止了。而在后台启动,关闭终端不影响它的启动。

前台启动: ./bin/activemq start    或者   ./bin/activemq console

后台启动:参考

 nohup ./bin/activemq start &

在后台启动后,如何关闭呢?首先找到相应的进程号: ps -ef | grep activemq,然后 kill pid 即可。

 

三,JNDI解释

JNDI是一个与具体实现无关的API,不管你用的是ActiveMQ还是WebSphereMQ还是Microsoft Message Queuing,它都可用作目录服务。JMS Client使用目录服务来访问ConnectionFactory和Destination(即主题和队列对象)。

ConnectionFactory及Destination 与JMS中的Producer、Consumer、Session、Connection不同,后者是用JMS API通过工厂模式直接生成的。而前者则需要JNDI查询获得。

复制代码
在JMS中,JNDI主要用于命名服务来定位受管对象。受管对象就是由系统管理员创建和配置的JMS对象。受管对象包括JMS ConnectionFactory 和 Destination 对象

受管对象和命名服务中的一个名称相互绑定在一起。一个命名服务将名称和分布式对象、文件、设施关联起来,以便它们可以使用简单的名称而不是密码似的网络地址实现在网络上的定位。
如,DNS,它将域名www.cnblogs.com 转换成一个网络地址(ip),而浏览器使用该ip连接到web服务器上

命名服务将分布式对象、JMS受管对象……和名称相互绑定,并按照和文件系统相似的层次进行组织

JNDI提供了一个隐藏命名服务细节的抽象。使用JNDI,JMS Client 能够浏览一个命名服务,并在不知道命名服务细节的情况下引用受管对象。

通常,JMS服务器会和一个标准的JNDI驱动器(也称为服务提供者)以及类似于LDAP(轻量级目录访问协议)这样的目录服务 结合使用。或者,JMS 服务器提供一个私有的JNDI服务提供者 和 目录服务。
复制代码

 

java.naming.factory.initial = org.apache.activemq.jndi.ActiveMQInitialContextFactory
java.naming.provider.url=tcp://192.168.121.35:61616
//other jndi config......

 

四,建立连接的详细过程

Chat类的构造函数通过连接JMS服务器使用的JNDI命名服务来建立连接

public Chat(String topicFactory, String topicName, String userName)throws Exception {
        InitialContext ctx = new InitialContext();//使用jndi.properties 获得一个JNDI连接

InitialContex就是所有JNDI查找的起始点,相当于文件系统的根目录。它提供了一条到目录服务的网络连接,这个目录服务就发挥访问JMS受管对象的根目录的作用。

在我们的示例中,ActiveMQ就是JMS服务器,JMS受管对象就是在 conf/activemq.xml 文件中配置的destionations 和 ConnectionFactory

通过new一个InitialContex对象,就建立起了一个到ActiveMQ目录服务的连接,然后在ActiveMQ的命名服务中查找TopicConnectionFactory

TopicConnectionFactory conFactory = (TopicConnectionFactory)ctx.lookup(topicFactory);

受管对象TopicConnectionFactory的属性和行为则由系统管理员配置。比如,一个连接工厂可配置为使用特定的协议、安全模式...连接。

 

有了TopicConnectionFactory之后,就可以创建TopicConnection了。

TopicConnection connection = conFactory.createTopicConnection();

 

连接的认证则可以在jndi.properties文件中配置

java.naming.security.principal=system
java.naming.security.credentials=manager

 

TopicConnection表示和消息服务器(ActiveMQ)的一个连接。一个JMS Client 可以选择从同一连接工厂创建多个连接,但是由于创建连接开销很大,一般是从同一连接中创建多个Session对象。

TopicSession pubSession = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
TopicSession subSession = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);

关闭TopicConnection将关闭和该连接有关的所有对象,包括TopicSession,TopicPublisher、TopicSubscriber。

 

TopicSession是创建Message、TopicPublisher、TopicSubscriber对象的工厂,用它来创建TopicPublisher和TopicSubscriber。TopicPublisher和TopicSubscriber对象都有一定特定的Topic标识,且这两个对象专属于创建它们的TopicSession。

TopicPublisher publisher = pubSession.createPublisher(chatTopic);
TopicSubscriber subscriber = pubSession.createSubscriber(chatTopic, null, true);

 

TopicPublisher向Topic发送消息,TopicSubscriber从Topic订阅消息。JNDI用于定位一个Topic对象,它是类似于TopicConnectionFactory的一个受管对象。

        Topic chatTopic = (Topic)ctx.lookup(topicName);

Topic对象是消息服务器上的一个实际主题的句柄,该主题称为物理主题(physical topic)。一个物理主题就是多个Client订阅和发布消息的一条电子通道。

这样,发布者和订阅者就知道往哪里发送/接收消息了。

 TopicPublisher是将消息异步发送到Topic,TopicPublisher不会阻塞直到所有的订阅者接收到该消息为止,而是只要消息服务器一接收到消息,TopicPublisher就会从publish()方法返回。

它依赖消息服务器将消息传送给该主题的所有订阅者,消息服务器将消息异步推送给TopicSubscriber。

 

整个完整程序代码:

复制代码
 1 import java.io.BufferedReader;
 2 import java.io.InputStreamReader;
 3 
 4 import javax.jms.JMSException;
 5 import javax.jms.Message;
 6 import javax.jms.MessageListener;
 7 import javax.jms.Session;
 8 import javax.jms.TextMessage;
 9 import javax.jms.Topic;
10 import javax.jms.TopicConnection;
11 import javax.jms.TopicConnectionFactory;
12 import javax.jms.TopicPublisher;
13 import javax.jms.TopicSession;
14 import javax.jms.TopicSubscriber;
15 import javax.naming.InitialContext;
16 
17 public class Chat implements MessageListener{
18 
19     private TopicSession pubSession;
20     private TopicPublisher publisher;
21     private TopicConnection connection;
22     private String userName;
23     
24     
25     public Chat(String topicFactory, String topicName, String userName)throws Exception {
26         InitialContext ctx = new InitialContext();//使用jndi.properties 获得一个JNDI连接
27         
28         TopicConnectionFactory conFactory = (TopicConnectionFactory)ctx.lookup(topicFactory);
29         TopicConnection connection = conFactory.createTopicConnection();
30         
31         TopicSession pubSession = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
32         TopicSession subSession = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
33         
34         Topic chatTopic = (Topic)ctx.lookup(topicName);
35         
36         TopicPublisher publisher = pubSession.createPublisher(chatTopic);
37         TopicSubscriber subscriber = pubSession.createSubscriber(chatTopic, null, true);
38         
39         subscriber.setMessageListener(this);
40         
41         
42         this.connection = connection;
43         this.pubSession = pubSession;
44         this.publisher = publisher;
45         this.userName = userName;
46         
47         connection.start();
48     }
49     
50     @Override
51     public void onMessage(Message message) {
52         try{
53             TextMessage textMessage = (TextMessage)message;
54             System.out.println(textMessage.getText());
55         }catch(JMSException e){e.printStackTrace();}
56     }
57      
58     public void writeMessage(String text)throws JMSException{
59         TextMessage message = pubSession.createTextMessage();
60         message.setText(userName + ": " + text);
61         publisher.publish(message);
62     }
63     
64     public void close()throws JMSException{
65         connection.close();
66     }
67     
68     public static void main(String[] args) {
69         try{
70             if(args.length != 3)
71                 System.out.println("Factory, Topic or userName missing");
72             Chat chat = new Chat(args[0], args[1], args[2]);
73             
74             BufferedReader commandLine = new java.io.BufferedReader(new InputStreamReader(System.in));
75             
76             while(true){
77                 String s = commandLine.readLine();
78                 if(s.equalsIgnoreCase("exit")){
79                     chat.close();
80                     System.exit(0);
81                 }else
82                     chat.writeMessage(s);
83             }
84         }catch(Exception e){e.printStackTrace();}
85     }
86 }
相关实践学习
RocketMQ一站式入门使用
从源码编译、部署broker、部署namesrv,使用java客户端首发消息等一站式入门RocketMQ。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
相关文章
|
1月前
|
存储 网络协议 JavaScript
浅谈WebSocket及如何搭建实时聊天系统
WebSocket 是一种在客户端和服务器之间提供全双工、双向通信的网络协议。它是基于TCP/IP协议栈的应用层协议,旨在实现浏览器与服务器之间的实时、低延迟且高效的长连接通信
141 1
|
8月前
|
存储 JavaScript 前端开发
SpringBoot集成WebSocket实现及时通讯聊天功能!!!
SpringBoot集成WebSocket实现及时通讯聊天功能!!!
245 0
|
8月前
|
网络协议 Linux API
C/C++服务器和客户端交互笔记
Socket通信三要素:通信的目的地址、使用的端口号(http 80 / smtp 25)、使用的传输协议(TCP、UDP)。
89 0
BXA
|
11月前
|
监控 网络协议 JavaScript
Spring WebSocket构建实时交互系统
Spring WebSocket是基于WebSocket协议的一个开源框架,它使得开发人员可以更加方便地建立实时通信机制,以推送消息和数据并实时更新通信系统中的状态。Spring WebSocket被广泛应用于社交网站、电子商务、在线游戏等WEB应用程序中,以实现实时通信和即时响应。
BXA
144 0
|
移动开发 网络协议 前端开发
为美多商城(Django2.0.4)添加基于websocket的实时通信,主动推送,聊天室及客服系统
websocket是个啥? webSocket是一种在单个TCP连接上进行全双工通信的协议 webSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输
为美多商城(Django2.0.4)添加基于websocket的实时通信,主动推送,聊天室及客服系统
|
存储 监控 数据安全/隐私保护
05、Netty学习笔记—(案例:聊天业务)(一)
05、Netty学习笔记—(案例:聊天业务)(一)
05、Netty学习笔记—(案例:聊天业务)(一)
05、Netty学习笔记—(案例:聊天业务)(二)
05、Netty学习笔记—(案例:聊天业务)(二)
05、Netty学习笔记—(案例:聊天业务)(二)
|
网络协议 C#
C#实现聊天功能
C#实现聊天功能
209 0
C#实现聊天功能
|
人工智能 Java 机器人
编写Java程序,使用 Socket类模拟用户加入 QQ 群时,QQ 小冰发送欢迎消息的场景(用户充当客户端,QQ 小冰充当服务端)
编写Java程序,使用 Socket类模拟用户加入 QQ 群时,QQ 小冰发送欢迎消息的场景(用户充当客户端,QQ 小冰充当服务端)
246 0
编写Java程序,使用 Socket类模拟用户加入 QQ 群时,QQ 小冰发送欢迎消息的场景(用户充当客户端,QQ 小冰充当服务端)
|
JavaScript 前端开发
语音聊天程序源码——简单的聊天室搭建
安装好swoole后开始搭建前端 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>聊天室</title> </head> <style> #set_name{ mar...
2190 0