これは、私がこれまでに作成した最初のロング ポーリング アプリケーションであり、Twisted を使用した 2 番目のプロジェクトです。そのため、私のコード内の何かについて誰かがフィードバックをいただければ幸いです。
さまざまな例をまとめてみましたが、ほとんど機能していますが、データを 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)の### チャット サーバー部分 (後半) をリッスンします:
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
送信されます。 とにかくそれが理論です - 前の文は私が私の問題があると思う場所です.
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 の理解に半分も自信がないので、そこに問題があると思いますが、ここから先に進むために何ができるかわかりません。どんな助けでも大歓迎です。