Spring WebFlux 反应式 WebSocket 阻止连接关闭
Spring WebFlux reactive WebSocket prevent connection closing
我正在为我的应用程序开发简单的聊天模块,使用 Spring WebFlux,在后端使用 ReactiveMongoRepository,在前端使用 Angular 4。我能够通过 WebSocketSession 接收数据,但是在流式传输来自数据库的所有消息后,我想保持连接以便更新消息列表。谁能告诉我如何实现这一点,或者我可能遵循了错误的假设?
Java 负责 WebSocket 的后端,我的订阅者只记录当前状态,与那里无关:
WebFlux 配置:
@Configuration
@EnableWebFlux
public class WebSocketConfig {
private final WebSocketHandler webSocketHandler;
@Autowired
public WebSocketConfig(WebSocketHandler webSocketHandler) {
this.webSocketHandler = webSocketHandler;
}
@Bean
@Primary
public HandlerMapping webSocketMapping() {
Map<String, Object> map = new HashMap<>();
map.put("/websocket-messages", webSocketHandler);
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(10);
mapping.setUrlMap(map);
return mapping;
}
@Bean
public WebSocketHandlerAdapter handlerAdapter() {
return new WebSocketHandlerAdapter();
}
}
WebSocketHandler 实现
@Component
public class MessageWebSocketHandler implements WebSocketHandler {
private final MessageRepository messageRepository;
private ObjectMapper mapper = new ObjectMapper();
private MessageSubscriber subscriber = new MessageSubscriber();
@Autowired
public MessageWebSocketHandler(MessageRepository messageRepository) {
this.messageRepository = messageRepository;
}
@Override
public Mono<Void> handle(WebSocketSession session) {
session.receive()
.map(WebSocketMessage::getPayloadAsText)
.map(this::toMessage)
.subscribe(subscriber::onNext, subscriber:: onError, subscriber::onComplete);
return session.send(
messageRepository.findAll()
.map(this::toJSON)
.map(session::textMessage));
}
private String toJSON(Message message) {
try {
return mapper.writeValueAsString(message);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
private Message toMessage(String json) {
try {
return mapper.readValue(json, Message.class);
} catch (IOException e) {
throw new RuntimeException("Invalid JSON:" + json, e);
}
}
}
和 MongoRepo
@Repository
public interface MessageRepository extends
ReactiveMongoRepository<Message,String> {
}
前端处理:
@Injectable()
export class WebSocketService {
private subject: Rx.Subject<MessageEvent>;
constructor() {
}
public connect(url): Rx.Subject<MessageEvent> {
if (!this.subject) {
this.subject = this.create(url);
console.log('Successfully connected: ' + url);
}
return this.subject;
}
private create(url): Rx.Subject<MessageEvent> {
const ws = new WebSocket(url);
const observable = Rx.Observable.create(
(obs: Rx.Observer<MessageEvent>) => {
ws.onmessage = obs.next.bind(obs);
ws.onerror = obs.error.bind(obs);
ws.onclose = obs.complete.bind(obs);
return ws.close.bind(ws);
});
const observer = {
next: (data: Object) => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify(data));
}
}
};
return Rx.Subject.create(observer, observable);
}
}
在其他服务中,我将可观察到的响应映射到我的类型
constructor(private wsService: WebSocketService) {
this.messages = <Subject<MessageEntity>>this.wsService
.connect('ws://localhost:8081/websocket-messages')
.map((response: MessageEvent): MessageEntity => {
const data = JSON.parse(response.data);
return new MessageEntity(data.id, data.user_id, data.username, data.message, data.links);
});
}
最后订阅发送功能,由于连接关闭我无法使用:
ngOnInit() {
this.messages = [];
this._ws_subscription = this.chatService.messages.subscribe(
(message: MessageEntity) => {
this.messages.push(message);
},
error2 => {
console.log(error2.json());
},
() => {
console.log('Closed');
}
);
}
sendTestMessage() {
this.chatService.messages.next(new MessageEntity(null, '59ca30ac87e77d0f38237739', 'mickl', 'test message angular', null));
}
假设您的聊天消息在接收时被保存到数据存储中,您可以使用 Spring 数据 MongoDB 反应中的可尾游标功能(参见 reference documentation) .
所以您可以在您的存储库上创建一个新方法,例如:
public interface MessageRepository extends ReactiveSortingRepository< Message, String> {
@Tailable
Flux<Message> findWithTailableCursor();
}
请注意,tailable 游标有一些限制:您 mongo 集合需要设置上限,条目按插入顺序流式传输。
Spring WebFlux websocket 支持尚不支持 STOMP 或消息代理,但这可能是此类用例的更好选择。
我正在为我的应用程序开发简单的聊天模块,使用 Spring WebFlux,在后端使用 ReactiveMongoRepository,在前端使用 Angular 4。我能够通过 WebSocketSession 接收数据,但是在流式传输来自数据库的所有消息后,我想保持连接以便更新消息列表。谁能告诉我如何实现这一点,或者我可能遵循了错误的假设?
Java 负责 WebSocket 的后端,我的订阅者只记录当前状态,与那里无关:
WebFlux 配置:
@Configuration
@EnableWebFlux
public class WebSocketConfig {
private final WebSocketHandler webSocketHandler;
@Autowired
public WebSocketConfig(WebSocketHandler webSocketHandler) {
this.webSocketHandler = webSocketHandler;
}
@Bean
@Primary
public HandlerMapping webSocketMapping() {
Map<String, Object> map = new HashMap<>();
map.put("/websocket-messages", webSocketHandler);
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(10);
mapping.setUrlMap(map);
return mapping;
}
@Bean
public WebSocketHandlerAdapter handlerAdapter() {
return new WebSocketHandlerAdapter();
}
}
WebSocketHandler 实现
@Component
public class MessageWebSocketHandler implements WebSocketHandler {
private final MessageRepository messageRepository;
private ObjectMapper mapper = new ObjectMapper();
private MessageSubscriber subscriber = new MessageSubscriber();
@Autowired
public MessageWebSocketHandler(MessageRepository messageRepository) {
this.messageRepository = messageRepository;
}
@Override
public Mono<Void> handle(WebSocketSession session) {
session.receive()
.map(WebSocketMessage::getPayloadAsText)
.map(this::toMessage)
.subscribe(subscriber::onNext, subscriber:: onError, subscriber::onComplete);
return session.send(
messageRepository.findAll()
.map(this::toJSON)
.map(session::textMessage));
}
private String toJSON(Message message) {
try {
return mapper.writeValueAsString(message);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
private Message toMessage(String json) {
try {
return mapper.readValue(json, Message.class);
} catch (IOException e) {
throw new RuntimeException("Invalid JSON:" + json, e);
}
}
}
和 MongoRepo
@Repository
public interface MessageRepository extends
ReactiveMongoRepository<Message,String> {
}
前端处理:
@Injectable()
export class WebSocketService {
private subject: Rx.Subject<MessageEvent>;
constructor() {
}
public connect(url): Rx.Subject<MessageEvent> {
if (!this.subject) {
this.subject = this.create(url);
console.log('Successfully connected: ' + url);
}
return this.subject;
}
private create(url): Rx.Subject<MessageEvent> {
const ws = new WebSocket(url);
const observable = Rx.Observable.create(
(obs: Rx.Observer<MessageEvent>) => {
ws.onmessage = obs.next.bind(obs);
ws.onerror = obs.error.bind(obs);
ws.onclose = obs.complete.bind(obs);
return ws.close.bind(ws);
});
const observer = {
next: (data: Object) => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify(data));
}
}
};
return Rx.Subject.create(observer, observable);
}
}
在其他服务中,我将可观察到的响应映射到我的类型
constructor(private wsService: WebSocketService) {
this.messages = <Subject<MessageEntity>>this.wsService
.connect('ws://localhost:8081/websocket-messages')
.map((response: MessageEvent): MessageEntity => {
const data = JSON.parse(response.data);
return new MessageEntity(data.id, data.user_id, data.username, data.message, data.links);
});
}
最后订阅发送功能,由于连接关闭我无法使用:
ngOnInit() {
this.messages = [];
this._ws_subscription = this.chatService.messages.subscribe(
(message: MessageEntity) => {
this.messages.push(message);
},
error2 => {
console.log(error2.json());
},
() => {
console.log('Closed');
}
);
}
sendTestMessage() {
this.chatService.messages.next(new MessageEntity(null, '59ca30ac87e77d0f38237739', 'mickl', 'test message angular', null));
}
假设您的聊天消息在接收时被保存到数据存储中,您可以使用 Spring 数据 MongoDB 反应中的可尾游标功能(参见 reference documentation) .
所以您可以在您的存储库上创建一个新方法,例如:
public interface MessageRepository extends ReactiveSortingRepository< Message, String> {
@Tailable
Flux<Message> findWithTailableCursor();
}
请注意,tailable 游标有一些限制:您 mongo 集合需要设置上限,条目按插入顺序流式传输。
Spring WebFlux websocket 支持尚不支持 STOMP 或消息代理,但这可能是此类用例的更好选择。