Spring Boot学习笔记-------(九)[1] RabbitMQ介绍

一、消息服务的分类

1.JMS(Java Message Servcie)Java消息服务

基于java消息代理的规范,ActiveMQ、HornetMQ。

  • 通过java api进行调用
  • 不可跨平台
  • 支持pear-2-pear,pub/sub两种模式
  • 支持TextMessage、MapMessage、BytesMessage、StreamMessage、ObjectMessage几种消息格式

2.AMQP(Advance Message Queuing Protocol)

高级消息队列协议

RabbitMQ

  • 通过网络级协议调用
  • 跨平台支持
  • 支持direct exchange、fanout exchange、topic exchange、headers exchange、system exchange几种消息订阅模式

二、RabbitMQ介绍

AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然。
AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。
RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。

三、相关概念

  • Server:接收客户端的连接,实现AMQP实体服务。
  • Connection:连接,应用程序与Server的网络连接,TCP连接。
  • Channel:信道,消息读写等操作在信道中进行。客户端可以建立多个信道,每个信道代表一个会话任务。
  • Message:消息,应用程序和服务器之间传送的数据,消息可以非常简单,也可以很复杂。有Properties和Body组成。Properties为外包装,可以对消息进行修饰,比如消息的优先级、延迟等高级特性;Body就是消息体内容。
  • Virtual Host:虚拟主机,用于逻辑隔离。一个虚拟主机里面可以有若干个Exchange和Queue,同一个虚拟主机里面不能有相同名称的Exchange或Queue。
  • Exchange:交换器,接收消息,按照路由规则将消息路由到一个或者多个队列。如果路由不到,或者返回给生产者,或者直接丢弃。RabbitMQ常用的交换器常用类型有direct、topic、fanout、headers四种,后面详细介绍。
  • Binding:绑定,交换器和消息队列之间的虚拟连接,绑定中可以包含一个或者多个RoutingKey。
  • RoutingKey:路由键,生产者将消息发送给交换器的时候,会发送一个RoutingKey,用来指定路由规则,这样交换器就知道把消息发送到哪个队列。路由键通常为一个“.”分割的字符串,例如“com.rabbitmq”。
  • Queue:消息队列,用来保存消息,供消费者消费。

四、交换机介绍

1、direct exchange

direct类型的Exchange路由规则也很简单,它会把消息路由到那些binding key与routing key完全匹配的Queue中。

  • 绑定的queue需要跟routekey完全匹配才能路由到该队列。
  • 一个queue可以根据不同的routekey多次绑定到同一个交换机。
  • *、#通配符在该模式下不生效。
  • 同一个queue如果有多个连接时,默认以轮询的方式接收到消息。

上图在springboot中实现如下:

@Test
void directExchange(){
	//创建direct交换机
	amqpAdmin.declareExchange(new DirectExchange("direct.exchange"));
	//创建orange队列
	amqpAdmin.declareQueue(new Queue("fruit.queue",true,false,true,null));
	//绑定fruit.queue到direct.exchange,routekey为orange的消息将会匹配到该队列
	amqpAdmin.declareBinding(new Binding("fruit.queue", Binding.DestinationType.QUEUE,"direct.exchange","orange",null));
	//创建color队列
	amqpAdmin.declareQueue(new Queue("color.queue",true,false,true,null));
	//绑定color.queue到direct.exchange,routekey为black、green的消息将会匹配到该队列
	amqpAdmin.declareBinding(new Binding("color.queue", Binding.DestinationType.QUEUE,"direct.exchange","black",null));
	amqpAdmin.declareBinding(new Binding("color.queue", Binding.DestinationType.QUEUE,"direct.exchange","green",null));
}

执行完后在RabbitMQ 控制台看到绑定情况如下:

测试情况如下:

 

@Test
void sendCheliangMessage(){
	Map<String,Object> map = new HashMap<>();
	map.put("title","a web message");
	map.put("time",new Date());
	rabbitTemplate.convertAndSend("direct.exchange","",map); //都不能收到消息
	rabbitTemplate.convertAndSend("direct.exchange","orange",map); //fruit.queue 能收到消息
	rabbitTemplate.convertAndSend("direct.exchange","apple",map); //都不能收到消息
	rabbitTemplate.convertAndSend("direct.exchange","black",map); //color.queue能收到消息
	rabbitTemplate.convertAndSend("direct.exchange","green",map); //color.queue能收到消息
	rabbitTemplate.convertAndSend("direct.exchange","red",map); //都能收到消息
}

2、fanout exchange

fanout类型的Exchange路由规则非常简单,它会把所有发送到该Exchange的消息路由到所有与它绑定的Queue中。

  • 消息通过广播直接发送到绑定的queue。
  • routekey在该模式下不起作用。
  • 同一个queue如果有多个连接时,默认以轮询的方式接收到消息。

上图在springboot中实现如下:

@Test
void fanoutExchange(){
	//创建fanout交换机
	amqpAdmin.declareExchange(new FanoutExchange("fanout.exchange"));
	//创建fanout.queue.1队列
	amqpAdmin.declareQueue(new Queue("fanout.queue.1",true,false,true,null));
	//绑定fanout.queue.1到fanout.exchange,routekey在该模式下不生效
	amqpAdmin.declareBinding(new Binding("fanout.queue.1", Binding.DestinationType.QUEUE,"fanout.exchange","",null));
	//创建fanout.queue.2队列
	amqpAdmin.declareQueue(new Queue("fanout.queue.2",true,false,true,null));
	//绑定fanout.queue.2到fanout.exchange,routekey在该模式下不生效
	amqpAdmin.declareBinding(new Binding("fanout.queue.2", Binding.DestinationType.QUEUE,"fanout.exchange","",null));
}

 执行完后在RabbitMQ 控制台看到绑定情况如下:

测试情况如下: 

@Test
void sendCheliangMessage(){
	Map<String,Object> map = new HashMap<>();
	map.put("title","a web message");
	map.put("time",new Date());
    //广播模式下queue1和queue2消息都能正常收到
	rabbitTemplate.convertAndSend("fanout.exchange","",map);
}

 

3、topic exchange

topic类型的Exchange在匹配规则上进行了扩展,它与direct类型的Exchage相似,也是将消息路由到binding key与routing key相匹配的Queue中,但这里的匹配规则有些不同,它约定:

  • routing key为一个句点号“. ”分隔的字符串(我们将被句点号“. ”分隔开的每一段独立的字符串称为一个单词),如“stock.usd.nyse”、“nyse.vmw”、“quick.orange.rabbit”
  • binding key与routing key一样也是句点号“. ”分隔的字符串
  • binding key中可以存在两种特殊字符“*”与“#”,用于做模糊匹配,其中“*”用于匹配一个单词,“#”用于匹配多个单词(可以是零个)

上图在springboot中实现如下:

@Test
void topicExchange(){
	//创建topic交换机
	amqpAdmin.declareExchange(new TopicExchange("topic.exchange"));
	//创建topic.queue.1队列
	amqpAdmin.declareQueue(new Queue("topic.queue.1",true,false,true,null));
	//绑定topic.queue.1到topic.exchange,能匹配到routekey为两个单词,其中第二个单词为orange的消息
	amqpAdmin.declareBinding(new Binding("topic.queue.1", Binding.DestinationType.QUEUE,"topic.exchange","*.orange",null));
	//创建topic.queue.2队列
	amqpAdmin.declareQueue(new Queue("topic.queue.2",true,false,true,null));
	//绑定topic.queue.2到topic.exchange,能匹配routekey三个单词,其中最后一个单词为rabbit的消息,以及能匹配以单词lazy开头的消息
	amqpAdmin.declareBinding(new Binding("topic.queue.2", Binding.DestinationType.QUEUE,"topic.exchange","*.*.rabbit",null));
	amqpAdmin.declareBinding(new Binding("topic.queue.2", Binding.DestinationType.QUEUE,"topic.exchange","lazy.#",null));
}

创建完成后rabbitMQ控制台绑定情况如下:

消息测试情况如下:

@Test
void sendCheliangMessage(){
	Map<String,Object> map = new HashMap<>();
	map.put("title","a web message");
	map.put("time",new Date());

	rabbitTemplate.convertAndSend("topic.exchange","hangzhou.orange",map);  //queue.1 能收到
	rabbitTemplate.convertAndSend("topic.exchange","china.hangzhou.orange",map); //都收不到

	rabbitTemplate.convertAndSend("topic.exchange","hangzhou.rabbit",map); //都收不到
	rabbitTemplate.convertAndSend("topic.exchange","china.hangzhou.rabbit",map); //queue.2能收到

	rabbitTemplate.convertAndSend("topic.exchange","lazy.orange",map);  //queue1 queue2 都能收到
	rabbitTemplate.convertAndSend("topic.exchange","lazy.orange",map);  //queue1 queue2 都能收到 *.orange lazy.#
	rabbitTemplate.convertAndSend("topic.exchange","lazy.rabbit",map);  //queue2 都能收到 匹配 lazy.#"
	rabbitTemplate.convertAndSend("topic.exchange","lazy.hangzhou.rabbit",map);  //queue2 能收到 匹配*.*.rabbit  lazy.#
	rabbitTemplate.convertAndSend("topic.exchange","lazy.hangzhou.china",map);  //queue2 能收到 匹配lazy.#
}

四、springboot监听

在springboot进行RabbitMQ的消息监听步骤如下:

1.在application中添加@EnableRabbit注解。

2.新建service增加@Service注解,在监听函数中增加@RabbitListener注解

@Service
public class FanoutService {

    //在默认情况下此模式,执行时如果rabbitMQ中未建queue则会报错,关闭应用后queue也被删除了。
    @RabbitListener(queues="fanout.queue.1")
    public void receive(Object obj){
        System.out.println("fanoutService Receive:"+obj.toString());

    }
}
public static final String RABBITMQ_EXCHANGENAME_WEB="hz-cheliang-exchanges-web-topic";
public static final String CHELIANG_UP_WEB_MSG_QUEUE="hz-cheliang-up-queue-msg-web";//上行消息到web监控平台的队列
public static final String CHELIANG_UP_APP_MSG_QUEUE="hz-cheliang-up-queue-msg-app";//上行消息到app监控平台的队列

@Service
public class AppService {

    //启动时会自动创建queue
    @RabbitListener(bindings = @QueueBinding(
            value=@Queue(value = RabbitmqApplication.CHELIANG_UP_APP_MSG_QUEUE,durable = "true",autoDelete = "true"),
            exchange = @Exchange(value = RabbitmqApplication.RABBITMQ_EXCHANGENAME_WEB,type="topic"),
            key = "cheliang_app.*")
    )
    public void reeceive(Object obj){
        System.out.println("AppService Receive:"+obj.toString());
    }
}

3.对RabbitMQ服务信息进行配置。

spring.rabbitmq.addresses=172.16.36.157
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.port=5672

配套代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值