Package: py-netty
py-netty :rocket:An event-driven TCP networking framework.Ideas and concepts under the hood are build upon those ofNetty, especially the IO and executor model.APIs are intuitive to use if you are a Netty alcoholic.Featurescallback based application invocationnon blocking IOrecv/write is performed only in IO threadadaptive read bufferall platform supported (linux: epoll, mac: kqueue, windows: select)Installationpipinstallpy-nettyGetting StartedStart an echo server:frompy_nettyimportServerBootstrapServerBootstrap().bind(address='0.0.0.0',port=8080).close_future().sync()Start an echo server (TLS):frompy_nettyimportServerBootstrapServerBootstrap(certfile='/path/to/cert/file',keyfile='/path/to/cert/file').bind(address='0.0.0.0',port=9443).close_future().sync()As TCP client:frompy_nettyimportBootstrap,ChannelHandlerAdapterclassHttpHandler(ChannelHandlerAdapter):defchannel_read(self,ctx,buffer):print(buffer.decode('utf-8'))remote_address,remote_port='www.google.com',80b=Bootstrap(handler_initializer=HttpHandler)channel=b.connect(remote_address,remote_port).sync().channel()request=f'GET / HTTP/1.1\r\nHost:{remote_address}\r\n\r\n'channel.write(request.encode('utf-8'))input()# pausechannel.close()As TCP client (TLS):frompy_nettyimportBootstrap,ChannelHandlerAdapterclassHttpHandler(ChannelHandlerAdapter):defchannel_read(self,ctx,buffer):print(buffer.decode('utf-8'))remote_address,remote_port='www.google.com',443b=Bootstrap(handler_initializer=HttpHandler,tls=True,verify=True)channel=b.connect(remote_address,remote_port).sync().channel()request=f'GET / HTTP/1.1\r\nHost:{remote_address}\r\n\r\n'channel.write(request.encode('utf-8'))input()# pausechannel.close()TCP port forwarding:frompy_nettyimportServerBootstrap,Bootstrap,ChannelHandlerAdapter,EventLoopGroupclassProxyChannelHandler(ChannelHandlerAdapter):def__init__(self,remote_host,remote_port,client_eventloop_group):self._remote_host=remote_hostself._remote_port=remote_portself._client_eventloop_group=client_eventloop_groupself._client=Nonedef_client_channel(self,ctx0):class__ChannelHandler(ChannelHandlerAdapter):defchannel_read(self,ctx,bytebuf):ctx0.write(bytebuf)defchannel_inactive(self,ctx):ctx0.close()ifself._clientisNone:self._client=Bootstrap(eventloop_group=self._client_eventloop_group,handler_initializer=__ChannelHandler).connect(self._remote_host,self._remote_port).sync().channel()returnself._clientdefexception_caught(self,ctx,exception):ctx.close()defchannel_read(self,ctx,bytebuf):self._client_channel(ctx).write(bytebuf)defchannel_inactive(self,ctx):ifself._client:self._client.close()proxied_server,proxied_port='www.google.com',443client_eventloop_group=EventLoopGroup(1,'ClientEventloopGroup')sb=ServerBootstrap(parant_group=EventLoopGroup(1,'Acceptor'),child_group=EventLoopGroup(1,'Worker'),child_handler_initializer=lambda:ProxyChannelHandler(proxied_server,proxied_port,client_eventloop_group))sb.bind(port=8443).close_future().sync()Event-driven callbacksCreate handler with callbacks for interested events:frompy_nettyimportChannelHandlerAdapterclassMyChannelHandler(ChannelHandlerAdapter):defchannel_active(self,ctx:'ChannelHandlerContext')->None:# invoked when channel is active (TCP connection ready)passdefchannel_read(self,ctx:'ChannelHandlerContext',msg:Union[bytes,socket.socket])->None:# invoked when there is data ready to processpassdefchannel_inactive(self,ctx:'ChannelHandlerContext')->None:# invoked when channel is inactive (TCP connection is broken)passdefchannel_registered(self,ctx:'ChannelHandlerContext')->None:# invoked when the channel is registered with a eventlooppassdefchannel_unregistered(self,ctx:'ChannelHandlerContext')->None:# invoked when the channel is unregistered from a eventlooppassdefchannel_handshake_complete(self,ctx:'ChannelHandlerContext')->None:# invoked when ssl handshake is complete, this only applies to client sidepassdefexception_caught(self,ctx:'ChannelHandlerContext',exception:Exception)->None:# invoked when there is any exception raised during processpassBenchmarkTest is performed using echo client/server mechanism on a 1-Core 2.0GHz Intel(R) Xeon(R) Platinum 8452Y with 4GB memory, Ubuntu 22.04.
(Please seebm_echo_server.pyfor details.)3 methods are tested:BIO (Traditional thread based blocking IO)Asyncio (Python built-in async IO)NIO (py-netty with 1 eventloop)3 metrics are collected:Throughput (of each connection) to indicate overall stabilityAverage throughput (of all connections) to indicate overall performanceRamp up time (seconds consumed after all connections established) to indicate responsivenessCase 1: Concurrent 64 connections with 32K/sCase 2: Concurrent 64 connections with 4M/sCase 3: Concurrent 128 connections with 4M/sCase 4: Concurrent 128 connections with 8M/sCase 5: Concurrent 256 connections with 8M/sCPU UsageCaveatsNo pipeline, supports only one handler FOR NOWNo batteries-included codecs FOR NOWNo pool or refcnt for bytes buffer, bytes objects are created and consumed at your disposal