2、发布订阅模式基础:什么是发布订阅模式、与请求响应模式的区别、核心角色(Publisher、Subscriber、Event)
2.1 先聊聊:为什么需要发布订阅模式?
做车载通信这些年,我经常被问到这样一个问题:
「老张,我车上有几十个ECU,每个都要实时知道车速、转向角、刹车状态。如果都用请求响应,那网络不就炸了吗?」
问得好。你想想看,如果每个需要车速的节点都去问「车速多少?」,那每秒得产生多少请求?
嗯,这就是发布订阅模式诞生的原因。
一句话总结:发布订阅模式,就是「谁生产数据谁发布,谁需要数据谁订阅」。数据生产者不用关心谁在收,数据消费者也不用关心谁在发。
2.2 发布订阅 vs 请求响应:到底差在哪?
我刚开始做SOME/IP时,也经常把这两个搞混。后来在项目中踩过一次坑,才彻底分清楚。
咱们直接看对比:
| 对比维度 | 请求响应模式 | 发布订阅模式 |
|---|---|---|
| 通信方向 | 点对点(Client→Server→Client) | 一对多(Publisher→多个Subscriber) |
| 触发方式 | 由Client主动发起 | 由Publisher主动推送 |
| 数据流向 | 请求→响应,一问一答 | 发布→订阅,持续推送 |
| 典型场景 | 读取某个传感器的当前值 | 实时获取车速、转向角等周期性数据 |
| 网络负载 | 请求多时负载高 | 一次发布,多人接收,效率高 |
| 实时性 | 取决于请求频率 | Publisher主动推送,延迟低 |
说白了,请求响应就像你打电话问朋友「现在几点了?」——每次都要拨号、等待、挂断。而发布订阅就像你订阅了一个天气预报公众号,每天自动推送,不用你主动去问。
我个人习惯:在项目中,如果数据是「状态类」的(比如车速、温度、位置),我优先用发布订阅。如果是「操作类」的(比如打开车门、设置参数),我优先用请求响应。
2.3 三个核心角色:Publisher、Subscriber、Event
发布订阅模式里,只有三个角色。不多不少,刚刚好。
2.3.1 Publisher(发布者)
Publisher就是数据的生产者。它负责:
- 定义Event(事件)
- 在合适的时机发布Event
- 管理Subscriber的订阅关系
我记得有一次做ADAS项目,摄像头模块就是Publisher。它每30毫秒发布一次车道线检测结果。所有需要这个数据的模块——比如车道保持、自适应巡航——都来订阅它。
代码示例(伪代码):
// Publisher端
class LaneDetectionPublisher {
Event laneEvent; // 定义事件
void publishLaneData() {
LaneData data = detectLane();
// 发布事件,所有订阅者都会收到
SOMEIP_Publish(laneEvent, data);
}
}
2.3.2 Subscriber(订阅者)
Subscriber就是数据的消费者。它负责:
- 向Publisher发起订阅请求
- 接收Publisher推送的Event数据
- 处理接收到的数据
这里有个坑,我曾经踩过:
我曾经在项目中遇到:某个Subscriber订阅了Event,但忘记处理数据。结果Publisher一直发,Subscriber一直收,但数据没人用,白白浪费带宽。所以,订阅之前一定要想清楚——你真的需要这个数据吗?
代码示例:
// Subscriber端
class LaneKeepAssist {
void subscribeToLaneData() {
// 订阅车道线事件
SOMEIP_Subscribe(laneEvent, onLaneDataReceived);
}
void onLaneDataReceived(LaneData data) {
// 处理接收到的数据
adjustSteering(data);
}
}
2.3.3 Event(事件)
Event是发布订阅模式的核心载体。它定义了:
- 数据的类型(比如int32、float、结构体)
- 数据的ID(唯一标识)
- 数据的生命周期(比如周期性发布、变化时发布)
在SOME/IP协议里,Event有几种发布方式:
| 发布方式 | 说明 | 适用场景 |
|---|---|---|
| 周期性发布 | 每隔固定时间发布一次 | 车速、转速等周期性信号 |
| 变化时发布 | 数据变化超过阈值时才发布 | 温度、压力等变化不频繁的信号 |
| 事件触发发布 | 特定事件发生时发布 | 故障码、报警信号 |
避坑指南:Event的ID一定要全局唯一。我见过一个项目,两个Publisher用了相同的Event ID,结果Subscriber收到了错误的数据,排查了整整两天。嗯,从那以后,我每次设计Event ID都会画一张全局分配表。
2.4 发布订阅的完整流程
咱们走一遍完整的流程,你就全明白了:
- Publisher启动:先注册自己的Event,告诉网络「我有这些数据可以发布」
- Subscriber发起订阅:Subscriber向Publisher发送订阅请求,说「我要订阅这个Event」
- Publisher确认订阅:Publisher收到请求后,把Subscriber加入订阅列表
- Publisher发布数据:当数据准备好时,Publisher把数据推送给所有订阅者
- Subscriber接收数据:Subscriber收到数据后,调用回调函数处理
- Subscriber取消订阅:当不再需要数据时,Subscriber可以主动取消订阅
这个流程看起来简单,但实际项目中要考虑的东西很多。比如:
- 如果Subscriber订阅时,Publisher还没启动怎么办?
- 如果Publisher挂了,Subscriber怎么知道?
- 如果网络延迟高,数据过期了怎么办?
这些问题,咱们后面的章节会一一解答。今天先把基础打牢。
2.5 小结
发布订阅模式,说白了就是「你发我收,互不干扰」。它和请求响应的最大区别在于:
- 请求响应是「你问我答」,适合操作类场景
- 发布订阅是「你发我收」,适合状态类场景
三个核心角色:
- Publisher:数据生产者,负责发布Event
- Subscriber:数据消费者,负责订阅Event
- Event:数据载体,定义了数据的类型、ID和发布方式
我的建议:刚开始学SOME/IP时,先把这个模式理解透。后面的服务发现、网络绑定、序列化,都是建立在这个基础之上的。地基打不牢,后面盖楼会歪的。
下一章,咱们聊聊SOME/IP的Event是如何定义的,以及怎么在代码里实现一个完整的发布订阅流程。