2016-04-18 26 views
2

我試圖圍繞谷歌App Engine的channel功能,因爲他們不(很容易)提供websocket。GAE頻道給多個客戶?

我目前的情況是,我有一個很長的工作(文件處理),通過一個工人異步執行。 此工作人員在每一行更新數據庫中文件處理的狀態以通知客戶。

從目前的角度來看,F5將表明處理的演變。

現在我想實施一個實時更新系統。當然,我可以每5秒做一次XHR請求,但實時連接似乎更好......引入頻道,因爲Websockets似乎沒有可能性。

從我的理解,我可以channel.send_message只有一個客戶端,而不是一個「房間」。這裏的問題是,處理文件的工作人員沒有任何客戶當前連接的信息(可能是一個,可能是十個)。

我可以遍歷所有客戶併發送給每個client_id,懷疑至少其中一個人會收到該消息,但這非常無用且過於機智。

我希望有更好的方法來實現這個目標嗎?也許一個很好的替代谷歌Channels功能,而不必重新配置我的整個App Engine系統(如Websockets)?

回答

0

一個解決方案,我能想到的,這不是絕對的理想,但會更適合,是管理專用數據庫表(也可以在內存緩存來實現)有:

  • 包含的表客房
  • 包含連接到房間

例如client_id列表的表的列表:

  • 房間(ID,姓名)
  • 客戶端(ID,ROOM_ID,CLIENT_ID)

現在,而不是張貼到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中有一個類似實施的房間。

此解決方案的缺點是在數據庫中添加兩個表(或等效項)。

有人有更好的?

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} 
     }); 
    } 

現在應用引擎處理程序啓動後端任務隊列。我使用緩衝庫來執行此操作。 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"}) 
+0

對不起,我相信你誤解了我的問題。這裏的問題是,我怎樣才能將相同的消息發送給連接到應用程序的許多客戶端(因爲Google不允許這樣做) –

+0

噢,是的,我確實想過要發送郵件給特定的客戶。我的錯。 – bscott

+0

我感到尷尬,因爲你寫了一個全面的完整答案:/ –