Last active
May 15, 2019 09:53
-
-
Save cosven/49d2aa16d827f10afbb40d837329c8fd to your computer and use it in GitHub Desktop.
reinvent the gunicorn
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import signal | |
import socket | |
import sys | |
import time | |
def be_a_worker(sock): | |
"""将当前进程变成一个 worker | |
worker 不处理请求之后,应该退出:也就是在退出 while 循环后, | |
调用 sys.exit(0) 来退出进程。 | |
""" | |
stopped = False | |
pid = os.getpid() | |
print('Booting worker with pid: {}'.format(pid)) | |
def stop(sig, frame): | |
nonlocal stopped | |
print(pid, 'recv term signal') | |
stopped = True | |
signal.signal(signal.SIGTERM, stop) | |
while not stopped: | |
# 多个 worker 等待 accpet 连接,当连接来了的时候,只有一个会成功 | |
# 据说在低版本内核中,accept 会出现惊群,但现在不会了 | |
conn, addr = sock.accept() | |
print('worker:{} recv request: {}'.format(pid, addr)) | |
# 处理请求 | |
# TODO: 这一块处理的逻辑,之后可以改进,比如遵守 WSGI 协议 | |
# TODO: 以后,我们可以在这里给 conn 设置 close-on-exec | |
conn.recv(1024) | |
conn.sendall(b'HTTP/1.0 200 OK\r\n') | |
conn.close() | |
# worker 结束后,需要退出 | |
sys.exit(0) | |
def run(worker_num=1, host='0.0.0.0', port=8081): | |
stopped = False | |
def stop(sig, frame): | |
nonlocal stopped | |
stopped = True | |
# 监听 Ctrl-c 信号 | |
signal.signal(signal.SIGINT, stop) | |
worker_pids = [] | |
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | |
# sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) | |
sock.bind((host, port)) | |
sock.listen(2048) | |
print('Medusa listening at {}:{}'.format(host, port)) | |
for _ in range(0, worker_num): | |
pid = os.fork() | |
if pid != 0: # 主进程 | |
worker_pids.append(pid) | |
continue | |
# 子进程 | |
be_a_worker(sock) | |
# 主进程不用 sock 了,于是 close 它,这里其实不会真正的 shutdown 这个 socket | |
# 只会让 socket 的引用计数减 1(操作系统管理了这个引用计数,不是 Python 的引用计数) | |
sock.close() | |
# TODO: 在这里,可以作一些子进程管理的工作,比如检查子活着的子进程个数 | |
# TODO: 还可以加个机制检测子进程的活跃程度 | |
while not stopped: | |
time.sleep(0.1) | |
for pid in worker_pids: | |
os.kill(pid, signal.SIGTERM) | |
kpid, stat = os.waitpid(pid, os.WNOHANG) | |
if not kpid: | |
print('WARNING child process {} is still alive, force kill'.format(pid)) | |
os.kill(pid, signal.SIGKILL) | |
if __name__ == '__main__': | |
run() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment