• RabbitMQ


    在publish/subscribe模式中使用fanout类型有个缺陷,就是不能选择性接收的消息。
    我们可以让consumer获得所有已发布的消息中指定的几个消息。


    在之前的例子中我们这样绑定exchange和队列:

    channel.queueBind(queueName, EXCHANGE_NAME, "");
    

    暂且不论该代码中绑定的exchange类型,这里空着的参数就是routing key。
    routing key的意义与exchange类型有关,比如使用fanout类型就会忽略掉routing key。


    而解决这一问题的就是direct类型。
    direct exchange并不复杂,只不过是producer和consumer双方的exchange对应时还需要对应routing key。

    以下代码中,同一个exchange和两个队列进行绑定,两个队列分别和不同的binding key绑定。
    (PS:当然,我们也可以将同一个routing key绑定给不同的队列也没有问题。)
    另外,SERVERITY变量是rounting数组,假设将日志通过exchange发送出去,consumer根据自己的需要获取不同级别的日志:

    
    final class ChannelFactory_{
        private final static ConnectionFactory connFactory = new ConnectionFactory();
     
        public final static String EXCHANGE_NAME = "direct_exchange";
        public final static String[] SEVERITY = {"info","warning","error"};
     
        static {
            Channel temp = getChannel();
            try {
                temp.exchangeDeclare(EXCHANGE_NAME, ExchangeTypes.DIRECT);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
     
        public static Channel getChannel(int channelNumber){
            try {
                Connection connection = connFactory.newConnection();
                return connection.createChannel(channelNumber);
            } catch (IOException e) {
                e.printStackTrace();
            }return null;
        }
     
        public static Channel getChannel(){
            try {
                Connection connection = connFactory.newConnection();
                return connection.createChannel();
            } catch (IOException e) {
                e.printStackTrace();
            }return null;
        }
     
        public static void  closeChannel(Channel channel) throws IOException {
            channel.close();
            channel.getConnection().close();
        }
     
    }
    

    确认定义:

    consumer只需要warning和error级别(routing)的日志消息:

    
    public static void main(String[] args) throws IOException, InterruptedException {
            Channel channel = ChannelFactory_.getChannel();
     
            String queueName = channel.queueDeclare().getQueue();
            channel.queueBind(queueName, ChannelFactory_.EXCHANGE_NAME,"warning");
            channel.queueBind(queueName, ChannelFactory_.EXCHANGE_NAME,"error");
     
            QueueingConsumer consumer = new QueueingConsumer(channel);
            channel.basicConsume(queueName,true,consumer);
            while(true){
                QueueingConsumer.Delivery delivery = consumer.nextDelivery();
                String message = new String(delivery.getBody());
                String routingKey = delivery.getEnvelope().getRoutingKey();
     
                System.out.println(" [x] Received '" + routingKey + "':'" + message + "'");
            }
     
        }
    

    producer将所有级别的日志都发送出去:

    
    public static void main(String[] args) throws IOException {
            Channel channel = ChannelFactory_.getChannel();
            String content = "message "+new Date();
     
            for (int i = 0; i <ChannelFactory_.SEVERITY.length ; i++) {
                channel.basicPublish(EXCHANGE_NAME,ChannelFactory_.SEVERITY[i],null,content.getBytes());
            }
            ChannelFactory_.closeChannel(channel);
        }
    

    运行结果:

    direct exchange可以让我们有选择性地接受消息。
    但这样做仍然有缺陷。
    虽然我可以只要求error和warning级别的日志,但是我不能再进行细分。
    比如我只想要数据库相关的error和warning级别的日志。


    为了实现这一点,我们需要使用另一个exchange类型——Topic。
    exchange类型为topic时,routing key是一组用"."隔开的词,但仅限255bytes。
    比如:"stock.usd.nyse", "nyse.vmw", "quick.orange.rabbit"


    topic和direct的不同点还有在consumer中定义routing key时我们可以使用通配符,比如:
    符号'*':可以匹配某一个词。
    符号'#':可以匹配0~N个词。

    举个例子说明,假设我们用rounting key描述一个动物。
    格式为: <性格>.<颜色>.<种类>
    用符号'*',我想要得到桔***的动物,即:"*.orange.*"
    用符号'#',我想要得到懒散的动物,即:"lazy.#"
    如果使用过程中有人破坏了格式,即使rounting key为"lazy.orange.male.rabbit"也可以匹配"lazy.#"。


    稍微修改上面的代码,首先定义一个topic exchange。

    
    public  final static String EXCHANGE_NAME = "topic_exchange";
    
    
    temp.exchangeDeclare(EXCHANGE_NAME, ExchangeTypes.TOPIC);
    

    确认定义:

    发送sql相关的log:

    
    public static void main(String[] args) throws IOException {
            Channel channel = ChannelFactory_.getChannel();
            String content = "message #$#$#$#$#$#$";
     
            channel.basicPublish(EXCHANGE_NAME,"warning.sql.connection.close",null,content.getBytes());
            channel.basicPublish(EXCHANGE_NAME,"error.sql.syntax",null,content.getBytes());
     
            ChannelFactory_.closeChannel(channel);
        }
    

    consumer接收所有sql相关的warning和所有error:

    
    public static void main(String[] args) throws IOException, InterruptedException {
            Channel channel = ChannelFactory_.getChannel();
     
            String queueName = channel.queueDeclare().getQueue();
            channel.queueBind(queueName, ChannelFactory_.EXCHANGE_NAME,"warning.sql.#");
            channel.queueBind(queueName, ChannelFactory_.EXCHANGE_NAME,"error.#");
     
            QueueingConsumer consumer = new QueueingConsumer(channel);
            channel.basicConsume(queueName,true,consumer);
            while(true){
                QueueingConsumer.Delivery delivery = consumer.nextDelivery();
                String message = new String(delivery.getBody());
                String routingKey = delivery.getEnvelope().getRoutingKey();
     
                System.out.println(" [x] Received '" + routingKey + "':'" + message + "'");
            }
     
        }
    

    运行结果:

  • 相关阅读:
    面对祖传屎山代码应该采用的5个正确姿势
    一行代码卖出570美元, 天价代码的内幕
    漫画 | 悲催的中国式软件开发
    看看我每天的工作,你们这些程序员都是“辣鸡”!
    漫画 | 浏览器一个比一个“无耻”
    程序员应该造的五大轮子
    我所尊敬的三位女程序员
    重磅!七国首脑会议决定制裁Go语言!
    漫画 | C语言哭了,过年回家,只有我还没对象
    漫画 | CPU战争40年,真正的王者终于现身!
  • 原文地址:https://www.cnblogs.com/kavlez/p/4100128.html
Copyright © 2020-2023  润新知