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

Goals & Motivations for RS Network Java APIs #1

Open
benjchristensen opened this issue Mar 4, 2015 · 16 comments
Open

Goals & Motivations for RS Network Java APIs #1

benjchristensen opened this issue Mar 4, 2015 · 16 comments

Comments

@benjchristensen
Copy link
Contributor

I propose collaborating on reactive, stream oriented Java APIs for common network protocols. It would primarily consist of interfaces, not concrete implementations, but could have some abstract/concrete classes such as HttpResponse, Cookie, StatusCode and other such utilities common to a protocol.

I am interested in network APIs for both client and server for TCP, UDP, HTTP/1, HTTP/2, WebSockets, SSE, etc as well as Reactive Streams IO (RS.io) when it exists. I and colleagues like @NiteshKant have been exploring these APIs at Netflix and interacting with others such as @jbrisbin and @smaldini to see if these APIs make sense standardized or not. We can obviously create them on our own, but they feel so similar to what each of us keep defining, I'm curious as to whether there is value in have standardized RS Network APIs (RS.net).

The proposed value would be that different underlying transport implementations could expose the same RS Network APIs that different RS implementations could all consume.

For example, with HTTP/1, we all need to model these things:

  • request headers and body
  • response headers and body
  • mime/content types
  • status codes
  • etc

Using Reactive Streams, an HTTP/1 response is most like something like this:

Publisher<HttpResponse<T>> response = submitGet(...);

The HttpResponse then would have the status codes, headers, etc and expose the body as another Publisher, something perhaps like this:

Publisher<T> content = httpResponse.getContent();

There are only so many ways that these various network protocols can be combined with RS since RS is defined, and the protocols are defined.

If these RS.net interfaces existed, then a networking library like Akka.io, Spray, Netty, etc could implement the transport layer however it deems best and expose the various protocols using RS.net interfaces.

Then consumers could use existing bindings for Akka Streams, RxJava, Reactor etc for consuming, transforming and composing the IO.

I see value in this layer of abstraction between transport implementations and stream implementations and will end up building it for my needs. Before I do it on my own I want to find out if others see value in this and would like to participate.

@jbrisbin
Copy link

jbrisbin commented Mar 4, 2015

I see value in this collaborative effort as well. Reactor has already implemented Reactive Streams over Netty and I'm currently taking a deep look at our Reactive Streams implementation to see if we can extract the necessary parts to create a generalized set of classes that these network libraries can use to expose the standard interfaces you mention using the Reactive Streams spec.

@benjchristensen
Copy link
Contributor Author

@reactive-streams/contributors any others see value in this type of collaboration?

@sirthias
Copy link

sirthias commented Mar 5, 2015

I think the amount of effort required to pull this off increases significantly with the stack-level of the protocol. TCP and UDP are going to be much simpler than HTTP.
If you look at the HTTP model for akka-http you'll see that the API surface is huge compared to TCP/UDP. Reaching cross-organizational consensus on all of this would be a massive undertaking that I am not sure it worth the effort. Especially since the model would have to be touched much more frequently (e.g. to add previously unmodelled things) than for the lower-level protocols.

I'd propose to focus the effort on TCP first. Based on our experience with Akka's RS TCP API coming up with an agreed standard interface will already be quite a feat.

@jbrisbin
Copy link

jbrisbin commented Mar 5, 2015

@sirthias I think the scope for this project is much, much smaller than what akka-http (or any user-facing library) provides.

There isn't actually that much to support HTTP 1 and 2 at their most basic. The models only need to cover what all HTTP servers have in common: verbs, URLs, query parameters, headers, and arbitrary content. Other things like Content-Types and utilities to do content negotiation would be provided by the frameworks that actually use this low-level library, not by the library itself.

It's unlikely that an end user of the composition library wrapped around this base library would ever interact with these low-level models, so there's no need to make them complicated by assumptions about how users want to interact with those models.

Additionally, Reactive Streams is already an agreement among parties about how components should interact. All this project would be is a "Reactive Streams" way to do some standard, HTTP-like things.

@sirthias
Copy link

sirthias commented Mar 5, 2015

@jbrisbin Yes, I understand. What I pointed to above is only the HTTP model that Akka provides.
I.e. "only" the basic data structures for requests, responses, entities, headers, methods, URI, content- and media-types, status codes, charsets, encodings, byte ranges, languages, cache control directives, etc. That is a ton of stuff already, even though it contains almost no logic.

I guess what you have in mind is something like a Reactive-Streams enabled Rack API for the JVM. If performance is not a primary concern then this might be doable, I agree. The standard API could fall back to untyped strings wherever possible (e.g. for methods, URIs, header names and header values).
Of course, apart form general type-safety concerns, a largely string-based API between an application and an underlying HTTP server would not be as efficient as one with dedicated types for the core headers. So there is also a performance vs. API surface tradeoff here.

As a performance-minded user I'd always want to go for a server implementation that directly works with the proper, "full-blown" model that my application operates on. I wouldn't want to have the server produce a throw-away intermediate representation of everything that then needs to "upgraded" once more on my side of the API boundary.

All this project would be is a "Reactive Streams" way to do some standard, HTTP-like things.

The thing that probably has the deepest RS involvement is the entity model.
Here is a reduced high-level overview over the entity model in Akka-HTTP:
http://spray.io/jfokus/#/32

As you can see even that is tightly coupled with everything else, because we want to

  1. tie our Content-Type model directly to the entities
  2. use Akka's ByteString abstractions
  3. use Akka-Stream's RS types (rather than "raw" RS Publishers)
  4. have the multipart model be properly integrated

This shows that just coming up with a reduced standard API for the message entities that is flexible enough to be supported by something like Akka HTTP will not be that easy.

@jbrisbin
Copy link

jbrisbin commented Mar 5, 2015

I don't think this is an easy task, no matter what. But the differences between HTTP libraries on the Java side seem to be far fewer than the similarities, which is, I think, the point of this exercise. Maybe that's not the case with Scala/Akka; not being all that familiar with either, I can't say. :)

I was hoping that this library would focus less on the concrete types used to "model" HTTP in the traditional sense--meaning what an end-user would expect to interact with. RS-IO wouldn't make any assumptions about what specific type a header value would have to be, for example. It would simply provide a Publisher<Header> that a concrete implementation could subscribe to that would deal with a Header as it becomes available. After all, an HTTP header at its most basic consists only of a key and value of the native types provided by the transport library. One could choose to represent that as a Map or something more specific as in the case of Akka-HTTP. It could decode it or choose to leave it untouched until asked for by the user. Those decisions would be made by the higher-level library that provided composition over these primitives and implemented a proper Subscriber<Header>.

In my view, RS-IO would simply ReactiveStream-Ize the process of doing network IO using the types provided by the transport library and pass them on to the composition library while integrating the Reactive Streams contract between the two (e.g. providing a means to communicate backpressure all the way to the source in a standard way, rather than having each library use its own proprietary method of doing so). If using Netty, this type would likely be a ByteBuf, but if using another transport library (XNIO maybe?), would be something else.

RS-IO could provide default implementations and specific types for some of these things if it wanted to, but I don't think it would be necessary to get the basic functionality.

@NiteshKant
Copy link

Great discussion here folks!

I totally believe that it is not a small task to get to consensus on these APIs. I also concur with @sirthias point that it is much better to break this down into different protocols and starting with TCP would be a very good indication of how feasible it would be to design these APIs. I would not attempt discussing and evolving all protocol APIs at the same time.

I see two possible outcomes of this attempt:

  • We all can reasonably converge on the interaction model over different protocols. I am sure this will take a long time and effort.
  • We can discuss this for a while and come to a conclusion that it is not reasonable to define an API without compromising the performance, usability or implementation overhead of adhering to such an API. IOW, creating a "false abstraction" in the name of having a common abstraction.

The primary question here now is:

Whether there is a value in attempting to define such an abstraction?

It is understood that it is a huge effort to do so.

I would like to clarify (what I think @jbrisbin has hinted in his conversations) that this is not intended to be a framework, it is intended to be a library that models different transports as RS APIs. It should not intend to prescribe what are the performance best practices, ease of use, implementation details, etc as they are opinionated. However, it should not limit implementations from doing all those. This is part of the reason why involvement of the community is important i.e. to make sure we don't make choices which do not suit most of the use cases out there.

I will go ahead and create issues to discuss various semantics of a TCP client and server to give an idea of the depth of the APIs we intend to create here. It will be great to discuss upper bounds of how much this API should define.

@jbrisbin
Copy link

jbrisbin commented Mar 5, 2015

@NiteshKant thanks for clarifying. That is indeed what I was trying to get at in my own, roundabout way. :)

Looking forward to the issues on specific APIs as I know several people here at Pivotal are watching this closely and are anxious to discuss some of these things.

@NiteshKant
Copy link

No worries @jbrisbin!

I have created issues #2 #3 and #4 to discuss TCP client, server APIs

@viktorklang
Copy link

Apologies for the belated reply, my great excuse is that I've been on vacation...

Wouldn't it be much easier to define protocol level interop rather than trying to agree on end-user API?
Scala is going to want to use scala collections, Option-types etc, Java JDK types, Clojure clojure types etc?

Or, In case I misunderstood the scope, would someone mind elaborating?

@benjchristensen
Copy link
Contributor Author

I believe it is incorrect to call this project net-jvm, it should be net-java as it would target Java types which would not be desirable from Scala, Clojure, etc.

As I originally expected, this is a long-shot and looks like it's probably not worth pursuing.

Does anyone else think this is worth pursuing or should I kill this repo?

@benjchristensen
Copy link
Contributor Author

By the way, @jbrisbin @NiteshKant @smaldini and I are giving this a try in a more concrete form at https://github.com/reactive-ipc/reactive-ipc-jvm.

@viktorklang
Copy link

@benjchristensen Any reason to keep it in its own org? (protocol/spec seems to make sense given RS scoping?)

@benjchristensen
Copy link
Contributor Author

Reasoning is primarily that Reactive Streams is for the spec, Reactive-IPC is a concrete implementation including things like:

  • Netty 4 Transport
  • Servlet 3.1 Transport
  • Aeron Transport
  • RxJava 1.x impl of APIs
  • RxJava 2.x impl of APIs
  • TCP, HTTP, memcached, Kafka, etc
  • targeting Java 7+

So I want Reactive Streams IO as a network protocol and Reactive IPC would be a concrete implementation.

Then this project was conceived as standardization of the Java network APIs using RS with Reactive IPC as a concrete implantation.

@viktorklang
Copy link

Oh, so the protocol spec will be RS and the application thereof will be in reactive-ipc?

@benjchristensen
Copy link
Contributor Author

Yes, my current thinking on what I'm trying to achieve is:

  • Reactive-Streams-JVM => JVM specification for Reactive Streams
  • Reactive-Streams-IO => RS network protocol specification
  • Reactive-Streams-Net-JVM => this repo attempting to define RS oriented Java APIs for various network protocols
  • Reactive-IPC => a concrete Java implementation of various network protocols (including Reactive-Streams-IO) on top of various transports (Netty 4, Netty 5, Aeron, Servlet 3.1, etc) using Reactive-Streams-JVM interfaces and behaviors along with RS composition implementations such as RxJava 1.x/2.x.

If this repo "Reactive-Streams-Net-JVM" succeeds, then the intent would be for Reactive-IPC to concretely implement the Reactive-Streams-Net-JVM APIs. It seems that is not a realistic goal, or should at least wait until we can show Reactive-IPC working and then try to extract interfaces from it if that is indeed useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants