Spring学习之消息

这里主要说的是异步消息,异步消息时一个应用程序向另一个应用程序间接发送消息的一种方式,这种方式无需等待对方的响应。

JMS

JMS(Java Message Service)是一个Java标准,定义了使用消息代理的通用API。借助JMS,所有遵从规范的实现都使用通用的接口,这就类似于JDBC为数据库操作提供了通用的接口一样。

JMS支持点对点消息模型和发布-订阅模型。

点对点消息模型

在点对点消息模型中,消息发送者将消息发送到队列中,接收者从队列中取出消息。

点对点消息模型

消息队列对消息发送者和消息接收者进行了解耦。虽然队列可以有多个接收者,但是每一条消息只能被一个接收者取走。

发布-订阅消息模型

在发布-订阅消息模型中,消息会发送给一个主题,多个接收者都可以监听一个主题。与队列不同,该主题的订阅者都会接受到此消息的副本。

发布-订阅消息模型

ActiveMQ就是遵从JMS规范的框架。

AMQP

AMQP(Advanced Message Queuing Protocol)是一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。
相比JMS,AMQP的优势在于它具有更加灵活和透明的消息模型。除了JMS支持的消息模型,AMQP还能够让我们以其他的多种方式来发送消息,这是通过将消息的生产者与存放消息的队列解耦实现的。

AMQP消息模型

AMQP的生产者不会直接将消息发布到队列中。AMQP在消息的生产者以及传递消息的队列之间引入了一种间接的机制:Exchange。

AMQP消息模型

消息的生产者将信息发布到一个Exchange。Exchange会绑定到一个或多个队列上,它负责将信息路由到队列上。消息的消费者会从队列中提取数据并进行处理。

AMQP定义了四中不同类型的Exchange,每一种都有不同的路由算法,这些算法决定了是否要将信息放到队列中:

  • Direct:如果消息的routing key 与binding的routing key 直接匹配的话,消息将会路由到该队列上;
  • Topic: 如果消息的routing key 与binding的routing key 符合通配符匹配的话,消息将会路由到该队列上;
  • Headers: 如果消息参数表中的头信息和值与binding参数表中相匹配,消息将会路由到该队列上;
  • Fanout: 不管消息的routing key 和参数表的头信息/值是什么,消息将会路由到所有队列上。

RabbitMQ

RabbitMQ是一个流行的开源消息代理,它实现了AMQP。Spring AMQP为RabbitMQ提供了支持,包括RabbitMQ链接工厂,模板以及Spring配置命名空间。

安装&启动服务

首先我们需要安装RabbitMQ并启动服务。
Download
安装的话可以搜索一下教程,网上很多。安装好之后,配置环境,然后我们通过命令行输入rabbitmq-server启动服务。出现如下界面表示服务已启动:


  ##  ##
  ##  ##      RabbitMQ 3.7.7. Copyright (C) 2007-2018 Pivotal Software, Inc.
  ##########  Licensed under the MPL.  See http://www.rabbitmq.com/
  ######  ##
  ##########  Logs: /usr/local/rabbitmq_server-3.7.7/var/log/rabbitmq/rabbit@JuliedeMacBook-Pro.log
                    /usr/local/rabbitmq_server-3.7.7/var/log/rabbitmq/rabbit@JuliedeMacBook-Pro_upgrade.log

              Starting broker...
 completed with 3 plugins.

Spring配置

首先是pom.xml中导入依赖:

<dependency>
    <groupId>org.springframework.amqp</groupId>
    <artifactId>spring-rabbit</artifactId>
</dependency>

其次创建一个配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/rabbit
    http://www.springframework.org/schema/rabbit/spring-rabbit-1.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

    <!--导入配置文件-->
    <context:property-placeholder location="classpath:rabbit.properties"/>

    <!-- 连接配置 -->
    <rabbit:connection-factory id="connectionFactory"
                               host="${mq.host}"
                               username="${mq.username}"
                               password="${mq.password}"
                               port="${mq.port}"
                               virtual-host='/'/>

    <rabbit:admin connection-factory="connectionFactory"/>

    <!--创建一个队列-->
    <rabbit:queue id="spittleAlertQueue" name="spittle.alerts" />

    <!--配置rabbitTemplate-->
    <rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>

    <!--队列监听器-->
    <rabbit:listener-container connection-factory="connectionFactory">
        <rabbit:listener queues="spittleAlertQueue" ref="queueListener"/>
    </rabbit:listener-container>

    <!--注册相关bean-->
    <bean id="alertService" class="com.hubert.rabbit.AlertServiceImpl">
        <constructor-arg ref="rabbitTemplate"/>
    </bean>
    <bean id="queueListener" class="com.hubert.rabbit.QueueListener"/>
</beans>

rabbit.properties

mq.host=localhost
mq.username=guest
mq.password=guest
mq.port=5672

bean

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;

public class QueueListener implements MessageListener {

    Logger logger = LoggerFactory.getLogger(QueueListener.class);

    @Override
    public void onMessage(Message msg) {
        logger.info("receive");
        try {
            logger.info(msg.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
import org.springframework.amqp.rabbit.core.RabbitTemplate;

public class AlertServiceImpl {
    private RabbitTemplate rabbit;

    public AlertServiceImpl(RabbitTemplate rabbit) {
        this.rabbit = rabbit;
    }

    public void sendSpittleAlert() {
        rabbit.convertAndSend("spittle.alerts", "object");
    }
}

启动

最后是在main方法中发送消息:

public class RabbitMain {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("rabbitmq-config.xml");
        AlertServiceImpl service = (AlertServiceImpl) applicationContext.getBean("alertService");
        service.sendSpittleAlert();
    }
}

顺利的话我们就会看到处理消息的日志:

INFO com.hubert.rabbit.QueueListener - (Body:'object' MessageProperties [headers={}, timestamp=null, messageId=null, userId=null, receivedUserId=null, appId=null, clusterId=null, type=null, correlationId=null, correlationIdString=null, replyTo=null, contentType=text/plain, contentEncoding=UTF-8, contentLength=0, deliveryMode=null, receivedDeliveryMode=PERSISTENT, expiration=null, priority=0, redelivered=false, receivedExchange=, receivedRoutingKey=spittle.alerts, receivedDelay=null, deliveryTag=12, messageCount=0, consumerTag=amq.ctag-rLNiWGT6LCAMD3li8xv1qA, consumerQueue=spittle.alerts])

这个过程并不是那么顺利,可能会出现链接错误,权限被拒的情况,一般都是RabbitMQ的用户权限或者Virtual Host的权限的问题。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,874评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,151评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,270评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,137评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,116评论 5 370
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,935评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,261评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,895评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,342评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,854评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,978评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,609评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,181评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,182评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,402评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,376评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,677评论 2 344

推荐阅读更多精彩内容

  • http://liuxing.info/2017/06/30/Spring%20AMQP%E4%B8%AD%E6%...
    sherlock_6981阅读 15,869评论 2 11
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,599评论 18 139
  • 本文基于《Spring实战(第4版)》所写。 异步消息是一个应用程序向另一个应用程序间接发送消息的一种方式,这种方...
    阳光的技术小栈阅读 1,848评论 0 1
  • 来源 RabbitMQ是用Erlang实现的一个高并发高可靠AMQP消息队列服务器。支持消息的持久化、事务、拥塞控...
    jiangmo阅读 10,344评论 2 34
  • 关于消息队列,从前年开始断断续续看了些资料,想写很久了,但一直没腾出空,近来分别碰到几个朋友聊这块的技术选型,是时...
    预流阅读 584,409评论 51 785