Skip to main content

Zero is a simple and fast Python RPC framework

Project description

Zero is a simple Python framework (RPC like) to build fast and high performance microservices or distributed servers



Features:

  • Zero provides faster communication (see benchmarks) between the microservices using zeromq under the hood.
  • Zero uses messages for communication and traditional client-server or request-reply pattern is supported.
  • Support for both async and sync.
  • The base server (ZeroServer) utilizes all cpu cores.
  • Code generation! See example 👇

Philosophy behind Zero:

  • Zero learning curve: The learning curve is tends to zero. Just add functions and spin up a server, literally that's it! The framework hides the complexity of messaging pattern that enables faster communication.
  • ZeroMQ: An awesome messaging library enables the power of Zero.

Let's get started!

Getting started 🚀

Ensure Python 3.8+

pip install zeroapi

For Windows, tornado needs to be installed separately (for async operations). It's not included with zeroapi because for linux and mac-os, tornado is not needed as they have their own event loops.

  • Create a server.py

    from zero import ZeroServer
    
    app = ZeroServer(port=5559)
    
    @app.register_rpc
    def echo(msg: str) -> str:
        return msg
    
    @app.register_rpc
    async def hello_world() -> str:
        return "hello world"
    
    
    if __name__ == "__main__":
        app.run()
    
  • The RPC functions only support one argument (msg) for now.

  • Also note that server RPC functions are type hinted. Type hint is must in Zero server. Supported types can be found here.

  • Run the server

    python -m server
    
  • Call the rpc methods

    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()
    
  • Or using async client -

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

Serialization 📦

Default serializer

Msgspec is the default serializer. So msgspec.Struct (for high performance) or dataclass or any supported types can be used easily to pass complex arguments, i.e.

from dataclasses import dataclass
from msgspec import Struct
from zero import ZeroServer

app = ZeroServer()

class Person(Struct):
    name: str
    age: int
    dob: datetime

@dataclass
class Order:
    id: int
    amount: float
    created_at: datetime

@app.register_rpc
def save_person(person: Person) -> bool:
    # save person to db
    ...

@app.register_rpc
def save_order(order: Order) -> bool:
    # save order to db
    ...

Return type

The return type of the RPC function can be any of the supported types. If return_type is set in the client call method, then the return type will be converted to that type.

@dataclass
class Order:
    id: int
    amount: float
    created_at: datetime

def get_order(id: str) -> Order:
    return zero_client.call("get_order", id, return_type=Order)

Code Generation 🤖

Easy to use code generation tool is also provided!

After running the server, like above, call the server to get the client code. This makes it easy to know what functions are available in the local or remote server.

Using zero.generate_client generate client code for even remote servers using the --host and --port options.

python -m zero.generate_client --host localhost --port 5559 --overwrite-dir ./my_client

It will generate client 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)

Use the client -

from my_client import RpcClient, zero_client

client = RpcClient(zero_client)

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

Currently, the code generation tool supports only ZeroClient and not AsyncZeroClient.

WIP - Generate models from server code.

Important notes! 📝

  • ZeroServer should always be run under if __name__ == "__main__":, as it uses multiprocessing.
  • ZeroServer creates the workers in different processes, so anything global in your code will be instantiated N times where N is the number of workers. So if you want to initiate them once, put them under if __name__ == "__main__":. But recommended to not use global vars. And Databases, Redis, other clients, creating them N times in different processes is fine and preferred.
  • The methods which are under register_rpc() in ZeroServer should have type hinting, like def echo(msg: str) -> str:

Let's do some benchmarking! 🏎

Zero is all about inter service communication. In most real life scenarios, we need to call another microservice.

So we will be testing a gateway calling another server for some data. Check the benchmark/dockerize folder for details.

There are two endpoints in every tests,

  • /hello: Just call for a hello world response 😅
  • /order: Save a Order object in redis

Compare the results! 👇

Benchmarks 🏆

11th Gen Intel® Core™ i7-11800H @ 2.30GHz, 8 cores, 16 threads, 16GB RAM (Docker in Ubuntu 22.04.2 LTS)

(Sorted alphabetically)

Framework "hello world" (req/s) 99% latency (ms) redis save (req/s) 99% latency (ms)
aiohttp 14391.38 10.96 9470.74 12.94
aiozmq 15121.86 9.42 5904.84 21.57
fastApi 9590.96 18.31 6669.81 24.41
sanic 18790.49 8.69 12259.29 13.52
zero(sync) 24805.61 4.57 16498.83 7.80
zero(async) 22716.84 5.61 17446.19 7.24

Roadmap 🗺

  • Make msgspec as default serializer
  • Add support for async server (currently the sync server runs async functions in the eventloop, which is blocking)
  • Add pub/sub support

Contribution

Contributors are welcomed 🙏

Please leave a star ⭐ if you like Zero!

"Buy Me A Coffee"

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

zeroapi-0.5.2.tar.gz (30.4 kB view details)

Uploaded Source

Built Distribution

zeroapi-0.5.2-py3-none-any.whl (38.8 kB view details)

Uploaded Python 3

File details

Details for the file zeroapi-0.5.2.tar.gz.

File metadata

  • Download URL: zeroapi-0.5.2.tar.gz
  • Upload date:
  • Size: 30.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.1 CPython/3.11.6

File hashes

Hashes for zeroapi-0.5.2.tar.gz
Algorithm Hash digest
SHA256 91578210c799f9229a1284dd3a89cd2fdc9c56834189692160f084c9cf4921a6
MD5 9f262d6b6ec16dfef48455ccddb2043d
BLAKE2b-256 8669a13f450e257a71bbd65e0d91b030a1077f133f9188b283af0e6d92b75f8e

See more details on using hashes here.

File details

Details for the file zeroapi-0.5.2-py3-none-any.whl.

File metadata

  • Download URL: zeroapi-0.5.2-py3-none-any.whl
  • Upload date:
  • Size: 38.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.1 CPython/3.11.6

File hashes

Hashes for zeroapi-0.5.2-py3-none-any.whl
Algorithm Hash digest
SHA256 a928b75bc116ee6af3d30c738aac0f36bd8b009809c3c6f0020034b1cb3708c9
MD5 af655b8521aa4464366f70d752e85dbb
BLAKE2b-256 ac12b418cf2f61dcb8e864026663644f1dbf26b8a5e9de162b385d9c61902878

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page