我试图绕过Google App Engine的channel
功能,因为他们不(轻松)提供网页套件。
我目前的情况是我有一个很长的工作(文件处理)正在通过工作人员异步执行。 该工作人员在每行更新数据库中文件处理的状态,以通知客户。
从目前的角度来看,F5将指示处理的演变。
现在我想实施一个实时更新系统。当然,我可以每5秒做一次XHR请求,但实时连接似乎更好......引入频道,因为Websockets看起来不可能。
根据我的理解,我可以channel.send_message
仅限一个客户,而不是#34;房间"。这里的问题是,处理文件的工人没有任何客户当前连接的信息(可能是一个,可能是10个)。
我可以遍历所有客户并发布到每个client_id,怀疑其中至少有一个会收到消息,但这非常无用且资源太多。
我希望有更好的方法来实现这一目标?也许是Google Channels
功能的一个不错的选择,而无需重新配置我的整个App Engine系统(如Websockets)?
答案 0 :(得分:0)
我能想到的一个解决方案,即不是绝对理想但更适合的解决方案,是管理专用数据库表(也可以在Memcache中实现):
client_id
列表e.g。 :
现在,不是张贴到channel.send_message(client_id, Message)
,而是制作一个这样的包装器:
def send_to_room(room, message):
# Queries are SQLAlchemy like :
room = Rooms.query.filter(Rooms.name === room).first()
if not room:
raise Something
clients = Clients.query.filter(Rooms.room_id === room.id).all()
for client in clients:
channel.send_message(client.client_id, message)
瞧,你在Google App Engine中有类似房间的实现。
此解决方案的缺点是在数据库中添加两个表(或等效表)。
有人有更好的吗?
答案 1 :(得分:0)
我假设客户端正在启动长时间运行的任务。 因此,在您启动任务之前,请从客户端向类似于此处理程序的处理程序发出ajax请求。此处理程序有两个返回给客户端的东西。 javascript api用于创建通道的令牌参数,以及用于确定哪个客户端创建了通道的cid param。
from google.appengine.api import channel
@ae.route("/channel")
class CreateChannel(webapp2.RequestHandler):
def get(self):
cid = str(uuid.uuid4())
token = channel.create_channel(cid)
data = {
"cid":cid,
"token":token
}
self.response.write(json.dumps(data))
现在使用频道javascript api创建一个新频道 https://cloud.google.com/appengine/docs/python/channel/javascript
var onClosed = function(resp){
console.log("channel closed");
};
var onOpened = function(resp){
console.log("channel created");
};
var onmessage = function(resp){
console.log("The client received a message from the backend task");
console.log(resp);
};
var channel_promise = $.ajax({
url: "/channel",
method: "GET"
});
channel_promise.then(function(resp){
//this channel id is important you need to get it to the backend process so it knows which client to send the message to.
var client_id = resp.data.cid;
var channel = new goog.appengine.Channel(resp.data.token);
handler = {
'onopen': $scope.onOpened,
'onmessage': $scope.onMessage,
'onerror': function () {
},
'onclose': function () {
alert("channel closed.")
}
};
socket = channel.open(handler);
//onOpened is the callback function to call after channel has been created
socket.onopen = onOpened;
//onClose is the callback function to call after channel has been closed
socket.onclose = onClosed;
//onmessage is the callback function to call when receiving messages from your task queue
socket.onmessage = onMessage;
});
现在我们都设置为收听频道消息。 因此,当用户单击按钮时,我们需要启动后端任务。
var letsDoSomeWorkOnClick = function(){
//make sure you pass the client id with every ajax request
$.ajax({
url: "/kickoff",
method: "POST",
params: {"cid":client_id}
});
}
现在app引擎处理程序启动后端任务队列。我使用deffered库来做到这一点。 https://cloud.google.com/appengine/articles/deferred
@ae.route("/kickoff")
KickOffHandler(webapp2.RequestHandler):
def post(self):
cid = self.request.get("cid")
req = {}
req['cid'] = cid
task = MyLongRunningTask()
deferred.defer(task.long_runner_1, req, _queue="my-queue")
示例任务:
class MyLongRunningTask:
def long_runner_1(self,req):
# do a whole bunch of stuff
channel.send_message(req["cid"], json.dumps({"test":"letting client know task is done"})