如何使用Spring WebFlux和WebSocket确保反应流完成
我已经在Kotlin为SpringWebFlux编写了一个测试客户端和服务器。客户端向服务器发送一个数字(例如4)并返回那么多数字(例如0、1、2和3)。以下是服务器实现:如何使用Spring WebFlux和WebSocket确保反应流完成,websocket,kotlin,reactive-programming,spring-webflux,project-reactor,Websocket,Kotlin,Reactive Programming,Spring Webflux,Project Reactor,我已经在Kotlin为SpringWebFlux编写了一个测试客户端和服务器。客户端向服务器发送一个数字(例如4)并返回那么多数字(例如0、1、2和3)。以下是服务器实现: class NumbersWebSocketHandler : WebSocketHandler { override fun handle(session: WebSocketSession): Mono<Void> { var index = 0 var count =
class NumbersWebSocketHandler : WebSocketHandler {
override fun handle(session: WebSocketSession): Mono<Void> {
var index = 0
var count = 1
val publisher = Flux.generate<Int> { sink ->
if (index < count) {
sink.next(index)
index++
} else {
sink.complete()
}
}.map(Int::toString)
.map(session::textMessage)
.delayElements(Duration.ofMillis(500))
return session.receive()
.map(WebSocketMessage::getPayloadAsText)
.doOnNext {
println("About to send $it numbers")
count = it.toInt()
}
.then()
.and(session.send(publisher))
.then()
}
}
而且:
.delayElements(Duration.ofMillis(500))
.doFinally { session.close() }
但两者都不会对客户的行为产生任何影响。在调用“send”后尝试显式关闭会话也不会:
.and(session.send(publisher))
.then()
.and { session.close() }
.then()
从中可以看出,它希望上游发布服务器发送一个onComplete
事件来发出事件本身。我不确定,但我认为只有当连接正常终止时才会发出。这就是为什么永远不会执行doOnNext
我认为您应该使用subscribe()
来代替reduce
,并定义您自己的Subscriber
实例,您可以在其中保存状态
编辑:您不能在此处使用subscribe
方法。但如果服务器上的连接已关闭,则应该是这样:
.map(Int::toString)
.map(session::textMessage)
.delayElements(Duration.ofMillis(500))
.then(session::close)
从中可以看出,它希望上游发布服务器发送一个onComplete
事件来发出事件本身。我不确定,但我认为只有当连接正常终止时才会发出。这就是为什么永远不会执行doOnNext
我认为您应该使用subscribe()
来代替reduce
,并定义您自己的Subscriber
实例,您可以在其中保存状态
编辑:您不能在此处使用subscribe
方法。但如果服务器上的连接已关闭,则应该是这样:
.map(Int::toString)
.map(session::textMessage)
.delayElements(Duration.ofMillis(500))
.then(session::close)
从WebSocketHandler
返回的Mono
指示处理何时完成,进而指示WebSocket连接应保持打开的时间。问题是,在两侧,返回的Mono
永远不会完成
客户端使用reduce
等待来自服务器端的输入结束,但服务器使用receive()
+doOnNext
+then()
永远等待接收消息。所以客户端和服务器都在等待对方。在客户端添加take
有助于打破僵局:
client.execute(uri, session -> session
.send(input.map(session::textMessage))
.then(session.receive()
.map(WebSocketMessage::getPayloadAsText)
.map(Integer::valueOf)
.take(3)
.reduce((a,b) -> {
logger.debug("Reduce called with " + a + " and " + b);
return a + b;
})
.doOnNext(logger::debug)
)
.then()
).block();
第二个问题是服务器的组成不正确。发送与接收通过和并行触发。相反,发送流应取决于首先接收计数:
session.receive()
.map(WebSocketMessage::getPayloadAsText)
.flatMap(s -> {
logger.debug("About to send " + s + " numbers");
count.set(Integer.parseInt(s));
return session.send(publisher);
})
.then()
从WebSocketHandler
返回的Mono
指示处理何时完成,进而指示WebSocket连接应保持打开的时间。问题是,在两侧,返回的Mono
永远不会完成
客户端使用reduce
等待来自服务器端的输入结束,但服务器使用receive()
+doOnNext
+then()
永远等待接收消息。所以客户端和服务器都在等待对方。在客户端添加take
有助于打破僵局:
client.execute(uri, session -> session
.send(input.map(session::textMessage))
.then(session.receive()
.map(WebSocketMessage::getPayloadAsText)
.map(Integer::valueOf)
.take(3)
.reduce((a,b) -> {
logger.debug("Reduce called with " + a + " and " + b);
return a + b;
})
.doOnNext(logger::debug)
)
.then()
).block();
第二个问题是服务器的组成不正确。发送与接收通过和并行触发。相反,发送流应取决于首先接收计数:
session.receive()
.map(WebSocketMessage::getPayloadAsText)
.flatMap(s -> {
logger.debug("About to send " + s + " numbers");
count.set(Integer.parseInt(s));
return session.send(publisher);
})
.then()
这就引出了一个问题:为什么连接没有正常终止?服务器端遗漏了什么?此外,“订阅”不能与“ReactorNettyWebSocketClient”一起使用,因为他们设计API的方式不同。看到这是因为服务器在初始流结束时不调用close()
:…().delayElements(…)。然后(session::close)代码>或者更确切地说,我可以编写'delayElements(…).doOnComplete{session.close()}'或'delayElements(…).doFinally{session.close()}',但这两种代码都不会影响输出。对“.then(…)”的调用不会在该位置编译。这就引出了一个问题:为什么连接没有正常终止?服务器端遗漏了什么?此外,“订阅”不能与“ReactorNettyWebSocketClient”一起使用,因为他们设计API的方式不同。看到这是因为服务器在初始流结束时不调用close()
:…().delayElements(…)。然后(session::close)代码>或者更确切地说,我可以编写'delayElements(…).doOnComplete{session.close()}'或'delayElements(…).doFinally{session.close()}',但这两种代码都不会影响输出。对“.then(…)”的调用不会在该位置编译。您可以使用take()
运算符,因为您知道将接收多少项。您可以使用take()
运算符,因为您知道将接收多少项。