RabbitMq的学习(三):Java使用Exchange(路由)

RabbitMq的Exchange(路由)官方文档:http://www.rabbitmq.com/getstarted.html(语言记得选Java)

路由概要说明:一个Exchange(路由)可以绑定多个Queue(队列),

根据不同的Exchange类型可以实现不同Exchange策略。

 

一、fanout

说明:把所有发送到该Exchange的消息路由到所有与它绑定的Queue中

代码如下:

package com.mq;

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.TimeoutException;

public class ProducerExchangeTest {

    public static void main(String[] args) {
        // 创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        // 连接
        Connection connection = null;
        // 通道
        Channel channel = null;
        try {
            // amqp://用户名:密码@IP:端口号/虚拟服务器名称
            factory.setUri("amqp://testuser:testuser@192.168.162.130:5672/my_test");
            // 获取连接
            connection = factory.newConnection();
            System.out.println(connection);

            // 创建通道
            channel = connection.createChannel();
            System.out.println(channel);

            /**
             * 创建一个队列
             *
             * queue 队列名称
             * durable 是否申明持久队列,true:rabbitmq重启后继续存在
             * exclusive 是否独占,ture:只有该连接能访问
             * autoDelete 是否自动删除,true:服务器不再使用时,将自动删除该队列
             * arguments 队列其它参数
             */
            channel.queueDeclare("testQueue", true, false, false, null);


            /**
             * 创建路由
             *
             * exchange 路由名称
             * type 路由类型,这里可以用string类型,我直接用了架包里自带的枚举类
             * durable true:持久化操作,rabbitmq重启后,依然存在
             * autoDelete true:自动删除,当没被使用时,自动删除该路由
             * internal
             * arguments 创建路由时的构造参数
             */
            channel.exchangeDeclare("testExchange",BuiltinExchangeType.FANOUT,
                    true, false, false, null);


            /**
             * 队列绑定路由
             *
             * queue 队列名称
             * exchange 路由名称
             * routingKey 路由密钥
             */
            channel.queueBind("testQueue","testExchange","");

            String msg = "今天天气不错";

            /**
             * 推送消息
             *
             * exchange 路由
             * routingKey 路由密钥
             * props 消息的头信息
             * body 消息主体
             */
            channel.basicPublish("testExchange", "", null, msg.getBytes("UTF-8"));

        } catch (URISyntaxException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

与之前的代码相比较,就加了两行代码,1创建Exchange,2将Exchange和Queue绑定。

运行以上代码之后,RabbitMq管理后台如下图所示:

testExchange内容如下:

testExchange已经成功绑定到testQueue。

一个Exchange可以绑定多个Queue,我这里只绑定了一个Queue。

二、direct

说明:把消息路由到bindingKey与routingKey完全匹配的Queue中

代码如下:

package com.mq;

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.TimeoutException;

public class ProducerExchangeTest {

    public static void main(String[] args) {
        // 创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        // 连接
        Connection connection = null;
        // 通道
        Channel channel = null;
        try {
            // amqp://用户名:密码@IP:端口号/虚拟服务器名称
            factory.setUri("amqp://testuser:testuser@192.168.162.130:5672/my_test");
            // 获取连接
            connection = factory.newConnection();
            System.out.println(connection);

            // 创建通道
            channel = connection.createChannel();
            System.out.println(channel);

            /**
             * 创建一个队列
             *
             * queue 队列名称
             * durable 是否申明持久队列,true:rabbitmq重启后继续存在
             * exclusive 是否独占,ture:只有该连接能访问
             * autoDelete 是否自动删除,true:服务器不再使用时,将自动删除该队列
             * arguments 队列其它参数
             */
            channel.queueDeclare("queueDirect", true, false, false, null);

            /**
             * 创建路由
             *
             * exchange 路由名称
             * type 路由类型,这里可以用string类型,我直接用了架包里自带的枚举类
             * durable true:持久化操作,rabbitmq重启后,依然存在
             * autoDelete true:自动删除,当没被使用时,自动删除该路由
             * internal
             * arguments 创建路由时的构造参数
             */
            channel.exchangeDeclare("exchangeDirect",BuiltinExchangeType.DIRECT,
                    true, false, false, null);
            
            /**
             * 队列绑定路由
             *
             * queue 队列名称
             * exchange 路由名称
             * routingKey 路由密钥
             */
            channel.queueBind("queueDirect","exchangeDirect","test.direct.my");

            String msg = "今天天气不错";

            /**
             * 推送消息
             *
             * exchange 路由
             * routingKey 路由密钥
             * props 消息的头信息
             * body 消息主体
             */
            channel.basicPublish("exchangeDirect", "test.direct.my",
                    null, msg.getBytes("UTF-8"));

        } catch (URISyntaxException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

创建Queue的方式不变,创建Channel时,将类型修改成direct,绑定和推送时,加上routingKey即可。

在推送消息的时候,把routingKey修改掉,比如:test.direct.*

看看还能不能推送成功,显然queueDirect里并没有消息。

RabbitMq后台查看routingKey:

三、topic

说明:把消息路由到bindingKey与routingKey模糊匹配的Queue中

*】表示匹配1个单词

#】表示匹配0个或多个单词

 

(1)给张官方图例,方便理解

a.orange.rabbit能分发到Q1,Q2

a.orange.b只能分发到Q1

a.b.rabbit只能分发到Q2

lazy.a.b或者lazy.a只能分发到Q2

(2)下面是我自己写的,代码如下:

package com.mq;

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.TimeoutException;

public class ProducerExchangeTest {

    public static void main(String[] args) {
        // 创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        // 连接
        Connection connection = null;
        // 通道
        Channel channel = null;
        try {
            // amqp://用户名:密码@IP:端口号/虚拟服务器名称
            factory.setUri("amqp://testuser:testuser@192.168.162.130:5672/my_test");
            // 获取连接
            connection = factory.newConnection();
            System.out.println(connection);

            // 创建通道
            channel = connection.createChannel();
            System.out.println(channel);

            /**
             * 创建一个队列
             *
             * queue 队列名称
             * durable 是否申明持久队列,true:rabbitmq重启后继续存在
             * exclusive 是否独占,ture:只有该连接能访问
             * autoDelete 是否自动删除,true:服务器不再使用时,将自动删除该队列
             * arguments 队列其它参数
             */
            channel.queueDeclare("queueTopic1", true, false, false, null);

            /**
             * 创建一个队列
             *
             * queue 队列名称
             * durable 是否申明持久队列,true:rabbitmq重启后继续存在
             * exclusive 是否独占,ture:只有该连接能访问
             * autoDelete 是否自动删除,true:服务器不再使用时,将自动删除该队列
             * arguments 队列其它参数
             */
            channel.queueDeclare("queueTopic2", true, false, false, null);

            /**
             * 创建一个队列
             *
             * queue 队列名称
             * durable 是否申明持久队列,true:rabbitmq重启后继续存在
             * exclusive 是否独占,ture:只有该连接能访问
             * autoDelete 是否自动删除,true:服务器不再使用时,将自动删除该队列
             * arguments 队列其它参数
             */
            channel.queueDeclare("queueTopic3", true, false, false, null);

            /**
             * 创建路由
             *
             * exchange 路由名称
             * type 路由类型,这里可以用string类型,我直接用了架包里自带的枚举类
             * durable true:持久化操作,rabbitmq重启后,依然存在
             * autoDelete true:自动删除,当没被使用时,自动删除该路由
             * internal
             * arguments 创建路由时的构造参数
             */
            channel.exchangeDeclare("exchangeTopic",BuiltinExchangeType.TOPIC,
                    true, false, false, null);

            /**
             * 队列绑定路由
             *
             * queue 队列名称
             * exchange 路由名称
             * routingKey 路由密钥
             */
            channel.queueBind("queueTopic1","exchangeTopic","*.topic.*");

            /**
             * 队列绑定路由
             *
             * queue 队列名称
             * exchange 路由名称
             * routingKey 路由密钥
             */
            channel.queueBind("queueTopic2","exchangeTopic","test.topic.*");

            /**
             * 队列绑定路由
             *
             * queue 队列名称
             * exchange 路由名称
             * routingKey 路由密钥
             */
            channel.queueBind("queueTopic3","exchangeTopic","test.#");

            String msg = "今天天气不错";

            /**
             * 推送消息
             *
             * queueTopic1,queueTopic2,queueTopic3队列能分发到
             */
            channel.basicPublish("exchangeTopic", "test.topic.a",
                    null, msg.getBytes("UTF-8"));

            /**
             * 推送消息
             *
             * 只有queueTopic3队列能分发到
             */
//            channel.basicPublish("exchangeTopic", "test.t.a",
//                    null, msg.getBytes("UTF-8"));

            /**
             * 推送消息
             *
             * 只有queueTopic1队列能分发到
             */
//            channel.basicPublish("exchangeTopic", "a.topic.a",
//                    null, msg.getBytes("UTF-8"));

        } catch (URISyntaxException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

我创建了3个队列,并分别绑定了不同的routingKey。通配符的使用,慢慢尝试吧,我代码里只列举了一部分。

四、headers

说明:headers类型的Exchange不依赖于routingKey与bindingKey的匹配规则来路由消息,

而是根据发送的消息内容中的headers属性进行匹配

注意:关于headers的官方说明,我找了几次都没找到,下面是我自己测试以及结合网络资料得出来的。

所以谨慎参考。

headers里有个key“x-match”,有两个取值:all(默认),any

(1).all,匹配所有headers里的键值对,只有全部全部匹配才能成功推送消息。

匹配基准是Exchange绑定Queue时,写入的headers,我这里称之为bindHeaders。

推送消息时的headers,我这里称之为pushHeaders。

pushHeaders键值对要大于等于bindHeaders,才能成功推送。

如下图:

(2).any,就比较简单了,bindHeaders和pushHeaders只要有一个键值对匹配即可成功推送。

下面直接上代码:

package com.mq;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;

public class ProducerExchangeTest {

    public static void main(String[] args) {
        // 创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        // 连接
        Connection connection = null;
        // 通道
        Channel channel = null;
        try {
            // amqp://用户名:密码@IP:端口号/虚拟服务器名称
            factory.setUri("amqp://testuser:testuser@192.168.162.130:5672/my_test");
            // 获取连接
            connection = factory.newConnection();
            System.out.println(connection);

            // 创建通道
            channel = connection.createChannel();
            System.out.println(channel);

            /**
             * 创建一个队列
             *
             * queue 队列名称
             * durable 是否申明持久队列,true:rabbitmq重启后继续存在
             * exclusive 是否独占,ture:只有该连接能访问
             * autoDelete 是否自动删除,true:服务器不再使用时,将自动删除该队列
             * arguments 队列其它参数
             */
            channel.queueDeclare("queueHeaders", true, false, false, null);

            /**
             * 创建路由
             *
             * exchange 路由名称
             * type 路由类型,这里可以用string类型,我直接用了架包里自带的枚举类
             * durable true:持久化操作,rabbitmq重启后,依然存在
             * autoDelete true:自动删除,当没被使用时,自动删除该路由
             * internal
             * arguments 创建路由时的构造参数
             */
            channel.exchangeDeclare("exchangeHeaders",BuiltinExchangeType.HEADERS,
                    true, false, false, null);

            // Exchange绑定Queue的头部
            Map<String, Object> headers = new HashMap<>();
            headers.put("x-match", "all"); // x-match:all(默认),any
            headers.put("sign", "123456");
            headers.put("a", "000");

            /**
             * 队列绑定路由
             *
             * queue 队列名称
             * exchange 路由名称
             * routingKey 路由密钥
             */
            channel.queueBind("queueHeaders","exchangeHeaders","", headers);

            String msg = "今天天气不错";

            // 推送时的头部
            Map<String, Object> pushHeaders = new HashMap<>();
            pushHeaders.put("sign", "123456");
            pushHeaders.put("a", "000");
            AMQP.BasicProperties properties = new AMQP.BasicProperties().builder().headers(pushHeaders).build();

            // 下面写法是错误的,不要踩坑
//            AMQP.BasicProperties properties = new AMQP.BasicProperties();
//            properties.builder().headers(pushHeaders).build();

            /**
             * 推送消息
             *
             * exchange 路由
             * routingKey 路由密钥
             * props 消息的头信息
             * body 消息主体
             */
            channel.basicPublish("exchangeHeaders", "",
                    properties, msg.getBytes("UTF-8"));


        } catch (URISyntaxException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

我代码里只列举了all的用法,其它自行测试吧。运行之后,RabbitMq后台部分信息如下:

 

五、Exchange绑定Exchange

哈哈,没想到吧,路由还可以绑定路由,用法和绑定队列的用法类似。

下面我就贴一下简单的代码:

package com.mq;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;

public class ProducerExchangeTest {

    public static void main(String[] args) {
        // 创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        // 连接
        Connection connection = null;
        // 通道
        Channel channel = null;
        try {
            // amqp://用户名:密码@IP:端口号/虚拟服务器名称
            factory.setUri("amqp://testuser:testuser@192.168.162.130:5672/my_test");
            // 获取连接
            connection = factory.newConnection();
            System.out.println(connection);

            // 创建通道
            channel = connection.createChannel();
            System.out.println(channel);

            /**
             * 创建路由
             *
             * exchange 路由名称
             * type 路由类型,这里可以用string类型,我直接用了架包里自带的枚举类
             * durable true:持久化操作,rabbitmq重启后,依然存在
             * autoDelete true:自动删除,当没被使用时,自动删除该路由
             * internal
             * arguments 创建路由时的构造参数
             */
            channel.exchangeDeclare("exchange1",BuiltinExchangeType.FANOUT,
                    true, false, false, null);

            /**
             * 创建路由
             *
             * exchange 路由名称
             * type 路由类型,这里可以用string类型,我直接用了架包里自带的枚举类
             * durable true:持久化操作,rabbitmq重启后,依然存在
             * autoDelete true:自动删除,当没被使用时,自动删除该路由
             * internal
             * arguments 创建路由时的构造参数
             */
            channel.exchangeDeclare("exchange2",BuiltinExchangeType.FANOUT,
                    true, false, false, null);

            /**
             * destination 目标路由
             * source 来源路由
             * routingKey 密钥
             * argument 自定义参数
             *
             * 绑定方向:source->destination,即exchange2->exchange1
             */
            channel.exchangeBind("exchange1", "exchange2", "", null);

        } catch (URISyntaxException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

顺便也贴一下,运行成功之后的RabbitMq后台部分信息:

exchange2信息:

exchange1信息:

 

ok,结束。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值