https://kotlinlang.org logo
Title
r

rocketraman

06/08/2022, 2:25 PM
Rsocket is supposed to be bidirectional right? In kotlin-rsocket, I'm not seeing any methods like
requestResponse
on
RSocketServer
. I thought potentially I would create a single
Transport
and then use it to configure both a
ConnectionAcceptor
for the server as well as attach it to an
RSocketConnector
. However, since we have different
ServerTransport
and
ClientTransport
types, this doesn't seem possible either. What am I missing?
I see the way the chat sample works is that the server stores a set of channels for each client, and then uses that to send data over a stream. Works but more complicated than I would expect it to be, and requires implementing the interaction models manually from streams.
o

Oleg Yukhnevich

06/08/2022, 3:20 PM
As I understand, you want to initiate requests from server and handle them from client, right?
so, let's go simple on both client and server side, you can get
requester
from
ConnectionAcceptor
+ you can get it on client side as result of calling
connect
but configuring of
ConnectionAcceptor
is different for client and server for server you provide it on calling bind but for client, you need to provide it during building
RSocketConnector
so like:
RSocketConnector {
  acceptor {
    //you can access requester here and return responder
  }
}
In meantime, I have WIP prototype of new configuration API, where client and server configuration will be more aligned, and you will be able to configure responder, or use requester, or configure resume/lease using same function/class both for client and server
Regarding Client/Server Transport instances They are different, as ServerTransport can accept MULTIPLE connections, while ClientTransport create exactly ONE connection similarly how with TCP sockets - server socket can accept multiple connections so why now API is different
BTW: what do you want to achieve in the end? 🙂
r

rocketraman

06/08/2022, 3:34 PM
Client side using
connect
is clear. Server-side is unclear. I pass the acceptor to
bindIn
, but the logic that does
accept
in server-side is called for me, so how do I obtain the
RSocket
?
BTW: what do you want to achieve in the end?
So basically I have some server-to-server communication. Both sides periodically need to initiate some request/response or fire-forget to the other side. Obviously one side has to be the client and the other side the server from a TCP perspective, but I thought the bi-di nature of the rsocket protocol would allow me to abstract away that detail and allow either side to easily use an RSocket to send/receive messages.
Also I do understand that the server can receive connections from multiple clients. Ideally I'd be able to obtain a list of current connections on the server-side and send to some or all of them.
o

Oleg Yukhnevich

06/08/2022, 3:36 PM
on server side you can access
requester: RSocket
from
ConnectionAcceptorContext
, or I miss something?
so like this:
RSocketServer().bind(transport) {
  val rSocket: RSocket = requester //comes from ConnectionAcceptorContext
  launch {
    delay(1000)
    requester.requestResponse(somePayload)
  }
  createResponder()
}
r

rocketraman

06/08/2022, 3:42 PM
Hmm... well, practically, the calls to requestor won't happen in the
ConnectionAcceptor
definition. Would you use some kind of call-back function to save the
requestor
RSocket elsewhere for re-use? How would one track the state of these rsockets i.e. connected/disconnected, etc?
o

Oleg Yukhnevich

06/08/2022, 3:53 PM
for now state can be tracked only via
coroutineContext
of
RSocket
requester - but for this I also have some prototypes to improve (also you can keep track an issue https://github.com/rsocket/rsocket-kotlin/issues/205) can you also look at https://github.com/rsocket/rsocket-kotlin/blob/master/samples/chat/server/src/commonMain/kotlin/acceptor.kt#L45 - why it's not acceptable for you? I don't know your specifics, but I don't understand why you can't just launch coroutine inside acceptor (like in my previous comment) and put server code there or you can even just create RSocket class which accepts requester and provide it as responder
class ServerRSocket(private val requester: RSocket): RSocket {
  //you can also launch some coroutines here
  override fun requestResponse(payload: Payload): Payload {
    //here comes responder logic, and you can call requester here
  }
}
or if you want to get symmetric API for server, like after calling
bind
you will receive RSocket instance of requester? If so, why?
r

rocketraman

06/08/2022, 4:10 PM
Subscribed to 205. That would be very useful API surface. My server implementation is based on the chat sample you sent. I can launch a coroutine and put server code there for sure, but obviously events happen elsewhere in the application which trigger the calls to that
requestor
. I can create some channels/callbacks/flows or something to communicate between these external triggers and that code in the acceptor but that's all significantly more code than just being able to obtain a reference to the connected RSocket's directly and use them.
o

Oleg Yukhnevich

06/08/2022, 4:13 PM
so If I understand correctly you want something like this:
val serverRequester: RSocket = RSocketServer().bind(transport) { createServerResponder() }
so it suspend until first connection appears and you can just use this requester everywhere in your app, right?
r

rocketraman

06/08/2022, 4:20 PM
Yeah, pretty much. Well, that would be fine for my current use case, but wouldn't work well with multiple connections. Maybe return some kind of
RSocketClients
wrapper which would allow access to all clients (maybe along with some state information as per issue 205).
That could also expose a way to register listeners for client connection/disconnection events.
o

Oleg Yukhnevich

06/08/2022, 4:33 PM
Now I see, but Im not sure, that we can now provide something like this out of the box as I see a lot of issues like • what do we need to do, if clients connect with different setup payload - in that case may be someone will need to split those client connections per some parameter; f.e. different microservices communicating peer to peer, or some rsocket orchestration where multiple DIFFERENT microservices connects to other microservice (which is started via RSocketServer) • this will only give user ability to just iterate over clients (requster, connection stats, etc), right? nothing more? should it be Channel or unlimited SharedFlow to be able subscribe on changes several times? may be some selector also would be needed? • do we need to handle some server-server specific case vs client-server, as for me it should be the same API for both cases It will be really good, if you will create an issue in rsocket-kotlin for your specific use case, and we can then decide on what can be done here