第二章:核心概念与协议——消息队列、发布-订阅与TCP/UDP
各位同学,欢迎来到第二章。上一章我们聊了实时数据通道的整体轮廓,今天咱们把地基打牢。说白了,这一章讲的就是「数据怎么在通道里流动」这件事。我做了这么多年实时系统,发现很多问题都出在基础概念没吃透上。嗯,咱们一个一个来。
2.1 消息队列:Kafka vs RabbitMQ
消息队列是什么?你可以把它想象成一个「邮局」。生产者把信投进去,消费者从信箱里取信。但不同的邮局,处理方式天差地别。
2.1.1 Kafka——为吞吐而生
Kafka 是我个人最常用的消息队列。它设计的初衷就是「快」。有多快?单机每秒能处理几十万条消息。我在做金融行情系统时,就用 Kafka 扛过每秒 50 万笔的订单流。
Kafka 的核心是「日志」——消息被追加到分区里,消费者自己维护读取位置。这种设计让 Kafka 特别适合做数据管道和日志收集。
- 消息持久化到磁盘,不怕丢
- 支持消息回溯重放
- 天然支持分区和副本
- 吞吐量极高,延迟中等(几毫秒到几十毫秒)
2.1.2 RabbitMQ——为可靠而生
RabbitMQ 走的是另一条路。它更强调「消息一定要送到」。我记得有一次做电商订单系统,要求每笔订单都不能丢,哪怕系统重启也要保证最终送达。这时候 RabbitMQ 的「确认机制」就派上用场了。
RabbitMQ 基于 AMQP 协议,消息路由非常灵活。你可以用交换机、队列、绑定键玩出各种花样。但代价是吞吐量不如 Kafka,单机大概每秒几万条。
- 需要高吞吐、日志收集、数据管道 → 选 Kafka
- 需要可靠投递、复杂路由、任务队列 → 选 RabbitMQ
- 两者都想要?可以组合使用,我就在一个项目里用过 Kafka 做数据管道,RabbitMQ 做业务消息
2.2 发布-订阅模式
发布-订阅模式,简称 Pub/Sub。说白了就是「一对多」的消息分发。生产者发布一条消息,所有订阅者都能收到。
你想想看,这和传统的点对点模式有什么区别?点对点是一条消息只能被一个消费者消费,像排队打饭。而 Pub/Sub 就像广播电台——你播节目,所有收音机都能收到。
Kafka 的消费者组就是 Pub/Sub 的典型实现。同一个组内的消费者竞争消费(点对点),不同组的消费者各自消费(发布-订阅)。
2.3 生产者-消费者模型
这个模型是消息队列的基石。生产者只管发消息,消费者只管收消息,两者完全解耦。好处很明显:
- 异步处理:生产者不用等消费者处理完
- 削峰填谷:流量高峰时,消息先堆积在队列里,消费者慢慢消化
- 系统解耦:生产者和消费者可以独立部署、独立升级
我在做实时风控系统时,就用这个模型处理过每秒 10 万次的请求。生产者把请求扔进 Kafka,多个消费者并行处理。如果不用消息队列,直接调接口,系统早就被冲垮了。
下面是一个简单的生产者代码示例(Kafka Java 客户端):
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
Producer<String, String> producer = new KafkaProducer<>(props);
producer.send(new ProducerRecord<>("my-topic", "key", "hello world"));
producer.close();
消费者代码也很简单:
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("group.id", "my-group");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Arrays.asList("my-topic"));
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
for (ConsumerRecord<String, String> record : records) {
System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value());
}
}
2.4 TCP vs UDP:协议选择
聊完消息队列,咱们得说说底层传输协议。TCP 和 UDP 的选择,直接影响实时通道的性能和可靠性。
| 特性 | TCP | UDP |
|---|---|---|
| 连接方式 | 面向连接(三次握手) | 无连接 |
| 可靠性 | 保证送达,自动重传 | 不保证送达,可能丢包 |
| 顺序性 | 保证数据顺序 | 不保证顺序 |
| 延迟 | 较高(重传、拥塞控制) | 极低 |
| 适用场景 | 消息队列、HTTP、文件传输 | 实时音视频、游戏、监控 |
Kafka 和 RabbitMQ 都基于 TCP。为什么?因为消息队列必须保证消息不丢、不乱序。你想想看,如果订单消息丢了,那可就出大事了。
但有些场景必须用 UDP。比如实时音视频通话,丢几帧画面没关系,但延迟高了就卡顿。我做过一个低延迟行情推送系统,就是用 UDP 把延迟压到了 1 毫秒以内。TCP 在这种场景下根本做不到。
2.5 知识体系总览
下面这张图,是我画的本章节知识结构。你可以把它当作一张地图,随时回来查阅。
这张图把本章的核心概念串起来了。从实时数据传输通道出发,往下分消息队列、发布-订阅、生产者-消费者,再到底层传输协议 TCP/UDP,最后落到具体应用场景。你可以在学习后续章节时,随时回来对照这张图。
- 消息队列是实时通道的「血管」,Kafka 重吞吐,RabbitMQ 重可靠
- 发布-订阅模式实现了一对多消息分发,是实时系统的核心模式
- 生产者-消费者模型让系统解耦,能应对流量冲击
- TCP 保证可靠但延迟高,UDP 延迟低但可能丢包——根据场景选
好了,这一章的内容就到这里。概念虽然多,但都是后面实战的基础。下一章我们会动手搭建第一个实时数据通道,到时候这些概念都会用上。