10-rabbitmq-消息主题-spring

10-rabbitmq-消息主题-spring

【博文总目录>>>】


【工程下载>>>】


先决条件


本教程假定RabbitMQ已在标准端口(5672)上的localhost上安装并运行。如果使用不同的主机,端口或凭据,连接设置将需要调整。

主题


在上一个教程中,我们改进了我们的消息灵活性。。而不是使用只能够进行虚拟广播的扇出交换器,我们使用直接交换器,并且获得了基于路由密钥有选择地接收消息的可能性。

虽然使用直接交换器改进了我们的系统,但它仍然有限制 - 它不能基于多个标准进行路由选择。

在我们的日志记录系统中,我们可能不仅要根据严重性订阅日志,还可以基于发出日志的源进行订阅。您可能会从syslog unix工具中了解这一概念,该工具根据严重性(信息/警告/错误…)和设施(auth / cron / kern …)路由日志。

这个例子会给我们很大的灵活性 - 我们可能想监听来自“cron”的重要错误,也可以监听“kern”的所有日志。

要在我们的日志记录系统中实现这种灵活性,我们需要了解一个更复杂的主题交换。

主题交换


发送到主题交换器的消息不能有任意的 routing_key - 它必须是由点分隔的单词列表。这些词可以是任何东西,但通常它们指定与消息相关联的一些功能。几个有效的路由密钥示例:“stock.usd.nyse”“nyse.vmw”“quick.orange.rabbit”。路由密钥中可以有任意多的单词,最多可达255个字节。
绑定键也必须是相同的形式。主题交换器背后的逻辑类似于直接交换,一个使用特定路由密钥发送的消息将被传递到与匹配的绑定密钥绑定的所有队列。但是,绑定密钥有两个重要的特殊情况:

  • *(星)可以替代一个字。
  • #(哈希)可以替换零个或多个单词。

在一个例子中最简单的解释一下:

这里写图片描述

在这个例子中,我们将发送所有描述动物的消息。消息将使用由三个字(两个点)组成的路由密钥发送。路由密钥中的第一个字将描述速度,第二个颜色和第三个种类:“<speed>.<color>.<species>”
我们创建了三个绑定:Q1绑定键“* .orange.*”和Q2与“*.*.rabbit”“lazy.#” 绑定。

这些绑定可以总结为:

  • Q1对所有的橙色动物感兴趣。
  • Q2想听听有关兔子的一切,以及关于懒惰动物的一切。

将路由密钥设置为“quick.orange.rabbit ”的消息将传递给两个队列。消息“lazy.orange.elephant ”也会发送到他们两个队列。另一方面,“quick.orange.fox”只会转到第一个队列,而“lazy.brown.fox”只能到第二个。“lazy.pink.rabbit”将只被传递到第二个队列一次,即使它匹配两个绑定。“quick.brown.fox”不匹配任何绑定,所以它将被丢弃。

如果我们违反合同并发送一个或四个字的消息,如“orange”“quick.orange.male.rabbit”,会发生什么?那么这些消息将不会匹配任何绑定,并将丢失。

另一方面,“ lazy.orange.male.rabbit ”即使它有四个字,将匹配上一个绑定,并将被传递到第二个队列。

主题交换


主题交换是强大的,可以像其他交换器一样行事。
当队列用“#”(哈希)绑定键绑定时,它将接收所有消息,而不管路由密钥如扇出交换。

当特殊字符“*”(星号)和“#”(哈希)不用于绑定时,主题交换将表现得像一个直接交换。

把它们放在一起


我们将在我们的消息系统中使用主题交换。我们将从一个工作假设开始,路由密钥将利用两个通配符和哈希标签。

代码与上一个教程几乎相同。首先,在tut5包的Tut5Config.java中配置一些配置文件和bean :

package com.example.rabbitmq.tut5;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

/**
 * Author: 王俊超
 * Date: 2017-06-17 20:52
 * All Rights Reserved !!!
 */
@Profile({"tut5", "topics"})
@Configuration
public class Tut5Config {

    @Bean
    public TopicExchange topic() {
        return new TopicExchange("tut.topic");
    }

    @Profile("receiver")
    private static class ReceiverConfig {

        @Bean
        public Tut5Receiver receiver() {
            return new Tut5Receiver();
        }

        @Bean
        public Queue autoDeleteQueue1() {
            return new AnonymousQueue();
        }

        @Bean
        public Queue autoDeleteQueue2() {
            return new AnonymousQueue();
        }

        @Bean
        public Binding binding1a(TopicExchange topic, Queue autoDeleteQueue1) {
            return BindingBuilder.bind(autoDeleteQueue1).to(topic).with("*.orange.*");
        }

        @Bean
        public Binding binding1b(TopicExchange topic, Queue autoDeleteQueue1) {
            return BindingBuilder.bind(autoDeleteQueue1).to(topic).with("*.*.rabbit");
        }

        @Bean
        public Binding binding2a(TopicExchange topic, Queue autoDeleteQueue2) {
            return BindingBuilder.bind(autoDeleteQueue2).to(topic).with("lazy.#");
        }

    }

    @Profile("sender")
    @Bean
    public Tut5Sender sender() {
        return new Tut5Sender();
    }

}

我们设置我们的配置文件,以执行profile作为“tut5”或“topics”的选择。然后我们为TopicExchange创建了bean。“receiver”配置文件是ReceiverConfig,定义了我们的接收方,两个AnonymousQueues如前面的教程中所述,并且使用主题语法的主题的绑定。我们还创建了“sender”配置文件作为创建Tut5Sender类。

Tut5Sender再次使用@RabbitListener从相应主题接收消息。

package com.example.rabbitmq.tut5;

import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.util.StopWatch;

/**
 * Author: 王俊超
 * Date: 2017-06-17 20:53
 * All Rights Reserved !!!
 */
public class Tut5Receiver {
    @RabbitListener(queues = "#{autoDeleteQueue1.name}")
    public void receive1(String in) throws InterruptedException {
        receive(in, 1);
    }

    @RabbitListener(queues = "#{autoDeleteQueue2.name}")
    public void receive2(String in) throws InterruptedException {
        receive(in, 2);
    }

    public void receive(String in, int receiver) throws InterruptedException {
        StopWatch watch = new StopWatch();
        watch.start();
        System.out.println("instance " + receiver + " [x] Received '" + in + "'");
        doWork(in);
        watch.stop();
        System.out.println("instance " + receiver + " [x] Done in " + watch.getTotalTimeSeconds() + "s");
    }

    private void doWork(String in) throws InterruptedException {
        for (char ch : in.toCharArray()) {
            if (ch == '.') {
                Thread.sleep(1000);
            }
        }
    }
}

Tut5Sender.java的代码:

package com.example.rabbitmq.tut5;

import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;

/**
 * Author: 王俊超
 * Date: 2017-06-17 20:53
 * All Rights Reserved !!!
 */
public class Tut5Sender {
    @Autowired
    private RabbitTemplate template;

    @Autowired
    private TopicExchange topic;

    private int index;

    private int count;

    private final String[] keys = {"quick.orange.rabbit",
            "lazy.orange.elephant", "quick.orange.fox",
            "lazy.brown.fox", "lazy.pink.rabbit", "quick.brown.fox"};

    @Scheduled(fixedDelay = 1000, initialDelay = 500)
    public void send() {
        StringBuilder builder = new StringBuilder("Hello to ");
        if (++this.index == keys.length) {
            this.index = 0;
        }
        String key = keys[this.index];
        builder.append(key).append(' ');
        builder.append(Integer.toString(++this.count));
        String message = builder.toString();
        // 使用不同的跺
        template.convertAndSend(topic.getName(), key, message);
        System.out.println(" [x] Sent '" + message + "'");
    }
}

运行


先运行接收者,需要添加运行参数

--spring.profiles.active=topics,receiver --tutorial.client.duration=60000

再运行发送者,需要添加运行参数

--spring.profiles.active=topics,sender --tutorial.client.duration=60000

发件者的输出将如下所示:

Ready ... running for 60000ms
 [x] Sent 'Hello to lazy.orange.elephant 1'
 [x] Sent 'Hello to quick.orange.fox 2'
 [x] Sent 'Hello to lazy.brown.fox 3'
 [x] Sent 'Hello to lazy.pink.rabbit 4'
 [x] Sent 'Hello to quick.brown.fox 5'
 [x] Sent 'Hello to quick.orange.rabbit 6'
 [x] Sent 'Hello to lazy.orange.elephant 7'
 [x] Sent 'Hello to quick.orange.fox 8'
 [x] Sent 'Hello to lazy.brown.fox 9'
 [x] Sent 'Hello to lazy.pink.rabbit 10'

接收者的输出如下:

instance 1 [x] Received 'Hello to lazy.orange.elephant 1'
instance 2 [x] Received 'Hello to lazy.orange.elephant 1'
instance 2 [x] Done in 2.005s
instance 1 [x] Done in 2.005s
instance 1 [x] Received 'Hello to quick.orange.fox 2'
instance 2 [x] Received 'Hello to lazy.brown.fox 3'
instance 1 [x] Done in 2.003s
instance 2 [x] Done in 2.003s
instance 1 [x] Received 'Hello to lazy.pink.rabbit 4'
instance 2 [x] Received 'Hello to lazy.pink.rabbit 4'
instance 1 [x] Done in 2.006s
instance 2 [x] Done in 2.006s
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值