RabbitMQ(四):Exchange交换器–direct

RabbitMQ(四):Exchange交换器–direct

内容翻译自:RabbitMQ Tutorials Java版


RabbitMQ(一):Hello World程序

RabbitMQ(二):Work Queues、循环分发、消息确认、持久化、公平分发

RabbitMQ(三):Exchange交换器–fanout

RabbitMQ(四):Exchange交换器–direct

RabbitMQ(五):Exchange交换器–topic

RabbitMQ(六):回调队列callback queue、关联标识correlation id、实现简单的RPC系统

RabbitMQ(七):常用方法说明 与 学习小结


Routing:

在上一篇博客中,我们创建了一个简单的日志系统。我们可以将日志消息广播给所有的接收者(消费者)。

在这个教程中,我们将为我们的日志系统添加一个功能:仅仅订阅一部分消息。比如,我们可以直接将关键的错误类型日志消息保存到日志文件中,还可以同时将所有的日志消息打印到控制台。


绑定(Bindings):

在之前的例子中,我们已经创建了绑定:

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

一个绑定是建立在一个队列和一个路由器之间的关系,可以解读为:该队列对这个路由器中的消息感兴趣。

绑定可以设置另外的参数:路由键routingKey。为了避免和void basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)中的routingKey混淆,我们将这里的key称为绑定键binding key,下面的代码展示了如何使用绑定键来创建一个绑定关系:

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

绑定键的含义取决于路由器的类型,我们之前使用的fanout类型路由器会忽略该值。


直接路由器 (Direct Exchange):

我们之前的日志系统会将所有消息广播给所有消费者。现在我们想根据日志的严重程度来过滤日志。比如,我们想要一个程序来将error日志写到磁盘文件中,而不要将warninginfo日志写到磁盘中,以免浪费磁盘空间。

我们之前使用的fanout路由器缺少灵活性,它只是没头脑地广播消息。所以,我们用direct路由器来替换它。direct路由器背后的路由算法很简单:只有当消息的路由键routing key与队列的绑定键binding key完全匹配时,该消息才会进入该队列。

为了演示上面拗口的表述中的意思,考虑下面的设置:

RabbitMQ(四):Exchange交换器--direct

上图中,直接路由器X与两个队列绑定。第一个队列以绑定键orange来绑定,第二个队列以两个绑定键blackgreen和路由器绑定。

按照这种设置,路由键为orange的消息在发布给路由器后,将会被路由到队列Q1,路由键为black或者green的消息将会路由到队列Q2。


多重绑定(Multiple bindings):

RabbitMQ(四):Exchange交换器--direct

多个队列以相同的绑定键binding key绑定到同一个Exchange上,是完全可以的。按照这种方式设置的话,直接路由器就会像fanout路由器一样,将消息广播给所有符合路由规则的队列。一个路由键为black的消息将会发布到队列Q1和Q2。


发布消息:

在这个教程中,我们使用direct路由器来代替上个教程中的fanout路由器。同时,我们为日志设置严重级别,并将此作为路由键。这样,接收者(消费者)就可以选择性地接收日志消息。

首先,创建一个路由器:

channel.exchangeDeclare(EXCHANGE_NAME, "direct");

接着,发送一个消息:

channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes());

简单起见,我们假设severity只能是infowarningerror中的一种。


消息订阅:

接收消息将会和之前的教程类似,只是我们会为每一个级别的消息来创建不同的绑定:

String queueName = channel.queueDeclare().getQueue();
for(String severity : argv){
  channel.queueBind(queueName, EXCHANGE_NAME, severity);
}

放在一块:

RabbitMQ(四):Exchange交换器--direct

生产者EmitLogDirect.java的完整代码:

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

public class EmitLogDirect {

    private static final String EXCHANGE_NAME = "direct_logs";

    public static void main(String[] argv) throws Exception {
        //创建连接
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        //声明路由器和路由器的类型
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
        
        String severity = "info";
        String message = ".........i am msg.........";

        //发布消息
        channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes("UTF-8"));
        System.out.println(" [x] Sent '" + severity + "':'" + message + "'");

        channel.close();
        connection.close();
    }
    
}

消费者ReceiveLogsDirect.java的完整代码如下:

import com.rabbitmq.client.*;
import java.io.IOException;

public class ReceiveLogsDirect {

    private static final String EXCHANGE_NAME = "direct_logs";

    public static void main(String[] argv) throws Exception {
        //建立连接和通道
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        //声明路由器和类型
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
        //声明队列
        String queueName = channel.queueDeclare().getQueue();
        //定义要监听的级别
        String[] severities = {"info", "warning", "error"};
        //根据绑定键绑定
        for (String severity : severities) {
            channel.queueBind(queueName, EXCHANGE_NAME, severity);
        }
        System.out.println(" [*] Waiting for messages. To exit press CTRL+C");

        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                                       AMQP.BasicProperties properties, byte[] body) throws IOException {
                String message = new String(body, "UTF-8");
                System.out.println(" [x] Received '" + envelope.getRoutingKey() + "':'" + message + "'");
            }
        };
        channel.basicConsume(queueName, true, consumer);
    }
}

现在可以进行测试了。首先,启动一个消费者实例(ReceiveLogsDirect.java),然后将其中的要监听的级别改为String[] severities = {"error"};,再启动另一个消费者实例。此时,这两个消费者都开始监听了,一个监听所有级别的日志消息,另一个监听error日志消息。
然后,启动生产者(EmitLogDirect.java),之后将String severity = "info";中的info,分别改为warningerror后运行。
这样,就可以在控制台看到如下输出:

//生产者
[x] Sent 'warning':'.........i am msg.........'
[x] Sent 'info':'.........i am msg.........'
[x] Sent 'error':'.........i am msg.........'
//消费者1
 [*] Waiting for messages. To exit press CTRL+C
 [x] Received 'info':'.........i am msg.........'
 [x] Received 'error':'.........i am msg.........'
 [x] Received 'warning':'.........i am msg.........'
//消费者2
 [*] Waiting for messages. To exit press CTRL+C
 [x] Received 'error':'.........i am msg.........'

 


说明:

①与原文略有出入,如有疑问,请参阅原文

②原文均是编译后通过javacp命令直接运行程序,我是在IDE中进行的,相应的操作做了修改。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/114606.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • vs2013安装失败解决办法_dns解析失败怎么解决

    vs2013安装失败解决办法_dns解析失败怎么解决个人解决方案:在控制面板-》软件卸载中将VisualC++2012redistributable、VisualC++2010redistributable相关的编程语言的东西卸载后,重启再安装就可以了。有个问题就是:在重启的过程中发现win10关机和启动花了很长时间,可能因为我卸载了上面这些组件导致其需要更新。关于VisualC++2012redistributable是什么,

    2022年8月12日
    3
  • linux发邮件2种常见客户端命令[通俗易懂]

    linux发邮件2种常见客户端命令[通俗易懂]mail命令格式:mail-s”标题”邮件地址<文件echo”正文”|mail-s”标题”邮件地址例如:mail-s”liwen”1935845114@qq.com<hostsmutt命令mutt-s”标题”邮件地址<文件命令发邮件需要邮件服务的支持mail默认是调用本机MTA发送邮件的,这意味着需要在本机上安装sendmail或postfixCentos5默认使用Sendmail邮件服务,开启方式/etc/init.d…

    2022年10月20日
    0
  • 零阶矩、一阶矩、二阶矩、三阶矩

    参考:Moment(mathematics)-Wikipedia此处截取其中的一段:Inmathematics,amomentisaspecificquantitativemeasure,usedinbothmechanicsandstatistics,oftheshapeofasetofpoints.Ifthepointsrepre

    2022年4月3日
    92
  • Prometheus(普罗米修斯)监控系统「建议收藏」

    Prometheus(普罗米修斯)监控系统「建议收藏」Prometheus(普罗米修斯)是一套开源的监控&报警&时间序列数据库的组合,由SoundCloud公司开发。Prometheus基本原理是通过HTTP协议周期性抓取被监控组件的状态,这样做的好处是任意组件只要提供HTTP接口就可以接入监控系统,不需要任何SDK或者其他的集成过程。这样做非常适合虚拟化环境比如VM或者Docker。Prometheus应该是为数不多的适合Docker、Mesos、Kubernetes环境的监控系统之一。…

    2022年7月19日
    54
  • 移动端app开发问题及理解

    移动端app开发问题及理解框架使用的是vant跟web端不太一样的是事件有点不同web端事件有onload页面中所有标签,文档,图片等资源加载完毕后触发onclick鼠标单击事件ondbclick鼠标双击事件onmousedown鼠标按下onmouseup鼠标按键抬起onmouseover鼠标进入某个元素onmousemove鼠标移动到元素onmouseout鼠标离开元素onfocus元素得到焦点onblur元素失去焦点onchange用户改变文本域内容oninput实时监听输入框

    2022年6月15日
    27
  • Java对象数组

    Java对象数组所谓的对象数组,就是指包含了一组相关的对象,但是在对象数组的使用中一定要清楚一点:数组一定要先开辟空间,但是因为其是引用数据类型,所以数组里面的每一个对象都是null值,则在使用的时候数组中的每一个对象必须分别进行实例化操作。 对象数组的声明先定义,再开辟空间类名称对象数组名[]=null;对象数组名=new类名称[长度]; 定义并开辟数组类名称对象数…

    2022年5月5日
    43

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号