-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathminichat.py
executable file
·137 lines (115 loc) · 4.07 KB
/
minichat.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#!/usr/bin/python2
#coding:utf-8
import tornado.ioloop
import tornado.options
import tornado.web
import os.path
import redis
import datetime
import json
import subprocess
import lib
class DataBase(object):
'''数据库操作,具体细节可以不用去管'''
db = redis.Redis(host='localhost', port=8042, db=0)
def items(self):
'''从数据库获取所有条目'''
itemid = self.maxid()
items = []
if not itemid: return items
while itemid > 0:
item = {
'content': self.db.hget("item:content", itemid),
'name': self.db.hget("item:name", itemid),
'avatar': self.db.hget("item:avatar", itemid),
'date': self.db.hget("item:date", itemid),
'id': itemid,
}
items.append(item)
itemid -= 1
return items
def add_item(self, item):
'''添加一个新的条目'''
item["id"] = int(self.db.incr("item:id:max"))
item['content'] = lib.escape(item['content'])
nowtime = lib.strtime(datetime.datetime.now())
self.db.hset("item:content", item['id'], item['content'])
self.db.hset("item:name", item['id'], item['name'])
self.db.hset("item:avatar", item['id'], item['avatar'])
self.db.hset("item:date", item['id'], nowtime)
def maxid(self):
try:
return int(self.db.get("item:id:max"))
except TypeError:
return None
class Base(DataBase, tornado.web.RequestHandler):
pass
class Poll(Base):
waiter = []
def queue(self, callback, sence):
'''排队,将回调函数添加到waiter队列里面'''
me = Poll
me.waiter.append({"callback": callback, "sence": sence})
def broadcast(self):
'''广播,调用队列中的回调函数,返回新增的条目,结束长轮询'''
me = Poll
for waiter in me.waiter:
newitems_num = int(self.maxid()) - int(waiter["sence"])
newitems = self.items()[:newitems_num]
waiter["callback"](newitems)
Poll.waiter = []
class GetItems(Poll):
@tornado.web.asynchronous # 使用Tornado提供的异步非阻塞特性
def get(self):
sence = int(self.get_argument("sence"))
if sence == 0 and self.maxid():
self.finish(json.dumps(self.items()))
return
self.queue(self.send, sence) #添加到等待队列中
def send(self,newitems):
'''作为广播队列中的回调函数,一旦有新条目回调函数就会被调用,
返回新条目的json。'''
try:
self.finish(json.dumps(newitems))
except IOError:
print "Happen a IOError"
class AddItem(Poll):
def post(self):
'''获取用户提交的条目'''
if not self.validate(): raise tornado.web.HTTPError(500)
item = {}
item['name'] = self.get_argument("name")
item['avatar'] = lib.gravatar(self.get_argument("email"))
item['content'] = self.get_argument("content")
self.add_item(item) #往数据库添加新的条目
self.broadcast() #给所有进行长轮询的客户端广播新的条目
def validate(self):
'''检查输入'''
if not "name" in self.request.arguments: return False
elif not "email" in self.request.arguments: return False
elif not "content" in self.request.arguments: return False
return True
class Home(Base):
def get(self):
self.render("home.html")
handlers = [
(r"/", Home),
(r"/add/", AddItem),
(r"/items.json", GetItems),
]
settings = dict(
xsrf_cookies = False,
static_path = 'static',
debug = True,
port = 8888,
template_path = 'templates'
)
def run():
#创建一个子进程来运行数据库服务器
subprocess.Popen(
("/usr/bin/redis-server", 'redis.conf'))
tornado.options.parse_command_line()
app = tornado.web.Application(handlers, **settings)
app.listen(settings['port'])
tornado.ioloop.IOLoop.instance().start()
if __name__ == "__main__": run()