What is RPC and how it works
When I got my current job, it didn’t take long for me to meet something called ‘fastrpc’ which is a technology my company opensourced (if you clicked be warned, it’s not the most friendly repository, or technology for that matter). It’s XML-RPC, but sent in binary, therefore fast. As a junior I understond neither of those words, so I was just content with considering it magic and focusing on the important.
So if you don’t know what any of this means, good! I’ll try and explain. RPC stands for Remote Procedure Call, to put it in our human language, it lets you call a function that you yourself didn’t define anywhere, on someone else’s server.
>>> magical_server.add(3, 4)
7
The XML is there just to tell you what structure it uses to communicate. So while you think you might be calling a function add, in reality that is not what’s going on and you’re just somehow sending some XML. In case of JSON-RPCit’s JSON, FastRPC sends XML in binary and gRPC can support multiple things. Yes, it is just an alternative to communicating via REST API.
So how does it work? I wanted to have our FastRPC server run on something else than C metaserver-thingy which somehow injected itself into python and ran it, only supporting Python 2… And some folks made it work in Python frameworks Tornado and Aiohttp. Yes, that is correct, RPC uses HTTP to communicate, so any website can also be a RPC server — brilliant. As a Flask lover, without any need for async (that’s what Tornado and Aiohttp are for the nonpythonistas), I decided to write (copy and edit from aiohttp) a FastRPC server extension for Flask. Let’s dissect it and see what’s really happening.
Function handle()
is what we are interested first, this is where incoming HTTP request comes in:
accept_cts = self._get_accepted_content_types()
if not accept_cts.intersection(self.allowed_content_types):
logging.warning('No supported content type requested: "%s"', accept_cts)
return 'Content types in Accept not supported', 400if request.headers['Content-Type'] not in self.allowed_content_types:
logging.warning('Content-Type "%s" is not supported', request.headers['Content-Type'])
return 'Content-Type not supported', 400if fastrpc:
args, method_name = fastrpc.loads(request.data)
else:
args, method_name = xmlrpc.loads(request.data)logging.info('Calling method %s with args: %s', method_name, args)return self._create_response(method_name, args, accept_cts)
Let’s try and not go to deep, judging from the method name and log message, we can assume that _get_accepted_content_types
takes a loot at the incoming request and reads what the client says he’s gonna accept. I should now mention that I made the extension work without our internal fastrpc and that fastrpc itself is capable of handling XML-RPC. So there is a HTTP request with Accept
header.
Then there’s Content-Type
header — that probably tells us what type of data we should be reading. Good. Easy peasy so far, classic HTTP.
Hmm, fastrpc.loads()
and xmlrpc.loads()
doesn’t look like rocket sciente either. It’s just going to go through the data of the request and extract arguments and name of the method from the XML. So some XML parsing… Wait, is that it? Do we just then call the method with the arguments and return it back? Surely the magic I always thought this is must be somewhere!
This is the interesting part of _create_response
:
headers = {}
if FRPC_CONTENT_TYPE in accept_cts and fastrpc:
use_binary = True
headers['Content-Type'] = FRPC_CONTENT_TYPE
else:
use_binary = False
headers['Content-Type'] = RPC_CONTENT_TYPEif fastrpc:
body = fastrpc.dumps((response,), methodresponse=True, useBinary=use_binary)
else:
body = xmlrpc.dumps((response,), methodresponse=True)headers['Content-Length'] = str(len(body))
resp = Response(body, headers=headers)
return resp
So, if we have fastrpc
and they accept fastrpc
… Yeah that’s not interesting for our explanation of RPC servers. So we will return header with Content-Type
we will be returning, okay. We will then dump our response into XML, sure. Then we’ll see what the Content-Lenth
is and add that to the header and… That’s it. That’s RPC. No magic to be seen here. Now you hopefully understand.
Congratulations! This pre-recorded congratulations assumes you have mastered the principles of porta… RPC.