一般的web應用,都會使用ajax輪詢來獲得伺服器的更新。但輪詢方式會增加伺服器的壓力,而且很多請求獲得的資料並沒有更新,這些請求都是無意義的,徒增伺服器壓力而已。
使用Comet技 術可以一定程度地解決以上問題。Comet的實現很多,大多數需要使用特定的HTTP Server來實現。本文介紹使用基於python的Tornado Web Server來實現Server Side的Comet,為了貼近生產環境,還會介紹Tornado如何配合Nginx工作。
安裝
到http://www.tornadoweb.org/可以下載到最新的Tornado,那裡也有安裝的說明。
編寫第一個Tornado程式
編輯comet.py,內容如下
#!/usr/bin/env pythonimport tornado.httpserverimport tornado.ioloopimport tornado.webclass MainHandler(tornado.web.RequestHandler): @tornado.web.asynchronous #說明1 def get(self): some_async_func(callback=self.wait) def wait(self, result): if result: self.write(result) self.finish() #說明2 else: tornado.ioloop.IOLoop.instance().add_timeout(time.time() + 0.5, lambda: some_async_func(callback=self.wait)) #說明3application = tornado.web.Application([ (r"/", MainHandler),])if __name__ == "__main__": http_server = tornado.httpserver.HTTPServer(application) http_server.listen(8999) tornado.ioloop.IOLoop.instance().start()說明1:使用此decorator即可以把get方法non-blocking化說明2:non-blocking化的請求必須調用self.finish()完成請求說明3:如果需要sleep等待,應該使用Tornado的IOLoop的add_timeout方法。使用其他休眠方法如time.sleep會block住當前線程,而Tornado是一個單線程Server,會導致其他請求無法執行的問題。
此時執行comet.py,一個non-blocking的server端已經搭建成功。
要注意的問題是,如果不加逾時機制,服務端會不斷執行,即使用戶端已經斷開。因此還需自己實現逾時機制。
Nginx配置
upstream frontends { server 127.0.0.1:8999;}server { listen 8888; location / { proxy_read_timeout 1800; proxy_pass_header Server; proxy_set_header Host $http_host; proxy_redirect off; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; proxy_pass http://frontends; }}
此配置採用proxy upstream到backend的原因是方便upstream負載平衡,只需要upstream中添加多幾個ip連接埠即可。前面提過,Tornado是 單線程Server,啟動cpu核心數個Tornado進程並綁定到cpu,能夠提高機器的輸送量。
需要特別指出的是,要根據上述應用的逾時機制,設定proxy_read_timeout,proxy_read_timeout必須大於應用的逾時。否則將會有504請求返回。
至此,一個Comet應用的伺服器端就完成了。由於Comet使用的是長串連機制,需要特別注意從用戶端到真正後端程式之間每個環節的逾時機制,而且網路連接的不可靠性影響將會更大,需要考慮更多邊界條件。
參考資料:
- http://www.tornadoweb.org/documentation