RabbitMQ交换器

概述

  RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。RabbitMQ服务器是用Erlang语言编写的,而集群和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均有与代理接口通讯的客户端库。

交换器

RabbitMQ共有4种交换(Exchange)策略:

  • Direct (路由模式)
  • Topic(通配符模式)
  • Fanout (广播模式)
  • Header(头模式)

在RabbitMQ中,所有消息生产者提交的消息都会交由Exchange进行分配,Exchange会根据不同的策略将将消息分发到不同的队列中

Direct

DirectExchange的路由策略是将消息绑定到一个DirectExchange上,当一条消息到达DirectExchange时会被转发到与该条routingKey对应的队列上。

Direct 配置如下:

@Configuration
public class RabbitDirectConfig {

    public final static String DIRECT_NAME = "user-direct";

    /**
     * 创建队列 test-queue
     */
    @Bean
    Queue phoneQueue() {
        return new Queue("phone");
    }

    @Bean
    Queue zjsQueue() {
        return new Queue("zjs");
    }

    /**
     * 创建交换策略 direct
     */
    @Bean
    DirectExchange directExchange() {
        return new DirectExchange(DIRECT_NAME, true, false);
    }

    /**
     * 将队列与交换策略进行绑定
     */
    @Bean
    Binding binding1() {
        return BindingBuilder.bind(phoneQueue())
                .to(directExchange()).with("direct");
    }

    @Bean
    Binding binding2() {
        return BindingBuilder.bind(zjsQueue())
                .to(directExchange()).with("direct");
    }
}

Direct 消费者配置如下:

@Component
public class Consumer {

    @RabbitListener(queues = "phone")
    public void phone(String message) {
        System.out.println("phone - " + message);
    }

    @RabbitListener(queues = "zjs")
    public void news(String message) {
        System.out.println("zjs - " + message);
    }
}

使用@RabbitListener指定监听的队列,若存在多个方法同时监听一个队列,会随机发送一条消息给其中一个方法。

Direct 生产者配置如下:

@RunWith(SpringRunner.class)
@SpringBootTest
public class MqApplicationTests {

    @Resource
    private RabbitTemplate template;

    @Test
    public void directPhone() {
        template.convertAndSend("phone", "11111111111");
    }

    @Test
    public void directZjs() {
        template.convertAndSend("zjs", "浙江省");
    }
}

运行结果:

direct

Fanout

FanoutExchange的数据交换策略是把所有到达FanoutExchange的消息转发给所有与他绑定的队列,这种策略下routingKey将不起任何作用。

Fanout 配置如下:

@Configuration
public class RabbitFanoutConfig {

    public final static String FANOUT_NAME = "user-fanout";

    /**
     * 创建队列 test-queue
     */
    @Bean
    Queue phoneQueue() {
        return new Queue("phone");
    }

    @Bean
    Queue zjsQueue() {
        return new Queue("zjs");
    }

    /**
     * 创建交换策略 fanout
     */
    FanoutExchange fanoutExchange() {
        return new FanoutExchange(FANOUT_NAME, true, false);
    }

    /**
     * 将队列与交换策略进行绑定
     */
    @Bean
    Binding binding1() {
        return BindingBuilder.bind(phoneQueue()).to(fanoutExchange());
    }

    @Bean
    Binding binding2() {
        return BindingBuilder.bind(zjsQueue()).to(fanoutExchange());
    }
}

Fanout 消费者配置上同。

Fanout 生产者配置如下:

@RunWith(SpringRunner.class)
@SpringBootTest
public class MqApplicationTests {

    @Resource
    private RabbitTemplate template;

    @Test
    public void fanout() {
        template.convertAndSend(RabbitFanoutConfig.FANOUT_NAME, null, "Hello world!");
    }
}

运行结果:

fanout

Topic

TopicExchange是一种比较灵活的路由策略,他会将所有所有到达TopicExchange的消息根据routingKey分发到一个或多个与他绑定的队列上。

Topic 配置如下:

@Configuration
public class RabbitTopicConfig {

    public final static String TOPIC_NAME = "user_topic";

    /**
     * 创建队列 test-queue
     */
    @Bean
    Queue phoneQueue() {
        return new Queue("phone");
    }

    @Bean
    Queue zjsQueue() {
        return new Queue("zjs");
    }

    /**
     * 创建交换策略 topic
     */
    @Bean
    TopicExchange topicExchange() {
        return new TopicExchange(TOPIC_NAME, true, false);
    }

    /**
     * 将队列与交换策略进行绑定
     */
    @Bean
    Binding binding1() {
        return BindingBuilder.bind(phoneQueue()).to(topicExchange()).with("#.phone.#");
    }

    @Bean
    Binding binding2() {
        return BindingBuilder.bind(zjsQueue()).to(topicExchange()).with("zjs.#");
    }
}

使用通配符作为路由策略,将消息发送到指定的队列中。

Topic 消费者配置上同。

Topic 生产者配置如下:

    @Test
    public void phonTopic() {
        template.convertAndSend(RabbitTopicConfig.TOPIC_NAME, "phone", "11111111111");
    }

运行结果:

topic-1

使用通配符策略:#.phone.#根据routingKey:phone将消息发送到phone队列中。


    @Test
    public void zjsPhoneTopic() {
        template.convertAndSend("zjs.phone", null, "11111111111");
    }

运行结果:

topic-2

使用通配符策略:#.phone.#zjs.#根据routingKey:zjs.phone将消息发送到zjs,phone队列中。


    @Test
    public void zjsPhoneAddressTopic() {
        template.convertAndSend(RabbitTopicConfig.TOPIC_NAME,"zjs.phone.address", "杭州");
    }

运行结果:

topic-3

使用通配符策略:#.phone.#zjs.#根据routingKey:zjs.phone.address将消息发送到zjs,phone队列中。


    @Test
    public void zjsNewTopic() {
        template.convertAndSend(RabbitTopicConfig.TOPIC_NAME, "zjs.new", "新闻");
    }

运行结果:

topic-4

使用通配符策略: zjs.#根据routingKey:zjs.new将消息发送到zjs队列中。

Header

HeaderExchange是一种使用比较少的路由策略,他会根据消息的Header将消息发送到不同的队列上,这种策略与routingKey无关。

Header配置如下:

@Configuration
public class RabbitHeaderConfig {

    public final static String HEADER_NAME = "user_header";

    /**
     * 创建队列 test-queue
     */
    @Bean
    Queue phoneQueue() {
        return new Queue("phone");
    }

    /**
     * 创建交换策略 header
     */
    @Bean
    HeadersExchange topicExchange() {
        return new HeadersExchange(HEADER_NAME, true, false);
    }


    /**
     * 若头存在 name 使用该策略
     */
    @Bean
    Binding binding1() {
        return BindingBuilder.bind(phoneQueue())
                .to(topicExchange())
                .where("name")
                .exists();
    }

    /**
     * 若头存在 name 且值为 ruoshy 使用该策略
     */
    @Bean
    Binding binding2() {
        return BindingBuilder.bind(phoneQueue()).to(topicExchange())
                .where("name")
                .matches("ruoshy");
    }

    /**
     * 若存在 name,age 使用该策略
     */
    @Bean
    Binding binding3() {
        return BindingBuilder.bind(phoneQueue()).to(topicExchange())
                .whereAny("name", "age")
                .exist();
    }

    /**
     * 若头存在 name,age 且值分别为 ruoshy,20 使用该策略
     */
    @Bean
    Binding binding4() {
        Map<String, Object> map = new HashMap<>();
        map.put("name", "ruoshy");
        map.put("age", 20);
        return BindingBuilder.bind(phoneQueue()).to(topicExchange())
                .whereAny(map)
                .match();
    }
}

Header 消费者配置如下:

@Component
public class Consumer {

    @RabbitListener(queues = "phone")
    public void phone(byte[] bytes) {
        System.out.println("phone - " + new String(bytes));
    }
}

Header 生产者配置如下

@RunWith(SpringRunner.class)
@SpringBootTest
public class MqApplicationTests {

    @Resource
    private RabbitTemplate template;

    /**
     * 添加头 name
     */
    @Test
    public void whereExists() {
        Message message = MessageBuilder.withBody("11111111111".getBytes())
                .setHeader("name", null)
                .build();
        template.convertAndSend(RabbitHeaderConfig.HEADER_NAME, null, message);
    }

    /**
     * 添加头 name 值 ruoshy
     */
    @Test
    public void whereMatches() {
        Message message = MessageBuilder.withBody("22222222222".getBytes())
                .setHeader("name", "ruoshy")
                .build();
        template.convertAndSend(RabbitHeaderConfig.HEADER_NAME, null, message);
    }

    /**
     * 添加头 name age
     */
    @Test
    public void whereAnyExist() {
        Message message = MessageBuilder.withBody("33333333333".getBytes())
                .setHeader("name", null)
                .setHeader("age", null)
                .build();
        template.convertAndSend(RabbitHeaderConfig.HEADER_NAME, null, message);
    }

    /**
     * 添加头 name 值 ruoshy
     * 添加头 age 值 20
     */
    @Test
    public void whereAnyMatches() {
        Message message = MessageBuilder.withBody("44444444444".getBytes())
                .setHeader("name", "ruoshy")
                .setHeader("age", 20)
                .build();
        template.convertAndSend(RabbitHeaderConfig.HEADER_NAME, null, message);
    }essage message = MessageBuilder.withBody("44444444444".getBytes())
                .setHeader("name", "ruoshy")
                .setHeader("age", 20)
                .build();
        template.convertAndSend(RabbitHeaderConfig.HEADER_NAME, null, message);
    }
}

运行结果:

header

实例地址

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

推荐阅读更多精彩内容