Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Any plans on building reactive client? #317

Open
gytis-ivaskevicius opened this issue Jun 14, 2020 · 11 comments
Open

Any plans on building reactive client? #317

gytis-ivaskevicius opened this issue Jun 14, 2020 · 11 comments
Labels
proposal Enhancement idea or proposal

Comments

@gytis-ivaskevicius
Copy link

Just like the title says - I was wondering if you guys are planning on building a reactive version of this client using ProjectReactor

@gytis-ivaskevicius
Copy link
Author

@sasbury @RichardHightower

@sasbury
Copy link
Contributor

sasbury commented Jun 15, 2020

I don't have plans for that. I think there is some talk of doing an API revisit for all the clients, so perhaps during that. Perhaps Richard has some insight.

@brimworks
Copy link
Collaborator

@agcom @jameshilliard @gytis-ivaskevicius a few questions about this, since it is a substantial undertaking:

  • Is the desire to replace the per-thread Dispatcher with an RXJava based interface such that all publishes would be emitted from a single thread?
  • ...or do you want it to go "deeper" such that the ConnectionReader/ConnectionWriter/connect/drain threads used by the NatsConnection are no longer needed and it is full end-to-end async nio?

If it is the later option, what is the use-case, since typically you just have a single NatsConnection in your JVM, and thus moving to nio would not likely create a big performance improvement.

@agcom
Copy link

agcom commented Aug 14, 2021

@brimworks a reactive client is easier to use; an end-to-end reactive client is easier to use and more efficient.

Is the efficiency of an end-to-end reactive client considerable? My blind opinion: in extreme conditions yes; e.g. a short-lived app, running on a mobile device, a military software, a developer with OCD, and an over-engineer.

If it's gonna be a refactor, just a reactive interface is sufficient; at some point, throughout optimizing internal codes, there is nothing to do other than making tasks lazy by chaining them to their reactive listeners. If there is gonna be a big-bang, there shouldn't be any reason against making it end-to-end from the beginning.

@scottf
Copy link
Contributor

scottf commented Aug 15, 2021

@agcom So my take is that you believe strongly in reactive architecture and that's fine, but it's hard to see your comments as anything other than opinion. But that's okay, you can address that easily. Why specifically is a reactive client easier to use and why is it more efficient, for what use cases do you mean? How would these use cases apply to the common developer where traffic is not high? Where it is high? Very distributed? Not at all distributed? How will being reactive improve applications where the problem isn't the number of transactions, but where the information is located, i.e. on the edge? Is it more secure or do you have to do additional work to be secure? Is it faster or slower, or is that even the point? What are the trade offs of using a reactive pattern? These are an example of things I'd like to see in analysis so we can determine if, given our limited resources, undertaking this development is a good idea.

@agcom
Copy link

agcom commented Aug 16, 2021

@scottf I'm not the right guy to do such a survey on. Below says why I think non-blocking is more efficient.

I always strive for perfection; and believe that going non-blocking is the way, but of-course it might not be economically efficient.

In terms of efficiency, non-blocking architecture uses less resources, because it spawns fewer OS threads, and memory allocations & using CPU cycles happen on demand; e.g. binary to text to object conversation happens when someone is waiting for it. It's faster because benchmarks do say so; e.g. consider Apache Tomcat vs Netty (Vertex, Reactor Netty).

Currently in Java, writing non-blocking code is harder because you can't write synchronous style non-blocking code, in contrast with Kotlinx coroutines and Golang; Project Loom is to rescue in the future.

@agcom
Copy link

agcom commented Aug 16, 2021

@scottf @brimworks And hey, I don't think making a fully non-blocking client is a substantial undertake. I wrote a simple (publish & sub) fully non-blocking Nats client on top of Kotlinx coroutines and NIO2 (async NIO) in 1 day, most of the time was reading Nats docs.

Ignore my comment if I'm being naive.

@brimworks
Copy link
Collaborator

So, I'm still trying to wrap my mind around how this would be implemented @agcom. Specifically, there is the JDK Flow API, and then there is RXJava, AkkaStreams, and ProjectReactor which allow interop with JDK Flow API.

Is the idea here that a NATS connection would implement the JDK Flow API. Specifically, the Flow.Publisher API for publishing messages and Flow.Subscriber for subscriptions?

If so, then using blocking I/O with a reader and writer thread would just be an "implementation detail"... and I haven't seen any use-cases where anyone is trying to scale up the number of instances of a NATS connection, and thus I don't see any benefit in implementing on top of a non-blocking socket.

Is someone on this thread willing to sketch out an API for this so there is something more concrete to work off of?

@agcom
Copy link

agcom commented Aug 17, 2021

@brimworks JDK Flow, RXJava, AkkaStreams, and ProjectReactor, all allow interop with each other directly or indirectly through JDK Flow or org.reactivestreams (org.reactivestreams suggest migrating to JDK Flow).

It would be wise to use only JDK Flow API in ABI (application binary interface, e.g. Connection interface) --- no transitive dependencies on external reactive libraries and more hidden impl details.

Yes, deep down it can use a blocking TCP client with two threads (reader & writer), which is completely fine. But, I mean, using a non-blocking TCP client isn't that hard; specially if it's a rich TCP client, e.g. Reactor Netty. I guess the only difficulty of using one is handling buffers (e.g. pooling, concatenating), in contrast to having a byte stream.

...haven't seen any use-cases where anyone is trying to scale up the number of instances of NATS connections...

Me neither, but it isn't impossible to do so.

@brimworks
Copy link
Collaborator

Actually, one other benefit of going full end-to-end reactive is avoiding the need for the OS to context-switch threads when doing basic PING/PONG type of operations. For example, if doing a health check by subscribing to an _INBOX, and then PUBlishing a message to this _INBOX requires context switching from the health check thread, over to the writer thread, then to the reader thread to read the response, then to the dispatcher thread handling the _INBOX subscription, and then finally to the health check thread. I've seen some really bad round-trip times in this scenario, although I have not put a profiler on it yet. However, I have a hunch that doing 4 thread context switches is probably adding a lot of unnecessary latency when running in the context of a high load on a single CPU VM, since many other processes may be ran between these context switches.

@srikrishnacj
Copy link

+1

@bruth bruth added proposal Enhancement idea or proposal and removed 🙋customer requested labels Aug 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
proposal Enhancement idea or proposal
Projects
None yet
Development

No branches or pull requests

7 participants