长大后想做什么?做回小孩!

0%

SpringBoot与消息服务

消息(Message)是指软件对象之间进行交互作用和通讯利用的一种方式。中间件(Middleware)是处于操作系统和应用程序之间的软件,也有人认为它应该属于操作系统中的一部分。消息中间件则是将软件与软件之间的交互消息、交互方式进行存储和管理的一种技术,也可以看做是一种容器。消息中间件关注于数据的发送和接收,利用高效可靠的异步消息传递机制集成分布式系统。

消息服务的应用场景有很多很多:系统解耦、异步、扩展、流量销峰、日志处理、消息通信等等。。。通过几个简单例子了解一下消息服务的作用和好处:

例子1(异步通信):

用户注册成功,将注册信息写入数据库然后给用户发送注册邮件和发送注册短信,串行方式:

KAbLlQ.png

注册信息持久化之后的发送注册邮件和注册短信可以使用多线程并发操作,并行方式:

KAbOyj.png

但是,当注册信息写入数据库、发送短信和邮件之前就应该让用户完成注册了。我们可以引入消息队列,只要将后序要用的数据写入消息队列就算注册流程完毕了。发送邮件和发送短信可以通过异步读取的方式从消息队列中取出数据,然后完成相应的服务。

KAbXOs.png

传统方式的系统性能(并发量,吞吐量,响应时间)会有瓶颈。通过引入消息队列,将不是必须的业务逻辑,异步处理,从而达到提高系统性能的目的。

例子2(应用解耦):

一个应用写有订单系统和库存系统,我们下单然后去调用库存系统的方法来检索库存,传统做法:订单系统去调用库存系统对外提供的接口。但是传统的做法存在缺点:

  1. 假如库存系统无法访问,则订单减库存将失败,从而导致订单失败。
  2. 订单系统与库存系统耦合。

为了解决以上缺点,我们可以引入消息队列,并将订单系统和库存系统抽取出来作为两个独立的服务:

  • 订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功。
  • 库存系统:订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作。

KALubn.png

假如:在下单时库存系统不能正常使用。也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。实现订单系统与库存系统的应用解耦。至于怎么保证两个服务之间库存量等数据一致,暂时先不讨论。。。

例子3(流量销峰):

流量削锋也是消息队列中的常用场景,一般在秒杀或团抢活动中使用广泛。

应用场景:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要加入消息队列来控制用户数量和缓解高流量的压力。

KALnDs.png

用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求并做出一定的响应操作。秒杀业务根据消息队列中的请求信息,再做后续处理。这只是很笼统很模糊的说了一下,具体细节还有很多很多。。


消息服务规范

概念

消息服务中有两个重要的概念:消息代理(MassageBroker)、目的地(destination)

当消息发送者发送消息以后,将由消息代理接管,消息代理保证消息传递到指定目的地

消息队列主要有两种形式的目的地:

  1. 队列(queue):点对点式消息通信(point-to-point),消息发送者发送消息,消息代理将其放入一个队列中,消息接收者从队列中获取消息内容,消息读取后被移出队列,消息只有唯一的发送者和接受者,但并不是说只有一个接收者,也就是说A、B、C等等接收者都有资格去接受消息,但是一个消息最终只能被一个接收者所接受。有点拗口,但是很好理解。。。
  2. 主题(topic):发布(publish)/订阅(subscribe)式消息通信,发布者发送消息到主题,多个接收者(订阅者)监听(订阅)这个主题,那么当消息到达主题的时候这个主题所有的接收者(订阅者)都会受到消息

JMS

Java消息服务(Java Message Service)即JMS,是一个Java平台中关于面向消息中间件的api,用于在两个应用程序之间或分布式系统中发送消息,进行异步通信。

基于JVM消息代理的规范,ActiveMQ、HornetMQ是JMS实现。

AMQP

高级消息队列协议(advanced message queuing protocol)即AMQP,是一个提供统一消息服务的应用层标准协议,基于此协议的客户端与消息中间件可传递消息,并且不受客户端/中间件不同的产品、不同的开发语言等条件的限制。

也是一个消息代理的规范,兼容JMS,RabbitMQ是AMQP的实现。

再偷一张JMS和AMQP对比图:

KmAEM4.png

Spring支持

  • spring-jms提供了对JMS的支持。
  • spring-rabbit提供了对AMQP的支持。
  • 需要ConnectionFactory的实现来连接消息代理。
  • 提供JmsTemplate、RabbitTemplate来发送消息。
  • @JmsListener(JMS)、@RabbitListener(AMQP)注解在方法上监听消息代理发布的消息。
  • @EnableJms、@EnableRabbit开启支持。

SpringBoot自动配置:

  • JmsAutoConfiguration
  • RabbitAutoConfiguration

RabbitMQ

RabbitMQ是一个基于AMQP使用erlang开发的开源实现。

核心概念

Kmmbin.png

Message:消息,消息是不具名的,他有消息头和消息体组成,消息体是不透明的,而消息头则由一系列的可选属性组成,这些属性包括routing-key(路由键)、priority(相对于其他消息的优先权)、delivery-mode(指出该消息可能需要持久性存储)等等。。

Publisher:消息的生产者,也是一个向交换器发布消息的客户端应用程序。

Exchange:交换器,用来接收生产者发送的消息并将这些消息路由给容器中的队列交换器有点像是网络中的交换机,而容器中的队列像是连接在交换机上的主机。Exchange有四种类型:direct(默认)、fanout、topic和headers,不同类型的Exchange转发消息的策略有所区别。

Queue:消息队列,用来保存消息直到发送给消费者。它是消息的容器,也是消息的终点。一个消息可投入一个或者多个队列。消息一直在队列里面,等待消费者连接到这个队列将其取走。

Binding:绑定,用于消息队列交换器之间的关联。一个绑定就是基于路由键交换器消息队列连接起来的路由规则,所以可以将其理解成一个由绑定构成的路由表。ExchangeQueue的绑定可以是多对多的关系。

Connection:网络连接,比如一个TCP连接。

Channel:信道,多路复用连接中的一条独立的双向数据流通道。信道是建立在真实的TCP连接内的虚拟连接,AMQP命令都是通过信道发出去的,不管是发布消息、订阅队列还是接收消息,这些动作都是通过信道完成。因为对于操作系统来说建立和销毁TCP都是非常昂贵的开销,所以引入了信道的概念,以复用一条TCP连接。

Consumer:消息的消费者,表示一个从消息队列中取得信息的客户端应用程序。

Virtual Host:虚拟主机,表示一批交换器消息队列相关对象虚拟主机是共享相同的身份认证和加密环境的独立服务器域。每个虚拟主机本质上就是一个mini版的RabbitMQ服务器,拥有自己的队列交换器绑定权限机制虚拟主机是AMQP概念的基础,必须在连接时指定,RabbitMQ默认的虚拟主机是/

Broker:表示消息队列服务器实体。

运行机制

AMQP中的消息路由过程和Java开发者熟悉的JMS存在一些差别,AMQP中增加了ExchangeBinding的角色,生产者消息发布到Exchange上,消息最终到达队列并被消费者接收,而Binding决定交换器消息应该发送到哪个队列

Kmn2TJ.png

Exchange:

分发消息时根据交换器类型的不同分发策略有区别,之前说过共有四种交换器类型:direct、fanout、topic、headers(很少用)。header匹配AMQP消息的header而不是路由键,headers交换器和direct交换器完全一致,但性能差很多,目前几乎用不到了,所以着重学习另外三种类型:

DirectExchange

KmMDSI.png

消息中路由键(routing key)如果和Binding中的binding key一致,交换器就将消息发送到对应的队列中。路由键队列名必须完全匹配,如果一个队列绑定到交换器要求路由键为“dog”,则其转发routing key标记为“dog”的消息,不会转发“dog.puppy”,也不转发”dog.guard”等等。它是完全匹配、单播的模式,典型的点对点通信模型。

FanoutExchange

KmGXtO.png

每个发到fanout类型交换器的消息都会分到所有绑定的队列上去。fanout交换器不处理路由键,只是简单的将队列绑定到交换器上,每个发送到交换器的消息都会被转发到与该交换器绑定的所有队列。很像子网广播,每台子网内的主机都获得了一份赋值的消息。fanout类型转发消息是最快的。这也是JMS里面发布/订阅模型一个参考实现。

TopicExchange

KmGjhD.png

topic交换器通过模式匹配分配消息的路由键属性,将路由键和某个模式进行匹配,此时队列需要绑定到一个模式上。它将路由键和绑定建的字符串切分成单词,这些单词之间用点隔开。它同样也会识别两个通配符“#”和“”,#匹配0个或多个任意单词,\匹配一个任意单词。


RabbitMq的安装和测试就不记录了,网上教程很多很详细,各种问题大多也都有解决方案。

接下来另起新篇,记录简单的SpringBoot整合RabbitMQ!