Модули Python для приложений и сервис каналов

Я использую модули App Engine в моем проекте python. (https://developers.google.com/appengine/docs/python/modules/#Python_Background_threads)

Я также использую каналы в проекте m: https://developers.google.com/appengine/docs/python/channel/

Я хочу направить подключенные/отключенные сообщения ('/_ah/channel/connected/', '/_ah/channel/disconnected/') в мой модуль api. Прямо сейчас я не могу заставить их отображаться в любом модуле (по умолчанию или api)

app.yaml

    api_version: 1
    application: integrate
    version: 1-0-0
    runtime: python27
    threadsafe: true

    builtins:
      - deferred: on

    libraries:
      - name: pycrypto
      version: "2.6"

    handlers:
      - url: /favicon\.ico
      static_files: static/favicon.ico
      upload: static/favicon\.ico

      - url: /admin/.+
      script: src.default.main.app
      login: admin

      - url: /.*
      script: src.default.main.app

api.yaml

    api_version: 1
    application: integrate
    module: api
    version: 1-0-0
    runtime: python27
    threadsafe: true

    inbound_services:
      - channel_presence

    builtins:
      - deferred: on

    libraries:
      - name: pycrypto
      version: "2.6"

    handlers:
      - url: /admin/.+
      script: src.api.main.app
      login: admin

      - url: /.*
      script: src.api.main.app

dispatch.yaml

    application: integrate

    dispatch:
       - url: "*/_ah/channel/*"
       module: api

Примечание. Чтобы это было ясно, все это работает в режиме dev локально.

api.main.app

    app = webapp2.WSGIApplication(debug=True)
    _routes = [
        :
        ChannelDisconnectedHandler.mapping(),
        ChannelConnectHandler.mapping()
    ]

    for r in self._routes:
        app.router.add(r)

ChannelDisconnectHandler

    CHANNEL_DISCONNECTED_URL_PATTERN = '/_ah/channel/disconnected/'


    class ChannelDisconnectedHandler(RequestHandler):

        @classmethod
        def mapping(cls):
            return CHANNEL_DISCONNECTED_URL_PATTERN, cls

        def post(self):
            """
            Channel Presence handler. Will be called when a client disconnects.
            """
            channel_id = self.request.get('from')
            logging.info("Channel Disconnect. Id: %s" % channel_id)

ChannelConnectHandler

    CHANNEL_CONNECT_URL_PATTERN = '/_ah/channel/connected/'

    class ChannelConnectHandler(RequestHandler):

        @classmethod
        def mapping(cls):
            return CHANNEL_CONNECT_URL_PATTERN, cls

        def post(self):
            """
            Channel Presence handler. Will be called when a client connects.
            """
            channel_id = self.request.get('from')
            logging.info("Channel Connect. Id: %s" % channel_id)

Итак, мой клиент (написанный на javascript) отправляется в мой модуль api и открывает канал.

    var open_channel = function(tokenResponse) {
        console.log("Open Channel. token Response: " + tokenResponse)
        token = tokenResponse.token;
        var channel = new goog.appengine.Channel(token);
        if (socket != null) {
            socket.close();
        }
        socket = channel.open();
        socket.onopen = onOpened;
        socket.onmessage = onMessage;
        socket.onerror = onError;
        socket.onclose = onClose;
    };

    onOpened = function() {
        console.info("Channel API Connection is open.");
    };

    onError = function(e) {
        console.info("CHANNEL Error. Code: " + e.code + ", Description: " + e.description);
    };

    onClose = function() {
        console.info("Close Channel");
    };

    onMessage = function(msg) {
       console.info("Message Received: " + msg + ", Data: " + msg.data);
    };

Эта функция обратного вызова достигается с помощью действительного токена. Я успешно создаю сокет и выполняю эту функцию, как и ожидалось. В моей локальной системе вызывается функция onOpened, и я получаю сообщения с сервера. В производстве onOpened никогда не вызывается, и я никогда не получаю никаких сообщений./_ah/channel/connected/также не вызывается.

Не поддерживается ли служба канала с помощью модулей? Любые мысли о том, чего я не хватает?

Ответ 1

В соответствии с поддержкой Google Enterprise (слегка измененный от исходного ответа):

  • channel_presence должна быть включена входящая служба в app.yaml.

    inbound_services:
    - channel_presence
    

    Включение этой входящей службы в модули yaml файла (например, api.yaml в этом вопросе) не будет включать эту службу.

  • Пути URL, начинающиеся с */_ah, не являются отправляемыми путями и не могут быть перенаправлены dispatch.yaml. Поэтому, channel_presence обработчики путей URL должны быть описаны в app.yaml.

    handlers:
    - url: /_ah/channel/connected/
      script: mymodule.application
    

Ответ 2

Вы должны объявить маршрутизацию hadler для подключения и развязывания URL-адресов.

Маршрутизация обработчика в main.py:

application = webapp2.WSGIApplication([
    ...
    # Define a URL routing for /_ah/channel/connected/
    webapp2.Route(r'/_ah/channel/connected/',
                  handler=ChannelConnectedHandler,
                  name='channel_connected')

], debug=True, config=webapp2_config)


# Implement class handler of /_ah/channel/connected/
class ChannelConnectedHandler(webapp2.RequestHandler):
    def post(self):
        client_id = self.request.get('from')
        logging.info('client %s has connected!' % client_id)
        ...

Ответ 3

Я столкнулся с проблемами с использованием API канала в модулях, и я попытался обойти их, используя аналогичный трюк, который упоминает Эмиль, перенаправляя запросы к модулям.

Это была немного более сложная настройка, хотя, поскольку у меня на самом деле было 3 модуля, где 2 из них использовали Channel API, а один - "frontend". Что-то вроде этого:

  • frontend интерфейса (по умолчанию)
  • модуль serviceA (с использованием канала api 1)
  • обслуживание модуля B (с использованием канала api 2)

Я хотел иметь возможность слушать "уведомления" из двух отдельных служб в интерфейсе.

И как мне удалось обойти это (в dev), было добавить перенаправления к интерфейсу, который читал токены, которые я префикс на каждой службе и перенаправлял каждую службу.

"Отлично, это работает!" Я подумал, но потом, когда я попытался развернуть приложение, я понял, что там больше, потому что конечные точки TalkGadget, используемые внутри API канала, казалось, ожидали определенного исходного приложения и, таким образом, не разрешали перекрестное доменное общение.

Итак, я закончил использование нескольких проектов вместо модулей и поставил HTML-iframe "мост postMessage", чтобы обойти проблемы с перекрестным доменом. И с радостью это работает очень хорошо, и в качестве побочного эффекта я получаю в два раза больше "бесплатных" каналов для использования.

Я нашел проблему, связанную с этим здесь, которая может быть интересна для вас: https://code.google.com/p/googleappengine/issues/detail?id=10293