2014-01-22 87 views
1

我已經在Python-App-Engine上部署了一個網站。部署在Google-App-Engine上的Python應用程序很少引發異常

由於GAE不保證「保活」,我實現了一個無狀態服務器:

  1. 在內部變量的每一個變化,他們會立即存入GQL資料庫。

  2. 無論何時啓動進程,所有內部變量都從GQL數據庫加載。

我有很少拋出異常的情況下,我一直沒能追查:

  • 客戶端發送一個同步AJAX POST請求。

  • 服務器創建會話並在響應中發送唯一的會話ID。

  • 客戶端以session-ID作爲參數發送同步AJAX GET請求。

  • 服務器在響應中發送一些文本消息。

由於客戶端請求是同步的,整個序列也是同步的。

這裏是我的服務器內的相關映射:

from webapp2 import WSGIApplication 
from Handler import MyRequestHandler 

app = WSGIApplication([ 
    ('/request1' ,MyRequestHandler), # post request 
    ('/request2(.*)',MyRequestHandler), # get request 
]) 

下面是相關請求處理我的服務器中:

from webapp2 import RequestHandler 
from Server import MyServer 

myServer = MyServer() 

class MyRequestHandler(RequestHandler): 
    def post(self): 
     try: 
      if self.request.path.startswith('/request1'): 
       sessionId = myServer.GetNewSessionId() 
       self.SendContent('text/plain',sessionId) 
     except Exception,error: 
      self.SendError(error) 
    def get(self,sessionId): 
     try: 
      if self.request.path.startswith('/request2'): 
       textMessage = myServer.GetMsg(sessionId) 
       self.SendContent('text/plain',textMessage) 
     except Exception,error: 
      self.SendError(error) 
    def SendContent(self,contentType,contentData): 
     self.response.set_status(200) 
     self.response.headers['content-type'] = contentType 
     self.response.headers['cache-control'] = 'no-cache' 
     self.response.write(contentData) 
    def SendError(self,error): 
     self.response.set_status(500) 
     self.response.write(error.message) 

這裏是內部實現的我服務器:

class MyServer(): 
    def __init__(self): 
     self.sessions = SessionsTable.ReadSessions() 
    def GetNewSessionId(self): 
     while True: 
      sessionId = ... # a 16-digit random number 
      if SessionsTable.ReserveSession(sessionId): 
       self.sessions[sessionId] = ... # a text message 
       SessionsTable.WriteSession(self.sessions,sessionId) 
       return sessionId 
    def GetMsg(self,sessionId): 
     return self.sessions[sessionId] 

最後,這裏是我的服務器中的數據的基礎保養:

from google.appengine.ext import db 

class SessionsTable(db.Model): 
    message = db.TextProperty() 
    @staticmethod 
    def ReadSessions(): 
     sessions = {} 
     for session in SessionsTable.all(): 
      sessions[session.key().name()] = session.message 
     return sessions 
    @staticmethod 
    @db.transactional 
    def ReserveSession(sessionId): 
     if not SessionsTable.get_by_key_name(sessionId): 
      SessionsTable(key_name=sessionId,message='').put() 
      return True 
     return False 
    @staticmethod 
    def WriteSession(sessions,sessionId): 
     SessionsTable(key_name=sessionId,message=sessions[sessionId]).put() 
    @staticmethod 
    def EraseSession(sessionId): 
     SessionsTable.get_by_key_name(sessionId).delete() 

例外本身表明使用sessionIdsessions詞典的非法訪問。根據我的觀察,只有當服務器在「睡眠」了相當長的時間(如幾天左右)之後啓動此問題開始時描述的客戶機 - 服務器序列時纔會發生。它可能會提供某種線索來解決這個問題的根源,儘管我無法看到它。

我的問題:

  1. 有什麼明顯的錯誤與我的設計?

  2. 有沒有人在GAE上遇到類似的問題?

  3. 有沒有人看到一個明顯的解決方案,甚至可能有助於理解這個問題的調試方法?

感謝

+0

使用類級別的變量可能是一個壞主意。如果你啓用了線程,那麼你可能會泄露信息。 –

+0

你是指'class SessionsTable'嗎?我沒有這個類的實例,所以這裏不應該有任何問題,對吧?或者你的意思是全局變量'myServer'可能存在問題,它沒有得到正確保護?我沒有使用任何線程,所以我有理由「擔心」GAE會隱式創建它們嗎?謝謝 –

+0

所有這些都可能是一個問題。如果啓用了線程,則appengine將隱含地創建線程來爲每個請求提供服務(如果實例正在快速響應)。所以你必須設計你的應用程序是線程安全的。除非可以共享,否則不要在模塊級別存儲事物,也不要將其存儲爲類屬性。即它可以作爲共享數據的實例級緩存。 –

回答

3

您錯誤地假定所有請求都由同一個實例處理。事實並非如此:與大多數託管環境一樣,GAE不能保證哪個服務器進程能夠處理任何請求。

您的實現正在使用模塊級變量myServer,它是一個具有自己的實例變量的類實例。但是每個服務器進程都有自己的myServer實例,並且它們不在進程之間共享:所以在一個請求中創建的字典條目不一定會存在於第二個進程中。

您需要考慮如何在實例間持久保存這些數據。真的,數據存儲首先是這樣的;如果您擔心開銷,則應該使用memcache進行調查。

+0

謝謝。我確實考慮了多個服務器實例同時運行的可能性,但是我沒有將它添加到一個服務器將處理第一個(POST)請求並且另一個已經存在的服務器**將處理第二個(GET)請求。我將客戶端請求定義爲同步的,並且我猜我也隱式地假定服務器端的「同步」行爲。 –

+0

雖然有一個問題:服務器很少被「中斷」(我的網站還不是很受歡迎),如果有的話,我必須在某個時間處理0個服務器實例的情況(這導致我'無狀態'設計)。爲什麼GAE會爲幾乎不爲任何人服務的應用程序創建兩個實例?我問這個的原因是,我懷疑手頭可能還有另一個問題。謝謝。 –

+1

它總是發生,它們啓動一個實例,第二個請求在啓動期間進入,並且不處理,然後第二個請求啓動。那麼誰知道哪個實例將爲下一個請求提供服務。 –

相關問題