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 }
相关实践学习
消息队列+Serverless+Tablestore:实现高弹性的电商订单系统
基于消息队列以及函数计算,快速部署一个高弹性的商品订单系统,能够应对抢购场景下的高并发情况。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
相关文章
【Xamarin】使用WebSocket开发实时通信应用程序
WebSockets是一种双向通信协议,或TCP连接上的持久通信通道,由许多游戏,具有聊天功能的应用程序和实时应用程序(如股票行情)利用的非常强大的协议。 在这篇博文中,我们将讨论如何构建一个简单的Xamarin聊天室应用程序,该应用程序将利用并连接到ASP.NET Core WebSocket服务。
3235 0
|
3月前
|
前端开发 JavaScript 开发者
JSF与WebSockets,打造实时通信魔法!让你的Web应用秒变聊天室,用户体验飞升!
【8月更文挑战第31天】在现代Web应用开发中,实时通信对于提升用户体验至关重要。本文探讨了如何在主要面向Web应用开发的JSF(JavaServer Faces)框架中引入WebSockets支持,以实现客户端与服务器之间的全双工通信。通过具体示例展示了在JSF应用中实现WebSockets的基本步骤:添加依赖、创建服务器端点以及在前端页面中嵌入JavaScript客户端代码。尽管这一过程中可能会遇到一些挑战,如复杂代码编写和额外配置需求,但借助AWS等云服务平台,开发者仍能高效地完成部署和管理工作,从而增强Web应用的实时通信能力。
41 0
|
3月前
|
C# 开发者
全面提升开发效率:详解如何使用Blazor Server与SignalR打造实时Web应用,从零开始构建聊天室示例并掌握实时通信核心技术
【8月更文挑战第31天】提高生产力不仅关乎效率提升,更在于用更少时间完成更多任务。本文将通过具体代码示例,介绍如何结合 Blazor Server 和 SignalR 构建实时 Web 应用。从创建 Blazor 项目到添加 SignalR 支持,再到实现客户端与服务器间的实时通信,每个步骤都详细讲解。通过这一组合,C# 开发者能获得前后端一致的编程体验,轻松打造高效、响应迅速的实时应用。实时通信功能已在社交、协作等多个领域发挥重要作用,本文将助你掌握这一强大技术组合。
54 0
|
5月前
|
前端开发 JavaScript 容器
程序与技术分享:BeetleX之快速构建Web多房间聊天室
程序与技术分享:BeetleX之快速构建Web多房间聊天室
42 0
|
6月前
|
Java
JavaFX是一个用于构建富客户端应用程序的平台
【5月更文挑战第1天】JavaFX库可用于在Java中绘制K线图。以下是一个简单的示例,展示如何创建并显示一个基本的K线图。代码首先创建OHLCChart,设置标题和坐标轴,然后创建数据集并添加数据点,最后将数据集和图表显示在场景中。要自定义图表外观,可探索JavaFX库的更多功能和文档。
39 0
|
存储 JavaScript 前端开发
SpringBoot集成WebSocket实现及时通讯聊天功能!!!
SpringBoot集成WebSocket实现及时通讯聊天功能!!!
329 0
BXA
|
监控 网络协议 JavaScript
Spring WebSocket构建实时交互系统
Spring WebSocket是基于WebSocket协议的一个开源框架,它使得开发人员可以更加方便地建立实时通信机制,以推送消息和数据并实时更新通信系统中的状态。Spring WebSocket被广泛应用于社交网站、电子商务、在线游戏等WEB应用程序中,以实现实时通信和即时响应。
BXA
179 0
|
Web App开发 JavaScript 前端开发
常见的Web实时消息交互方式和SignalR
原文:常见的Web实时消息交互方式和SignalR 标签: WebSocket SignalR 前言 1. Web消息交互技术 1.1 常见技术 1.2 WebSocket介绍 1.3 WebSocket示例 2.
2069 0
|
消息中间件 微服务
MassTransit 实现应用程序间交互
MassTransit 介绍  先看下masstransit 官网介绍:MassTransit 是一个自由、开源、轻量级的消息总线, 用于使用. NET 框架创建分布式应用程序。MassTransit 在现有消息传输上提供了一组广泛的功能, 从而使开发人员能够友好地使用基于消息的会话模式异步连接服务。
1939 0
|
Web App开发 前端开发 数据库
基于comet服务器推送技术(web实时聊天)
http://www.cnblogs.com/zengqinglei/archive/2013/03/31/2991189.html Comet 也称反向 Ajax 或服务器端推技术.其思想很简单:将数据直接从服务器推到浏览器,而不必等到浏览器请求数据。
1898 0