自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Python程序中不同的重啟機(jī)制

大數(shù)據(jù)
Gunicorn作為Python的Web容器之一,會接收用戶的請求,雖然我們通常都會使用nginx放在Gunicorn前方做反向代理,通常也可以使用nginx來做upstream offline、online的熱重啟,但那就不是一個(gè)層次的事情了。

 [[189945]]

分析典型案例:

  1. Celery 分布式異步任務(wù)框架
  2. Gunicorn Web容器

之所以挑這兩個(gè),不僅僅是應(yīng)用廣泛,而且兩個(gè)的進(jìn)程模型比較類似,都是Master、Worker的形式,在熱重啟上思路和做法又基本不同,比較有參考意義

知識點(diǎn):

  • atexit
  • os.execv
  • 模塊共享變量
  • 信號處理
  • sleep原理:select
  • 文件描述符共享

這幾個(gè)知識點(diǎn)不難,區(qū)別只在于Celery和Gunicorn的應(yīng)用方式。如果腦海中有這樣的知識點(diǎn),這篇文章也就是開闊下視野而已。。。

Celery和Gunicorn都會在接收到HUP信號時(shí),進(jìn)行熱重啟操作

Celery的重啟:關(guān)舊Worker,然后execv重新啟動整個(gè)進(jìn)程

Gunicorn的重啟:建立新Worker,再關(guān)舊Worker,Master不動

下面具體的看下它們的操作和核心代碼。

對于Celery:

  1. def _reload_current_worker(): 
  2.     platforms.close_open_fds([ 
  3.         sys.__stdin__, sys.__stdout__, sys.__stderr__, 
  4.     ]) 
  5.     os.execv(sys.executable, [sys.executable] + sys.argv) 
  6.   
  7.   
  8.    
  9. def install_worker_restart_handler(worker, sig='SIGHUP'): 
  10.     def restart_worker_sig_handler(*args): 
  11.         """Signal handler restarting the current python program.""" 
  12.         import atexit 
  13.         atexit.register(_reload_current_worker) 
  14.         from celery.worker import state 
  15.         state.should_stop = EX_OK 
  16.     platforms.signals[sig] = restart_worker_sig_handler 

HUP上掛的restart_worker_sig_handler 就做了兩件事:

  1. 注冊atexit函數(shù)
  2. 設(shè)置全局變量

考慮到這個(gè)執(zhí)行順序,應(yīng)該就能明白Celery 是Master和Worker都退出了,嶄新呈現(xiàn)。。

看過APUE的小伙伴,應(yīng)該比較熟悉 atexit 了,這里也不多說。os.execv還挺有意思,根據(jù)Python文檔,這個(gè)函數(shù)會執(zhí)行一個(gè)新的函數(shù)用于替換掉 當(dāng)前進(jìn)程 ,在Unix里,新的進(jìn)程直接把可執(zhí)行程序讀進(jìn)進(jìn)程,保留同樣的PID。

在Python os標(biāo)準(zhǔn)庫中,這是一整套基本一毛一樣的函數(shù),也許應(yīng)該叫做函數(shù)族了:

  • os. execl ( path , arg0 , arg1 , … )
  • os. execle ( path , arg0 , arg1 , … , env )
  • os. execlp ( file , arg0 , arg1 , … )
  • os. execlpe ( file , arg0 , arg1 , … , env )
  • os. execv ( path , args )
  • os. execve ( path , args , env )
  • os. execvp ( file , args )
  • os. execvpe ( file , args , env )

以exec開頭,后綴中的l和v兩種,代表命令行參數(shù)是否是變長的(前者不可變),p代表是否使用PATH定位程序文件,e自然就是在新進(jìn)程中是否使用ENV環(huán)境變量了

然后給worker的state.should_stop變量設(shè)置成False。。。 模塊共享變量 這個(gè)是 Python FAQ里提到的一種方便的跨模塊消息傳遞的方式,運(yùn)用了Python module的單例。我們都知道Python只有一個(gè)進(jìn)程,所以單例的變量到處共享

而should_stop這個(gè)變量也是簡單粗暴,worker在執(zhí)行任務(wù)的循環(huán)中判斷這個(gè)變量,即執(zhí)行異步任務(wù)->查看變量->獲得異步任務(wù)->繼續(xù)執(zhí)行 的循環(huán)中,如果True就拋出一個(gè)【應(yīng)該關(guān)閉】異常,worker由此退出。

這里面有一個(gè)不大不小的坑是:信號的發(fā)送對于外部的工具,例如kill,是非阻塞的,所以當(dāng)HUP信號被發(fā)出后,worker可能并沒有完成重啟(等待正在執(zhí)行的舊任務(wù)完成 才退出和新建),因此如果整個(gè)系統(tǒng)中只使用HUP信號挨個(gè)灰度各個(gè)機(jī)器,那么很有可能出現(xiàn)全部worker離線的情況

接下來我們看看Gunicorn的重啟機(jī)制:

信號實(shí)質(zhì)上掛在在Arbiter上,Arbiter相當(dāng)于master,守護(hù)和管理worker的,管理各種信號,事實(shí)上它init的時(shí)候就給自己起好Master的名字了,打印的時(shí)候會打出來:

  1. class Arbiter(object): 
  2.     def __init__(self, app): 
  3.         #一部分略 
  4.         self.master_name = "Master"    
  5.     def handle_hup(self): 
  6.         """\ 
  7.         HUP handling. 
  8.         - Reload configuration 
  9.         - Start the new worker processes with a new configuration 
  10.         - Gracefully shutdown the old worker processes 
  11.         ""
  12.         self.log.info("Hang up: %s", self.master_name) 
  13.         self.reload() 

這里的函數(shù)文檔里寫了處理HUP信號的過程了,簡單的三行:

  1. 讀取配置
  2. 開啟新worker
  3. 優(yōu)雅關(guān)閉舊Worker

reload函數(shù)的實(shí)現(xiàn)本身沒什么復(fù)雜的,因?yàn)镚unicorn 是個(gè)Web容器,所以master里面是沒有業(yè)務(wù)邏輯的,worker都是master fork出來的,fork是可以帶著文件描述符(自然也包括socket)過去的。這也是Gunicorn可以隨意增減worker的根源

master只負(fù)責(zé)兩件事情:

  1. 拿著被Fork的worker的PID,以供關(guān)閉和處理
  2. 1秒醒來一次,看看有沒有worker超時(shí)了需要被干掉
  1. while True
  2.     sig = self.SIG_QUEUE.pop(0) if len(self.SIG_QUEUE) else None 
  3.     if sig is None: 
  4.         self.sleep() 
  5.         self.murder_workers() 
  6.         self.manage_workers() 
  7.         continue 
  8.     else
  9.         #處理信號 

在sleep函數(shù)中,使用了select.select+timeout實(shí)現(xiàn),和time.sleep的原理是一樣的,但不同之處在于select監(jiān)聽了自己創(chuàng)建的一個(gè)PIPE,以供wakeup立即喚醒

總結(jié)

以上就介紹了Celery和Gunicorn的重啟機(jī)制差異。

從這兩者的設(shè)計(jì)來看,可以理解他們這樣實(shí)現(xiàn)的差異。

Celery是個(gè)分布式、異步的任務(wù)隊(duì)列,任務(wù)信息以及排隊(duì)信息實(shí)質(zhì)上是持久化在外部的MQ中的,例如RabbitMQ和redis,其中的持久化方式,這應(yīng)該另外寫一篇《高級消息隊(duì)列協(xié)議AMQP介紹》,就不在這里說了,對于Celery的Master和Worker來說,可以說是完全沒有狀態(tài)的。由Celery的部署方式也可以知道,近似于一個(gè)服務(wù)發(fā)現(xiàn)的框架,下線的Worker不會對整個(gè)分布式系統(tǒng)帶來任何影響。唯一的例外可能是Beat組件,作為Celery定時(shí)任務(wù)的節(jié)拍器,要做少許改造以適配分布式的架構(gòu),并且實(shí)現(xiàn)Send Once功能。

Gunicorn作為Python的Web容器之一,會接收用戶的請求,雖然我們通常都會使用nginx放在Gunicorn前方做反向代理,通常也可以使用nginx來做upstream offline、online的熱重啟,但那就不是一個(gè)層次的事情了

這里回頭來再吐槽Golang

項(xiàng)目中使用到了Golang的一個(gè)Web框架,Golang在1.8中也已經(jīng)支持Http.Server的熱關(guān)閉了,但是一是因?yàn)閯偝霾痪?竟然現(xiàn)在才出),二是因?yàn)镚olang的進(jìn)程模型和Python大相近庭,go協(xié)程嘛,目前還沒有看到那個(gè)Web框架中真正實(shí)現(xiàn)Gunicorn類似的熱重啟。

Golang 在fcgi的操作應(yīng)該就類似Python之于wsgi了。。我感覺我是選擇錯了一個(gè)web框架

也沒看見有人用syscall.Exec來用一下execve系統(tǒng)調(diào)用,Golang也沒看見有人用socket REUSE。。作為一個(gè)懶惰的人感覺很蛋疼。。。

先讓nginx大法做這件事情好了。

責(zé)任編輯:武曉燕 來源: 36大數(shù)據(jù)
相關(guān)推薦

2024-11-21 12:00:00

字典緩存Python

2009-03-31 11:08:26

B-Tree索引數(shù)據(jù)庫

2010-02-01 18:13:34

Python

2024-04-24 12:03:20

2017-06-12 17:38:32

Python垃圾回收引用

2011-06-09 15:04:22

RAII機(jī)制

2011-06-09 14:34:04

C++NVI

2011-06-09 14:52:09

Pimpl機(jī)制

2022-02-17 20:34:12

Python短路機(jī)制開發(fā)

2015-07-29 13:37:41

妹子處理

2013-03-28 09:07:37

Android開發(fā)Intent機(jī)制

2011-04-19 10:53:05

SSAS

2012-03-01 14:13:36

Java

2017-05-15 19:40:40

AndroidIPC機(jī)制

2023-03-26 22:48:46

Python引用計(jì)數(shù)內(nèi)存

2010-02-26 13:34:50

WCF編碼機(jī)制

2012-08-03 09:46:09

Zend Framew

2009-09-29 10:45:17

UnixLinuxshell

2011-05-24 09:15:26

JavaC#

2011-01-06 16:29:08

linuxtasklet機(jī)制
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號