uvicorn 是如何实现多进程同时监听一个 socket 的?如何设置了多个工作进程,http 请求是如何在多个 worker 进程中分配的?是由谁来分配的?是操作系统把 http 请求给 master 进程,再有 master 经常转发给 worker 进程,还是操作系统直接发给 worker 进程?如果是操作系统直接发给 worker 进程,具体又是怎么给的?是操作系统轮训 worker,均匀分配给 worker HTTP 请求?还是操作系统会把每个HTTP 请求都给每个 worker 进程?还是让 worker 进程主动来抢?先到先得?
同样的问题?我也想知道 gunicorn 和 uwsgi 是如何实现的?
贴一个来自 chatGPT 的回答:
uvicorn、gunicorn 和 uwsgi 都是 Python Web 应用程序的服务器,它们都采用了多进程模型来提高并发处理能力。这些服务器通常都是使用操作系统提供的网络套接字来监听客户端请求,然后将请求分配给各个工作进程。
在多进程模型中,每个工作进程都会绑定到相同的网络套接字上。这里的 SO_REUSEPORT 和 SO_REUSEADDR 都是用来实现端口共享的。
SO_REUSEADDR 允许多个进程在同一台计算机上绑定到相同的 IP 地址和端口号上。但是,当一个进程绑定到端口号时,直到操作系统释放这个端口,其他进程才能够再次绑定到该端口,而这个等待时间是通过 TIME_WAIT 状态来实现的。因此,SO_REUSEADDR 并不能解决多进程监听同一个端口的问题。
SO_REUSEPORT 则允许多个进程在同一台计算机上绑定到相同的 IP 地址和端口号上,并且同时接收客户端请求,从而解决了多进程监听同一个端口的问题。但是,SO_REUSEPORT 并不是所有操作系统都支持的,目前只有 Linux 和 FreeBSD 支持。
在实现上,uvicorn、gunicorn 和 uwsgi 都是通过监听同一个网络套接字来实现多进程共享端口的。具体地,它们在主进程中创建一个监听套接字,然后在每个工作进程中使用 fork() 系统调用复制该套接字,从而实现多进程共享端口。
当一个客户端连接时,操作系统会将连接请求分配给监听套接字,然后选择一个空闲的工作进程将连接套接字交给它处理。具体来说,操作系统使用一些负载均衡算法来选择工作进程,比如轮询、随机等等。对于 uvicorn、gunicorn 和 uwsgi,具体的负载均衡算法是由它们自己实现的,不同的服务器可能有不同的实现方式。
总之,uvicorn、gunicorn 和 uwsgi 都是使用多进程模型来提高并发处理能力,它们通过监听同一个网络套接字来实现多进程共享端口,并且由操作系统使用负载均衡算法将客户端请求分配给不同的工作进程来处理。