Browse Source
Added actor rsocket conn controller
Added actor rsocket conn controller
Some refactorings Added counterpart of existing rsocket connection controller that is based on kotlin coroutines and actor modelmaster
Rohan Sircar
4 years ago
6 changed files with 189 additions and 27 deletions
-
29frontend/src/main.ts
-
9src/main/kotlin/com/example/demo/controller/HomeRestController.kt
-
34src/main/kotlin/com/example/demo/controller/RsocketActor.kt
-
38src/main/kotlin/com/example/demo/controller/RsocketConnectionController.kt
-
86src/main/kotlin/com/example/demo/controller/RsocketConnectionControllerAsync.kt
-
20src/main/kotlin/com/example/demo/controller/RsocketDemoController.kt
@ -0,0 +1,34 @@ |
|||||
|
package com.example.demo.controller |
||||
|
|
||||
|
import io.vavr.collection.HashMap |
||||
|
import io.vavr.collection.Map |
||||
|
import kotlinx.coroutines.CompletableDeferred |
||||
|
import kotlinx.coroutines.CoroutineScope |
||||
|
import kotlinx.coroutines.ObsoleteCoroutinesApi |
||||
|
import kotlinx.coroutines.channels.actor |
||||
|
import org.springframework.messaging.rsocket.RSocketRequester |
||||
|
|
||||
|
// Message types for counterActor |
||||
|
sealed class Message |
||||
|
data class AddRequester(val requester: RSocketRequester, val clientId: String) : Message() |
||||
|
data class RemoveRequester(val clientId: String) : Message() |
||||
|
data class GetRequesters(val response: CompletableDeferred<Map<String, RSocketRequester>>) : Message() |
||||
|
|
||||
|
// This function launches a new rsocket actor |
||||
|
@ObsoleteCoroutinesApi // Actor API is planned to be overhauled so this annotation is required for now |
||||
|
fun CoroutineScope.rsocketActor() = actor<Message> { |
||||
|
var requesterMap: Map<String, RSocketRequester> = HashMap.empty() // actor state |
||||
|
for (msg in channel) { // iterate over incoming messages |
||||
|
when (msg) { |
||||
|
is AddRequester -> { |
||||
|
println("Actor adding requester") |
||||
|
requesterMap = requesterMap.put(msg.clientId, msg.requester) |
||||
|
} |
||||
|
is RemoveRequester -> { |
||||
|
println("Actor removing requester") |
||||
|
requesterMap = requesterMap.remove(msg.clientId) |
||||
|
} |
||||
|
is GetRequesters -> msg.response.complete(requesterMap) |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,86 @@ |
|||||
|
package com.example.demo.controller |
||||
|
|
||||
|
import com.example.demo.model.Message |
||||
|
import com.example.demo.model.User |
||||
|
import kotlinx.coroutines.* |
||||
|
import org.slf4j.LoggerFactory |
||||
|
import org.springframework.messaging.handler.annotation.MessageMapping |
||||
|
import org.springframework.messaging.handler.annotation.Payload |
||||
|
import org.springframework.messaging.rsocket.RSocketRequester |
||||
|
import org.springframework.messaging.rsocket.annotation.ConnectMapping |
||||
|
import org.springframework.stereotype.Controller |
||||
|
import io.vavr.collection.Map |
||||
|
import org.springframework.messaging.handler.annotation.MessageExceptionHandler |
||||
|
|
||||
|
@Controller |
||||
|
class RsocketConnectionControllerAsync : CoroutineScope { |
||||
|
private val log = LoggerFactory.getLogger(RsocketConnectionControllerAsync::class.java) |
||||
|
private val job = Job() |
||||
|
override val coroutineContext = Dispatchers.Unconfined + job |
||||
|
|
||||
|
@ObsoleteCoroutinesApi |
||||
|
private val myRsocketActor = rsocketActor() |
||||
|
|
||||
|
@ObsoleteCoroutinesApi |
||||
|
suspend fun addRequester(rSocketRequester: RSocketRequester, clientId: String) { |
||||
|
log.info("adding requester {}", clientId) |
||||
|
myRsocketActor.send(AddRequester(rSocketRequester, clientId)) |
||||
|
} |
||||
|
|
||||
|
@ObsoleteCoroutinesApi |
||||
|
suspend fun removeRequester(clientId: String) { |
||||
|
log.info("removing requester {}", clientId) |
||||
|
myRsocketActor.send(RemoveRequester(clientId)) |
||||
|
} |
||||
|
|
||||
|
@ObsoleteCoroutinesApi |
||||
|
@ConnectMapping(value = ["client-id2"]) |
||||
|
fun onConnect( |
||||
|
rSocketRequester: RSocketRequester, |
||||
|
@Payload user: User |
||||
|
) { |
||||
|
launch(coroutineContext) { |
||||
|
val clientId = user.id.toString() |
||||
|
addRequester(rSocketRequester, clientId) |
||||
|
rSocketRequester |
||||
|
.rsocket() |
||||
|
.onClose() |
||||
|
.subscribe(null, null, { |
||||
|
log.info("{} just disconnected", clientId) |
||||
|
launch(coroutineContext) { removeRequester(clientId) } |
||||
|
}) |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@ObsoleteCoroutinesApi |
||||
|
@MessageMapping("private.news.2") |
||||
|
fun privateNews(message: Message) { |
||||
|
launch { |
||||
|
val res = CompletableDeferred<Map<String, RSocketRequester>>() |
||||
|
myRsocketActor.send(GetRequesters(res)) |
||||
|
res |
||||
|
.await() |
||||
|
.filterKeys { key -> key == message.toUser || key == message.fromUser } |
||||
|
.values() |
||||
|
.forEach { requester -> |
||||
|
println("Sending message") |
||||
|
sendMessage(requester, message) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private fun sendMessage(requester: RSocketRequester, message: Message) = |
||||
|
requester |
||||
|
.route("user.queue.reply") |
||||
|
.data(message) |
||||
|
.send() |
||||
|
.subscribe() |
||||
|
|
||||
|
@MessageExceptionHandler |
||||
|
suspend fun handleException(ex: IllegalArgumentException): String { |
||||
|
delay(10) |
||||
|
return "${ex.message} handled" |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,20 @@ |
|||||
|
package com.example.demo.controller |
||||
|
|
||||
|
import com.example.demo.service.UserService |
||||
|
import kotlinx.coroutines.coroutineScope |
||||
|
import org.springframework.messaging.handler.annotation.DestinationVariable |
||||
|
import org.springframework.messaging.handler.annotation.MessageMapping |
||||
|
import org.springframework.stereotype.Component |
||||
|
|
||||
|
@Component |
||||
|
class RsocketDemoController(private val userService: UserService) { |
||||
|
@MessageMapping("messages.findAll") |
||||
|
suspend fun all() = coroutineScope { |
||||
|
userService.getAllMessages() |
||||
|
} |
||||
|
|
||||
|
@MessageMapping("users.{name}") |
||||
|
suspend fun getUser(@DestinationVariable name: String) = coroutineScope { |
||||
|
userService.getUserByName(name) |
||||
|
} |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue