发布订阅模式
这一篇主要是java 客户端使用rabbitMQ发布和订阅消息。
前面几篇讲的是将一个消息只发给一个worker,这里讲的是将一个消息同时发给多个订阅者,也就是发布/订阅模式。
为了阐明这种模式,这里将创建一个简单的日志系统,这个系统由2部分组成:第一部分,发送日志消息;第二部分接收和打印日志消息。
在这个日志系统中,复制了一份receiver,一个接收者用来把收到的消息存储到硬盘中,另一个用来把消息展示到屏幕上。
在前面的文章中,我们通过队列发送和接收消息,现在将介绍一种新的消息模式。
Exchanges (交换机)
这种模式的核心思想是:生产者从不直接发送消息到任意一个队列中,也不知道消息将被发送到哪个队列中,而是把消息发送到交换机中,
交换机 是一个简单的东西,它一方面从生产者这里接收消息,另一方面将收到的消息发送到队列中。但交换机必须知道怎么处理它收到的消息,是放到指定的队列中?放到很多个队列中?还是应该丢弃?这个规则,由exchange type定义。
一些可用的交换机模型:direct, topic, match,trace,headers 和fanout。
默认使用的direct。
在前面的文章中,我们不知道交换机的存在,仍然能给发送消息到队列中,是因为使用了默认的交换机
channel.basicPublish("", "hello", null, message.getBytes());
第一个参数“”就是交换机的名字
channel.exchangeDeclare("logs", "fanout");
上面声明了交换机的名字和类型。
fanout是把它收到的消息广播给所有的它知道的队列(绑定到这个交换机的队列)。这也是这个日志系统所用到的。
String queueName = channel.queueDeclare().getQueue();
这会创建一个非持久的,唯一的,自动删除的队列。
如果没有消费者接收消息,消息会消失。
生产者代码:
public static void main(String[] args) {
try(Connection connection=getConnection("guest","guest","localhost",5672,"/");
Channel channel=connection.createChannel()) {
channel.exchangeDeclare(args[0],"fanout");
String queueName= channel.queueDeclare().getQueue();
System.out.println(queueName);
String message=getMessage(new String[]{"a","b","c"});
// channel.queueBind(queueName,args[0],"");
int i=0;
while (true){
Thread.sleep(1000);
channel.basicPublish(args[0],queueName, MessageProperties.PERSISTENT_TEXT_PLAIN,(i+++message).getBytes("utf-8"));
}
// Thread.sleep(2000);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
消费者代码:
public static void main(String[] args) {
try(Connection connection=BaseTest2.getConnection("guest","guest","localhost",5672,"/");
Channel channel=connection.createChannel()) {
channel.exchangeDeclare(args[0],"fanout");
// channel.queueDeclare(BaseTest2.queueName,true,false,false,null);
String queueName= channel.queueDeclare().getQueue();
channel.queueBind(queueName,args[0],"");
System.out.println(queueName);
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");
try {
doWork(message);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(message+" [x] Done");
}
channel.basicAck(envelope.getDeliveryTag(),false);
}
};
channel.basicConsume(queueName,false, consumer);
Thread.sleep(200000);
} catch (Exception e) {
throw new RuntimeException(e);
}
}