gRPC over protocol buffers
In gRPC, a client application can directly call a method on a server application on a different machine as if it were 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 (referred to as just a client in some languages) that provides the same methods as the server
gRPC clients and servers can run and talk to each other in a variety of environments - from servers inside Google to your own desktop - and can be written in any of gRPC’s supported languages. So, for example, you can easily create a gRPC server in Java with clients in Go, Python, or Ruby. In addition, the latest Google APIs will have gRPC versions of their interfaces, letting you easily build Google functionality into your applications.
Installation
$ pip install grpcio grpcio-tools
We will build our first gRPC service
create a folder to keep all our files together, I named mine grpc, and in that folder, create a subfolder named protos
This is what it looks like for me:
create a folder to keep all our files together, I named mine grpc, and in that folder, create a subfolder named protos
This is what it looks like for me:
Write the proto definition file and place it in the proto folder, name it hellounh.proto
syntax = "proto3"; service Greeter { rpc HelloUNH (HelloRequest) returns (HelloReply) {} rpc SayHelloAgain (HelloRequest) returns (HelloReply) {} } message HelloRequest { string name = 1; int32 number = 2; } message HelloReply { string message = 1; }
then run
$ python -m grpc_tools.protoc -I protos --python_out=. --grpc_python_out=. protos/hellounh.proto
this will generate 2 .py files in your directory:
hellounh_pb2_grpc.py
hellounh_pb2.py
that will serve as the interface for your server and client programs
We now can create a server:
#provides interface for asynchronous execution from concurrent import futures import grpc #classes for the messages described in .proto import hellounh_pb2 #classes for the services described in .proto import hellounh_pb2_grpc #in .proto we had #service Greeter { #we now extend it and implement the methods class Greeter(hellounh_pb2_grpc.GreeterServicer): #in .proto we had #rpc HelloUNH (HelloRequest) returns (HelloReply) {} #we now implement it def HelloUNH(self, request, context): resp = "" #HelloUNH takes a HelloRequest that contains number and name #you can access them over the request object for i in range(0, request.number): resp+= 'Hello, {}!\n'.format(request.name) #HelloUNH returns a HelloReply which contains a single string - message return hellounh_pb2.HelloReply(message=resp) def serve(): #spin up a server that can serve up to 10 clients #extra requests get queued server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) #add_GreeterServicer_to_server was added when we compiled the .proto hellounh_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server) server.add_insecure_port('[::]:50051') server.start() server.wait_for_termination() if __name__ == '__main__': serve()
and the client:
import grpc #classes for the messages described in .proto import hellounh_pb2 #classes for the services described in .proto import hellounh_pb2_grpc def run(): #connect to the server and port where the server is running with grpc.insecure_channel('localhost:50051') as channel: stub = hellounh_pb2_grpc.GreeterStub(channel) #invoke the remote method response = stub.HelloUNH(hellounh_pb2.HelloRequest(name='Gula', number=4)) print("Greeter client received: " + response.message) if __name__ == '__main__': run()
The SayHelloAgain method in the .proto was not implemented, please implement it and call it from the client
#provides interface for asynchronous execution from concurrent import futures import grpc #classes for the messages described in .proto import hellounh_pb2 #classes for the services described in .proto import hellounh_pb2_grpc #in .proto we had #service Greeter { #we now extend it and implement the methods class Greeter(hellounh_pb2_grpc.GreeterServicer): #in .proto we had #rpc HelloUNH (HelloRequest) returns (HelloReply) {} #we now implement it def HelloUNH(self, request, context): resp = "" #HelloUNH takes a HelloRequest that contains number and name #you can access them over the request object for i in range(0, request.number): resp+= 'Hello, {}!\n'.format(request.name) #HelloUNH returns a HelloReply which contains a single string - message return hellounh_pb2.HelloReply(message=resp) def SayHelloAgain(self, request, context): return hellounh_pb2.HelloReply(message="Well, Hello Again!") def serve(): #spin up a server that can serve up to 10 clients #extra requests get queued server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) #add_GreeterServicer_to_server was added when we compiled the .proto hellounh_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server) server.add_insecure_port('[::]:50051') server.start() server.wait_for_termination() if __name__ == '__main__': serve()
Client:
import grpc #classes for the messages described in .proto import hellounh_pb2 #classes for the services described in .proto import hellounh_pb2_grpc def run(): #connect to the server and port where the server is running with grpc.insecure_channel('localhost:50051') as channel: stub = hellounh_pb2_grpc.GreeterStub(channel) #invoke the remote method response = stub.HelloUNH(hellounh_pb2.HelloRequest(name='Gula', number=4)) print("Greeter client received: " + response.message) with grpc.insecure_channel('localhost:50051') as channel: stub = hellounh_pb2_grpc.GreeterStub(channel) #invoke the remote method response = stub.SayHelloAgain(hellounh_pb2.HelloRequest(name='Gula', number=4)) print("Greeter client received: " + response.message) if __name__ == '__main__': run()