New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sync vs Async workers #1409
Comments
This page describe the design an give some informations about the workers: I will answer in a generic manner if it's OK for you. Hopefully it will give you enough hint to answer yourself to the scenarios above. If you run gunicorn behind a proxy that buffer the connection the key point is not the number the number of connections that can accept gunicorn,but rather the number of slow connections (worker that do a huge task, connecting to an api, a database ...) or connections that will be used for a long time (longpolling, ..) . In such case an async worker is advised. When you return quite immediately, then a sync worker is enough and in most case when a database is locale or in the same network with a low latency it can also be used. If your run gunicorn standalone. Then you will need a threaded or an async worker if you expect to have a lot of concurrent connections. Increasing the number of worker when using a sync worker is also enough sometimes when you don't expect a large number of connectios or can handle some latency in the responses. |
I will also add that monley patching add some border effects to your application which cna be an issue or not. Using other async workers don't suffer such border effects. At least for the tornado and threaded workers. |
@benoitc thanks for your answer! I've already read the docs. Essentially, my point is that the docs are way to short. There are important implementation details, which aren't mentioned yet. Firstly, it came as a surprise to me, that gevent-workers implicitly call Secondly, it's not very clear, how the Thirdly, how exactly does gunicorn restart after the
So, in case I have a server with 30 workers, a long-running
Fourthly, what happens if the master process is sent with two Fifthly, has the recommendation about And so on. |
Can you open a ticket about it? |
to answer on your last questions: max_requests is used when you know yuou will have to recycle (kill the current worker) at some point. It's useful when you know that your worker is leaking some memory, or need to be reset at some point. Hooks must be processed fast If not, you may either block the worker or the arbiter preventing any other scheduled actions. The arbiter queue the signals so 2 HUP will be handled concurrently. N2+1 is a generic rule to load balance the sockets between the workers between CPUs/cores especially useful for the sync worker. |
Actually, I stumbled into this venue of questioning recently at work and was rather badly bitten by mixing gevent with gunicorn without considering it's Python too. @benoitc my simple question is this, assuming I'm using SyncWorker as my worker, and somewhere in the code serving request I call |
@mdomans In what way were you bitten by mixing gevent and gunicorn? (I'm curious because we've been using gunicorn+gevent successfully for 2 years now.) |
I used gevent based grequests library which calls @RonRothman curious to talk about your architecture :) |
closing the issue, superseded by #1746 |
Hi!
The documentation doesn't state clearly enough, how the async workers are different from the sync ones and what should be done by a programmer to make use of their difference.
I assume, that asynchronous workers are spawned in separate processes based on the pre-fork model.
Say, we want to see a difference between a
sync
and agevent
worker classes in an example of a simple application. Here go four scenarios:Scenario №1
The application accepts a request, makes 10 external calls using the
requests
library and returns an array of 10 responses.My assumption: there is no difference, the class of the workers doesn't matter.
Scenario №2
The application calls
gevent.monkey.patch_all()
in thepre_fork()
function of the master process. Then the first scenario takes place: the app accepts a request, makes 10 external calls using therequests
library and returns an array of 10 responses.My assumption: synchronous workers implicitly turn into gevent workers.
Scenario №3
The same as the Scenario№2, but the monkey-patch is called in a worker.
My assumptions:
gevent.monkey.patch_all()
doesn't affect the way workers listen on the socket. Synchronous workers don't turn into gevent workers and don't accept new calls until the previous are handled.requests
occurs. The allowed count of concurrently handled calls is capped by worker_connections setting. That's the only difference.Scenario №4
The application accepts a request, spawns 5 gevent jobs and joins them; their 5 responses will be the result. After that it spawns another 10 jobs, doesn't join them and returns immediately.
My assumptions:
gevent. joinall(...)
will be called and they might be scheduled to be executed.gevent. joinall(...)
, after having any of the jobs done, etc.).I feel, many of these assumptions must be wrong. Could you please correct me and expand on it in the documentation respectively?
The text was updated successfully, but these errors were encountered: