Zero: A fast and high performance Python RPC framework for building microservices or distributed servers
Tags: python, microservices, webdev, opensource
Published: 2021-10-03 12:20:21

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
```

server.py
```python
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()
```

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 - 

```python
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()
```

Simple and usual. You need performance? We also support `async` 😃

```python
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())
```

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
```python
def pythagoras(msg: typing.Tuple[int, int]) -> int:
    a, b = msg
    return a**2 + b**2
```

client.py
```python
def get_pythagoras(a: int, b:int) -> int:
    return zero_server.call("pythagoras", (4, 5))
```

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
```

This will generate a client code for you like this - 

```python
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)
```

And you can use like this -

```python
from my_client import RpcClient, zero_client

client = RpcClient(zero_client)

if __name__ == "__main__":
    client.echo("Hi there!")
    client.hello_world(None)
```

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 👇