2015-06-21 198 views
3

我想處理在Heroku上賽道重新根據其描述here如何在使用Django時處理dyno重新啓動?

在這段時間裏,他們應該停止接受新的請求或工作,eattempt到完成他們的當前請求,或把工作回其他工作進程處理的隊列。

從外觀上來看,當接收蟒SIGTERM和信號處理程序被調用(每signal.signal),當前線程的運行被停止,所以該請求被停止在運行的中間。

我該如何滿足這兩個要求? (停止接受新的請求+完成當前的請求)

+0

我從來沒有聽說過任何特殊的要求。正如Django的前BDFL爲Heroku工作,如果是這樣,你會認爲它會被記錄下來。 –

+0

@DanielRoseman更新了與鏈接 –

+1

的問題*爲了滿足應用程序的乾淨關閉,* *用於服務請求的底層併發模型必須支持從SIGTERM/SIGINT'排隊關閉請求,完成當前請求正在進行並終止。至少在異步web框架中,我是這麼做的。 –

回答

2

編輯:添加了簡化的示例代碼,更好地解釋了正在進行的請求/終止並添加了CrazyPython的要點。

面對它,你有4個問題需要解決。我會帶他們反過來再給出一些示例代碼,應有助於澄清:

處理SIGTERM

這很簡單。你只需要設置一個信號處理程序來注意你需要關閉。 PMOTW有一個很好的例子,如何捕捉信號。您可以使用此代碼的變體來捕獲SIGTERM並設置一個表明您正在關閉的全局標誌。

拒絕新的請求

Django middleware提供任何掛鉤HTTP請求到應用程序的一種巧妙的方法。你可以創建一個簡單的process_request()鉤子,如果設置了全局標誌(從上面),它將返回一個錯誤頁面。

完成現有請求

任何新的請求停止,你現在必須讓你的當前請求完成。雖然你現在可能不相信,但這意味着你什麼都不做,讓程序在SIGTERM之後照常運行。讓我擴展一下...

與heroku的合同是你必須在SIGTERM的10s內完成,否則它將發送一個SIGKILL。這意味着你不能做任何事情(作爲一個行爲良好的應用程序)來確保所有請求總是完成。考慮兩種情況:

  1. 您的應用程序在10秒內處理所有現有的請求。在這種情況下,只要讓程序運行就可以完成請求。不需要特殊的代碼來運行這些請求 - 所有的線程/進程都已經在做你所需要的了!
  2. 對於某些請求,您的應用程序需要超過10秒。在這種情況下,有什麼也沒有你可以做 - 它會在長請求完成之前以heroku的終極力量終止。如果您認爲可以忽略SIGKILL,那麼考慮其他...這是不允許的 - 請參閱signals documentation

因此,在這兩種情況下,解決方案只是讓程序繼續運行,以便在終止之前完成許多當前請求。

終止您的應用程序

做可能是等待SIGKILL沿着從Heroku的10秒晚一點最簡單的事情。這不是優雅的,但它應該是好的,因爲你拒絕任何新的請求。

如果這還不夠好,您需要跟蹤未完成的請求並使用它來決定何時關閉應用程序。關閉應用程序的確切方式取決於託管它的任何東西,所以我不能在那裏給你確切的指導。不過,希望示例代碼能夠給你足夠的指針。

示例代碼

從PMOTW信號處理器的示例開始,我已經加強了在代碼中加入多線程處理請求和終止經理捕捉的信號,並允許應用正常關閉。你應該可以在Python2.7中運行它,然後嘗試殺死進程。

基於這個例子,CrazyPython創建了這個gist給django一個具體的實現。

import signal 
import os 
import time 
import threading 
import random 


class TerminationManager(object): 

    def __init__(self): 
     self._running = True 
     self._requests = 0 
     self._lock = threading.Lock() 
     signal.signal(signal.SIGTERM, self._start_shutdown) 

    def _start_shutdown(self, signum, stack): 
     print 'Received:', signum 
     self._running = False 

    def start_request(self): 
     with self._lock: 
      self._requests += 1 

    def stop_request(self): 
     with self._lock: 
      self._requests -= 1 

    def is_running(self): 
     return self._running or self._requests > 0 

    def running_requests(self): 
     return self._requests 


class DummyWorker(threading.Thread): 

    def __init__(self, app_manager): 
     super(DummyWorker, self).__init__() 
     self._manager = app_manager 

    def run(self): 
     while self._manager.is_running(): 
      # Emulate random work and delay between requests. 
      if random.random() > 0.9: 
       self._manager.start_request() 
       time.sleep(random.randint(1, 3)) 
       self._manager.stop_request() 
      else: 
       time.sleep(1) 
     print "Stopping worker" 


manager = TerminationManager() 
print 'My PID is:', os.getpid() 

for _ in xrange(10): 
    t = DummyWorker(manager) 
    t.start() 

while manager.is_running(): 
    print 'Waiting with {} running requests'.format(manager.running_requests()) 
    time.sleep(5) 

print 'All done!' 
+0

我已經知道PMOTW的大部分內容,但我無法理解如何應用它,因爲它暫停處理請求。所以剩下的一個問題是:我如何完成我目前的要求? –

+0

我可以等待一個操作系統警報1秒鐘,然後我收到SIGKILL並使用sys.exit() –

+0

這個答案在改進之前不符合獎勵條件,儘管非常接近被授予賞金。我與OP有同樣的看法,改進這一點,我可以授予它。 –

相關問題