• 九、主题


    前面的路由教程中改进了日志系统,使用了direct类型,而不是只能进行虚拟广播的fanout的交换器,并且有可能选择性地接收日志。

    但前面的direct类型交换器还是有局限性。在日志系统中,不仅要查看指定error级别的日志消息,还要知道是哪个地方、哪个类出现的错误消息,例如下面日志消息

    INFO  c.z.springboot01logging.controller.service.InfoTest - 这是InfoTest的日志
    WARN  c.z.springboot01logging.controller.service.WarnTest - 这是WarnTest日志
    ERROR c.z.springboot01logging.controller.service.ErrorTest - 这是ErrorTest日志
    

    上面日志消息除了标记info级别,还有日志消息的来源:c.z.springboot01logging.controller.serviceInfoTestWarnTestErrorTest三个类。

    如上面日志所示,要查看哪些类的日志消息,如果用前面的direct类型查询,只查像上面几个类日志还好,如果成百上千条,就不能一个个写,此时就需要topic类型交换器。

    日志消息发送客户端:

    /**
     * @author Hayson
     * @date 2018/11/26 17:17
     * @description 路由发送消息
     */
    public class Send {
        final static String EXCHANGE = "logs_topic";
    
        public static void main(String[] args) throws IOException, TimeoutException {
            Map<String, String> message = new HashMap<>();
            message.put("error.err.rabbit", "error message");
            message.put("error.info.rabbit2", "error2 message");
            message.put("error.rabbit.message", "error2 message");
            message.put("warning.warn.rabbit", "warning message");
            message.put("info.rabbit", "info message");
            send(message);
        }
    
        public static void send(Map<String, String> message) throws IOException, TimeoutException {
            //获取连接
            Connection connection = ConnectionUtils.getConnection();
            //通过连接创建信道
            Channel channel = connection.createChannel();
    
            channel.exchangeDeclare(EXCHANGE, BuiltinExchangeType.TOPIC);
    
            //发送消息
            Iterator<Map.Entry<String, String>> iterator = message.entrySet().iterator();
            while (iterator.hasNext()){
                Map.Entry<String, String> next = iterator.next();
                String key = next.getKey();
                String value = next.getValue();
                channel.basicPublish(EXCHANGE, key, null, value.getBytes("utf-8"));
                System.out.println("发送:" + key + " : " + value);
            }
            //关闭信道和连接
            channel.close();
            connection.close();
        }
    }
    

    日志消息接收客户端:

    /**
     * @author Hayson
     * @date 2018/11/23 13:41
     * @description rabbitmq消费者接收消息2
     */
    public class Receiver {
        final static String EXCHANGE = "logs_topic";
    
        public static void main(String[] args) throws IOException, TimeoutException {
            String[] routings = {"error.#"};
            recevier(routings);
        }
    
        public static void recevier(String[] routings) throws IOException, TimeoutException {
            //获取连接
            Connection connection = ConnectionUtils.getConnection();
            //通过连接创建信道
            Channel channel = connection.createChannel();
            //创建交换器
            channel.exchangeDeclare(EXCHANGE, BuiltinExchangeType.TOPIC);
            //创建队列
            String queue = channel.queueDeclare().getQueue();
    
            for (String routing : routings) {
                //交换器和队列绑定
                channel.queueBind(queue, EXCHANGE, routing);
            }
            Consumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
                        throws IOException {
                    String routingKey = envelope.getRoutingKey();
                    String message = new String(body, "UTF-8");
                    System.out.println("接收到消息:" + routingKey + " : " + message);
                }
            };
            channel.basicConsume(queue, true, consumer);
            //关闭信道、连接
            //channel.close();
            //connection.close();
        }
    }
    

    上面消费客户端接收所有routingKeyrabbit开头的消息。

    关于topic类型的匹配规则:

    匹配规则

    * 匹配一个单词
    # 匹配0个或多个字符
    *# 只能写在.号左右,且不能挨着字符
    单词和单词之间需要.隔开。

    例子

    • BindingKey是user.log.#,因为#是匹配0个或多个字符,所以下面RoutingKey的可以匹配:

      user.log
      user.log.info
      user.log.a
      user.log.info.login
      
    • BindingKey是user.log.*,因为* 匹配一个单词,所以

      user.log.info 可以匹配
      user.log 不能匹配
      user.log.info.login 不能匹配,二个单词
      
    • BindingKey是#.log.#, 可以匹配:

      log
      user.log
      log.info
      user.log.info
      user.log.info.a
      
    • BindingKey是*.log.*

      log 不匹配
      user.log 不匹配
      log.info 不匹配
      user.log.info 匹配,前后各一个单词
      user.log.info.a 不匹配
      a.user.log.info 不匹配
      
    • BindingKey是*.action.#

      action 不符合
      action.log 不符合
      user.action.log 符合
      user.action.log.info 符合
      user.action 符合
      user.log.action 不符合
      
    • BindingKey是#.action.*

      action 不符合
      user.action 不符合
      user.action.action 符合
      user.action.login 符合
      user.action.login.count 不符合
      
    • BindingKey是* 表示匹配一个单词

    • BindingKey是#,或者#.# 表示匹配所有

  • 相关阅读:
    Delphi 2005 以上版本GIF动画播放设置
    WIN7中盾牌的编程-DELPHI
    Delphi中ComPort通信中的数据处理
    【ZJOI2008】树的统计(树链剖分)
    【CJOJ2440】大话西游(树链剖分)
    【洛谷3384】【模板】树链剖分
    【NOI2004】郁闷的出纳员(splay)
    【HNOI2004】宠物收养所(splay)
    【HNOI 2002 】营业额统计(splay)
    【Tyvj 1728】普通平衡树
  • 原文地址:https://www.cnblogs.com/zenghi-home/p/10065308.html
Copyright © 2020-2023  润新知