based on Greenlets (via Eventlet and Gevent) fork 孙子worker 比较 gevent不是异步 协程原理 占位符 placeholder (Future, Promise, Deferred) 循环引擎 greenlet 没有显式调度的微线程,换言之 协程
gevent
GitHub - gevent/gevent: Coroutine-based concurrency library for Python https://github.com/gevent/gevent
gevent - 廖雪峰的官方网站 https://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001407503089986d175822da68d4d6685fbe849a0e0ca35000
Python通过yield
提供了对协程的基本支持,但是不完全。而第三方的gevent为Python提供了比较完善的协程支持。
gevent是第三方库,通过greenlet实现协程,其基本思想是:
当一个greenlet遇到IO操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。
Design — Gunicorn 19.9.0 documentation
http://docs.gunicorn.org/en/stable/design.html#async-workers
eventlet-没有孙子worker.png
请求发来前运行后.png
worker 数固定
请求发来后-初始阶段-每个子worker新增孙worker.png
请求发来后-进行阶段-随着jmeter-线程数和循环数的增加-worker增加.png
https://www.tornadoweb.org/en/stable/guide/async.html#asynchronous
systems like gevent use lightweight threads to offer performance comparable to asynchronous systems, but they do not actually make things asynchronous
Asynchronous
An asynchronous function returns before it is finished, and generally causes some work to happen in the background before triggering some future action in the application (as opposed to normal synchronous functions, which do everything they are going to do before returning). There are many styles of asynchronous interfaces:
Callback argument
Return a placeholder (Future, Promise, Deferred)
Deliver to a queue
Callback registry (e.g. POSIX signals)
Regardless of which type of interface is used, asynchronous functions by definition interact differently with their callers; there is no free way to make a synchronous function asynchronous in a way that is transparent to its callers (systems like gevent use lightweight threads to offer performance comparable to asynchronous systems, but they do not actually make things asynchronous).
Asynchronous operations in Tornado generally return placeholder objects (Futures), with the exception of some low-level components like the IOLoop that use callbacks. Futures are usually transformed into their result with the await or yield keywords.
Here is a sample synchronous function:
from tornado.httpclient import HTTPClient
def synchronous_fetch(url):
http_client = HTTPClient()
response = http_client.fetch(url)
return response.body
And here is the same function rewritten asynchronously as a native coroutine:
from tornado.httpclient import AsyncHTTPClient
async def asynchronous_fetch(url):
http_client = AsyncHTTPClient()
response = await http_client.fetch(url)
return response.body
Or for compatibility with older versions of Python, using the tornado.gen module:
from tornado.httpclient import AsyncHTTPClient
from tornado import gen
@gen.coroutine
def async_fetch_gen(url):
http_client = AsyncHTTPClient()
response = yield http_client.fetch(url)
raise gen.Return(response.body)
Coroutines are a little magical, but what they do internally is something like this:
from tornado.concurrent import Future
def async_fetch_manual(url):
http_client = AsyncHTTPClient()
my_future = Future()
fetch_future = http_client.fetch(url)
def on_fetch(f):
my_future.set_result(f.result().body)
fetch_future.add_done_callback(on_fetch)
return my_future
Notice that the coroutine returns its Future before the fetch is done. This is what makes coroutines asynchronous.
Anything you can do with coroutines you can also do by passing callback objects around, but coroutines provide an important simplification by letting you organize your code in the same way you would if it were synchronous. This is especially important for error handling, since try/except blocks work as you would expect in coroutines while this is difficult to achieve with callbacks. Coroutines will be discussed in depth in the next section of this guide.
异步 在完成之前返回
协程 返回未来
小结:
1、
micro-thread with no implicit scheduling; coroutines, in other words.
没有显式调度的微线程,换言之 协程
2、
一个greenlet切换到另一个greenlet,前者被suspend推迟、暂停
uWSGI项目 — uWSGI 2.0 文档 https://uwsgi-docs-zh.readthedocs.io/zh_CN/latest/#
循环引擎 (实现事件和并发,组件可以在reforking, threaded, asynchronous/evented和green thread/coroutine模式下运行。支持多种技术,包括uGreen, Greenlet, Stackless, Gevent, Coro::AnyEvent, Tornado, Goroutines和Fibers)
greenlet: Lightweight concurrent programming — greenlet 0.4.0 documentation
https://greenlet.readthedocs.io/en/latest/#greenlet-lightweight-concurrent-programming
The “greenlet” package is a spin-off of Stackless, a version of CPython that supports micro-threads called “tasklets”. Tasklets run pseudo-concurrently (typically in a single or a few OS-level threads) and are synchronized with data exchanges on “channels”.
A “greenlet”, on the other hand, is a still more primitive notion of micro-thread with no implicit scheduling; coroutines, in other words. This is useful when you want to control exactly when your code runs. You can build custom scheduled micro-threads on top of greenlet; however, it seems that greenlets are useful on their own as a way to make advanced control flow structures. For example, we can recreate generators; the difference with Python’s own generators is that our generators can call nested functions and the nested functions can yield values too. (Additionally, you don’t need a “yield” keyword. See the example in test/test_generator.py
).
Greenlets are provided as a C extension module for the regular unmodified interpreter.
greenlet: Lightweight concurrent programming — greenlet 0.4.0 documentation
https://greenlet.readthedocs.io/en/latest/#introduction
A “greenlet” is a small independent pseudo-thread. Think about it as a small stack of frames; the outermost (bottom) frame is the initial function you called, and the innermost frame is the one in which the greenlet is currently paused. You work with greenlets by creating a number of such stacks and jumping execution between them. Jumps are never implicit: a greenlet must choose to jump to another greenlet, which will cause the former to suspend and the latter to resume where it was suspended. Jumping between greenlets is called “switching”.
When you create a greenlet, it gets an initially empty stack; when you first switch to it, it starts to run a specified function, which may call other functions, switch out of the greenlet, etc. When eventually the outermost function finishes its execution, the greenlet’s stack becomes empty again and the greenlet is “dead”. Greenlets can also die of an uncaught exception.
https://www.tornadoweb.org/en/stable/#threads-and-wsgi
Threads and WSGI
一个进程一个线程
Tornado is different from most Python web frameworks. It is not based on WSGI, and it is typically run with only one thread per process. See the User’s guide for more on Tornado’s approach to asynchronous programming.
While some support of WSGI is available in the tornado.wsgi
module, it is not a focus of development and most applications should be written to use Tornado’s own interfaces (such as tornado.web
) directly instead of using WSGI.
In general, Tornado code is not thread-safe. The only method in Tornado that is safe to call from other threads is IOLoop.add_callback
. You can also use IOLoop.run_in_executor
to asynchronously run a blocking function on another thread, but note that the function passed to run_in_executor
should avoid referencing any Tornado objects. run_in_executor
is the recommended way to interact with blocking code.
https://www.tornadoweb.org/en/stable/guide/async.html#asynchronous-and-non-blocking-i-o
Real-time web features require a long-lived mostly-idle connection per user. In a traditional synchronous web server, this implies devoting one thread to each user, which can be very expensive.
To minimize the cost of concurrent connections, Tornado uses a single-threaded event loop. This means that all application code should aim to be asynchronous and non-blocking because only one operation can be active at a time.
The terms asynchronous and non-blocking are closely related and are often used interchangeably, but they are not quite the same thing.
传统同步web服务,给每个用户一个线程 Tornado使用单线程的事件循环 这要求应用代码是异步的、非阻塞的,因为同时置疑一个操作时活跃的