九、主题

前面的路由教程中改进了日志系统,使用了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是#,或者#.# 表示匹配所有

posted @ 2018-12-04 17:14  Hayson  阅读(141)  评论(0编辑  收藏  举报