5.主题模式

在路由模式中,使用Direct交换机,从而可以选择性接收日志。

虽然使用Direct交换机改进了我们的系统,但它不能基于多个标准进行路由。

这是缺乏灵活性的,要在日志系统中实现这一点,我们需要了解更复杂的Topic交换机。

主题交换机 Topic exchange

发送到Topic交换机的消息,它的的routingKey,必须是由点分隔的多个单词。单词可以是任何东西,但通常是与消息相关的一些特性。

routingKey可以有任意多的单词,最多255个字节。

bindingKey也必须采用相同的形式。Topic交换机的逻辑与直连交换机类似——使用特定routingKey发送的消息将被传递到所有使用匹配bindingKey绑定的队列。bindingKey有两个重要的特殊点:

  • * 可以通配单个单词。
  • # 可以通配零个或多个单词。

如图所示:
image

如上图中,将routingKey设置为"quick.orange.rabbit"的消息将被发送到两个队列。消息 "lazy.orange.elephant“也发送到它们两个。另外”quick.orange.fox“只会发到第一个队列,”lazy.brown.fox“只发给第二个。”lazy.pink.rabbit“将只被传递到第二个队列一次,即使它匹配两个绑定。”quick.brown.fox"不匹配任何绑定,因此将被丢弃。

生产者

public class Test1 {
    public static void main(String[] args) throws Exception {
        ConnectionFactory f = new ConnectionFactory();
        f.setHost("192.168.64.140");
        f.setPort(5672);
        f.setUsername("admin");
        f.setPassword("admin");
        
        Connection c = f.newConnection();
        Channel ch = c.createChannel();
        
        //参数1: 交换机名
        //参数2: 交换机类型
        ch.exchangeDeclare("topic_logs", BuiltinExchangeType.TOPIC);
        
        while (true) {
            System.out.print("输入消息: ");
            String msg = new Scanner(System.in).nextLine();
            if ("exit".contentEquals(msg)) {
                break;
            }
            System.out.print("输入routingKey: ");
            String routingKey = new Scanner(System.in).nextLine();
            
            //参数1: 交换机名
            //参数2: routingKey, 路由键,这里我们用日志级别,如"error","info","warning"
            //参数3: 其他配置属性
            //参数4: 发布的消息数据 
            ch.basicPublish("topic_logs", routingKey, null, msg.getBytes());
            
            System.out.println("消息已发送: "+routingKey+" - "+msg);
        }

        c.close();
    }
}

消费者

public class Test2 {
    public static void main(String[] args) throws Exception {
        ConnectionFactory f = new ConnectionFactory();
        f.setHost("192.168.64.140");
        f.setUsername("admin");
        f.setPassword("admin");
        Connection c = f.newConnection();
        Channel ch = c.createChannel();
        
        ch.exchangeDeclare("topic_logs", BuiltinExchangeType.TOPIC);
        
        //自动生成对列名,
        //非持久,独占,自动删除
        String queueName = ch.queueDeclare().getQueue();
        
        System.out.println("输入bindingKey,用空格隔开:");
        String[] a = new Scanner(System.in).nextLine().split("\\s");
        
        //把该队列,绑定到 topic_logs 交换机
        //允许使用多个 bindingKey
        for (String bindingKey : a) {
            ch.queueBind(queueName, "topic_logs", bindingKey);
        }
        
        System.out.println("等待接收数据");
        
        //收到消息后用来处理消息的回调对象
        DeliverCallback callback = new DeliverCallback() {
            @Override
            public void handle(String consumerTag, Delivery message) throws IOException {
                String msg = new String(message.getBody(), "UTF-8");
                String routingKey = message.getEnvelope().getRoutingKey();
                System.out.println("收到: "+routingKey+" - "+msg);
            }
        };
        
        //消费者取消时的回调对象
        CancelCallback cancel = new CancelCallback() {
            @Override
            public void handle(String consumerTag) throws IOException {
            }
        };
        
        ch.basicConsume(queueName, true, callback, cancel);
    }
}

6.RPC模式

该模式不常使用,也较为复杂,了解即可.
RPC模式顾名思义也就是远程调用模式.

RabbitMQ去搭建一个RPC系统:一个客户端和一个可以升级(扩展)的RPC服务器

image

总结:
RPC的工作方式是这样的:

  • 对于RPC请求,客户端发送一条带有两个属性的消息:replyTo,设置为仅为请求创建的匿名独占队列,和correlationId,设置为每个请求的惟一id值。
  • 请求被发送到rpc_queue队列。
  • RPC工作进程(即:服务器)在队列上等待请求。当一个请求出现时,它执行任务,并使用replyTo字段中的队列将结果发回客户机。
  • 客户机在回应消息队列上等待数据。当消息出现时,它检查correlationId属性。如果匹配请求中的值,则向程序返回该响应数据。

迈克丝
82 声望5 粉丝

一步一步学技术,踏踏实实涨经验,兴趣广泛,广交好友,希望大家多多指正/批评.


引用和评论

0 条评论