1.介绍
JMS 是 SUN 公司开发的一套访问 MOM(Message-Oriented-Middleware) 消息服务中间件的标准 API,MON 提供消息接收和转发的服务 , 对消息进行缓存和持久操作 , 保证消息的安全性 ,JMS 让开发都无须了解远程过程调用的细节和网络通信协议的细节就可以通过 JMS 向 MOM 发送消息 , 借助消息我们可以松散耦合的方式集成不同的应用。
ActiveMQ 是Apache出品,最流行的、功能强大的即时通讯和集成模式的开源服务器。ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现。提供客户端支持跨语言和协议,带有易于在充分支持JMS 1.1和1.4使用J2EE企业集成模式和许多先进的功能。(ActiveMQ只是JMS的一个实现服务,其他的有jboss等)
2.环境安装
1、 下载ActiveMQ,下载地址:http://activemq.apache.org/activemq-5100-release.html
2、 解压apache-activemq-5.8.0.zip即可完成ActiveMQ的安装
3、 运行bin/activemq.bat即可
+bin (windows下面的bat和unix/linux下面的sh) 启动ActiveMQ的启动服务就在这里
+conf (activeMQ配置目录,包含最基本的activeMQ配置文件)
+docs (index,replease版本里面没有文档)
-activemq-all-5.8.0.jar (ActiveMQ的binary)
3.启动服务
如果报这种异常:
Caused by: java.io.IOException: Failed to bind to server socket: tcp://0.0.0.0:61616?maximumConnections=1000&wireformat.maxFrameSize=104857600 due to: java.net.SocketException: Unrecognized Windows Sockets error: 0: JVM_Bind |
如果报这种异常:
ERROR | Failed to start Apache ActiveMQ (localhost, ID:mac-4363-1389937469328-0:1). Reason: java.io.IOException:
Transport Connector could not be registered in JMX:
Failed to bind to server socket: amqp://0.0.0.0:5672?maximumConnections=1000&wireformat.maxFrameSize=104857600 due to:
java.net.BindException: Address already in use: JVM_Bind
|
<transportConnector name="amqp" uri="amqp://0.0.0.0:5672?maximumConnections=1000&wireformat.maxFrameSize=104857600"/>
就是把它注释掉,这个看其他的文档,应该是一种连接方式,就像上面的tcp一样(这里不用,就不深究了)。
4.管理界面
启动成功就可以访问管理员界面:http://localhost:8161/admin,默认用户名和密码admin/admin。如果你想修改用户名和密码的话,在conf/jetty-realm.properties中修改即可。
其中在导航菜单中,Queues是队列方式消息。Topics是主题方式消息。Subscribers消息订阅监控查询。Connections可以查看链接数,分别可以查看xmpp、ssl、stomp、openwire、ws和网络链接。Network是网络链接数监控。Send可以发送消息数据。
5.代码实现(仅仅用点对点queue实现)
先加入activemq.jar包
接收方:
package JMSTest;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.Queue;
import javax.jms.Session;
import javax.naming.Context;
import javax.naming.InitialContext;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
public class JMSReceiver {
public static void main(String args[]){
try{
ConnectionFactory factory=new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER,ActiveMQConnection.DEFAULT_PASSWORD,"tcp://127.0.0.1:61616");
Connection connection=factory.createConnection();
Session session=connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue=session.createQueue("SecondQueue");
// Context xtx=new InitialContext();
// System.out.println(xtx.lookup("jndi/jmsConn"));
MessageConsumer consumer=session.createConsumer(queue);
connection.start();
consumer.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message msg) {
System.out.println("你好"+msg);
}
});
}catch(Exception e){
System.out.println(e);
}
}
}
发送方:
package JMSTest;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
public class JMSSender {
public static void main(String args[]){
try{
ConnectionFactory connectionFactory=new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER,ActiveMQConnection.DEFAULT_PASSWORD,ActiveMQConnection.DEFAULT_BROKER_URL);
Connection connection=connectionFactory.createConnection();
Session session=connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
Queue queue=session.createQueue("SecondQueue");
MessageProducer messageProducer=session.createProducer(queue);
TextMessage textMessage=session.createTextMessage("It is a test");
messageProducer.send(textMessage);
session.close();
connection.close();
}catch(Exception e){
System.out.println(e);
}
}
}
上面的无所谓先启动哪个,但是肯定要先启动activemq.bat才行(相当于中间的服务器)
6.代码分析
ConnectionFactory 接口(连接工厂) 用户用来创建到JMS提供者的连接的被管对象。JMS客户通过可移植的接口访问连接,这样当下层的实现改变时,代码不需要进行修改。 管理员在JNDI名字空间中配置连接工厂,这样,JMS客户才能够查找到它们。根据消息类型的不同,用户将使用队列连接工厂,或者主题连接工厂。
Connection 接口(连接) 连接代表了应用程序和消息服务器之间的通信链路。在获得了连接工厂后,就可以创建一个与JMS提供者的连接。根据不同的连接类型,连接允许用户创建会话,以发送和接收队列和主题到目标。
Destination 接口(目标) 目标是一个包装了消息目标标识符的被管对象,消息目标是指消息发布和接收的地点,或者是队列,或者是主题。JMS管理员创建这些对象,然后用户通过 JNDI发现它们。和连接工厂一样,管理员可以创建两种类型的目标,点对点模型的队列,以及发布者/订阅者模型的主题。
MessageConsumer 接口(消息消费者) 由会话创建的对象,用于接收发送到目标的消息。消费者可以同步地(阻塞模式),或异步(非阻塞)接收队列和主题类型的消息。
MessageProducer 接口(消息生产者) 由会话创建的对象,用于发送消息到目标。用户可以创建某个目标的发送者,也可以创建一个通用的发送者,在发送消息时指定目标。
Message 接口(消息) 是在消费者和生产者之间传送的对象,也就是说从一个应用程序传送到另一个应用程序。一个消息有三个主要部分: 消息头(必须):包含用于识别和为消息寻找路由的操作设置。 一组消息属性(可选):包含额外的属性,支持其他提供者和用户的兼容。可以创建定制的字段和过滤器(消息选择器)。 一个消息体(可选):允许用户创建五种类型的消息(文本消息,映射消息,字节消息,流消息和对象消息)。 消息接口非常灵活,并提供了许多方式来定制消息的内容。
Session 接口(会话) 表示一个单线程的上下文,用于发送和接收消息。由于会话是单线程的,所以消息是连续的,就是说消息是按照发送的顺序一个一个接收的。会话的好处是它支持事 务。如果用户选择了事务支持,会话上下文将保存一组消息,直到事务被提交才发送这些消息。在提交事务之前,用户可以使用回滚操作取消这些消息。一个会话允 许用户创建消息生产者来发送消息,创建消息消费者来接收消息。
ActiviteMQ消息有3中形式
JMS 公共 | 点对点域 | 发布/订阅域 |
ConnectionFactory | QueueConnectionFactory | TopicConnectionFactory |
Connection | QueueConnection | TopicConnection |
Destination | Queue | Topic |
Session | QueueSession | TopicSession |
MessageProducer | QueueSender | TopicPublisher |
MessageConsumer | QueueReceiver | TopicSubscriber |
(1)、点对点方式(point-to-point)
点对点的消息发送方式主要建立在 Message Queue,Sender,reciever上,Message Queue 存贮消息,Sneder 发送消息,receive接收消息.具体点就是Sender Client发送Message Queue ,而 receiver Cliernt从Queue中接收消息和"发送消息已接受"到Quere,确认消息接收。消息发送客户端与接收客户端没有时间上的依赖,发送客户端可以在任何时刻发送信息到Queue,而不需要知道接收客户端是不是在运行
(2)、发布/订阅 方式(publish/subscriber Messaging)
发布/订阅方式用于多接收客户端的方式.作为发布订阅的方式,可能存在多个接收客户端,并且接收端客户端与发送客户端存在时间上的依赖。一个接收端只能接收他创建以后发送客户端发送的信息。作为subscriber ,在接收消息时有两种方法,destination的receive方法,和实现message listener 接口的onMessage 方法。
发送消息的基本步骤:
(1)、创建连接使用的工厂类JMS ConnectionFactory
(2)、使用管理对象JMS ConnectionFactory建立连接Connection,并启动
(3)、使用连接Connection 建立会话Session
(4)、使用会话Session和管理对象Destination创建消息生产者MessageSender
(5)、使用消息生产者MessageSender发送消息
消息接收者从JMS接受消息的步骤
(1)、创建连接使用的工厂类JMS ConnectionFactory
(2)、使用管理对象JMS ConnectionFactory建立连接Connection,并启动
(3)、使用连接Connection 建立会话Session
(4)、使用会话Session和管理对象Destination创建消息接收者MessageReceiver
(5)、使用消息接收者MessageReceiver接受消息,需要用setMessageListener将MessageListener接口绑定到MessageReceiver消息接收者必须实现了MessageListener接口,需要定义onMessage事件方法。
上面获得ConnectionFactory有三种方式:
1.当用spring容器时
ApplicationContext ctx = new FileSystemXmlApplicationContext("src/cn/com/snt/jms/applicationContext.xml");
ConnectionFactory factory=(ConnectionFactory) ctx.getBean("connectionFactory");
2.当用jndi容器时 Context ctx=new InitialContext();
ConnectionFactory factory=(ConnectionFactory)ctx.lookup("jndi/jmsConn");
3.直接调用
ConnectionFactory connectionFactory=
new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER,ActiveMQConnection.DEFAULT_PASSWORD,
ActiveMQConnection.DEFAULT_BROKER_URL);
(上面的是用配置文件默认的,最后的uri可以为failover://tcp://localhost:61616或者tcp://localhost:61616(当用tcp进行连接的时候),区别是加上failover可以进行备份连接,如第一次连接失败,可以进行第二次连接failover:(tcp://primary:61616,tcp://secondary:61616)?randomize=false )
4.注意接受者中的connection.start();这个必须要有,最好放到消费者创建以后再开启,为的就是节省资源。
5.接受者中,有两种方式接受,一种是同步的方法consumer.receive()或者consumer.receive(long),上面的方法是阻塞式的,consumer.receive(long)可以设置等待多长时间,如果为0,则永不超时。第二种方式是异步的,就是上面的consumer.setMessageListener(new MessageListener() {...}的方式,这种用的比较多。