使用带有Rsocket和Spring Webflux的WebSocket仅向特定客户端发送消息
我正在我的一个POC项目中尝试将Rsocket与websocket结合使用。在我的情况下,不需要用户登录。当我收到来自其他服务的消息时,我只想向某些客户发送消息。基本上,我的流程是这样的使用带有Rsocket和Spring Webflux的WebSocket仅向特定客户端发送消息,websocket,reactive-programming,spring-webflux,rsocket,Websocket,Reactive Programming,Spring Webflux,Rsocket,我正在我的一个POC项目中尝试将Rsocket与websocket结合使用。在我的情况下,不需要用户登录。当我收到来自其他服务的消息时,我只想向某些客户发送消息。基本上,我的流程是这样的 Service A Service B |--------| websocket |------------------| Queue based comm
Service A Service B
|--------| websocket |------------------| Queue based comm |---------------|
| Web |----------------->| Rsocket server |--------------------->| Another |
| |<-----------------| using Websocket |<---------------------| service |
|--------| websocket |------------------| Queue based comm |---------------|
服务A服务B
|--------|websocket |--------------------基于队列的通信|-------------------
|Web--------------------------->Rsocket服务器--------------------------------------------------------->另一个
||我个人还没有将RSocket与WebSocket传输一起使用,但正如RSocket规范中所述,底层传输协议甚至不应该很重要
一个RSocket组件同时是服务器和客户端。因此,当浏览器连接到RSocket“服务器”时,您可以插入RSocketRequester
实例,然后使用该实例向“客户端”发送消息
然后,您可以将这些实例添加到本地缓存中(例如,使用您选择的键将它们放入一些全局可用的ConcurrentHashMap
——您将知道/能够计算来自服务B的消息应该传播到哪些客户端)
然后,在从服务B接收消息的代码中,只需从本地缓存中获取符合您的条件的所有RSocketRequester
实例并向它们发送消息。基本上,我认为您有两个选择。第一个是过滤来自服务B
的流量,第二个是使用@NikolaB所述的RSocketRequester
和Map
第一个选项:
数据类新闻(val类别:String,val新闻:String)
数据类PrivateNews(val目的地:字符串,val新闻:新闻)
类新闻提供者{
专用val持续时间:长=250
private val externalNewsProcessor=DirectProcessor.create().serialize()
private val sink=externalNewsProcessor.sink()
fun allNews():Flux{
回流通量
.合并(
carNews()、bikeNews()、cosmeticsNews()、,
外部新闻处理器)
.延迟要素(持续时间(百万)
}
有趣的外部新闻():Flux{
返回外部新闻处理器;
}
有趣的附加外部新闻(新闻:新闻){
下沉。下一个(新闻);
}
有趣的卡纽斯():通量{
回流通量
.只是(“新款兰博!!”、“惊人的法拉利!”、“伟大的保时捷”、“非常酷的奥迪RS4前卫”、“特斯拉i比你聪明”)
.map{News(“CAR”,it)}
.延迟要素(持续时间(百万)
.log()
}
有趣的比基尼新闻():Flux{
回流通量
.just(“专业耐力赛仍然是最大的梦想”,“巨大的圣歌快如地狱”,“砾石长途测试”)
.map{News(“BIKE”,it)}
.延迟要素(持续时间(百万)
.log()
}
有趣的美容新闻():Flux{
回流通量
.只是(“nivea-没人想听这个”,“雷索纳除臭测试”)
.map{News(“化妆品”,it)}
.延迟要素(持续时间(百万)
.log()
}
}
@RestController
@请求映射(“/sse”)
@交叉原点(“*”)
类NewsRestController(){
private val log=LoggerFactory.getLogger(NewsRestController::class.java)
val newsProvider=newsProvider()
@GetMapping(value=[“/news/{category}]”,products=[MediaType.TEXT\u EVENT\u STREAM\u value])
fun allNewsByCategory(@PathVariable category:String):流量{
log.info(“您好,按类别获取所有新闻:{}!”,类别)
返回新闻提供者
.allNews()
.filter{it.category==category}
}
}
NewsProvider
类模拟了您的服务B
,它应该返回Flux
。无论何时调用addExternalNews
它都将推送allNews
方法返回的News
。在NewsRestController
类中,我们按类别过滤新闻。在localhost:8080/sse/news/CAR
上打开浏览器,仅查看汽车新闻
如果要改用RSocket,可以使用如下方法:
@MessageMapping(“新闻{category}”)
fun allNewsByCategory(@DestinationVariable category:String):流量{
log.info(“RSocket,按类别获取所有新闻:{}!”,类别)
返回新闻提供者
.allNews()
.filter{it.category==category}
}
第二选项:
让我们使用@ConnectMapping
将RSocketRequester
存储在HashMap
中(我使用vavr.io)
@控制器
类RSocketConnectionController{
private val log=LoggerFactory.getLogger(RSocketConnectionController::class.java)
private-var-requesterMap:Map=HashMap.empty()
@同步的
private fun getRequesterMap():映射{
返回请求器映射
}
@同步的
private fun addRequester(rSocketRequester:rSocketRequester,clientId:String){
info(“添加请求者{}”,clientId)
requesterMap=requesterMap.put(clientId,rSocketRequester)
}
@同步的
private fun removeRequester(客户端ID:String){
info(“删除请求者{}”,clientId)
requesterMap=requesterMap.remove(clientId)
}
@连接映射(“客户端id”)
趣味onConnect(rSocketRequester:rSocketRequester,客户端ID:String){
val clientIdFixed=clientId.replace(“\”,“”)//检查Serializer为什么要将“添加到字符串”
//rSocketRequester.rsocket().dispose()//拒绝连接
rSocketRequester
.rsocket()
.onClose()
.subscribe(null,null{