灵活路由的主题交换器
掌握使用通配符匹配实现复杂路由模式的主题交换器,设计灵活且可扩展的消息路由拓扑。
灵活路由的主题交换器 是 CoddyKit 上的免费 RabbitMQ Messaging & Async Systems 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 RabbitMQ Messaging & Async Systems 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 RabbitMQ Messaging & Async Systems 课程共包含 4 节课。
本课时的部分内容尚未翻译,以英文显示。
Topic Exchange: Flexible Routing
Welcome to the Topic Exchange! This exchange type offers the most flexible message routing, letting you send messages to queues based on complex patterns.
Unlike the simple Fanout or direct-match Direct exchange, Topic exchanges use special wildcards in routing keys to match messages dynamically.
Understanding Topic Routing Keys
With a Topic exchange, routing keys aren't exact matches. They are strings made of words separated by dots (.), much like parts of a filename or URL.
- Example:
animal.rabbit.fast - Example:
log.error.database
Each word provides a level of detail, allowing for hierarchical routing.
The Single-Word Wildcard: `*`
The asterisk (*) wildcard matches exactly one word in a routing key segment.
- Binding key:
animal.*.fastmatchesanimal.rabbit.fast - Binding key:
animal.*.fastdoes not matchanimal.dog.lazy.fast(too many words) - Binding key:
*.error.*matcheslog.error.database
It's great for matching a specific position in the key.
The Zero-or-More Wildcard: `#`
The hash (#) wildcard matches zero or more words in a routing key. It's much more powerful than *.
- Binding key:
log.#matcheslog.error,log.info.web,log.debug.cache.entry - Binding key:
animal.#matchesanimal.rabbit,animal.cat.sleepy
Use # to capture all messages that start with a certain pattern.
Producer: Declaring Topic Exchange
First, our producer needs to declare the Topic exchange. The type is simply "topic".
Try running this snippet to set up the exchange:
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class TopicProducerSetup {
private static final String EXCHANGE_NAME = "topic_logs";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost"); // Connect to local RabbitMQ
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) {
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
System.out.println("Topic exchange '" + EXCHANGE_NAME + "' declared.");
}
}
}Producer: Sending Messages
Now, let's send some messages using different routing keys. The exchange will use these keys to decide which queues receive the messages.
Notice how the keys are dot-separated, allowing for detailed categories.
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.nio.charset.StandardCharsets;
public class TopicMessageSender {
private static final String EXCHANGE_NAME = "topic_logs";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) {
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
sendMessage(channel, "animal.rabbit.fast", "A fast rabbit runs.");
sendMessage(channel, "animal.cat.sleepy", "A sleepy cat naps.");
sendMessage(channel, "log.error.db", "Database connection failed!");
sendMessage(channel, "log.info.web", "User logged in.");
System.out.println("Sent various topic messages.");
}
}
private static void sendMessage(Channel channel, String routingKey, String message) throws Exception {
channel.basicPublish(EXCHANGE_NAME, routingKey, null, message.getBytes(StandardCharsets.UTF_8));
System.out.println(" [x] Sent '" + routingKey + ":'" + message + "'");
}
}Consumer: Binding with `*`
Consumers bind their queues to the Topic exchange using binding keys that can contain wildcards. This consumer uses *.orange.*.
It will receive messages like animal.orange.fast but not fruit.apple.sweet or animal.orange.
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DeliverCallback;
public class TopicConsumerOrange {
private static final String EXCHANGE_NAME = "topic_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, "topic");
String queueName = channel.queueDeclare().getQueue();
String bindingKey = "*.orange.*"; // Matches e.g., 'animal.orange.fast'
channel.queueBind(queueName, EXCHANGE_NAME, bindingKey);
System.out.println(" [x] Waiting for messages matching: '" + bindingKey + "'");
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), "UTF-8");
System.out.println(" [x] Received '" + delivery.getEnvelope().getRoutingKey() + ":'" + message + "'");
};
channel.basicConsume(queueName, true, deliverCallback, consumerTag -> {});
}
}Consumer: Binding with `#`
This consumer uses the log.# binding key. This means it will receive all messages whose routing key starts with log., regardless of how many words follow.
This is extremely useful for systems like logging, where you might want a 'catch-all' listener for a category.
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DeliverCallback;
public class TopicConsumerLogs {
private static final String EXCHANGE_NAME = "topic_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, "topic");
String queueName = channel.queueDeclare().getQueue();
String bindingKey = "log.#"; // Matches e.g., 'log.error.db', 'log.info.web'
channel.queueBind(queueName, EXCHANGE_NAME, bindingKey);
System.out.println(" [x] Waiting for messages matching: '" + bindingKey + "'");
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), "UTF-8");
System.out.println(" [x] Received '" + delivery.getEnvelope().getRoutingKey() + ":'" + message + "'");
};
channel.basicConsume(queueName, true, deliverCallback, consumerTag -> {});
}
}Topic Exchange Use Cases
Topic exchanges shine in scenarios requiring flexible and hierarchical routing:
- Logging Systems: Route logs based on severity, source, and component (e.g.,
app.error.auth,app.info.payment). - Real-time Data Streams: Filter sensor data (e.g.,
sensor.room1.temp,sensor.room2.humidity). - Content Distribution: Deliver news articles to subscribers interested in specific categories or regions.
They provide fine-grained control over message flow.
Topic Routing Challenge
Consider the following messages and binding keys. Which messages will be delivered to a queue bound with the key animal.*.#.fast?
Recap: Topic Exchange Power
You've mastered the Topic exchange! It's an incredibly powerful tool for building flexible and scalable messaging architectures.
- Uses dot-separated routing keys.
*matches exactly one word.#matches zero or more words.- Ideal for logging, real-time analytics, and content filtering.
By using wildcards, you can create sophisticated routing rules to ensure messages go exactly where they're needed.
常见问题解答
「灵活路由的主题交换器」课时是免费的吗?
是的 — 「灵活路由的主题交换器」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 RabbitMQ Messaging & Async Systems 课程的其余内容,请升级到 CoddyKit PRO。 RabbitMQ Messaging & Async Systems 课程共包含 4 节课。
「灵活路由的主题交换器」这节课中我会学到什么?
掌握使用通配符匹配实现复杂路由模式的主题交换器,设计灵活且可扩展的消息路由拓扑。 你通过在浏览器中直接运行的动手代码来练习 RabbitMQ Messaging & Async Systems,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 RabbitMQ Messaging & Async Systems 需要有经验吗?
无需任何先前经验。CoddyKit 上的 RabbitMQ Messaging & Async Systems 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「灵活路由的主题交换器」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 RabbitMQ Messaging & Async Systems 课中编写并运行代码吗?
能。每节 RabbitMQ Messaging & Async Systems 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 用于发布/订阅的扇出交换器
- 用于路由的直连交换器
- 灵活路由的主题交换器
- 默认交换器与隐式绑定