在设计方案前,我们先梳理需求:
综合考虑以上要求,常见可选技术包括:RSocket、gRPC双向流、WebSocket(STOMP),以及其他适用于流式推送的协议如MQTT等。下来分别评估各方案在IoT不稳定网络下的稳定性、顺序性和容错性,来判断具体使用哪种协议。
1. RSocket
@Configuration
public class RSocketConfig {
// 配置Resume以支持断线重连
@Bean
RSocketServerCustomizer rSocketResume() {
return server -> server.resume(new Resume()
.sessionDuration(Duration.ofMinutes(10))
.retry(Retry.fixedDelay(Long.MAX_VALUE, Duration.ofSeconds(5))));
}
}
@Controller
public class DataController {
@MessageMapping("stream-data")
public Flux<String> streamData() {
// 模拟持续推送数据
return Flux.interval(Duration.ofMillis(100))
.map(i -> "数据"+i);
}
}
RSocket rSocket = RSocketConnector.create()
.keepAlive(Duration.ofSeconds(20), Duration.ofSeconds(90), Integer.MAX_VALUE)
.resume()
.connect(TcpClientTransport.create(host, port))
.block();
Flux<String> dataFlux = Mono.just(rSocket)
.flatMapMany(sock -> sock.requestStream(DefaultPayload.create("")));
dataFlux.subscribe(msg -> { /* 处理服务器推送 */ });
客户端需自行实现断线重连逻辑,并在重连时使用相同的Resume Token恢复会话。
2. gRPC 双向流
// 定义双向流RPC
service DataService {
rpc transfer (stream DataRequest) returns (stream DataResponse);
}
@GrpcService
public class DataServiceImpl extends DataServiceGrpc.DataServiceImplBase {
@Override
public StreamObserver<DataRequest> transfer(StreamObserver<DataResponse> responseObserver) {
return new StreamObserver<DataRequest>() {
@Override
public void onNext(DataRequest req) {
// 收到客户端消息,进行处理并回复
DataResponse resp = DataResponse.newBuilder()
.setResult("已收到: " + req.getPayload())
.build();
responseObserver.onNext(resp);
}
@Override
public void onError(Throwable t) { /* 处理错误 */ }
@Override
public void onCompleted() { responseObserver.onCompleted(); }
};
}
}
ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port)
.usePlaintext().build();
DataServiceGrpc.DataServiceStub stub = DataServiceGrpc.newStub(channel);
StreamObserver<DataResponse> responseObserver = new StreamObserver<>() {
public void onNext(DataResponse resp) { /* 处理服务端推送 */ }
public void onError(Throwable t) { /* 错误处理 */ }
public void onCompleted() { /* 流结束 */ }
};
StreamObserver<DataRequest> requestObserver = stub.transfer(responseObserver);
requestObserver.onNext(DataRequest.newBuilder().setPayload("Hello").build());
// ... 根据需要继续发送请求
客户端需要监听连接状态并实现断线重连,重新构建Channel和Stub后续传;并在消息中携带业务序号或令牌以在服务端恢复流。
3. WebSocket(含STOMP)
@Configuration
@EnableWebSocketMessageBroker
public class WsConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws-data").setAllowedOrigins("*").withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
}
@Controller
public class WsController {
@Autowired
private SimpMessagingTemplate template;
// 定时向订阅者推送数据
@Scheduled(fixedDelay = 500)
public void pushData() {
template.convertAndSend("/topic/data", "推送消息");
}
@MessageMapping("/client-reply")
public void handleReply(String msg) {
// 处理客户端返回的消息
}
}
OkHttpClient client = new OkHttpClient();
Request req = new Request.Builder().url("ws://host:port/ws-data/websocket").build();
WebSocket ws = client.newWebSocket(req, new WebSocketListener() {
@Override public void onMessage(WebSocket w, String text) { /* 处理消息 */ }
@Override public void onOpen(WebSocket w, Response r) { w.send("client-ready"); }
// 实现onFailure、onClosing以处理重连
});
或使用STOMP库:连接后订阅/topic/data接收消息,发送消息到/app/client-reply等。客户端需处理断线重连逻辑并可能使用本地存储缓存未处理消息。
4. 其他协议(MQTT 等)
5. 方案选择
技术方案 | 传输连接方式 | 顺序性及QoS | 物联网网络下稳定性 | 容错与补偿机制 | 典型场景适用性 |
---|---|---|---|---|---|
RSocket | TCP(或WebSocket) | TCP内置顺序;应用级分片+Resume重连 | 支持配置Resume恢复中断流;应用层可缓冲和重发 | 应用级背压流控;可结合持久化队列 | 实时双向流、大数据量传输 |
gRPC(双流) | HTTP/2 长连接 | TCP顺序;客户端需管理断点续传 | 长连接断开后需重连;本身无恢复机制;需客户端重连 | 高效RPC场景、强类型通信 | 高效RPC场景、强类型通信 |
WebSocket | TCP 长连接(SockJS) | TCP顺序;无原生QoS | 不稳定网络需断线重连(SockJS可降级轮询) | 客户端重连+心跳;可结合消息缓存池补偿 | 浏览器/移动端实时聊天、通知 |
MQTT | TCP(或WS) | 支持 ,Broker保证顺序 | 专为不可靠网络设计;持久化离线支持 | QoS保证不丢失;支持持久会话和重发 | 物联网遥测与控制;数据可靠推送 |
6. 示例代码(Spring Boot端)
@Configuration
public class RSocketConfig {
@Bean
RSocketServerCustomizer rSocketResume() {
return server -> server.resume(new Resume()
.sessionDuration(Duration.ofMinutes(10))
.retry(Retry.fixedDelay(Long.MAX_VALUE, Duration.ofSeconds(5))));
}
}
@Controller
public class RSocketController {
@MessageMapping("stream-data")
public Flux<String> streamData() {
return Flux.interval(Duration.ofMillis(100))
.map(i -> "数据"+i);
}
}
// proto定义
service DataService {
rpc transfer (stream DataRequest) returns (stream DataResponse);
}
@GrpcService
public class DataServiceImpl extends DataServiceGrpc.DataServiceImplBase {
@Override
public StreamObserver<DataRequest> transfer(StreamObserver<DataResponse> responseObserver) {
return new StreamObserver<DataRequest>() {
@Override
public void onNext(DataRequest req) {
DataResponse resp = DataResponse.newBuilder()
.setResult("已收到: " + req.getPayload())
.build();
responseObserver.onNext(resp);
}
@Override
public void onError(Throwable t) { /* 错误处理 */ }
@Override
public void onCompleted() { responseObserver.onCompleted(); }
};
}
}
@Configuration
@EnableWebSocketMessageBroker
public class WsConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws-data").setAllowedOrigins("*").withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
}
@Controller
public class WsController {
@Autowired
private SimpMessagingTemplate template;
@Scheduled(fixedDelay = 500)
public void pushData() {
template.convertAndSend("/topic/data", "推送消息");
}
@MessageMapping("/client-reply")
public void handleClientReply(String msg) {
// 处理客户端返回
}
}
@Configuration
public class MqttConfig {
@Bean
public MqttPahoClientFactory mqttClientFactory() {
DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
// 可配置用户名/密码等
return factory;
}
@Bean
public MqttPahoMessageHandler mqttOutbound() {
MqttPahoMessageHandler handler =
new MqttPahoMessageHandler("spring-server", mqttClientFactory());
handler.setAsync(true);
handler.setDefaultTopic("topic/data");
return handler;
}
@Autowired
private MqttPahoMessageHandler mqttOutbound;
// 发送示例
public void publishData(String payload) {
mqttOutbound.handleMessage(new GenericMessage<>(payload));
}
}
7. Android端接入方式(简要)
RSocketConnector connector = RSocketConnector.create()
.resume() // 启用重连
.keepAlive(Duration.ofSeconds(20), Duration.ofSeconds(90), Integer.MAX_VALUE);
RSocket rSocket = connector.connect(TcpClientTransport.create(host, port)).block();
Flux<String> flux = rSocket.requestStream(DefaultPayload.create(""));
flux.subscribe(msg -> { /* 处理推送内容 */ });
客户端需实现掉线重连(可重用Resume token)并在恢复时继续订阅。
ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port)
.usePlaintext().build();
DataServiceGrpc.DataServiceStub stub = DataServiceGrpc.newStub(channel);
StreamObserver<DataResponse> respObs = new StreamObserver<>() { ... };
StreamObserver<DataRequest> reqObs = stub.transfer(respObs);
reqObs.onNext(DataRequest.newBuilder().setPayload("Hello").build());
同样需监听连接状态并在断开后重连,使用业务令牌继续未完成的数据传输
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url("ws://host:port/ws-data/websocket").build();
WebSocket ws = client.newWebSocket(request, new WebSocketListener() {
@Override public void onOpen(WebSocket webSocket, Response response) {
webSocket.send("ready");
}
@Override public void onMessage(WebSocket webSocket, String text) {
// 处理服务器消息
}
@Override public void onFailure(WebSocket webSocket, Throwable t, Response r) {
// 重连逻辑
}
});
或使用STOMP客户端库,连接后订阅/topic/data,发送消息到/app/client-reply等。需实现断线重连和重建订阅。
MqttAndroidClient client = new MqttAndroidClient(context, "tcp://broker:1883", "android-client");
client.connect(options, null, new IMqttActionListener() { ... });
client.subscribe("topic/data", 1);
client.setCallback(new MqttCallback() {
public void messageArrived(String topic, MqttMessage message) {
// 处理推送
}
public void connectionLost(Throwable cause) {
// 重连逻辑
}
public void deliveryComplete(IMqttDeliveryToken token) {}
});
// 发布示例(向服务器或其他客户端发送)
client.publish("topic/data", new MqttMessage("payload".getBytes()));
MQTT客户端的重连和离线消息由Broker支持,Android端只需调用connect()重连即可。
8. 可靠性增强措施
9. 结论
综上所述,各方案各有优劣:
综合考虑:若重点关注稳定性与顺序一致(物联网通信),MQTT是首选;若需要高实时双向流和一定可靠性,可优先考虑RSocket(辅以Resume与业务重试);gRPC适合已有HTTP/2生态的项目;WebSocket适合快速集成和兼容浏览器场景。建议结合业务需求,可能混合使用:如将消息先推送到MQTT或内部队列,再由微服务通过RSocket或gRPC转发给移动端,充分利用各自优势。