RabbitMQ系列

来源:互联网 发布:下载软件管理器 编辑:程序博客网 时间:2024/05/10 02:52

本文出自于EumJi个人博客,仅限于学习用途的转载,转载请注明出处http://www.eumji025.com/article/details/258665

在上一篇教程发布和订阅中,我们构建了一个能向消费者广播信息的例子。

在本教程中,我们将为其添加一个功能 - 我们将可以仅订阅一部分消息。例如,我们将能够仅将关键的错误消息引导到日志文件(以节省磁盘空间),同时仍然能够在控制台上打印所有日志消息。

绑定

在之前的例子中,我们已经创建了绑定。绑定是exchanges和queue之间的关系。绑定可以使用额外的参数。为了避免与basic_publish 参数混淆,我们将其称为 routingKey。如下代码

channel.queueBind(queue, EXCHANGE_NAME, "routing_key");

由于之前我们使用的是fanout,fanout会忽略routingKey的存在,与所有的进行匹配。后面我们会出一篇讲解几种exchanges类型差异。

direct exchanges

我们从上上一篇教程[发布和订阅]向所有消费者广播所有消息。我们希望将其扩展为允许基于其严重性过滤消息。例如,我们可能需要一个将error日志消息写入磁盘的程序,而不会把warning或info级别的消息记录上的磁盘空间。

如果使用fanout类型的exchanges,不能给我们很大的灵活性 - 它只能没有选择地广播信息。

我们将使用direct exchanges。direct exchange背后的路由算法很简单 - 消息传递到绑定密钥与消息的路由密钥完全匹配的队列 。如下图所示:

direct exchange

在上述设置中direct类型的交换机通过routingKey为orange的和Q1绑定,routingKey为black,green的绑定Q2,联想我们上面的设定,我们可以把Q1假设为记录错误日志的队列。当然我们也可以使用一个routingKey绑定多个队列这都是可行的,如下图所示

routingKey绑定多个队列

案例展示

发布者

public class RoutingPublish {    private static Logger logger = LoggerFactory.getLogger(RoutingPublish.class);    private static final String EXCHANGE_NAME = "routing_worker";    public static void main(String[] args) throws IOException, TimeoutException {        ConnectionFactory factory = new ConnectionFactory();        factory.setHost("localhost");        Connection connection = null;        Channel channel = null;        try {            connection = factory.newConnection();            channel = connection.createChannel();            //设置为DIRECT            channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);            for (int i = 0; i < 10; i++) {                String message = getMessage();                String severity = getSeverity();                System.out.println("publish worker will send "+severity+" message:"+message);                channel.basicPublish(EXCHANGE_NAME,severity,null,getMessage().getBytes("UTF-8"));            }        } catch (IOException e) {            logger.error("connect io exception");            e.printStackTrace();        } catch (TimeoutException e) {            logger.error("connect timeout exception");            e.printStackTrace();        }finally {            channel.close();            connection.close();        }    }    //时间戳模拟消息    private static String getMessage() {        try {            Thread.sleep(1000);        } catch (InterruptedException e) {            e.printStackTrace();        }        return String.valueOf(System.currentTimeMillis());    }    //模拟安全类型,随机    private static String getSeverity() {        int num = new Random().nextInt(3);        switch (num){            case 0:return "info";            case 1:return "warning";            case 2:return "error";        }        return null;    }

和之前的推送差不多,只是我们在basicPublish时绑定了具体的routingKey。

订阅者

public class RoutingReceive extends Thread{    private static Logger logger = LoggerFactory.getLogger(RoutingReceive.class);    private static final String EXCHANGE_NAME = "routing_worker";    private String workerName;    private String security;    public RoutingReceive(String workerName,String security){        this.security = security;        this.workerName = workerName;    }    @Override    public void run() {        ConnectionFactory factory = new ConnectionFactory();        factory.setHost("localhost");        Connection connection;        Channel channel;        try {            connection = factory.newConnection();            channel = connection.createChannel();            channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);            String queue = channel.queueDeclare().getQueue();            channel.queueBind(queue, EXCHANGE_NAME, security);            System.out.println("receive worker wait  message!!!");            Consumer consumer = new DefaultConsumer(channel) {                @Override                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {                    super.handleDelivery(consumerTag, envelope, properties, body);                    String message = new String(body, 0, body.length, "UTF-8");                    System.out.println(workerName+" receive "+security+" message :" + message);                }            };            channel.basicConsume(queue, true, consumer);        } catch (IOException e) {            e.printStackTrace();        } catch (TimeoutException e) {            e.printStackTrace();        }    }  //模拟三个线程,分别接受不同类型的消息。    public static void main(String[] args) {        RoutingReceive error = new RoutingReceive("error_worker","error");        error.start();        RoutingReceive info = new RoutingReceive("info_worker","info");        info.start();        RoutingReceive warning = new RoutingReceive("warning_worker","warning");        warning.start();    }}

测试结果

首先运行发布者routingPublish,随机出现三种类型信息,具体如下图

publish worker will send info message:1495015315377publish worker will send error message:1495015317379publish worker will send warning message:1495015319381...

然后我们运行订阅者,如下图

info_worker receive info message :1495015401669info_worker receive info message :1495015403672warning_worker receive warning message :1495015405673warning_worker receive warning message :1495015407674warning_worker receive warning message :1495015409676error_worker receive error message :1495015411677...

结语

从上面的演示中我们可以将消息分类进行推送,就可以使用专门的线程接受error信息并将其记录。
与发布订阅的fanout主题相比,direct模式则限制则更大,routingkey使得exchange和queue的绑定非常 的细腻。
本文主要参照了RabbitMQ的官方教程并进行修改而成。

与君共勉!!!

参考文章

RabbitMQ-direct-java

源码地址

RabbitMQ-routing

原创粉丝点击