一、路由模式说明
前边我们学习了发布订阅模式,并且使用了fanout类型的交换机,使得每个消费者都会收到了消息,那么今天我们在此基础上再进一步探索:只接受一部分消息(差异化转发),这便是路由模式了。
那有了Routing模式,我们便可以改善我们的日志系统:根据严重级别作出不同的处理,将debug、info类型的日志打印到控制台,将error类型的日志保存到文件。
二、绑定关系与routingKey
还记得我们在使用发布、订阅模式的时候,使用了fanout交换机,并将队列与交换机进行了绑定:
channel.queueBind(queueName, EXCHANGE_NAME, "");
我们没有解释第三个参数,这个参数其实其实也是一个routingKey,我们在做发布、订阅的时候,将该参数设置为了”“,原因是fanout类型的交换机只会简单的为所有消费者发送消息,它会忽略这个参数!不管设置成什么参数,都没有任何影响。
该参数正好对应发送消息时指定的routingKey,也是之前被我们忽略了的”“,即下面的第二个参数
channel.basicPublish( "logs", "", null, message.getBytes());
fanout类型的交换机只会简单的为所有消费者发送消息,会忽略routingKey。 |
但是当我使用路由模式的时候,这个参数便会起到关键作用:只有对应的绑定routingKey的队列才会接收到消息,原理如下图:
队列Q1只能收到发布的routingKey为orange的消息,队列Q2只能收到发布的routingKey为black、green的消息。
当然,一个routingKey也可以被多个消费者指定,即多重绑定关系:
可以看到Q1、Q2队列都可以接受routingKey为black的发送消息。
三、日志系统改善
有了上面的原理,那么我们可以改善我们的日志系统了:
生产者-EmitLog.java:产生日志
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class EmitLog {
private static final String EXCHANGE_NAME = "logs2";
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
//debug日志
for (int i = 1; i <= 3; i++) {
String message = "debug_message" + i;
channel.basicPublish(EXCHANGE_NAME, "debug", null, message.getBytes());
}
//info日志
for (int i = 1; i <= 3; i++) {
String message = "info_message" + i;
channel.basicPublish(EXCHANGE_NAME, "info", null, message.getBytes());
}
//error日志
for (int i = 1; i <= 3; i++) {
String message = "error_message" + i;
channel.basicPublish(EXCHANGE_NAME, "error", null, message.getBytes());
}
channel.close();
connection.close();
}
}
消费者1-ReceiveLog.java:处理debug类型的日志
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class ReceiveLog1 {
private static final String EXCHANGE_NAME = "logs2";
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
String queueName = channel.queueDeclare().getQueue();
channel.queueBind(queueName, EXCHANGE_NAME, "debug");
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(message);
}
};
channel.basicConsume(queueName,true,consumer);
}
}
消费者2-ReceiveLog2.java:处理info类型的日志
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class ReceiveLog2 {
private static final String EXCHANGE_NAME = "logs2";
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
String queueName = channel.queueDeclare().getQueue();
channel.queueBind(queueName, EXCHANGE_NAME, "info");
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(message);
}
};
channel.basicConsume(queueName,true,consumer);
}
}
消费者3-ReceiveLog3.java:处理error类型的日志
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class ReceiveLog3 {
private static final String EXCHANGE_NAME = "logs2";
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
String queueName = channel.queueDeclare().getQueue();
channel.queueBind(queueName, EXCHANGE_NAME, "error");
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");
FileUtil.save(message);
System.out.println("添加记录到文件!");
}
};
channel.basicConsume(queueName,true,consumer);
}
}
保存文件工具-FileUtil
import java.io.File;
import java.io.FileOutputStream;
public class FileUtil {
public static void save(String content) {
try {
File dir = new File("D:/errors");
if (!dir.exists()) {
dir.mkdirs();
}
FileOutputStream fout = new FileOutputStream(dir +"/"+ content + ".txt");
fout.write(content.getBytes());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
运行结果:
下一篇,我们将来学习,更加个性化的模式:Topics