grpclib/docs/encoding.rst

108 lines
3.2 KiB
ReStructuredText

Encoding
========
GRPC supports sending messages using any encoding format, and grpclib supports
this feature as well.
By default, gRPC interprets ``application/grpc`` content type as
``application/grpc+proto`` content type. So by default gRPC uses Protocol
Buffers as encoding format.
But why content type has such name with a ``proto`` subtype? This is because
messages in gRPC are sent as length-delimited stream of binary blobs. This
format can't be changed, so content type should always be in the
form of ``application/grpc+{subtype}``, where ``{subtype}`` can be anything you
want, e.g. ``proto``, ``fbs``, ``json``, ``thrift``, ``bson``, ``msgpack``.
Codec
~~~~~
In order to use custom serialization format, you should implement
:py:class:`~grpclib.encoding.base.CodecBase` abstract base class:
.. code-block:: python3
from grpclib.encoding.base import CodecBase
class JSONCodec(CodecBase):
__content_subtype__ = 'json'
def encode(self, message, message_type):
return json.dumps(message, ensure_ascii=False).encode('utf-8')
def decode(self, data: bytes, message_type):
return json.loads(data.decode('utf-8'))
If your format doesn't have interface definition language (like protocol
buffers language) and code-generation tools (like ``protoc`` compiler), you will
have to manage your server-side and client-side code yourself. JSON format
doesn't have such tools, so let's try define our server-side and client side
code.
Naming Conventions
~~~~~~~~~~~~~~~~~~
Even if you don't use Protocol Buffers for messages encoding, this language also
defines coding style for services definition. These rules are related to
service names and method names, which are used by gRPC to build ``:path`` pseudo
header::
:path = /dotted.package.CamelCaseServiceName/CamelCaseMethodName
`Protocol Buffers Style Guide`_ says:
You should use CamelCase (with an initial capital) for both the service name
and any RPC method names.
Server example
~~~~~~~~~~~~~~
.. code-block:: python3
from grpclib.const import Cardinality, Handler
from grpclib.server import Server
class PingServiceHandler:
async def Ping(self, stream):
request = await stream.recv_message()
...
await stream.send_message({'value': 'pong'})
def __mapping__(self):
return {
'/ping.PingService/Ping': Handler(
self.UnaryUnary,
Cardinality.UNARY_UNARY,
None,
None,
),
}
server = Server([PingServiceHandler()], codec=JSONCodec())
Client example
~~~~~~~~~~~~~~
.. code-block:: python3
from grpclib.client import Channel, UnaryUnaryMethod
class PingServiceStub:
def __init__(self, channel):
self.Ping = UnaryUnaryMethod(
channel,
'/ping.PingService/Ping',
None,
None,
)
channel = Channel(codec=JSONCodec())
ping_stub = PingServiceStub(channel)
...
await ping_stub.Ping({'value': 'ping'})
.. _Protocol Buffers Style Guide: https://developers.google.com/protocol-buffers/docs/style