• United States+1
  • United Kingdom+44
  • Afghanistan (‫افغانستان‬‎)+93
  • Albania (Shqipëri)+355
  • Algeria (‫الجزائر‬‎)+213
  • American Samoa+1684
  • Andorra+376
  • Angola+244
  • Anguilla+1264
  • Antigua and Barbuda+1268
  • Argentina+54
  • Armenia (Հայաստան)+374
  • Aruba+297
  • Australia+61
  • Austria (Österreich)+43
  • Azerbaijan (Azərbaycan)+994
  • Bahamas+1242
  • Bahrain (‫البحرين‬‎)+973
  • Bangladesh (বাংলাদেশ)+880
  • Barbados+1246
  • Belarus (Беларусь)+375
  • Belgium (België)+32
  • Belize+501
  • Benin (Bénin)+229
  • Bermuda+1441
  • Bhutan (འབྲུག)+975
  • Bolivia+591
  • Bosnia and Herzegovina (Босна и Херцеговина)+387
  • Botswana+267
  • Brazil (Brasil)+55
  • British Indian Ocean Territory+246
  • British Virgin Islands+1284
  • Brunei+673
  • Bulgaria (България)+359
  • Burkina Faso+226
  • Burundi (Uburundi)+257
  • Cambodia (កម្ពុជា)+855
  • Cameroon (Cameroun)+237
  • Canada+1
  • Cape Verde (Kabu Verdi)+238
  • Caribbean Netherlands+599
  • Cayman Islands+1345
  • Central African Republic (République centrafricaine)+236
  • Chad (Tchad)+235
  • Chile+56
  • China (中国)+86
  • Christmas Island+61
  • Cocos (Keeling) Islands+61
  • Colombia+57
  • Comoros (‫جزر القمر‬‎)+269
  • Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)+243
  • Congo (Republic) (Congo-Brazzaville)+242
  • Cook Islands+682
  • Costa Rica+506
  • Côte d’Ivoire+225
  • Croatia (Hrvatska)+385
  • Cuba+53
  • Curaçao+599
  • Cyprus (Κύπρος)+357
  • Czech Republic (Česká republika)+420
  • Denmark (Danmark)+45
  • Djibouti+253
  • Dominica+1767
  • Dominican Republic (República Dominicana)+1
  • Ecuador+593
  • Egypt (‫مصر‬‎)+20
  • El Salvador+503
  • Equatorial Guinea (Guinea Ecuatorial)+240
  • Eritrea+291
  • Estonia (Eesti)+372
  • Ethiopia+251
  • Falkland Islands (Islas Malvinas)+500
  • Faroe Islands (Føroyar)+298
  • Fiji+679
  • Finland (Suomi)+358
  • France+33
  • French Guiana (Guyane française)+594
  • French Polynesia (Polynésie française)+689
  • Gabon+241
  • Gambia+220
  • Georgia (საქართველო)+995
  • Germany (Deutschland)+49
  • Ghana (Gaana)+233
  • Gibraltar+350
  • Greece (Ελλάδα)+30
  • Greenland (Kalaallit Nunaat)+299
  • Grenada+1473
  • Guadeloupe+590
  • Guam+1671
  • Guatemala+502
  • Guernsey+44
  • Guinea (Guinée)+224
  • Guinea-Bissau (Guiné Bissau)+245
  • Guyana+592
  • Haiti+509
  • Honduras+504
  • Hong Kong (香港)+852
  • Hungary (Magyarország)+36
  • Iceland (Ísland)+354
  • India (भारत)+91
  • Indonesia+62
  • Iran (‫ایران‬‎)+98
  • Iraq (‫العراق‬‎)+964
  • Ireland+353
  • Isle of Man+44
  • Israel (‫ישראל‬‎)+972
  • Italy (Italia)+39
  • Jamaica+1876
  • Japan (日本)+81
  • Jersey+44
  • Jordan (‫الأردن‬‎)+962
  • Kazakhstan (Казахстан)+7
  • Kenya+254
  • Kiribati+686
  • Kosovo+383
  • Kuwait (‫الكويت‬‎)+965
  • Kyrgyzstan (Кыргызстан)+996
  • Laos (ລາວ)+856
  • Latvia (Latvija)+371
  • Lebanon (‫لبنان‬‎)+961
  • Lesotho+266
  • Liberia+231
  • Libya (‫ليبيا‬‎)+218
  • Liechtenstein+423
  • Lithuania (Lietuva)+370
  • Luxembourg+352
  • Macau (澳門)+853
  • Macedonia (FYROM) (Македонија)+389
  • Madagascar (Madagasikara)+261
  • Malawi+265
  • Malaysia+60
  • Maldives+960
  • Mali+223
  • Malta+356
  • Marshall Islands+692
  • Martinique+596
  • Mauritania (‫موريتانيا‬‎)+222
  • Mauritius (Moris)+230
  • Mayotte+262
  • Mexico (México)+52
  • Micronesia+691
  • Moldova (Republica Moldova)+373
  • Monaco+377
  • Mongolia (Монгол)+976
  • Montenegro (Crna Gora)+382
  • Montserrat+1664
  • Morocco (‫المغرب‬‎)+212
  • Mozambique (Moçambique)+258
  • Myanmar (Burma) (မြန်မာ)+95
  • Namibia (Namibië)+264
  • Nauru+674
  • Nepal (नेपाल)+977
  • Netherlands (Nederland)+31
  • New Caledonia (Nouvelle-Calédonie)+687
  • New Zealand+64
  • Nicaragua+505
  • Niger (Nijar)+227
  • Nigeria+234
  • Niue+683
  • Norfolk Island+672
  • North Korea (조선 민주주의 인민 공화국)+850
  • Northern Mariana Islands+1670
  • Norway (Norge)+47
  • Oman (‫عُمان‬‎)+968
  • Pakistan (‫پاکستان‬‎)+92
  • Palau+680
  • Palestine (‫فلسطين‬‎)+970
  • Panama (Panamá)+507
  • Papua New Guinea+675
  • Paraguay+595
  • Peru (Perú)+51
  • Philippines+63
  • Poland (Polska)+48
  • Portugal+351
  • Puerto Rico+1
  • Qatar (‫قطر‬‎)+974
  • Réunion (La Réunion)+262
  • Romania (România)+40
  • Russia (Россия)+7
  • Rwanda+250
  • Saint Barthélemy (Saint-Barthélemy)+590
  • Saint Helena+290
  • Saint Kitts and Nevis+1869
  • Saint Lucia+1758
  • Saint Martin (Saint-Martin (partie française))+590
  • Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)+508
  • Saint Vincent and the Grenadines+1784
  • Samoa+685
  • San Marino+378
  • São Tomé and Príncipe (São Tomé e Príncipe)+239
  • Saudi Arabia (‫المملكة العربية السعودية‬‎)+966
  • Senegal (Sénégal)+221
  • Serbia (Србија)+381
  • Seychelles+248
  • Sierra Leone+232
  • Singapore+65
  • Sint Maarten+1721
  • Slovakia (Slovensko)+421
  • Slovenia (Slovenija)+386
  • Solomon Islands+677
  • Somalia (Soomaaliya)+252
  • South Africa+27
  • South Korea (대한민국)+82
  • South Sudan (‫جنوب السودان‬‎)+211
  • Spain (España)+34
  • Sri Lanka (ශ්‍රී ලංකාව)+94
  • Sudan (‫السودان‬‎)+249
  • Suriname+597
  • Svalbard and Jan Mayen+47
  • Swaziland+268
  • Sweden (Sverige)+46
  • Switzerland (Schweiz)+41
  • Syria (‫سوريا‬‎)+963
  • Taiwan (台灣)+886
  • Tajikistan+992
  • Tanzania+255
  • Thailand (ไทย)+66
  • Timor-Leste+670
  • Togo+228
  • Tokelau+690
  • Tonga+676
  • Trinidad and Tobago+1868
  • Tunisia (‫تونس‬‎)+216
  • Turkey (Türkiye)+90
  • Turkmenistan+993
  • Turks and Caicos Islands+1649
  • Tuvalu+688
  • U.S. Virgin Islands+1340
  • Uganda+256
  • Ukraine (Україна)+380
  • United Arab Emirates (‫الإمارات العربية المتحدة‬‎)+971
  • United Kingdom+44
  • United States+1
  • Uruguay+598
  • Uzbekistan (Oʻzbekiston)+998
  • Vanuatu+678
  • Vatican City (Città del Vaticano)+39
  • Venezuela+58
  • Vietnam (Việt Nam)+84
  • Wallis and Futuna+681
  • Western Sahara (‫الصحراء الغربية‬‎)+212
  • Yemen (‫اليمن‬‎)+967
  • Zambia+260
  • Zimbabwe+263
  • Åland Islands+358
Thanks! We'll be in touch in the next 12 hours
Oops! Something went wrong while submitting the form.

Implementing gRPC In Python: A Step-by-step Guide

Nikhil Manchanda

Full-stack Development

In the last few years, we saw a great shift in technology, where projects are moving towards “microservice architecture” vs the old "monolithic architecture". This approach has done wonders for us. 

As we say, “smaller things are much easier to handle”, so here we have microservices that can be handled conveniently. We need to interact among different microservices. I handled it using the HTTP API call, which seems great and it worked for me.

But is this the perfect way to do things?

The answer is a resounding, "no," because we compromised both speed and efficiency here. 

Then came in the picture, the gRPC framework, that has been a game-changer.

What is gRPC?

Quoting the official documentation-

gRPC or Google Remote Procedure Call is a modern open-source high-performance RPC framework that can run in any environment. It can efficiently connect services in and across data centers with pluggable support for load balancing, tracing, health checking and authentication.”

gRPC Implementation


Credit: gRPC

RPC or remote procedure calls are the messages that the server sends to the remote system to get the task(or subroutines) done.

Google’s RPC is designed to facilitate smooth and efficient communication between the services. It can be utilized in different ways, such as:

  • Efficiently connecting polyglot services in microservices style architecture
  • Connecting mobile devices, browser clients to backend services
  • Generating efficient client libraries

Why gRPC

- HTTP/2 based transport - It uses HTTP/2 protocol instead of HTTP 1.1. HTTP/2 protocol provides multiple benefits over the latter. One major benefit is multiple bidirectional streams that can be created and sent over TCP connections parallelly, making it swift. 

- Auth, tracing, load balancing and health checking - gRPC provides all these features, making it a secure and reliable option to choose.

- Language independent communication- Two services may be written in different languages, say Python and Golang. gRPC ensures smooth communication between them.

- Use of Protocol Buffers - gRPC uses protocol buffers for defining the type of data (also called Interface Definition Language (IDL)) to be sent between the gRPC client and the gRPC server. It also uses it as the message interchange format. 

Let's dig a little more into what are Protocol Buffers.

Protocol Buffers

Protocol Buffers like XML, are an efficient and automated mechanism for serializing structured data. They provide a way to define the structure of data to be transmitted. Google says that protocol buffers are better than XML, as they are:

  • simpler
  • three to ten times smaller
  • 20 to 100 times faster
  • less ambiguous
  • generates data access classes that make it easier to use them programmatically

Protobuf are defined in .proto files. It is easy to define them. 

Types of gRPC implementation

1. Unary RPCs:- This is a simple gRPC which works like a normal function call. It sends a single request declared in the .proto file to the server and gets back a single response from the server.

rpc HelloServer(RequestMessage) returns (ResponseMessage);
view raw unary.txt hosted with ❤ by GitHub

2. Server streaming RPCs:- The client sends a message declared in the .proto file to the server and gets back a stream of message sequence to read. The client reads from that stream of messages until there are no messages.

rpc HelloServer(RequestMessage) returns (stream ResponseMessage);
view raw stream.txt hosted with ❤ by GitHub

3. Client streaming RPCs:- The client writes a message sequence using a write stream and sends the same to the server. After all the messages are sent to the server, the client waits for the server to read all the messages and return a response.

rpc HelloServer(stream RequestMessage) returns (ResponseMessage);
view raw client.txt hosted with ❤ by GitHub

4. Bidirectional streaming RPCs:- Both gRPC client and the gRPC server use a read-write stream to send a message sequence. Both operate independently, so gRPC clients and gRPC servers can write and read in any order they like, i.e. the server can read a message then write a message alternatively, wait to receive all messages then write its responses, or perform reads and writes in any other combination.

rpc HelloServer(stream RequestMessage) returns (stream ResponseMessage);

**gRPC guarantees the ordering of messages within an individual RPC call. In the case of Bidirectional streaming, the order of messages is preserved in each stream.

Implementing gRPC in Python

Currently, gRPC provides support for many languages like Golang, C++, Java, etc. I will be focussing on its implementation using Python.

mkdir grpc_example
cd grpc_example
virtualenv -p python3 env
source env/bin/activate
pip install grpcio grpcio-tools

This will install all the required dependencies to implement gRPC.

Unary gRPC 

For implementing gRPC services, we need to define three files:-

  • Proto file - Proto file comprises the declaration of the service that is used to generate stubs (<package_name>_pb2.py and <package_name>_pb2_grpc.py). These are used by the gRPC client and the gRPC server.</package_name></package_name>
  • gRPC client - The client makes a gRPC call to the server to get the response as per the proto file.
  • gRPC Server - The server is responsible for serving requests to the client.

syntax = "proto3";
package unary;
service Unary{
// A simple RPC.
//
// Obtains the MessageResponse at a given position.
rpc GetServerResponse(Message) returns (MessageResponse) {}
}
message Message{
string message = 1;
}
message MessageResponse{
string message = 1;
bool received = 2;
}
view raw unary.proto hosted with ❤ by GitHub

In the above code, we have declared a service named Unary. It consists of a collection of services. For now, I have implemented a single service GetServerResponse(). This service takes an input of type Message and returns a MessageResponse. Below the service declaration, I have declared Message and Message Response.

Once we are done with the creation of the .proto file, we need to generate the stubs. For that, we will execute the below command:-

python -m grpc_tools.protoc --proto_path=. ./unary.proto --python_out=. --grpc_python_out=.
view raw stubs.txt hosted with ❤ by GitHub

Two files are generated named unary_pb2.py and unary_pb2_grpc.py. Using these two stub files, we will implement the gRPC server and the client.

Implementing the Server

import grpc
from concurrent import futures
import time
import unary.unary_pb2_grpc as pb2_grpc
import unary.unary_pb2 as pb2
class UnaryService(pb2_grpc.UnaryServicer):
def __init__(self, *args, **kwargs):
pass
def GetServerResponse(self, request, context):
# get the string from the incoming request
message = request.message
result = f'Hello I am up and running received "{message}" message from you'
result = {'message': result, 'received': True}
return pb2.MessageResponse(**result)
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
pb2_grpc.add_UnaryServicer_to_server(UnaryService(), server)
server.add_insecure_port('[::]:50051')
server.start()
server.wait_for_termination()
if __name__ == '__main__':
serve()
view raw unary_server.py hosted with ❤ by GitHub

In the gRPC server file, there is a GetServerResponse() method which takes `Message` from the client and returns a `MessageResponse` as defined in the proto file.

server() function is called from the main function, and makes sure that the server is listening to all the time. We will run the unary_server to start the server

python3 unary_server.py

Implementing the Client

import grpc
import unary.unary_pb2_grpc as pb2_grpc
import unary.unary_pb2 as pb2
class UnaryClient(object):
"""
Client for gRPC functionality
"""
def __init__(self):
self.host = 'localhost'
self.server_port = 50051
# instantiate a channel
self.channel = grpc.insecure_channel(
'{}:{}'.format(self.host, self.server_port))
# bind the client and the server
self.stub = pb2_grpc.UnaryStub(self.channel)
def get_url(self, message):
"""
Client function to call the rpc for GetServerResponse
"""
message = pb2.Message(message=message)
print(f'{message}')
return self.stub.GetServerResponse(message)
if __name__ == '__main__':
client = UnaryClient()
result = client.get_url(message="Hello Server you there?")
print(f'{result}')
view raw unary_client.py hosted with ❤ by GitHub

In the __init__func. we have initialized the stub using ` self.stub = pb2_grpc.UnaryStub(self.channel)’ And we have a get_url function which calls to server using the above-initialized stub  

This completes the implementation of Unary gRPC service.

Let's check the output:-

Run -> python3 unary_client.py 

Output:-

message: "Hello Server you there?"

message: "Hello I am up and running. Received ‘Hello Server you there?’ message from you"

received: true

Bidirectional Implementation

syntax = "proto3";
package bidirectional;
service Bidirectional {
// A Bidirectional streaming RPC.
//
// Accepts a stream of Message sent while a route is being traversed,
rpc GetServerResponse(stream Message) returns (stream Message) {}
}
message Message {
string message = 1;
}

In the above code, we have declared a service named Bidirectional. It consists of a collection of services. For now, I have implemented a single service GetServerResponse(). This service takes an input of type Message and returns a Message. Below the service declaration, I have declared Message.

Once we are done with the creation of the .proto file, we need to generate the stubs. To generate the stub, we need the execute the below command:-

python -m grpc_tools.protoc --proto_path=.  ./bidirecctional.proto --python_out=. --grpc_python_out=.
view raw stub.txt hosted with ❤ by GitHub

Two files are generated named bidirectional_pb2.py and bidirectional_pb2_grpc.py. Using these two stub files, we will implement the gRPC server and client.

Implementing the Server

from concurrent import futures
import grpc
import bidirectional.bidirectional_pb2_grpc as bidirectional_pb2_grpc
class BidirectionalService(bidirectional_pb2_grpc.BidirectionalServicer):
def GetServerResponse(self, request_iterator, context):
for message in request_iterator:
yield message
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
bidirectional_pb2_grpc.add_BidirectionalServicer_to_server(BidirectionalService(), server)
server.add_insecure_port('[::]:50051')
server.start()
server.wait_for_termination()
if __name__ == '__main__':
serve()

In the gRPC server file, there is a GetServerResponse() method which takes a stream of `Message` from the client and returns a stream of `Message` independent of each other. server() function is called from the main function and makes sure that the server is listening to all the time.

We will run the bidirectional_server to start the server:

python3 bidirectional_server.py
view raw bidirect.txt hosted with ❤ by GitHub

Implementing the Client

from __future__ import print_function
import grpc
import bidirectional.bidirectional_pb2_grpc as bidirectional_pb2_grpc
import bidirectional.bidirectional_pb2 as bidirectional_pb2
def make_message(message):
return bidirectional_pb2.Message(
message=message
)
def generate_messages():
messages = [
make_message("First message"),
make_message("Second message"),
make_message("Third message"),
make_message("Fourth message"),
make_message("Fifth message"),
]
for msg in messages:
print("Hello Server Sending you the %s" % msg.message)
yield msg
def send_message(stub):
responses = stub.GetServerResponse(generate_messages())
for response in responses:
print("Hello from the server received your %s" % response.message)
def run():
with grpc.insecure_channel('localhost:50051') as channel:
stub = bidirectional_pb2_grpc.BidirectionalStub(channel)
send_message(stub)
if __name__ == '__main__':
run()

In the run() function. we have initialised the stub using `  stub = bidirectional_pb2_grpc.BidirectionalStub(channel)’

And we have a send_message function to which the stub is passed and it makes multiple calls to the server and receives the results from the server simultaneously.

This completes the implementation of Bidirectional gRPC service.

Let's check the output:-

Run -> python3 bidirectional_client.py 

Output:-

Hello Server Sending you the First message

Hello Server Sending you the Second message

Hello Server Sending you the Third message

Hello Server Sending you the Fourth message

Hello Server Sending you the Fifth message

Hello from the server received your First message

Hello from the server received your Second message

Hello from the server received your Third message

Hello from the server received your Fourth message

Hello from the server received your Fifth message

For code reference, please visit here.

Conclusion

gRPC is an emerging RPC framework that makes communication between microservices smooth and efficient. I believe gRPC is currently confined to inter microservice but has many other utilities that we will see in the coming years. To know more about modern data communication solutions, check out this blog.

Get the latest engineering blogs delivered straight to your inbox.
No spam. Only expert insights.
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Did you like the blog? If yes, we're sure you'll also like to work with the people who write them - our best-in-class engineering team.

We're looking for talented developers who are passionate about new emerging technologies. If that's you, get in touch with us.

Explore current openings

Implementing gRPC In Python: A Step-by-step Guide

In the last few years, we saw a great shift in technology, where projects are moving towards “microservice architecture” vs the old "monolithic architecture". This approach has done wonders for us. 

As we say, “smaller things are much easier to handle”, so here we have microservices that can be handled conveniently. We need to interact among different microservices. I handled it using the HTTP API call, which seems great and it worked for me.

But is this the perfect way to do things?

The answer is a resounding, "no," because we compromised both speed and efficiency here. 

Then came in the picture, the gRPC framework, that has been a game-changer.

What is gRPC?

Quoting the official documentation-

gRPC or Google Remote Procedure Call is a modern open-source high-performance RPC framework that can run in any environment. It can efficiently connect services in and across data centers with pluggable support for load balancing, tracing, health checking and authentication.”

gRPC Implementation


Credit: gRPC

RPC or remote procedure calls are the messages that the server sends to the remote system to get the task(or subroutines) done.

Google’s RPC is designed to facilitate smooth and efficient communication between the services. It can be utilized in different ways, such as:

  • Efficiently connecting polyglot services in microservices style architecture
  • Connecting mobile devices, browser clients to backend services
  • Generating efficient client libraries

Why gRPC

- HTTP/2 based transport - It uses HTTP/2 protocol instead of HTTP 1.1. HTTP/2 protocol provides multiple benefits over the latter. One major benefit is multiple bidirectional streams that can be created and sent over TCP connections parallelly, making it swift. 

- Auth, tracing, load balancing and health checking - gRPC provides all these features, making it a secure and reliable option to choose.

- Language independent communication- Two services may be written in different languages, say Python and Golang. gRPC ensures smooth communication between them.

- Use of Protocol Buffers - gRPC uses protocol buffers for defining the type of data (also called Interface Definition Language (IDL)) to be sent between the gRPC client and the gRPC server. It also uses it as the message interchange format. 

Let's dig a little more into what are Protocol Buffers.

Protocol Buffers

Protocol Buffers like XML, are an efficient and automated mechanism for serializing structured data. They provide a way to define the structure of data to be transmitted. Google says that protocol buffers are better than XML, as they are:

  • simpler
  • three to ten times smaller
  • 20 to 100 times faster
  • less ambiguous
  • generates data access classes that make it easier to use them programmatically

Protobuf are defined in .proto files. It is easy to define them. 

Types of gRPC implementation

1. Unary RPCs:- This is a simple gRPC which works like a normal function call. It sends a single request declared in the .proto file to the server and gets back a single response from the server.

rpc HelloServer(RequestMessage) returns (ResponseMessage);
view raw unary.txt hosted with ❤ by GitHub

2. Server streaming RPCs:- The client sends a message declared in the .proto file to the server and gets back a stream of message sequence to read. The client reads from that stream of messages until there are no messages.

rpc HelloServer(RequestMessage) returns (stream ResponseMessage);
view raw stream.txt hosted with ❤ by GitHub

3. Client streaming RPCs:- The client writes a message sequence using a write stream and sends the same to the server. After all the messages are sent to the server, the client waits for the server to read all the messages and return a response.

rpc HelloServer(stream RequestMessage) returns (ResponseMessage);
view raw client.txt hosted with ❤ by GitHub

4. Bidirectional streaming RPCs:- Both gRPC client and the gRPC server use a read-write stream to send a message sequence. Both operate independently, so gRPC clients and gRPC servers can write and read in any order they like, i.e. the server can read a message then write a message alternatively, wait to receive all messages then write its responses, or perform reads and writes in any other combination.

rpc HelloServer(stream RequestMessage) returns (stream ResponseMessage);

**gRPC guarantees the ordering of messages within an individual RPC call. In the case of Bidirectional streaming, the order of messages is preserved in each stream.

Implementing gRPC in Python

Currently, gRPC provides support for many languages like Golang, C++, Java, etc. I will be focussing on its implementation using Python.

mkdir grpc_example
cd grpc_example
virtualenv -p python3 env
source env/bin/activate
pip install grpcio grpcio-tools

This will install all the required dependencies to implement gRPC.

Unary gRPC 

For implementing gRPC services, we need to define three files:-

  • Proto file - Proto file comprises the declaration of the service that is used to generate stubs (<package_name>_pb2.py and <package_name>_pb2_grpc.py). These are used by the gRPC client and the gRPC server.</package_name></package_name>
  • gRPC client - The client makes a gRPC call to the server to get the response as per the proto file.
  • gRPC Server - The server is responsible for serving requests to the client.

syntax = "proto3";
package unary;
service Unary{
// A simple RPC.
//
// Obtains the MessageResponse at a given position.
rpc GetServerResponse(Message) returns (MessageResponse) {}
}
message Message{
string message = 1;
}
message MessageResponse{
string message = 1;
bool received = 2;
}
view raw unary.proto hosted with ❤ by GitHub

In the above code, we have declared a service named Unary. It consists of a collection of services. For now, I have implemented a single service GetServerResponse(). This service takes an input of type Message and returns a MessageResponse. Below the service declaration, I have declared Message and Message Response.

Once we are done with the creation of the .proto file, we need to generate the stubs. For that, we will execute the below command:-

python -m grpc_tools.protoc --proto_path=. ./unary.proto --python_out=. --grpc_python_out=.
view raw stubs.txt hosted with ❤ by GitHub

Two files are generated named unary_pb2.py and unary_pb2_grpc.py. Using these two stub files, we will implement the gRPC server and the client.

Implementing the Server

import grpc
from concurrent import futures
import time
import unary.unary_pb2_grpc as pb2_grpc
import unary.unary_pb2 as pb2
class UnaryService(pb2_grpc.UnaryServicer):
def __init__(self, *args, **kwargs):
pass
def GetServerResponse(self, request, context):
# get the string from the incoming request
message = request.message
result = f'Hello I am up and running received "{message}" message from you'
result = {'message': result, 'received': True}
return pb2.MessageResponse(**result)
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
pb2_grpc.add_UnaryServicer_to_server(UnaryService(), server)
server.add_insecure_port('[::]:50051')
server.start()
server.wait_for_termination()
if __name__ == '__main__':
serve()
view raw unary_server.py hosted with ❤ by GitHub

In the gRPC server file, there is a GetServerResponse() method which takes `Message` from the client and returns a `MessageResponse` as defined in the proto file.

server() function is called from the main function, and makes sure that the server is listening to all the time. We will run the unary_server to start the server

python3 unary_server.py

Implementing the Client

import grpc
import unary.unary_pb2_grpc as pb2_grpc
import unary.unary_pb2 as pb2
class UnaryClient(object):
"""
Client for gRPC functionality
"""
def __init__(self):
self.host = 'localhost'
self.server_port = 50051
# instantiate a channel
self.channel = grpc.insecure_channel(
'{}:{}'.format(self.host, self.server_port))
# bind the client and the server
self.stub = pb2_grpc.UnaryStub(self.channel)
def get_url(self, message):
"""
Client function to call the rpc for GetServerResponse
"""
message = pb2.Message(message=message)
print(f'{message}')
return self.stub.GetServerResponse(message)
if __name__ == '__main__':
client = UnaryClient()
result = client.get_url(message="Hello Server you there?")
print(f'{result}')
view raw unary_client.py hosted with ❤ by GitHub

In the __init__func. we have initialized the stub using ` self.stub = pb2_grpc.UnaryStub(self.channel)’ And we have a get_url function which calls to server using the above-initialized stub  

This completes the implementation of Unary gRPC service.

Let's check the output:-

Run -> python3 unary_client.py 

Output:-

message: "Hello Server you there?"

message: "Hello I am up and running. Received ‘Hello Server you there?’ message from you"

received: true

Bidirectional Implementation

syntax = "proto3";
package bidirectional;
service Bidirectional {
// A Bidirectional streaming RPC.
//
// Accepts a stream of Message sent while a route is being traversed,
rpc GetServerResponse(stream Message) returns (stream Message) {}
}
message Message {
string message = 1;
}

In the above code, we have declared a service named Bidirectional. It consists of a collection of services. For now, I have implemented a single service GetServerResponse(). This service takes an input of type Message and returns a Message. Below the service declaration, I have declared Message.

Once we are done with the creation of the .proto file, we need to generate the stubs. To generate the stub, we need the execute the below command:-

python -m grpc_tools.protoc --proto_path=.  ./bidirecctional.proto --python_out=. --grpc_python_out=.
view raw stub.txt hosted with ❤ by GitHub

Two files are generated named bidirectional_pb2.py and bidirectional_pb2_grpc.py. Using these two stub files, we will implement the gRPC server and client.

Implementing the Server

from concurrent import futures
import grpc
import bidirectional.bidirectional_pb2_grpc as bidirectional_pb2_grpc
class BidirectionalService(bidirectional_pb2_grpc.BidirectionalServicer):
def GetServerResponse(self, request_iterator, context):
for message in request_iterator:
yield message
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
bidirectional_pb2_grpc.add_BidirectionalServicer_to_server(BidirectionalService(), server)
server.add_insecure_port('[::]:50051')
server.start()
server.wait_for_termination()
if __name__ == '__main__':
serve()

In the gRPC server file, there is a GetServerResponse() method which takes a stream of `Message` from the client and returns a stream of `Message` independent of each other. server() function is called from the main function and makes sure that the server is listening to all the time.

We will run the bidirectional_server to start the server:

python3 bidirectional_server.py
view raw bidirect.txt hosted with ❤ by GitHub

Implementing the Client

from __future__ import print_function
import grpc
import bidirectional.bidirectional_pb2_grpc as bidirectional_pb2_grpc
import bidirectional.bidirectional_pb2 as bidirectional_pb2
def make_message(message):
return bidirectional_pb2.Message(
message=message
)
def generate_messages():
messages = [
make_message("First message"),
make_message("Second message"),
make_message("Third message"),
make_message("Fourth message"),
make_message("Fifth message"),
]
for msg in messages:
print("Hello Server Sending you the %s" % msg.message)
yield msg
def send_message(stub):
responses = stub.GetServerResponse(generate_messages())
for response in responses:
print("Hello from the server received your %s" % response.message)
def run():
with grpc.insecure_channel('localhost:50051') as channel:
stub = bidirectional_pb2_grpc.BidirectionalStub(channel)
send_message(stub)
if __name__ == '__main__':
run()

In the run() function. we have initialised the stub using `  stub = bidirectional_pb2_grpc.BidirectionalStub(channel)’

And we have a send_message function to which the stub is passed and it makes multiple calls to the server and receives the results from the server simultaneously.

This completes the implementation of Bidirectional gRPC service.

Let's check the output:-

Run -> python3 bidirectional_client.py 

Output:-

Hello Server Sending you the First message

Hello Server Sending you the Second message

Hello Server Sending you the Third message

Hello Server Sending you the Fourth message

Hello Server Sending you the Fifth message

Hello from the server received your First message

Hello from the server received your Second message

Hello from the server received your Third message

Hello from the server received your Fourth message

Hello from the server received your Fifth message

For code reference, please visit here.

Conclusion

gRPC is an emerging RPC framework that makes communication between microservices smooth and efficient. I believe gRPC is currently confined to inter microservice but has many other utilities that we will see in the coming years. To know more about modern data communication solutions, check out this blog.

Did you like the blog? If yes, we're sure you'll also like to work with the people who write them - our best-in-class engineering team.

We're looking for talented developers who are passionate about new emerging technologies. If that's you, get in touch with us.

Explore current openings