这是我构建的第一个长轮询应用程序,也是 Twisted 的第二个项目,所以如果有人对我的代码中的任何内容提出任何反馈,我将不胜感激,因为我可能完全以错误的方式处理这件事。
我一直在拼凑各种示例,并且它几乎可以正常工作,但我似乎无法找到将数据返回到 Javascript 的方法。我有一个在 Twisted 上运行的 Django 站点,它似乎运行良好,所以我不会包含 Django 位,除非有人认为它很重要,而 Django 站点唯一做的就是主持聊天。我最初使用常规轮询设置它,但我被要求将其更改为长轮询并且我几乎就在那里(我希望)。
这是 HTML/JS (long.html):
<div class="chat-messages" style="width:300px;height:400px;border:1px solid black;overflow:scroll;" id="messages">
</div><br/>
<form action="javascript:sendMessage();" >
<input type="text" id="chat_nickname" name="author"/>
<input type="text" id="chat_input" name="message" class="chat-new"/>
<button class="submit">Submit</button>
</form>
</body>
<script type="text/javascript">
// keep track of the last time data wes received
var last_update = 0;
// call getData when the document has loaded
$(document).ready(function(){
getData(last_update);
});
// execute ajax call to chat_server.py
var getData = function(last_update){
$.ajax({
type: "GET",
url: "http://"+ window.location.hostname + ":8081?last_update=" + last_update + "&callback=?",
dataType: 'json',
async: true,
cache:false,
timeout: 300000,
success: function(response){
// append the new message to the message list
var messages = response.data.messages;
console.log(response);
for (i in messages){
$('<p><span class="time">[' + messages[i].time +']</span> - <span class="message">' + messages[i].message + '</span></p>').appendTo('#messages');
if (messages[i].time > last_update){
last_update = messages[i].time;
}
}
console.log("Last_update: " + last_update);
// Keep div scrolled to bottom
$("#messages").scrollTop($("#messages")[0].scrollHeight);
// Check again in a second
setTimeout('getData(' + last_update + ');', 1000);
},
error: function(XMLHttpRequest, textStatus, errorThrown){
// Try again in 10 seconds
setTimeout( "getData(" + last_update + ");", 10000);
},
failure: function(){ console.log('fail'); },
});
}
// Add a contribution to the conversation
function sendMessage(){
var nickname = $('#chat_nickname').val();
var message = $('#chat_input').val();
$('#chat_input').val("");
console.log( "nickname: " + nickname + "; message: " + message );
$.ajax({
type: 'POST',
url: '/chat/post_message/',
data: {
nickname: nickname,
message:message
},
success: function(data, status, xml){
console.log("Success! - " + status);
},
error: function(xml, status, error){
console.log(error + " - Error! - " + status);
},
complete: function(xml, status){
console.log("Complete! - " + status);
}
});
}
</script>
sendMessage
将表单中的数据传递给 Django,然后 Django 将其放入数据库(并为其添加时间)。
getData
指向:8081,Twisted 正在使用下一段代码(chat_server.py)的### Chat Server部分(后半部分)进行监听:
import datetime, json, sys, time, os, types
from twisted.web import client, resource, server, wsgi
from twisted.python import threadpool
from twisted.internet import defer, task, reactor
from twisted.application import internet, service
from twisted.enterprise import adbapi
from django.core.handlers.wsgi import WSGIHandler
## Django environment variables
sys.path.append("mydjangosite")
os.environ['DJANGO_SETTINGS_MODULE'] = 'mydjangosite.settings'
## Tying Django's WSGIHandler into Twisted
def wsgi_resource():
pool = threadpool.ThreadPool()
pool.start()
# Allow Ctrl-C to get you out cleanly:
reactor.addSystemEventTrigger('after', 'shutdown', pool.stop)
wsgi_resource = wsgi.WSGIResource(reactor, pool, WSGIHandler())
return wsgi_resource
## Twisted Application Framework
application = service.Application('twisted-django')
class Root(resource.Resource):
def __init__(self, wsgi_resource = None):
resource.Resource.__init__(self)
if wsgi_resource != None:
self.wsgi_resource = wsgi_resource
def getChild(self, path, request):
child_path = request.prepath.pop(0)
request.postpath.insert(0, child_path)
return self.wsgi_resource
def render_GET(self, request):
id = request.args.get('id', [""])[0]
command = request.args.get('command', [""])[0]
self.get_page(request, id)
return server.NOT_DONE_YET
@defer.inlineCallbacks
def get_page(self, request, id):
page = yield client.getPage("/chat/latest/%s" % id)
request.write(page)
request.finish()
## Create and attach the django site to the reactor
django_root = Root(wsgi_resource())
django_factory = server.Site(django_root)
reactor.listenTCP(8080, django_factory)
### Chat Server
class ChatServer(resource.Resource):
isLeaf = True
def __init__(self):
# throttle in seconds
self.throttle = 5
# store client requests
self.delayed_requests = []
# setup a loop to process collected requests
loopingCall = task.LoopingCall(self.processDelayedRequests)
loopingCall.start(self.throttle, False)
# Initialize
resource.Resource.__init__(self)
def render(self, request):
"""Handle a new request"""
request.setHeader('Content-Type', 'applicaton/json')
args = request.args
# set jsonp callback handler name if it exists
if 'callback' in args:
request.jsonpcallback = args['callback'][0]
# set last_update if it exists
if 'last_update' in args:
request.last_update = args ['last_update'][0]
data = self.getData(request)
if type(data) is not types.InstanceType and len(data) > 0:
# send the requested messages back
return self.__format_response(request, 1, data)
else:
# or put them in the delayed request list and keep the connection going
self.delayed_requests.append(request)
return server.NOT_DONE_YET
def getData(self, request):
data = {}
dbpool = adbapi.ConnectionPool("sqlite3", database="/home/server/development/twisted_chat/twisted-wsgi-django/mydjangosite/site.db", check_same_thread=False)
last_update = request.last_update
print "LAST UPDATE: ", last_update
new_messages = dbpool.runQuery("SELECT * FROM chat_message WHERE time > %r" % request.last_update )
return new_messages.addCallback(self.gotRows, request )
def gotRows(self, rows, request):
if rows:
data = {"messages":
[{ 'author': row[1], 'message':row[2],'timestamp': row[3] } for row in rows]
}
print 'MESSAGES: ', data
if len(data) > 0:
return self.__format_response(request, 1, data)
return data
def processDelayedRequests(self):
for request in self.delayed_requests:
data = self.getData(request)
if type(data) is not types.InstanceType and len(data) > 0:
try:
print "REQUEST DATA:", data
request.write(self.__format_response(request, 1, data))
request.finish()
except:
print 'connection lost before complete.'
finally:
self.delayed_requests.remove(request)
def __format_response(self, request, status, data):
response = json.dumps({ "status": status, "time": int(time.time()), "data": data })
if hasattr(request, 'jsonpcallback'):
return request.jsonpcallback + '(' + response + ')'
else:
return response
chat_server = ChatServer()
chat_factory = server.Site(chat_server)
reactor.listenTCP(8081, chat_factory)
在这里,render
尝试getData
(它可能永远不会?)并且当它不能将请求放入self.delayed_requests
. getData
使用 enterprise.adbapi 对 Django 的数据库进行查询,返回一个 Deferred 实例。 processedDelayedRequests
遍历延迟的请求队列,如果查询完成,该数据将传递给gotRows
,然后将其转换为我想要的格式并将__format_response
其发送到 JS 将数据发送回可以处理的 JS。 无论如何,这就是理论 - 上一句是我认为我的问题所在。
print "LAST UPDATE: ", last_update
总是打印 "LAST_UPDATE: 0" 但 last_update 是通过 JS 更新的,所以这不是错误。
print 'MESSAGES: ', data
打印“{'messages': [{'timestamp': u'2013-08-10 16:59:07.909350', 'message': u'chat message', 'author': u'test'}, {'timestamp ': u'2013-08-10 17:11:56.893340', 'message': u'hello', 'author': u'pardon'}]}" 等新消息添加到数据库中。发布帖子时它会获取新数据,否则似乎工作得很好。
print "REQUEST DATA:", data
根本不会触发...我认为这种方法是早期尝试使其正常工作时遗留下来的。
我得到了正确的输出,gotRows
但不知道如何将该输出传递回客户端。我对自己对 Deferreds 的理解甚至没有半点信心,所以我认为这就是我的问题所在,但我不知道我能做些什么才能从这里继续前进。任何帮助将不胜感激。