使用 Python 处理每秒百万请求
所有这些伟大的工作都激励我在Python广泛使用的领域之一 —— 网络和微服务开发领域进行创新。
Japronto 是为您的微服务需求量身打造的全新微架构。 其主要目标包括快速，可扩展和轻量级。 基于
asyncio，它可以让你做同步和异步编程。 它无比地快速。 甚至比
勘误：正如@heppu指出的那样，Go语言标准库中的HTTP服务器比图中显示的要快上12%。在这个特定的基准下，Go语言中还有一个很棒的fasthttp服务器,仅比Japronto慢18%左右。太棒了，更多细节请参照https://github.com/squeaky-pl/japronto/pull/12 和 https://github.com/squeaky-pl/japronto/pull/14。
我们租赁了亚马逊在圣保罗区的一个拥有8个虚拟CPU的c4.2xlarge实例，它默认具有共享租赁，硬件虚拟化以及磁性存储等特性。这些结果都是在这个实例上获得的。这台机器上使用了Ubuntu16.04.1 LTS(Xenial Xerus)操作系统以及Linux 4.4.0--53-generic x86_64内核。操作系统报告称使用了Xeon® CPU E5--2666 v3 @ 2.90GHz 型号的CPU，我使用了刚从源码编译的Python，其版本为3.6。
HTTP 流水线 (图片引自维基百科)
HTTP 流水线 在这里很关键，因为Japronto其中的一个优化细节就是在执行请求把其也考虑了进去。
The gory details of optimizations
When many small GET requests are pipelined together by the client, there's a high probability that they'll arrive in one TCP packet (thanks to Nagle's algorithm) on the server side, then be read back by one system call.
Doing a system call and moving data from kernel-space to user-space is a very expensive operation compared to, say, moving memory inside process space. That's why doing it's important to perform as few as necessary system calls (but no less).
When Japronto receives data and successfully parses several requests out of it, it tries to execute all the requests as fast as possible, glue responses back in correct order, then write back in one system call. In fact the kernel can aid in the gluing part, thanks to scatter/gather IO system calls, which Japronto doesn't use yet.
有 1 个译文正在审阅中...
Note that this isn't always possible, since some of the requests could take too long, and waiting for them would needlessly increase latency.
Take care when you tune heuristics, and consider the cost of system calls and the expected request completion time.
Japronto gives a 1,214,440 RPS median of grouped continuous data, calculated as the 50th percentile, using interpolation.
Besides delaying writes for pipelined clients, there are several other techniques that the code employs.
Japronto is written almost entirely in C. The parser, protocol, connection reaper, router, request, and response objects are written as C extensions.
Japronto tries hard to delay creation of Python counterparts of its internal structures until asked explicitly. For example, a headers dictionary won't be created until it's requested in a view. All the token boundaries are already marked before but normalization of header keys, and creation of several str objects is done when they're accessed for the first time.
Japronto relies on the excellent picohttpparser C library for parsing status line, headers, and a chunked HTTP message body. Picohttpparser directly employs text processing instructions found in modern CPUs with SSE4.2 extensions (almost any 10-year-old x86_64 CPU has it) to quickly match boundaries of HTTP tokens. The I/O is handled by the super awesome uvloop, which itself is a wrapper around libuv. At the lowest level, this is a bridge to epoll system call providing asynchronous notifications on read-write readiness.
Picohttpparser relies on SSE4.2 and CMPESTRI x86_64 intrinsic to do parsing
Python is a garbage collected language, so care needs to be taken when designing high performance systems so as not to needlessly increase pressure on the garbage collector. The internal design of Japronto tries to avoid reference cycles and do as few allocations/deallocations as necessary. It does this by preallocating some objects into so-called arenas. It also tries to reuse Python objects for future requests if they're no longer referenced instead of throwing them away.
All the allocations are done as multiples of 4KB. Internal structures are carefully laid out so that data used frequently together is close enough in memory, minimizing the possibility of cache misses.
Japronto tries to not copy between buffers unnecessarily, and does many operations in-place. For example, it percent-decodes the path before matching in the router process.
Open source contributors, I could use your help.
I've been working on Japronto continuously for past 3 months --- often during weekends, as well as normal work days. This was only possible due to me taking a break from my regular programmer job and putting all my effort into this project.
I think it's time to share the fruit of my labor with the community.
Currently Japronto implements a pretty solid feature set:
- HTTP 1.x implementation with support for chunked uploads
- Full support for HTTP pipelining
- Keep-alive connections with configurable reaper
- Support for synchronous and asynchronous views
- Master-multiworker model based on forking
- Support for code reloading on changes
- Simple routing
I would like to look into Websockets and streaming HTTP responses asynchronously next.
Also, if your company is looking for a Python developer who's a performance freak and also does DevOps, I'm open to hearing about that. I am going to consider positions worldwide.
I'd like to thank Python community for their continuous investment in performance engineering. Namely Victor Stinner @VictorStinner, INADA Naoki @methane and Yury Selivanov @1st1 and entire PyPy team.
For the love of Python.
我们的翻译工作遵照 CC 协议，如果我们的工作有侵犯到您的权益，请及时联系我们。