Can both ends of a gRPC connection accept method calls? - grpc

From the introduction on gRPC:
In gRPC a client application can directly call methods on a server application on a different machine as if it was a local object, making it easier for you to create distributed applications and services. As in many RPC systems, gRPC is based around the idea of defining a service, specifying the methods that can be called remotely with their parameters and return types. On the server side, the server implements this interface and runs a gRPC server to handle client calls. On the client side, the client has a stub that provides exactly the same methods as the server.
The above paragraph talks about a client and a server, with the former being the one who is invoking methods to the other. What am I wondering is: can the server-end of the connection invoke methods that have been registered on the client?

No, a server cannot invoke calls on the client. gRPC works with HTTP, and HTTP has not had such semantics in the past.
There has been discussion as to various ways to achieve such a feature, but I'm unaware of any work having started or general agreement on a design. gRPC does support bidirectional streaming, which may get you some of what you need. With bidirectional streaming the client can respond to messages from server, but the client still calls the server and only one type of message can be sent for that call.

The protocol does not implement it, but you may pretend this situation.
Define a server method that returns a stream of a ServerRequest message:
import "google/protobuf/any.proto";
service FullDuplex {
rpc WaitRequests (google.protobuf.Any) returns (stream ServerRequest);
}
message ServerRequest {
float someValue = 1;
float anotherAnother = 1;
}
ServerRequest may be an Oneof, so your may receive different types of server requests.
If you need that your client sends back a response for each request, you may create a stream from your client to the server, but you will need to implement a logic in your server side that triggers a timeout waiting for that response.
service FullDuplex {
rpc WaitRequests (stream ClientResponse) returns (stream ServerRequest);
}

What you can do is start a HTTP server in both processes and use clients at each end to initiate communication. There's a bit of boilerplate involved and you have to design a simple handshaking protocol (one end registers with the other, advertising its listen address) but it's not too much work.

Related

gRPC Server-side non-streaming request

I have a gRPC service, and I would like to have a message initiated from the server to get order states from the client. I would like this server=>client request to be synchronous, and the client must initiate the service because of firewall constraints.
I do not see a way to accomplish this with gRPC messages, but I came up with two approaches that may work.
message OrderStates {
repeated OrderState order_state = 1;
}
Option 1 - Non-streaming request + Streaming response
service < existing service > {
rpc OrderStatuses(OrderStates) returns (stream google.protobuf.Empty);
}
With this approach, the client sends OrderStates when it starts up. Each time the server wants to get the current states from the client, it sends the streamed Empty response.
Option 2 - Streaming request + Streaming response
service < existing service > {
rpc OrderStatuses(stream google.protobuf.Empty) returns (stream OrderStates);
}
This is the same as Option 1, but the client sends the initial request as a streaming request.
Any advice would be helpful.
Your approach is the way to accomplish this because you have a constraint that the server cannot act as a gRPC client and initiate a connection to the client acting as a gRPC server which would be the way to achieve this without your constraint.
Because of the constraint that the client must initiate the connection, the only solution is to hold the connection open (with a stream) so that the server may send messages to the client unbidden.
I would go with option #2 and the semantic of the RPC being "Hey server, ping me when you want OrderStates. You must use streaming on the client so that it can send updates.
An unstated optimization may be that, if the client remains alive but does not send an update in response to the server's ping within some timeframe, then the server assumes that there is no update.

How does communication between 2 microservices with grpc work?

Let's say you have an application like a bookstore, and you split it into two simple microservices in the backend ->
Microservice 1: Book purchasers (with Accounts)
Microservice 2: Book list.
Let's say you make a request from the front end, goes into a reverse proxy and the request goes to microservice 1.
How exactly can you visualize how microservice 1 communicates with microservice 2?
Do you containerize microservices, and inside it you have a grpc client and server?
Does the client communicate with microservice 1's server and also microservice 2's server?
In this image here it looks like you containerize the client and server separately...?
How exactly does gRPC communicate between microservices?
IIUC you are asking how the gRPC server implements the functionality described by the protobuf?
I think you're referring to this example
The protobuf compiler generates client and server stubs that you must implement. You can implement these in any language implementation. When you implement the server, you are entirely responsible for ensuring that e.g. ListBooks() (for a shelf) returns any books added to the shelf by CreateBook().
The implementation is independent of gRPC.
rpc ListBooks(ListBooksRequest) returns (ListBooksResponse) {}
rpc CreateBook(CreateBookRequest) returns (Book) {}
gRPC conceptually simply ensures that your client(s) think they're calling a local method: CreateBook() when, in fact, they're calling a local stub that transfer the request over a network to the remote server that receives the CreateBook() request and does something about it.
So, let's focus on the server, it will likely use some form of persistence to record shelves and books. In practice this will be some of database:
type Server struct {
db db
}
func (s *Server) CreateBook(r *pb.CreateBookRequest) {
shelf := s.db.Get(r.get_shelf())
shelf.Add(r.get_book())
}
func (s *server) ListBooksRequest(r *pb.ListBooksRequest) {
shelf := s.db.Get(r.get_shelf())
for _, book := range shelf.Get() {
fmt.Println(book)
}
}
NOTE In the above, the server implementation of the gRPC service includes a database connection that, the gRPC methods use to interact with the database. This could represent some other micro-service too...turtles all the way!
So, to answer your question, somewhere in the bowels of your micro-services, there's some form of shared state (e.g. database or similar) where e.g. books are persisted (in shelves).
Whether the clients and|or servers are containerized, while probably good practice, is irrelevant to the question of how communication occurs.

How can I rely on gRPC's flow control features when making async calls?

Is it possible for a Python gRPC client to asynchronously make simple unary calls (i.e. not streaming calls) to a server as fast as the server can service them but block if an async request can't be written into the client's outgoing connection due to back pressure?
There's very little documentation on the flow control features of the gRPC implementation.
Simply creating a call future doesn't seem to block ever.

TCP sessions with gRPC

Sorry if this question is naive. (gRPC novice here). But, I would like to understand this.
Let's say I have a gRPC service definition like this:
service ABC {
// Update one or more entities.
rpc Write(WriteRequest) returns (WriteResponse) {
}
// Read one or more entities.
rpc Read(ReadRequest) returns (stream ReadResponse)
{
}
// Represents the bidirectional stream
rpc StreamChannel(stream StreamMessageRequest)
returns (stream StreamMessageResponse) {
}
}
Our potential use case would be the server built using C++ and the client using Java. (Not sure is that matters).
I would like to understand how the TCP sessions are managed. The Stream Channel would be used for constant telemetry data streaming between the client and the server. (Constant data transfer, but the bulk from the server to the client).
Does the StreamChannel have a separate TCP session, while for every Write and Read a new session would be established and terminated after the call is done?
Or is there a single TCP session over which all the communication happens?
Again, please excuse me if this is very naive.
Thanks for your time.
Since gRPC uses HTTP/2, it can multiplex multiple RPCs on the same TCP connection. The Channel abstraction in gRPC lets gRPC make connection decisions without the application needing to be strongly-aware.
By default, gRPC uses the "pick first" load balancing policy, which will use a single connection to the backend. All new RPCs would go over that connection.
Connections may die (due to I/O failures) or need to be shut down (various reasons), so gRPC handles reconnecting automatically. Because it can take a very long time to shut down a connection (as gRPC waits for RPCs on that connection to complete), it's still possible that gRPC would have 2 or more connections to the same backend.
So for your case, all the RPCs would initially exist on the same connection. As time goes on new RPCs may use a newer connection, and an old, long-lived StreamChannel RPC may keep the initial TCP connection alive. If that long-lived StreamChannel is closed and re-created by the application, then it could share the newer connection again.
I also posted the same question in grpc.io, and the response I got was inline with the marked answer.
Summary:
If there is no load-balancing, all the RPCs use the same session. The session remains connected across requests. The session establishment happens the first time a call is attempted on the channel.

how does ASP.net "HttpResponse.IsClientConnected" work?

if HTTP is connection-less, how does ASP.net response property, HttpResponse.IsClientConnected detect client is connected or not?
HTTP is not "connection-less" - you still need a connection to receive data from the server; more correctly, HTTP is stateless. Applications running on-top of HTTP will most likely actually be stateful, but HTTP itself is not.
"Connectionless" can also refer to a system using UDP as the transport instead of TCP. HTTP primarily runs over TCP and pretty much every real webserver expects, and returns, TCP messages instead of UDP. You might see HTTP-like traffic in UDP-based protocols like UPnP, but because you want your webpage to be delivered reliably, TCP will always be used instead of UDP.
As for IsClientConnected, when you access that property it calls into the current HttpWorkerRequest which is an abstract class implemented by the current host environment.
IIS7+ implements it such that if it previously received a TCP disconnect message (that sets a field) the method would now return false.
The ISAPI implementation (IIS 6) instead calls into a function within IIS that informs the caller if the TCP client on the current request/response context is still connected, though presumably it works on the same basis: when the webserver receives a TCP timeout, disconnect or connection-reset message it sets a flag and lets execution continue instead of terminating the response-generator thread.
Here's the relevant source code:
HttpResponse.IsClientConnected: http://referencesource.microsoft.com/#System.Web/HttpResponse.cs,80335a4fb70ac25f
IIS7WorkerRequest.IsClientConnected: http://referencesource.microsoft.com/#System.Web/Hosting/IIS7WorkerRequest.cs,1aed87249b1e3ac9
ISAPIWorkerRequest.IsClientConnected: http://referencesource.microsoft.com/#System.Web/Hosting/ISAPIWorkerRequest.cs,f3e25666672e90e8
It all starts with an HTTP request. Inside it, you can, for example, spawn worker threads, that can outlive the request itself. Here is where IsClientConnected comes in handy, so that the worker thread knows that the client has already received the response and disconnected or not.

Resources