4. 服务发现基本流程:Find、Offer、Subscribe 与 Stop
好,咱们今天来聊聊服务发现的核心流程。说实话,SOME/IP 的整个服务发现机制,说白了就是四个动作:找服务、发服务、订阅服务、停止服务。我当年刚接触 SOME/IP 时,总觉得这玩意儿很玄乎,后来在项目里调了几个月 bug,才真正摸透它的脾气。
4.1 Find Service:客户端在「找」
客户端想用某个服务,但它不知道服务在哪儿。怎么办?发一个 Find Service 报文出去。
这个报文很简单,就告诉网络:「谁提供 Camera 服务?请回我。」
我个人习惯把 Find 报文理解成「广播寻人启事」。它不携带具体数据,只包含:
- Service ID:要找哪个服务
- Instance ID:要找哪个实例
- Major Version:主版本号(必须匹配)
- Minor Version:次版本号(可选匹配)
关键点:Find 报文是 UDP 多播 发送的。目标地址通常是 224.0.0.1 或配置的多播组。端口固定为 SD 端口(默认 30490)。
我在项目中遇到过一个问题:客户端发了 Find 报文,但服务端没收到。查了半天,发现是交换机没配置 IGMP Snooping,多播包被丢了。嗯,这种坑踩过一次就记住了。
4.2 Offer Service:服务端在「应」
服务端收到 Find 报文后,如果自己正好提供这个服务,就会回复一个 Offer Service 报文。
Offer 报文比 Find 丰富多了。它告诉客户端:
- 「我在这儿」—— 提供 IP 地址和端口
- 「我提供这些」—— 服务 ID、实例 ID、版本号
- 「我的配置」—— TTL(生存时间)、Major Version、Minor Version
你想想看,Offer 报文其实是在做「服务宣告」。它不光响应 Find,还会 主动定期发送。为什么?因为网络里可能有新上电的客户端,它们没发 Find,但需要知道服务存在。
我的建议:Offer 报文的发送间隔不要设得太短。我见过有人设成 100ms,结果网络被刷爆了。一般 2-5 秒一次就够了。如果网络环境差,可以适当缩短,但别低于 1 秒。
4.3 Subscribe / Subscribe ACK:建立连接
客户端找到服务后,想用它的 Event 或 Method。这时候需要 Subscribe 报文。
Subscribe 报文里包含:
- 要订阅的 Event Group ID(事件组)
- 客户端的 Endpoint(IP + 端口)
- 期望的 TTL
服务端收到 Subscribe 后,如果同意,就回复 Subscribe ACK。如果拒绝,回复 Subscribe NACK。
这里有个容易踩的坑:Subscribe ACK 不是必须的。有些实现里,服务端直接开始发 Event 数据,就算隐式同意了。但我个人习惯,一定要等 ACK 再开始收数据。为什么?因为有一次我在项目里没等 ACK,结果服务端根本没准备好,我这边收了一堆乱数据。
注意:Subscribe 报文里带的 TTL 很重要。如果客户端挂了,服务端会在 TTL 超时后自动清理订阅关系。我曾经见过一个 bug:客户端 TTL 设了 0,结果服务端以为客户端不想订阅,直接拒绝了。嗯,这种低级错误,代码审查时一定要盯紧。
4.4 Stop Offer / Stop Subscribe:优雅地结束
服务端要下线了,或者客户端不想用了,怎么办?发 Stop Offer 或 Stop Subscribe。
Stop Offer:服务端告诉所有客户端:「我不再提供这个服务了。」
- 报文内容跟 Offer 类似,但 Entry Type 不同
- 收到 Stop Offer 后,客户端必须停止使用该服务
Stop Subscribe:客户端告诉服务端:「我不再订阅这个事件组了。」
- 服务端收到后,停止发送该事件组的 Event
- 如果客户端没发 Stop Subscribe 就下线了,服务端靠 TTL 超时来清理
我记得有一次调试,服务端发了 Stop Offer,但客户端没收到。结果客户端一直等 Event,等不到就报错。后来发现是 Stop Offer 报文被防火墙拦截了。所以,Stop 报文一定要用可靠传输,或者配合 TTL 超时机制做兜底。
4.5 完整流程示例
咱们用一个实际例子串一下:
1. 客户端 A 上电
2. 客户端 A 发送 Find Service (Service ID=0x1234)
3. 服务端 B 收到后,回复 Offer Service (IP=192.168.1.10, Port=5000)
4. 客户端 A 收到 Offer,发送 Subscribe (Event Group ID=1)
5. 服务端 B 同意,回复 Subscribe ACK
6. 双方开始正常通信(Event / Method)
7. 客户端 A 要关机,发送 Stop Subscribe
8. 服务端 B 收到后,停止发送 Event
9. 服务端 B 要升级,发送 Stop Offer
10. 客户端 A 收到后,标记服务不可用
核心要点:整个流程是 异步 的。报文可能丢失、乱序、重复。所以每个节点都要有超时重传机制。我建议 Find 报文重传 3 次,间隔 500ms。Subscribe 重传 2 次,间隔 1 秒。
4.6 避坑指南
最后,分享几个我踩过的坑:
- 版本号不匹配:客户端 Find 时带了 Major Version=2,服务端只支持 Version=1。结果服务端不回复 Offer。客户端一直等,超时后报错。解决方案:客户端要兼容多个版本,或者服务端在 Offer 里明确告知支持的版本。
- TTL 设得太短:我见过有人把 TTL 设成 1 秒。结果网络稍微抖动一下,订阅就断了。建议 TTL 至少 5 秒,配合心跳机制。
- 多播地址冲突:两个不同的服务用了同一个多播组。结果客户端收到 Offer 后,发现 IP 不对。嗯,这种问题排查起来很头疼。建议每个服务用独立的多播组,或者用端口区分。
好了,服务发现的基本流程就这些。说白了就是「找-应-订-停」四个步骤。下节课咱们聊聊更高级的话题:服务发现中的状态机与超时处理。