Tornado的HTTPServer是單進程單線程模式,同時提供了創建多進程服務器的接口,具體實現是在主進程啓動HTTPServer時通過process.fork_processes(num_processes)產生新的服務器子進程,所有進程之間共享端口。
Tornado的多進程管理我們可以參看process.py這個文件。
global _task_id
assert _task_id is None
if num_processes is None or num_processes <= 0:
num_processes = cpu_count()
gen_log.info("Starting %d processes", num_processes)
children = {}
def cpu_count() -> int:
"""Returns the number of processors on this machine."""
if multiprocessing is None:
return 1
try:
return multiprocessing.cpu_count()
except NotImplementedError:
pass
try:
return os.sysconf("SC_NPROCESSORS_CONF") # type: ignore
except (AttributeError, ValueError):
pass
gen_log.error("Could not detect number of processors; assuming 1")
return 1
在沒有傳入進程數或者進程數<=0時,默認使用cpu個數作爲將要生成的進程個數。
def start_child(i: int) -> Optional[int]:
pid = os.fork()
if pid == 0:
# child process
_reseed_random()
global _task_id
_task_id = i
return i
else:
children[pid] = i
return None
這是一個內函數,作用就是生成子進程。它同時返回兩種狀態,os.fork()創建了子進程,如果子進程創建成功,返回子進程pid。子進程本身創建成功,返回自己的狀態碼,爲0.
如果pid==0表示cpu已經切換到子進程,else 則表示還在父進程中工作。
for i in range(num_processes):
id = start_child(i)
if id is not None:
return id
if id is not None表示如果我們在剛剛生成的那個子進程的上下文裏面,那麼就什麼都不幹,直接返回子進程的任務id就好了。如果還在父進程的上下文的話那麼就繼續生成子進程。
num_restarts = 0
while children:
pid, status = os.wait()
if pid not in children:
continue
id = children.pop(pid)
if os.WIFSIGNALED(status):
gen_log.warning(
"child %d (pid %d) killed by signal %d, restarting",
id,
pid,
os.WTERMSIG(status),
)
elif os.WEXITSTATUS(status) != 0:
gen_log.warning(
"child %d (pid %d) exited with status %d, restarting",
id,
pid,
os.WEXITSTATUS(status),
)
else:
gen_log.info("child %d (pid %d) exited normally", id, pid)
continue
num_restarts += 1
if num_restarts > max_restarts:
raise RuntimeError("Too many child restarts, giving up")
new_id = start_child(id)
if new_id is not None:
return new_id
上面這段代碼都是在父進程進行的。pid, status = os.wait()的意思是等待任意子進程退出或者結束,這時候我們就把它從我們的children表裏面去除掉,然後通過status判斷子進程退出的原因。
果子進程是因爲接收到kill信號或者拋出exception了,那麼我們就重新啓動一個子進程,用的當然還是剛剛退出的那個子進程的任務號。如果子進程是自己把事情做完了才退出的,那麼就算了,等待別的子進程退出吧。
if new_id is not None:
return new_id
主要就是退出子進程的空間,只在父進程上面做剩下的事情,不然剛纔父進程的那些代碼在子進程裏面也會同樣的運行,就會形成無限循環了