We get asked about this all the time. In general, gRPC and RSocket attempt to solve different problems. gRPC is an RPC framework using HTTP/2. RSocket is a message passing network layer that is lower level. So, a developer would use RSocket directly for low-level interaction and has the option to use RSocket-RPC as an easy to use RPC layer that sits on top of RSocket.
Now, let’s look more specifically at differences between gRPC and RSocket.
gRPC and RSocket are at different layers in the stack. gRPC is at OSI layer 7 — an RPC layer built on top of HTTP/2. RSocket is an OSI layer 5/6 that models Reactive Streams semantics over a network. Reactive Streams provides a way to model asynchronous streams with back-pressure. At a 10,000-foot view, you would use RSocket to build something like gRPC — RSocket-RPC for example — which would be at the OSI (application) layer 7.
gRPC is not a protocol in the traditional sense. It is comprised of HTTP/2 headers, generated code, and conventions in protobuf. There is not enough information in what is being transmitted across the network to determine what is happening. For instance, gRPC can have a unary call make a call to a stream if there is mismatch in the protobuf. gRPC design is analogous to Web Services.
RSocket is a layer 5/6 binary protocol with a formal definition. You do not need to generate code to determine what is happening with RSocket — you only need to understand the protocol.
gRPC is opinionated about the payload you are sending — for all intents and purposes it’s protobuf.
RSocket is not opinionated and lets you send opaque bytes.
Sending bytes is more flexible as you do not have to define a protobuf for sending data. RSocket could be used to transport database queries just as easily as transfer a picture.
gRPC is designed to work with HTTP/2 semantics. If you want to send it over a different transport you must mimic HTTP/2 semantics. Across the network it is effectively bound to HTTP/2 and TCP.
RSocket only needs a duplex connection — something that can send and receive bytes. This can be TCP, a File, WebSockets, etc. It is very easy in RSocket to receive a call from a browser via WebSockets and then make a call to a server using TCP. The interaction would feel transparent and identical to a developer.
gRPC does not work in a web browser. Support in a web browser requires additional code to be generated and deployed.
RSocket works in a web browser via Websockets. No new code is needed — all that’s needed is to start an RSocket server that accepts WebSocket connections.
Client / Server Interaction
gRPC has a traditional client/server model because it is based on HTTP/2 and RPC semantics. In a gRPC, a client connects to the server, but the server cannot make calls to a client. Additionally, because gRPC is an RPC layer you can only make calls defined in your protobuf file.
RSocket does not have a client server interaction in the traditional HTTP sense. In RSocket, once a client connects to a server, they both can be a requester and responder. This means that after they are connected, RSocket is fully duplex and server’s requester can initiate calls to a client’s requester — i.e. a server can make calls to a web browser once connected.
gRPC’s flow control is byte-based because it is ultimately HTTP/2 flow control. It does not have a concept of application flow control because it does not send messages between participants with this information. HTTP/2 flow control has limited value because TCP also provides byte level flow control.
RSocket’s flow control is application level flow control. This is governed by how many messages a can a recipient handle. RSocket has a REQUEST_N frame that is shared between the participants requesting a number of messages. Once those messages are handled, it requests more messages. This is independent of the underlying transports byte-level flow control. This means if a service slows down, it can slow down the number messages before network buffers are full. This information is propagated between services.
In gRPC, cancellations generally only come from a client to a server. The server is only supposed to send a cancel if a sequence is incomplete.
RSocket cancellations are sent between requester and responder. It does not matter which side is a client or a server. Additionally, because of reactive stream semantics they are automatically propagated between services.
gRPC is limited to HTTP/2 load balancing. You cannot load balance unary gRPC calls differently than Streaming calls. This means that a short-lived request/reply call is load-balanced the same as a long-running stream.
RSocket load balancing can happen at a transport level and the application level. This is because there is complete information about what is happening encapsulated in the message. You can create a load balancer that treats request/replies differently than request/streams.
gRPC is coupled to the URL and service that you are sending the request to. There must be a recipient on the other end to receive the call because the message is sent directly to them. If the recipient doesn’t exist, the call will fail. This is still true if you place a proxy between the client and server. The proxy then must exist. If it does not exist, the request will fail. If there isn’t anything for the proxy to call then its requests will fail as well.
RSocket is messaging passing based — it is not opinionated about where its frames are sent. The recipient does not need to exist on the other side when the message is sent because the message is sent via a stream — the frames in the stream merely need to handled. Also, as the frames in the streams are handled according to the RSocket protocol, it doesn’t matter where they are handled or who handles them.
This means with RSocket it is possible to model interactions like multi-cast and broadcast.
gRPC sends information over the network as an HTTP/2 stream. This means for request/reply and fire/forget interactions, more frames must be sent over the network. Also, with gRPC, information about streaming is encapsulated in application code. This means it is only possible to tell the difference between a GET and POST by looking at application code.
RSocket has a concept of interactions built into the protocol. This is similar in concept (but not function) to HTTP methods. The interaction models are request-reply (send one/receive one), fire-and-forgot (send one), request stream (send one/receive many), and request channel (send many/receive many). These are included in frames in the protocol and allow for implementations to optimize for the different interaction models. Streams send over a single connection.
The gRPC API is limited to the code generated by gRPC, and you must use an RPC-style interaction.
RSocket is more flexible. If you like RPC-style semantics, there is RSocket-RPC. If don’t like that, you can use Spring Messaging. And, if you are comfortable with lower-level application programming, you can use RSocket directly.
Both gRPC and RSocket provided non-blocking APIs.
Keep Alive and Health Checks
gRPC has both keep alive and a health check. The health check is special protobuf that can be implemented.
RSocket just uses a keep alive. If the keep alive fails, the connection is terminated.
RSocket supports connection resumption. If a connection fails, RSocket can re-connect automatically to give the impression of a stable connection.
RSocket Java Specific Differences
RSocket Java provides an API that lets developers manage the Netty ByteBuf lifecycle allowing for zero copy.
Automatic Request Batching
RSocket Java takes advantage of Reactive Stream semantics to batch requests over the network. Using this information, RSocket can determine to flush data when network buffers are full, or if it needs to flush based on the information about the stream. The result is that RSocket will send the optimal amount of data over the network based on what it observes without having to be tuned by the user.
RSocket Java uses the flyweight pattern to encode and decode frames. These flyweights do not allocate any data. Instead, they act as a lens over a ByteBuf to read without the overhead of decoding the frame.
RSocket Programming APIs
RSocket is an OSI layer 5/6 protocol, and while it can be used directly depending on the use-case, it is much easier to develop applications with a layer 7 protocol. Currently there is RPC, IPC, and soon to added GraphQL support. The most mature right now is RSocket RPC, which is a protobuf auto-marshalling layer that sits on top of RSocket to make message passing convenient. At its core it is not RPC — it is a message-passing layer that generates boilerplate methods and auto-marshals messages. It does this with a protobuf compiler. Unlike gRPC, which creates HTTP/2 messages with information in headers, RSocket RPC generates binary encoded frames. The frames have been standardized into the RSocket extension frames.
RSocket RPC shares RSocket’s concept of a requester and responder. This means that a client can connect to a server, and that server can make calls to a service the client is hosting.
All information about an interaction is encapsulated in the frames sent between requester and responder. You do not need generated code to determine how to handle the message. This means that you do not need generated code to understand RSocket RPC calls. If you do not like RPC-generated code, you can instead use IPC-style code to handle a message. The IPC code also allows you to specify what serializers to use and allows you to use send methods other than protobuf.
GraphQL support is being added to use the IPC-style code. This will support query, mutation, and subscription using GraphQL.