DEV Community

Cover image for Zero: A fast and high performance Python RPC framework for building microservices or distributed servers
Azizul Haque Ananto
Azizul Haque Ananto

Posted on • Edited on

Zero: A fast and high performance Python RPC framework for building microservices or distributed servers

I have been working with Zero for couple of months and I can feel that Zero has potential to make the difference in writing microservices. Rather than thinking about boilerplate coding and REST services, you can focus on making things, writing business logic - this is the goal of Zero.

Let's take a look at a simple example -

Install it first

pip install zeroapi
Enter fullscreen mode Exit fullscreen mode

server.py

from zero import ZeroServer

def echo(msg: str) -> str:
    return msg

async def hello_world() -> str:
    return "hello world"

if __name__ == "__main__":
    app = ZeroServer(port=5559)
    app.register_rpc(echo)
    app.register_rpc(hello_world)
    app.run()
Enter fullscreen mode Exit fullscreen mode

This is it! The ZeroServer utilizes the cpu cores unlike other Python programs.

If you save run python3 server.py this will run several processes and distribute the tasks among them.

So you register a function here and return something. So this works like a RPC. The main difference with RPC is - the functions can take only one parameter, that we call a message in-short msg. There are several types we support now - most of basic types like int, float, str, bool, list, dict, tuple, set. And some typing types typing.List, typing.Tuple, typing.Dict, typing.Union, typing.Optional. We have a plan to support Pydantic ๐Ÿ™Œ

Now let's make a client for the server -

from zero import ZeroClient

zero_client = ZeroClient("localhost", 5559)

def echo():
    resp = zero_client.call("echo", "Hi there!")
    print(resp)

def hello():
    resp = zero_client.call("hello_world", None)
    print(resp)

if __name__ == "__main__":
    echo()
    hello()
Enter fullscreen mode Exit fullscreen mode

Simple and usual. You need performance? We also support async ๐Ÿ˜ƒ

import asyncio

from zero import AsyncZeroClient

zero_client = AsyncZeroClient("localhost", 5559)

async def echo():
    resp = await zero_client.call("echo", "Hi there!")
    print(resp)

async def hello():
    resp = await zero_client.call("hello_world", None)
    print(resp)

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(echo())
    loop.run_until_complete(hello())
Enter fullscreen mode Exit fullscreen mode

Run and play with these.

If you need to send two, three ... n parameters. You can just wrap in a list, send and unwrap the list in server. Like this -

server.py

def pythagoras(msg: typing.Tuple[int, int]) -> int:
    a, b = msg
    return a**2 + b**2
Enter fullscreen mode Exit fullscreen mode

client.py

def get_pythagoras(a: int, b:int) -> int:
    return zero_server.call("pythagoras", (4, 5))
Enter fullscreen mode Exit fullscreen mode

So you can send arbitrary number of arguments like this.

Are you lazy like me? Zero also generates client code! ๐Ÿ™Œ

If you run -

python -m zero.generate_client --host localhost --port 5559 --overwrite-dir ./my_client
Enter fullscreen mode Exit fullscreen mode

This will generate a client code for you like this -

import typing  # remove this if not needed
from typing import List, Dict, Union, Optional, Tuple  # remove this if not needed
from zero import ZeroClient


zero_client = ZeroClient("localhost", 5559)


class RpcClient:
    def __init__(self, zero_client: ZeroClient):
        self._zero_client = zero_client

    def echo(self, msg: str) -> str:
        return self._zero_client.call("echo", msg)

    def hello_world(self, msg: str) -> str:
        return self._zero_client.call("hello_world", msg)
Enter fullscreen mode Exit fullscreen mode

And you can use like this -

from my_client import RpcClient, zero_client

client = RpcClient(zero_client)

if __name__ == "__main__":
    client.echo("Hi there!")
    client.hello_world(None)
Enter fullscreen mode Exit fullscreen mode

So try Zero and let me know ๐Ÿ“ฉ
Checkout the examples here - https://github.com/Ananto30/zero/tree/main/examples

If you like Zero please leave a star at GitHub: https://github.com/Ananto30/zero

And let's talk more in the comments ๐Ÿ‘‡

Top comments (2)

Collapse
 
xtofl profile image
xtofl

Wow - that's not an easy treat. It looks quite slim. What guidelines would you suggest to choose a lightweight service like this instead of 'that other' lightweight service, gRPC?

Collapse
 
ananto30 profile image
Azizul Haque Ananto

Ah the famous question ๐Ÿ˜…
The answer is gRPC is more famous than Zero ๐Ÿ˜œ

Jokes apart, gRPC has much more control with many languages support and with a huge community maintained by Google. If you use gRPC you should.

Zero is a newborn baby and it only supports Python. To be honest the power of Zero over gRPC is - Zero focus on faster delivery of a product and service. I am working on supporting Pydantic, using that the code generated client will be self sufficient, no need to design proto or anything, and these clients can be generated with just connecting to the server. Where in gRPC you will find it's really hard to know about the classes and object types and every time you need to follow the proto and generated code etc.

But it's not only the code generation or static typing war. It's just preference, to run a ZeroServer you need no other prior knowledge and it's super simple, you even don't need to know about the wsgi things. Also the protocol is different, it's zeromq over tcp. So there is no war actually, I love gRPC and I prefer it. But Zero is something that wants to have a place in the Python ecosystem for faster and easy development.

Please share your thoughts, I would like to know people's view on this and how Zero can evolve ๐Ÿ˜ƒ