Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/356.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何在SpringWebFlux中正确地从多个通量(WebsocketSession::receive)向接收器发出值?_Java_Kotlin_Spring Webflux_Project Reactor_Spring Websocket - Fatal编程技术网

Java 如何在SpringWebFlux中正确地从多个通量(WebsocketSession::receive)向接收器发出值?

Java 如何在SpringWebFlux中正确地从多个通量(WebsocketSession::receive)向接收器发出值?,java,kotlin,spring-webflux,project-reactor,spring-websocket,Java,Kotlin,Spring Webflux,Project Reactor,Spring Websocket,在我的简化示例中,我希望将WebSocket客户端发送的消息广播给所有其他客户端。该应用程序使用带弹簧的反应式WebSocket构建 我的想法是用单曲 接收器如果从客户端接收到消息,则在该接收器上发出该消息WebsocketSession::send仅将此接收器发出的事件转发给连接的客户端 @Component class ReactiveWebSocketHandler(private val sink: Sinks.Many<Message>,

在我的简化示例中,我希望将WebSocket客户端发送的消息广播给所有其他客户端。该应用程序使用带弹簧的反应式WebSocket构建

我的想法是用单曲
接收器
如果从客户端接收到消息,则在该接收器上发出该消息
WebsocketSession::send
仅将此
接收器发出的事件转发给连接的客户端

@Component
class ReactiveWebSocketHandler(private val sink: Sinks.Many<Message>,
                               private val objectMapper : ObjectMapper) : WebSocketHandler {

    override fun handle(session: WebSocketSession): Mono<Void> {

        val input = session.receive()
                .doOnNext {
                    sink.emitNext(fromJson(it.payloadAsText, Message::class.java), Sinks.EmitFailureHandler.FAIL_FAST)
                }
                .then()
        val output = session.send(sink.asFlux().map { message -> session.textMessage(toJson(message)) })

        return Mono.zip(input, output).then()
    }

    fun toJson(obj : Any) : String = objectMapper.writeValueAsString(obj)

    fun <T> fromJson(json : String, clazz : Class<T>) : T{
        return objectMapper.readValue(json, clazz)
    }

}
我可以看到的另一个选项是在一些常用锁上进行
同步
,以便发射是线程安全的:

@Component
class ReactiveWebSocketHandler(private val sink: Sinks.Many<Message>,
                               private val objectMapper : ObjectMapper) : WebSocketHandler {

    private val lock = Any()

    override fun handle(session: WebSocketSession): Mono<Void> {

        val input = session.receive()
                .doOnNext {
                    synchronized(lock) {
                        sink.emitNext(fromJson(it.payloadAsText, Message::class.java), Sinks.EmitFailureHandler.FAIL_FAST)
                    }
                }
                .then()
        ...
    }


}
@组件
类ReactiveWebSocketHandler(私有val sink:Sinks.Many,
私有val对象映射器:对象映射器):WebSocketHandler{
private val lock=Any()
覆盖乐趣句柄(会话:WebSocketSession):Mono{
val input=session.receive()
doOnNext先生{
已同步(锁定){
sink.emitNext(fromJson(it.payloadAsText,Message::class.java),Sinks.EmitFailureHandler.FAIL\u FAST)
}
}
.然后()
...
}
}
然而,我不确定是否应该这样做

问题是
在这种情况下,是否可以使用
publishOn
,以确保发射是线程安全的,如果没有,那么这个问题的其他解决方案是什么(除了像我使用
synchronized
关键字那样使用同步之外)。

而不是使用
synchronized
选项进行悲观锁定,您可以创建一个与
FAIL\u FAST
相当的
EmitFailureHandler
,除了它为
EmitResult.NON\u SERIALIZED\u ACCESS
返回
true

这将导致立即重试并发的发射尝试,就像在忙循环中一样


乐观地说,这最终会成功。如果您想对无限循环进行额外防御,甚至可以让自定义处理程序引入延迟或限制其返回的次数。

而不是使用
synchronized
选项进行悲观锁定,您可以创建一个与
FAIL\u FAST
相当的
EmitFailureHandler
,除了它为
EmitResult.NON\u SERIALIZED\u ACCESS
返回
true

这将导致立即重试并发的发射尝试,就像在忙循环中一样


乐观地说,这最终会成功。如果您想对无限循环进行额外的防御,您甚至可以让自定义处理程序引入延迟或限制其返回的次数。

单线程调度程序方法应该可以工作,但您需要为每个
ReactiveWebSocketHandler
使用相同的调度程序实例

您是否可以使用平面图而不是水槽来组合所有receive()通量

我自己解决这个问题的方法是“忙转法”


请参阅我的答案。

关于单线程调度程序方法的出版物应该可以工作,但您需要为每个
ReactiveWebSocketHandler
使用相同的调度程序实例

您是否可以使用平面图而不是水槽来组合所有receive()通量

我自己解决这个问题的方法是“忙转法”


请参阅我的答案。

除了@simon baslé答案之外,这里是示例代码(用于srping webflux)。它将请求下游发送到订阅服务器,如果出现
接收器,EmitResult.FAIL\u非序列化的
响应将重试。
这是Sinks.EmitFailureHandler的
定义:

private final Sinks.EmitFailureHandler emitFailureHandler = (signalType, emitResult) -> emitResult
            .equals(Sinks.EmitResult.FAIL_NON_SERIALIZED) ? true : false;
以下是将处理请求的控制器:

@org.springframework.web.bind.annotation.RestController
public class RestController {

    private final Many<String> sink = Sinks.many().multicast().directBestEffort();
    private final Sinks.EmitFailureHandler emitFailureHandler = (signalType, emitResult) -> emitResult
            .equals(Sinks.EmitResult.FAIL_NON_SERIALIZED) ? true : false;
    @Autowired
    public RestController(ServiceSubscriber serviceSubscriber) {
        sink.asFlux().subscribe(serviceSubscriber);
    }

    @GetMapping(path = "/{id}")
    public Mono<ResponseEntity<Void>> getData(@PathVariable String id) {
        return Mono.fromCallable(() -> {
            sink.emitNext(id, emitFailureHandler);
            return ResponseEntity.ok().<Void>build();
        });
    }
}
@org.springframework.web.bind.annotation.RestController
公共类RestController{
private final Many sink=Sinks.Many().multicast().directBestEffort();
private final Sinks.EmitFailureHandler EmitFailureHandler=(信号类型,emitResult)->emitResult
.equals(Sinks.EmitResult.FAIL\未序列化)?true:false;
@自动连线
公共RestController(ServiceSubscriber ServiceSubscriber){
sink.asFlux().subscribe(serviceSubscriber);
}
@GetMapping(path=“/{id}”)
公共Mono getData(@PathVariable字符串id){
返回Mono.fromCallable(()->{
sink.emitNext(id,emitFailureHandler);
返回ResponseEntity.ok().build();
});
}
}

除了@simon baslé答案之外,这里还有示例代码(用于srping webflux)。它将请求下游发送到订阅服务器,如果出现
接收器,EmitResult.FAIL\u非序列化的
响应将重试。
这是Sinks.EmitFailureHandler的
定义:

private final Sinks.EmitFailureHandler emitFailureHandler = (signalType, emitResult) -> emitResult
            .equals(Sinks.EmitResult.FAIL_NON_SERIALIZED) ? true : false;
以下是将处理请求的控制器:

@org.springframework.web.bind.annotation.RestController
public class RestController {

    private final Many<String> sink = Sinks.many().multicast().directBestEffort();
    private final Sinks.EmitFailureHandler emitFailureHandler = (signalType, emitResult) -> emitResult
            .equals(Sinks.EmitResult.FAIL_NON_SERIALIZED) ? true : false;
    @Autowired
    public RestController(ServiceSubscriber serviceSubscriber) {
        sink.asFlux().subscribe(serviceSubscriber);
    }

    @GetMapping(path = "/{id}")
    public Mono<ResponseEntity<Void>> getData(@PathVariable String id) {
        return Mono.fromCallable(() -> {
            sink.emitNext(id, emitFailureHandler);
            return ResponseEntity.ok().<Void>build();
        });
    }
}
@org.springframework.web.bind.annotation.RestController
公共类RestController{
private final Many sink=Sinks.Many().multicast().directBestEffort();
private final Sinks.EmitFailureHandler EmitFailureHandler=(信号类型,emitResult)->emitResult
.equals(Sinks.EmitResult.FAIL\未序列化)?true:false;
@自动连线
公共RestController(ServiceSubscriber ServiceSubscriber){
sink.asFlux().subscribe(serviceSubscriber);
}
@GetMapping(path=“/{id}”)
公共Mono getData(@PathVariable字符串id){
返回Mono.fromCallable(()->{
sink.emitNext(id,emitFailureHandler);
返回ResponseEntity.ok().build();
});
}
}

太好了,我会试试看。你知道为什么我在
onNext
之前将
publishOn
与单线程调度程序一起使用时,会向
Sink
发送一条消息,然后所有后续websocket连接立即接收onClose事件吗?在一个简单的独立应用程序中,这样的设置也解决了这个问题,但是在WebSocket的情况下,它失败了。@michalk emitNext硬编码为t