好了,所以這是我的我怎麼會用GET請求做例子。
我增加兩個主要組件:
首先是一個簡單的螺紋發佈訂閱監聽器,追加新的消息到本地列表對象。 我還向類中添加了列表訪問器,因此您可以像監聽器線程一樣從常規列表中讀取數據。就您的WebRequest
而言,您只是從本地列表對象讀取數據。這會立即返回,並且不會阻止當前請求完成或將來的請求被接受和處理。
class OpenChannel(threading.Thread):
def __init__(self, channel, host = None, port = None):
threading.Thread.__init__(self)
self.lock = threading.Lock()
self.redis = redis.StrictRedis(host = host or 'localhost', port = port or 6379)
self.pubsub = self.redis.pubsub()
self.pubsub.subscribe(channel)
self.output = []
# lets implement basic getter methods on self.output, so you can access it like a regular list
def __getitem__(self, item):
with self.lock:
return self.output[item]
def __getslice__(self, start, stop = None, step = None):
with self.lock:
return self.output[start:stop:step]
def __str__(self):
with self.lock:
return self.output.__str__()
# thread loop
def run(self):
for message in self.pubsub.listen():
with self.lock:
self.output.append(message['data'])
def stop(self):
self._Thread__stop()
第二個是ApplicationMixin類。這是繼承了Web請求類的輔助對象,以便添加功能和屬性。在這種情況下,它會檢查通道偵聽器是否已存在所請求的通道,如果找不到任何通道偵聽器,則會創建一個,並將偵聽器句柄返回給WebRequest。
# add a method to the application that will return existing channels
# or create non-existing ones and then return them
class ApplicationMixin(object):
def GetChannel(self, channel, host = None, port = None):
if channel not in self.application.channels:
self.application.channels[channel] = OpenChannel(channel, host, port)
self.application.channels[channel].start()
return self.application.channels[channel]
WebRequest類現在把聽者彷彿它是一個靜態列表(銘記你需要給self.write
字符串)
class ReadChannel(tornado.web.RequestHandler, ApplicationMixin):
@tornado.web.asynchronous
def get(self, channel):
# get the channel
channel = self.GetChannel(channel)
# write out its entire contents as a list
self.write('{}'.format(channel[:]))
self.finish() # not necessary?
最後,創建應用程序後,我加入一個空的字典作爲一個屬性
# add a dictionary containing channels to your application
application.channels = {}
除了正在運行的線程的一些清理,一旦你退出應用程序
# clean up the subscribed channels
for channel in application.channels:
application.channels[channel].stop()
application.channels[channel].join()
的完整代碼:
import threading
import redis
import tornado.web
class OpenChannel(threading.Thread):
def __init__(self, channel, host = None, port = None):
threading.Thread.__init__(self)
self.lock = threading.Lock()
self.redis = redis.StrictRedis(host = host or 'localhost', port = port or 6379)
self.pubsub = self.redis.pubsub()
self.pubsub.subscribe(channel)
self.output = []
# lets implement basic getter methods on self.output, so you can access it like a regular list
def __getitem__(self, item):
with self.lock:
return self.output[item]
def __getslice__(self, start, stop = None, step = None):
with self.lock:
return self.output[start:stop:step]
def __str__(self):
with self.lock:
return self.output.__str__()
# thread loop
def run(self):
for message in self.pubsub.listen():
with self.lock:
self.output.append(message['data'])
def stop(self):
self._Thread__stop()
# add a method to the application that will return existing channels
# or create non-existing ones and then return them
class ApplicationMixin(object):
def GetChannel(self, channel, host = None, port = None):
if channel not in self.application.channels:
self.application.channels[channel] = OpenChannel(channel, host, port)
self.application.channels[channel].start()
return self.application.channels[channel]
class ReadChannel(tornado.web.RequestHandler, ApplicationMixin):
@tornado.web.asynchronous
def get(self, channel):
# get the channel
channel = self.GetChannel(channel)
# write out its entire contents as a list
self.write('{}'.format(channel[:]))
self.finish() # not necessary?
class GetHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello world")
application = tornado.web.Application([
(r"/", GetHandler),
(r"/channel/(?P<channel>\S+)", ReadChannel),
])
# add a dictionary containing channels to your application
application.channels = {}
if __name__ == '__main__':
application.listen(8888)
print 'running'
try:
tornado.ioloop.IOLoop.instance().start()
except KeyboardInterrupt:
pass
# clean up the subscribed channels
for channel in application.channels:
application.channels[channel].stop()
application.channels[channel].join()
Redis的發佈/訂閱不應在'web.RequestHandler'使用,因爲它會阻止ioloop同時'發佈訂閱等待。聽()'。看看http://tornadogists.org/532067/瞭解一個可用的websocket示例。 – 2013-02-28 21:19:33
websocket是一個不錯的選擇,但是我的應用程序需要在瀏覽器中工作,這些瀏覽器不支持websockets。我正在使用長輪詢。這就是我需要「異步獲取」的原因。 – 2013-03-01 13:16:21
@HelieelsonSantos在這種情況下,您最好的選擇是保持訂閱渠道歷史的本地狀態(由單獨的線程提供),然後立即將該狀態寫入響應並完成「get」操作。客戶應該保留最後獲取索引的某些記錄,或者最後獲得時間等,這樣可以保持不同客戶的連續性。當我得到時間時,我會在幾個小時內用一個例子寫一個答案。 – 2013-03-01 16:24:48