本文目录导读:

- 基于消息中间件(MQ)的异步通知(最常用)
- 长轮询(Long Polling)(如Nacos、Apollo)
- WebSocket/Socket.io 实时推送
- ZooKeeper/Etcd 的Watcher机制
- HTTP/2 Server Push + SSE(Server-Sent Events)
- gRPC Stream(双向流)
- 技术选型对比
- 最佳实践建议
- 注意事项
在Java分布式系统中,数据发布API(如配置中心、元数据管理、状态同步等)需要通知客户端时,通常采用以下几种核心方案:
基于消息中间件(MQ)的异步通知(最常用)
实现原理
- 数据发布方将变更事件发送到消息队列(Kafka/RocketMQ/RabbitMQ)
- 客户端订阅对应主题,消费变更消息
代码示例(Spring Boot + Kafka)
// 发布方
@Service
public class DataPublisher {
@Autowired
private KafkaTemplate<String, DataChangeEvent> kafkaTemplate;
public void publishDataChange(String dataId, Object newData) {
DataChangeEvent event = new DataChangeEvent(dataId, newData);
kafkaTemplate.send("data-change-topic", dataId, event);
}
}
// 订阅方
@Component
public class DataChangeConsumer {
@KafkaListener(topics = "data-change-topic", groupId = "data-consumer-group")
public void handleDataChange(DataChangeEvent event) {
// 更新本地缓存或重启WebSocket连接等
updateLocalCache(event.getDataId(), event.getData());
}
}
长轮询(Long Polling)(如Nacos、Apollo)
实现原理
- 客户端发起HTTP长连接请求,服务端挂起请求直到有数据变更或超时
- 变更发生时立即返回新数据
简单实现示例
// 服务端
@RestController
public class ConfigController {
private final Map<String, String> configStore = new ConcurrentHashMap<>();
private final Map<String, List<DeferredResult<String>>> pendingRequests = new ConcurrentHashMap<>();
@GetMapping("/config/{key}")
public DeferredResult<String> longPoll(@PathVariable String key) {
DeferredResult<String> result = new DeferredResult<>(30000L, "timeout");
pendingRequests.computeIfAbsent(key, k -> new CopyOnWriteArrayList<>()).add(result);
return result;
}
public void updateConfig(String key, String value) {
configStore.put(key, value);
List<DeferredResult<String>> results = pendingRequests.remove(key);
if (results != null) {
results.forEach(r -> r.setResult(value));
}
}
}
WebSocket/Socket.io 实时推送
实现原理
- 建立TCP长连接,服务端主动推送变更数据
使用Spring WebSocket
// 服务端
@Component
public class DataWebSocketHandler extends TextWebSocketHandler {
private static final Set<WebSocketSession> sessions = ConcurrentHashMap.newKeySet();
@Override
public void afterConnectionEstablished(WebSocketSession session) {
sessions.add(session);
}
public void broadcastChange(String dataId, String data) {
JSONObject message = new JSONObject();
message.put("type", "data_change");
message.put("dataId", dataId);
message.put("data", data);
sessions.forEach(session -> {
try {
session.sendMessage(new TextMessage(message.toJSONString()));
} catch (IOException e) {
// handle exception
}
});
}
}
ZooKeeper/Etcd 的Watcher机制
实现原理
- 客户端监听特定节点,节点数据变化时Zookeeper/Etcd主动推送通知
Zookeeper示例
// 客户端
public class ConfigWatcher implements Watcher {
private ZooKeeper zk;
public void watchNode(String path) throws Exception {
zk.getData(path, this, null);
}
@Override
public void process(WatchedEvent event) {
if (event.getType() == Event.EventType.NodeDataChanged) {
// 重新获取数据
getDataAndUpdateCache(event.getPath());
// 重新注册监听
try {
zk.getData(event.getPath(), this, null);
} catch (Exception e) {
// handle
}
}
}
}
HTTP/2 Server Push + SSE(Server-Sent Events)
SSE实现示例
@RestController
public class SSEPublisher {
private final SseEmitter emitter = new SseEmitter(Long.MAX_VALUE);
@GetMapping("/events")
public SseEmitter stream() {
return emitter;
}
public void notifyChange(String data) throws IOException {
emitter.send(SseEmitter.event()
.name("data-change")
.data(data));
}
}
gRPC Stream(双向流)
Proto定义
service DataService {
rpc SubscribeData(SubscribeRequest) returns (stream DataResponse);
}
message SubscribeRequest {
repeated string data_ids = 1;
}
message DataResponse {
string data_id = 1;
bytes data = 2;
int64 timestamp = 3;
}
技术选型对比
| 方案 | 实时性 | 可靠性 | 复杂度 | 适用场景 |
|---|---|---|---|---|
| MQ推送 | 中等 | 高 | 中 | 跨服务、有事务需求的变更通知 |
| 长轮询 | 中等 | 高 | 低 | 配置中心(Nacos/Apollo) |
| WebSocket | 高 | 高 | 中 | 实时监控、协作编辑 |
| ZK Watcher | 高 | 高 | 中 | 分布式协调、服务发现 |
| SSE | 高 | 中 | 低 | 单向推送、浏览器友好 |
| gRPC Stream | 高 | 高 | 高 | 高性能微服务间通信 |
最佳实践建议
- 对等数据(配置/元数据):使用长轮询+Nacos/Apollo成熟方案
- 高频状态更新:gRPC Stream或WebSocket
- 跨服务事务通知:Kafka/RocketMQ
- 关键变更必须到达:MQ + 本地ACK确认机制
注意事项
- 客户端需要实现断线重连和增量更新逻辑
- 考虑数据版本号避免重复消费或混乱
- 对于关键变更,服务端应保存变更历史便于回溯
选择哪种方案取决于你的实时性要求、系统规模、现有架构和运维能力,对于大多数企业级应用,MQ异步通知 + 长轮询降级的组合是最稳健的实践。