Package: zeroapi
Zero is a simple Python framework (RPC like) to build fast and high performance microservices or distributed serversFeatures:Zero providesfaster communication(seebenchmarks) between the microservices usingzeromqunder the hood.Zero uses messages for communication and traditionalclient-serverorrequest-replypattern is supported.Support for bothasyncandsync.The base server (ZeroServer)utilizes all cpu cores.Code generation! Seeexample👇Philosophybehind 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 zeroapiFor Windows,tornadoneeds to be installed separately (for async operations). It's not included withzeroapibecause for linux and mac-os, tornado is not needed as they have their own event loops.Create aserver.pyfromzeroimportZeroServerapp=ZeroServer(port=5559)@app.register_rpcdefecho(msg:str)->str:returnmsg@app.register_rpcasyncdefhello_world()->str:return"hello world"if__name__=="__main__":app.run()TheRPC functions only support one argument(msg) for now.Also note that serverRPC functions are type hinted. Type hint ismustin Zero server. Supported types can be foundhere.Run the serverpython-mserverCall the rpc methodsfromzeroimportZeroClientzero_client=ZeroClient("localhost",5559)defecho():resp=zero_client.call("echo","Hi there!")print(resp)defhello():resp=zero_client.call("hello_world",None)print(resp)if__name__=="__main__":echo()hello()Or using async client -importasynciofromzeroimportAsyncZeroClientzero_client=AsyncZeroClient("localhost",5559)asyncdefecho():resp=awaitzero_client.call("echo","Hi there!")print(resp)asyncdefhello():resp=awaitzero_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 serializerMsgspecis the default serializer. Somsgspec.Struct(for high performance) ordataclassor anysupported typescan be used easily to pass complex arguments, i.e.fromdataclassesimportdataclassfrommsgspecimportStructfromzeroimportZeroServerapp=ZeroServer()classPerson(Struct):name:strage:intdob:datetime@dataclassclassOrder:id:intamount:floatcreated_at:datetime@app.register_rpcdefsave_person(person:Person)->bool:# save person to db...@app.register_rpcdefsave_order(order:Order)->bool:# save order to db...Return typeThe return type of the RPC function can be any of thesupported types. Ifreturn_typeis set in the clientcallmethod, then the return type will be converted to that type.@dataclassclassOrder:id:intamount:floatcreated_at:datetimedefget_order(id:str)->Order:returnzero_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.Usingzero.generate_clientgenerate client code for even remote servers using the--hostand--portoptions.python-mzero.generate_client--hostlocalhost--port5559--overwrite-dir./my_clientIt will generate client like this -importtyping# remove this if not neededfromtypingimportList,Dict,Union,Optional,Tuple# remove this if not neededfromzeroimportZeroClientzero_client=ZeroClient("localhost",5559)classRpcClient:def__init__(self,zero_client:ZeroClient):self._zero_client=zero_clientdefecho(self,msg:str)->str:returnself._zero_client.call("echo",msg)defhello_world(self,msg:str)->str:returnself._zero_client.call("hello_world",msg)Use the client -frommy_clientimportRpcClient,zero_clientclient=RpcClient(zero_client)if__name__=="__main__":client.echo("Hi there!")client.hello_world(None)Currently, the code generation tool supports onlyZeroClientand notAsyncZeroClient.WIP - Generate models from server code.Important notes 📝ZeroServershould always be run underif __name__ == "__main__":, as it uses multiprocessing.The methods which are underregister_rpc()inZeroServershould havetype hinting, likedef 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 thebenchmark/dockerizefolder for details.There are two endpoints in every tests,/hello: Just call for a hello world response 😅/order: Save a Order object in redisCompare 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)aiohttp14391.3810.969470.7412.94aiozmq15121.869.425904.8421.57fastApi9590.9618.316669.8124.41sanic18790.498.6912259.2913.52zero(sync)24805.614.5716498.837.80zero(async)22716.845.6117446.197.24Roadmap 🗺Make msgspec as default serializerAdd support for async server (currently the sync server runs async functions in the eventloop, which is blocking)Add pub/sub supportContributionContributors are welcomed 🙏Please leave a star ⭐ if you like Zero!