kotlinx.rpc 0.3.0 Help

Transport

Transport layer exists to abstract from the RPC requests logic and focus on delivering and receiving encoded RPC messages in kRPC Protocol. This layer is represented by RPCTransport interface. It supports two message formats — string and binary, and depending on which serialization format you choose, one or the other will be used.

Ktor transport

The kotlinx.rpc library provides integration with the Ktor framework with the in-house RPC protocol. This includes both server and client APIs. Under the hood, the library uses WebSocket plugin to create a RPCTransport and send and receive messages through it.

Client

kotlinx.rpc provides a way to plug-in into existing Ktor clients with your RPC services. To do that, the following DSL can be used:

val ktorClient = HttpClient { installRPC { // this: RPCConfigBuilder.Client waitForServices = true } } val rpcClient: KtorRPCClient = ktorClient.rpc("ws://localhost:4242/services") { // this: HttpRequestBuilder rpcConfig { // this: RPCConfigBuilder.Client waitForServices = false } } // access WebSocketSession that created the connection rpcClient.webSocketSession // create RPC service val myService: MyService = rpcClient.withService<MyService>()

Note that in this example, only the latter defined RPCConfig will be used.

Server

kotlinx.rpc provides a way to plug-in into existing server routing with your RPC services. To do that, the following DSL can be used:

fun Application.module() { install(RPC) { // this: RPCConfigBuilder.Server waitForServices = true } routing { rpc("/services") { // this RPCRoute, inherits WebSocketSession rpcConfig { // this: RPCConfigBuilder.Server waitForServices = false } registerService<MyService> { ctx -> MyServiceImpl(ctx) } registerService<MyOtherService> { ctx - MyOtherServiceImpl(ctx) } // etc } } }

Ktor application example

An example code for a Ktor web application may look like this:

// ### COMMON CODE ### @Serializable data class ProcessedImage( val url: String, val numberOfCats: Int, val numberOfDogs: Int ) interface ImageService : RPC { suspend fun processImage(url: Srting): ProcessedImage } // ### CLIENT CODE ### val client = HttpClient { installRPC { serialization { json() } } } val service = client.rpc("/image-recognizer").withService<ImageService>() service.processImage(url = "https://catsanddogs.com/cats/1") // ### SERVER CODE ### class ImageServiceImpl(override val coroutineContext: CoroutineContext) : ImageService { // user defined classes private val downloader = Downloader() private val recognizer = AnimalRecognizer() override suspend fun processImage(url: Srting): ProcessedImage { val image = downloader.loadImage(url) return ProcessedImage( url, recognizer.getNumberOfCatsOnImage(image), recognizer.getNumberOfDogsOnImage(image) ) } } fun main() { embeddedServer(Netty, port = 8080) { install(RPC) { serialization { json() } } routing { rpc("/image-recognizer") { registerService<ImageService> { ctx -> ImageServiceImpl(ctx) } } } }.start(wait = true) }

For more details and complete examples, see the code samples.

Other transports

Generally, there are no specific guidelines on how RPC should be set up for different transports, but structures and APIs used to develop integration with Ktor should outline the common approach. You can provide your own transport and even your own fully implemented protocols, while the library will take care of code generation.

Last modified: 20 June 2024