1.面向消息的中间件(MOM)

1.什么是MOM
    面向消息的中间件,Message Oriented Middleware,简称MOM,中文简称消息中间件,利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成。
    一个MOM系统,通常会包括客户端(Clients)、消息(Message)和 MOM,客户端是发送或者接受消息的应用程序,消息封装了要传递的内容,MOM可以存储和转发消息。如下图所示,系统A和系统B之间的消息传递,不是直接通信,而是通过中间件来间接的传递

2.MOM的好处
  1. 降低系统间通信复杂度:有了MOM,系统间的通信,不用考虑系统是什么语言开发的,也不用考虑复杂的网络编程,各个系统只需要关心自身和MOM之间如何进行消息的接受和发送即可,这些操作通过简单的API就可以完成。
  2. 提高了消息的灵活性:系统A通过MOM向系统B发送消息,消息可以存储在MOM中,并由MOM转发。即使是系统B不在线,MOM会持有这个消息,直到系统B连接并处理消息。这就是说,系统A发完消息后,就可以执行其它操作,而不必阻塞等待,尤其是对那些时间无关或者并行处理的操作,非常适用。
  3. 松散耦合:有了MOM的存在,对于系统B而言,只要发送的消息没有变化,就不必考虑系统A的变化。A系统的代码改变,不会影响到B系统,反之亦然。

2.JMS基本概念

1.JMS是什么
    Java消息服务(Java Message Service,JMS)应用程序接口是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。Java消息服务是一个与具体平台无关的API,绝大多数MOM提供商都对JMS提供支持。
    Java消息服务的规范包括两种消息模式,点对点和发布者/订阅者。
    Java消息服务支持同步和异步的消息处理。
    一个JMS系统,通常包括了以下部分:
  1. JMS Client,java语言编写的客户端,用于发送和接受消息。
  2. Non-JMS Client,使用消息系统本地API编写的客户端,而不是使用JMS API。
  3. Message,消息,被定义,用于在不同的客户端之间交换。
  4. JMS Provider,JMS提供商,是消息系统(比如ActiveMQ),当然,即可以支持JMS,也可以同时支持Non-JMS。
  5. Administered Objects,被管理的对象,预置的JMS对象,为客户端使用。
2.JMS、RPC、SOAP、Web Service的区别
  • soap描述的是xml格式的数据格式。关注于rpc,与wsdl组成一个体系。
  • jms描述更通用的消息。
  • 或者可以说soap专注于远程服务调用,jms专注于信息交换。
  • soap是两个点的连接系统,jms是3个点。
  • 一般来说,web service是同步的,jms是异步或同步的。
  • rpc是remote process call,rmi是一种rpc,rpc本身没有规范,只要是实现了远程调用的,都是rpc。比如rmi .net-remoting ws/soap/rest hessian xmlrpc thrift potocolbuffer 等等。
  • rpc有一个通用的结构,就是serialization/deserialization+stub+skeleton
  • 这么说rpc是一种远程调用设计思维,强调的是透明的调用远程的方法,看起来就跟本地调用是一样的
  • rpc强调的是透明的远程方法调用,而jms强调的是通用的信息交流。
3.JMS规范
    JMS规范只是定义了接口,并没有实现,规范全文见:JSR 914: JavaTM Message Service (JMS) API。地址:https://jcp.org/aboutJava/communityprocess/final/jsr914/index.html

3.JMS的消息模型

1.JMS模型简介
JMS支持两种消息通信模型:
  • 点对点模型(Point to Point,P2P)
  • 发布者/订阅者模型(publish/subscribe, pub/sub)

    P2P模型中,Sender把一个消息发送到Queue中,这个消息只能由一个客户端消费;一旦消息被消费,其它客户端就不能从这个Queue中获取到消息。巧克力糖盒子里只有一块糖,只有最先打开的那个人能吃到,后来的人就吃不到了。
    Pub/Sub模型中,一个消息主题(Topic)被发布以后,可以有多个订阅者收听,这些订阅者都可以获取到消息;前提是,订阅者订阅了这个主题,并且只能接受订阅以后的消息。这就像生活里的期刊订阅,我们只能收到订阅以后的期刊,之前的期刊,杂志社是不可能投递给我们的。
2.点对点模型
  • 只有一个消费者:每条消息只有一个消费者,如果这条消息被消费,那么其它消费者不能接受到此消息。
  • 时间无关性:消息的消费和时间无关,只要消息被发送了,在消息过期之前,如果没有其他消费者消费了这个消息,那么客户端可以在任何时候来消费这条消息。
  • 消费者必须确认:消费者收到消息之后,必须向Message Provider确认,否则会被认为消息没有被消费,仍然可以被其他消费者消费。可以设置自动确认。这个特点其实也是保证一条消息只能由一个消费者来消费。
  • 非持久化的消息只发一次:非持久化的消息,可能会丢失,因为消息会过期,另外Message Provider可能宕机。
  • 持久化的消息严格发一次:消息可以被持久化,比如持久化在文件系统或者数据库中,这样可以避免Message Provider的异常或者其它异常导致消息丢失。
3.发布者/订阅者模型
  • 每条消息可以有多个订阅者
  • 订阅者只能消费它们订阅topic之后的消息
  • 非持久化订阅,订阅者必须保持为活动状态才能使用这些消息,如果一个订阅者A断开了10分钟,那么A就会收不到这10分钟内的消息。
  • 持久化订阅,Message Provider会保存这些消息,即使订阅者因为网络原因断开了,再重新连接以后,能让消费这些消息。
  • 是否使用持久化订阅,需要根据业务场景判断。

4.队列和主题

1.队列和主题概念
    在MQ中,消息模型有两种,一种是队列(Queue),一种是主题(Topic)。队列是Point-To-Point的,队列中的消息,仅能被消费一次。主题是Pub/Sub模型,主题中的消息,可以由多个订阅者消费;订阅者只能消费它订阅以后的消息。这是遵循的JMS规范。
2.收发消息对象创建过程

如上图所示,JMS规范中,收发消息的对象创建过程如下,下面的示例代码中也将注释这些过程:
  1. 初始化ConnetionFactory
  2. ConnetionFactory创建Connection
  3. Connection创建Session
  4. Session创建Destination(包括Queue 和 Topic两种)
  5. 发:Session创建消息生产者MessageProducer(收:Session创建消息消费者MessageConsumer)
  6. Seesion创建Message,(发:)MessageProducer发送到Destination,(收:)MessageConsumer从Destination接受消息。
3.接口间的关系
    JMS规范定义了通用接口(JMS Common Interfaces)、队列接口(PTP-specific Interfaces)和 主题接口(Pub/Sub-specific Interfaces),队列接口和主题接口分别继承于通用接口,具体关系如下表所示。

    ActiveMQ对这些规范接口都有相应的实现。在实际的编程过程中,声明通用接口基本就够用了。如何区分Queue和Topic也很简单,参看下面的代码。
    1. //Queue,队列
    1. Destination destination = session.createQueue(subject);
    1. //Topic,主题
    1. Destination destination = session.createTopic(subject);

5.通过ActiveMQ收发消息

1.通过队列发送和接受消息
通过Queue发送消息
    1. public static void main(String[] args) throws Exception
    1. {
    1. String user = ActiveMQConnection.DEFAULT_USER;
    1. String password = ActiveMQConnection.DEFAULT_PASSWORD;
    1. String url = "tcp://localhost:61616";
    1. String subject = "TOOL.DEFAULT";
    1. // 1. 初始化连接工厂
    1. ConnectionFactory contectionFactory = new ActiveMQConnectionFactory(user, password, url);
    1. // 2. 创建连接
    1. Connection connection = contectionFactory.createConnection();
    1. connection.start();
    1. // 3.创建会话
    1. Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
    1. // 4. 打开队列
    1. Destination destination = session.createQueue(subject);
    1. // 5. MessageProducer负责发送消息
    1. MessageProducer producer = session.createProducer(destination);
    1. TextMessage message = session.createTextMessage();
    1. for (int i = 0; i < 100; i++)
    1. {
    1. String tmp = "测试消息:" + i;
    1. message.setStringProperty("Test", tmp);
    1. // 6. 发送消息
    1. producer.send(message);
    1. System.out.println("发送消息: " + tmp);
    1. // 只有commit之后,消息才会进入队列
    1. session.commit();
    1. }
    1. // 7. 关闭会话和连接
    1. session.close();
    1. connection.close();
    1. System.out.println("OK!!");
    1. }
通过Queue接受消息
    1. public static void main(String[] args) throws Exception
    1. {
    1. String user = ActiveMQConnection.DEFAULT_USER;
    1. String password = ActiveMQConnection.DEFAULT_PASSWORD;
    1. String url = "tcp://localhost:61616";
    1. String subject = "TOOL.DEFAULT";
    1. ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(user, password, url);
    1. Connection connection = connectionFactory.createConnection();
    1. connection.start();
    1. final Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
    1. Destination destination = session.createQueue(subject);
    1. // MessageConsumer负责接受消息
    1. MessageConsumer consumer = session.createConsumer(destination);
    1. consumer.setMessageListener(new MessageListener()
    1. {
    1. public void onMessage(Message msg)
    1. {
    1. try
    1. {
    1. String str = msg.getStringProperty("Test");
    1. System.out.println("收到消息:" + str);
    1. session.commit();
    1. }
    1. catch (JMSException e)
    1. {
    1. e.printStackTrace();
    1. }
    1. }
    1. });
    1. Thread.sleep(1000 * 5);
    1. session.close();
    1. connection.close();
    1. System.out.println("OK!");
    1. }
2.通过Topic订阅消息
通过Topic发布消息,为了使订阅者能够订阅消息,在运行程序时,需要先运行订阅者(Subscriber),后运行发布者(Publisher)。
    1. public static void main(String[] args) throws Exception
    1. {
    1. String user = ActiveMQConnection.DEFAULT_USER;
    1. String password = ActiveMQConnection.DEFAULT_PASSWORD;
    1. String url = "tcp://localhost:61616";
    1. String subject = "MQ.TOPIC";
    1. // 1. 初始化连接工厂
    1. ConnectionFactory contectionFactory = new ActiveMQConnectionFactory(user, password, url);
    1. // 2. 创建连接
    1. Connection connection = contectionFactory.createConnection();
    1. connection.start();
    1. // 3.创建会话
    1. Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
    1. // 4. 创建要发布的主题,和Queue的区别就在此
    1. Destination destination = session.createTopic(subject);
    1. // 5. MessageProducer负责发送消息
    1. MessageProducer producer = session.createProducer(destination);
    1. TextMessage message = session.createTextMessage();
    1. for (int i = 0; i < 10; i++)
    1. {
    1. String tmp = "测试消息TOPIC:" + i;
    1. message.setStringProperty("Test", tmp);
    1. // 6. 发送消息
    1. producer.send(message);
    1. System.out.println("发送消息: " + tmp);
    1. // 只有commit之后,消息才会进入队列
    1. session.commit();
    1. }
    1. // 7. 关闭会话和连接
    1. session.close();
    1. connection.close();
    1. System.out.println("OK!");
    1. }
通过Topic订阅消息,这里我们只有1个订阅者,想要验证多个订阅者,拷贝多份代码,改个类名即可。再次提醒,先运行订阅者。
    1. public static void main(String[] args) throws Exception
    1. {
    1. String user = ActiveMQConnection.DEFAULT_USER;
    1. String password = ActiveMQConnection.DEFAULT_PASSWORD;
    1. String url = "tcp://localhost:61616";
    1. String subject = "MQ.TOPIC";
    1. ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(user, password, url);
    1. Connection connection;
    1. connection = connectionFactory.createConnection();
    1. connection.start();
    1. final Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
    1. Topic topic = session.createTopic(subject);
    1. // MessageConsumer负责接受消息
    1. MessageConsumer consumer = session.createConsumer(topic);
    1. consumer.setMessageListener(new MessageListener()
    1. {
    1. public void onMessage(Message msg)
    1. {
    1. try
    1. {
    1. String str = msg.getStringProperty("Test");
    1. System.out.println("订阅者---SecondSubscriber---收到消息:" + str);
    1. session.commit();
    1. }
    1. catch (JMSException e)
    1. {
    1. e.printStackTrace();
    1. }
    1. }
    1. });
    1. Thread.sleep(1000 * 50);
    1. session.close();
    1. connection.close();
    1. System.out.println("OK!");
    1. }
-------------------------------------------------------------------------------------------------------------------------------

02.JMS基础的更多相关文章

  1. php从入门到放弃系列-02.php基础语法

    php从入门到放弃系列-02.php基础语法 一.学习语法,从hello world开始 PHP(全称:PHP:Hypertext Preprocessor,即"PHP:超文本预处理器&qu ...

  2. 数据分析02 /pandas基础

    数据分析02 /pandas基础 目录 数据分析02 /pandas基础 1. pandas简介 2. Series 3. DataFrame 4. 总结: 1. pandas简介 numpy能够帮助 ...

  3. 极简python教程02:基础变量,删繁就简

    python极简教程已经开赛,如果错过说明可以回翻: 极简python教程:赛前说明 借这个机会,我再讲讲我的教程和其他网上的教程的区别: 1 我分享的内容,是我在工作中会高频使用的语法,是精华内容 ...

  4. JMS基础(2)

    1.  JMS基本概念 JMS(Java Message Service) ,即Java消息服务.JMS是SUN提出的旨在统一各种消息中间件(MOM)系统接口的规范.它提供标准的产生.发送.接收消息的 ...

  5. JMS基础(1)

    1.  消息中间件: 将信息以消息的形式,从一个应用程序传送到另一个或多个应用程序. 主要特点: (1) 消息异步接收: 消息发送者不需要等待消息接收者的响应 (2) 消息可靠接收: 确保消息在中间件 ...

  6. 了不起的分支和循环02 - 零基础入门学习Python008

    了不起的分支和循环02 让编程改变世界 Change the world by program 上节课,小甲鱼教大家如何正确的打飞机,其要点就是:判断和循环,判断就是该是不该做某事,循环就是持续做某事 ...

  7. JMS 基础

    1. JMS基本概念 JMS(Java Message Service) 即Java消息服务.它提供标准的产生.发送.接收消息的接口简化企业应用的开发. 它支持两种消息通信模型:点到点(point-t ...

  8. JMS基础篇(二)

    简介 异构集成是消息发挥作用的一个领域,大型公司内部可能会遇到很多的平台,Java,.net或者公司自己的平台等. 传送消息还应该支持异步机制,以提高系统整体的性能.异步传输一条消息意味着,发送者不必 ...

  9. JMS基础篇

    首先我们需要下载 ActiveMQ:http://activemq.apache.org/. 启动 ActiveMQ 服务:解包下载的 ActiveMQ >进去其bin 目录>双击 act ...

随机推荐

  1. 下面关于XML叙述不正确的是( )

    A.XML标记必须关闭 B.XML是大小写敏感的 C.XML文件只能跟DTD文件一块使用 D.XML和XSL结合可以在浏览器上显示 解答:C

  2. (转)windows平台时间函数性能比较QueryPerformanceCounter,GetTickCount,ftime,time,GetLocalTime,GetSystemTimeAsFileTime

    执行 10000000 次, 耗时 2258,369 微秒     QueryPerformanceCounter 执行 10000000 次, 耗时 26,347 微秒    GetTickCoun ...

  3. 【JavaScript】使用setInterval()函数作简单的轮询操作

    轮询(Polling)是一种CPU决策怎样提供周边设备服务的方式,又称"程控输出入"(Programmed I/O). 轮询法的概念是.由CPU定时发出询问.依序询问每个周边设备是 ...

  4. wcf实体和ef实体冲突。。。

    指定的架构无效.错误: CLR 类型到 EDM 类型的映射不明确,因为多个 CLR 类型与 EDM 类型“agentinfo”匹配.以前找到的是 CLR 类型“chanchengFlow.Models ...

  5. mysql获取外键, 根据数据库名和表名获取表所对应的所有外键

    SELECT ii.`COLUMN_NAME` FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS ii WHERE ii.`CONSTRAINT_SCHEMA`= ...

  6. 左侧固定宽度,右侧自适应宽度的CSS布局

    BI上有高手专门讨论了这种布局方法,但他用了较多的hack,还回避了IE6的dtd.我在实际使用中,发现回避掉IE6的dtd定义后,会导致ajax模态框无法居中(VS的一个控件,自动生成的代码,很难修 ...

  7. oracle 无效索引

    错误信息:ORA-01502: index 'VOX_ID' or partition of such index is in unusable state 原因:将表的表空间做了更改,导致索引失效. ...

  8. Css中position、float和clear整理

    Position: absolute 生成绝对定位的元素,相对于 static 定位以外的第一个父元素进行定位. 元素的位置通过 "left", "top", ...

  9. C# 哈希加密

    1.方法一: [c-sharp] view plaincopy //适用于C#语言 //使用前需导入以下命名空间:using System.Web.Security; //第一个参数为需加密的字符串, ...

  10. angular学习(十五)——Provider

    转载请写明来源地址:http://blog.csdn.net/lastsweetop/article/details/60966263 Provider简单介绍 每一个web应用都是由多个对象协作完毕 ...