• Home
  • Учебник по ExtJS
  • О сайте
  •  



    Подсказки по использованию App Engine

    Как из приложения получить его идентификатор и версию?

    Используйте функцию os.getcwd() или переменную окружения os.environ['PATH_TRANSLATED']

    >>> os.getcwd()
    '/base/data/home/apps/shell/1.21'
    >>> os.getcwd().split('/')[-2]
    'shell'
    >>> os.getcwd().split('/')[-1]
    '1.21'
    
    >>> os.environ['PATH_TRANSLATED']
    '/base/data/home/apps/shell/1.21/shell.py'
    >>> os.environ['PATH_TRANSLATED'].split('/')[-3]
    'shell'

    Как определить текущий хост?

    Есть один очень интересный файл, который уникален на каждом сервере:

    >>> open('/base/python_dist/search.config').read()
    'datapath .\nsorttempdir .\ndisk /export/hdc3/borgletdata/dirs/prod-appengine.\
    mpm_python_dist_v12.apphosting.77627982/bigfiledata/466024'
    
    >>> open('/base/python_dist/search.config').read()
    'datapath .\nsorttempdir .\ndisk /export/hdc3/borgletdata/dirs/prod-appengine.\
    mpm_python_dist_v12.apphosting.77627739/bigfiledata/465336'

    Вы можете идентифицировать машину, на которой работает данный процесс, проанализировав хэш содержимого из этого файла. К примеру так:

    def get_server_id():
        try:
            fd   = open('/base/python_dist/search.config')
            data = fd.read()
            fd.close()
        except IOError:
            return 'unknown'
    
        return '%s' % data.__hash__()

    Google не сообщает, на скольких серверах работает ваше приложение (и скорее всего это будет зависеть от генерируемого вашим сайтом трафика). Для определения того, сколько машин используется для функционирования приложения можно воспользоваться следующей методикой: включить server_id в содержимое страницы сайта. Затем достаточно провести несколько загрузок, чтобы понять сколько уникальных идентификаторов сервера будет выдано.

    $ for i in `seq 20`; do
        curl -s http://cometchat.appspot.com|\
        grep server_id; \
      done    |sort -n|uniq -c
    
         20 server_id: '7341146770217830363'

    В примере видно, что приложение работает только на одном сервере.

    Как можно идентифицировать текущий процесс?

    И следующий вопрос: сколько процессов моего приложения запущено на одном сервере? Можно проанализировать с помощью глобальной переменной:

    the_process_global = "something"
    
    def get_process_id():
        return '%s' % id(the_process_global)

    Теперь мне известно, что приложение задействует два процесса:

    $ for i in `seq 20`; do
        curl -s http://cometchat.appspot.com|\
        grep _id;
      done    |sort -n|uniq -c
    
        13 process_id: '12457625149327067176'
         7 process_id: '3996238433791648184'

    Работаем ли мы в среде разработки или на сервере?

    Я использую такой шаблон:

    if os.environ.get('SERVER_SOFTWARE','').startswith('Devel'):
        HOST='local'
    elif os.environ.get('SERVER_SOFTWARE','').startswith('Goog'):
        HOST='google'
    else:
        # logging.error('Неизвестный сервер?')
        HOST='unknown'

    Cookies?

    Google в своем интерфейсе определило объекты request и response как наследники соответствующих классов из библиотеки WebOb. Таким образом мы можем из объекта request получить содержимое cookie запроса:

    username = self.request.cookies.get('username', '')

    К сожалению, вы не сможете напрямую использовать метод response.set_cookie из библиотеки WebOb. Но это можно всегда сделать вручную:

    self.response.headers.add_header(
            'Set-Cookie',
            'username=%s; expires=Fri, 31-Dec-2020 23:59:59 GMT' \
              % username.encode())

    Дополнительные методы работы с cookie есть в группе обсуждения на английском языке.

    Отладка работы с данными хранилища

    Я создал простой отладчик для работы с хранилищем. Он добавляет отладочную информацию в конец каждой создаваемой страницы. Для его использования требуется, чтобы классы обработчиков наследовались от debug.DebugMiddleware вместо webapp.RequestHandler.

    К примеру:

    class List(debug.DebugMiddleware):
        def get(self):
            ... blabla ...

    Отдалочная информация будет выглядеть так:

    **** Request took:   830ms/170ms (real time/cpu time)
    **** GQLs, datastore accessed 1 times.
    98ms GQL app: ":self"
                kind: "Image"
                Order {
                property: "modified"
                direction: 2
                }
                args: (50,) {}

    GQL запрос, который сгенерировал ее:

    ims = Image.all().order("-modified").fetch(50)

    Другой пример отладочной информации:

    **** Request took:   150ms/130ms (real time/cpu time)
    **** GQLs, datastore accessed xx times.
      219ms PUT ({'full':...
      178ms PUT ({'full':...
        6ms GET ([datastore_types.Key.from_path('Image', 350L, _app=u'srv')],) {}
        2ms GET ([datastore_types.Key.from_path('Image', 349L, _app=u'srv')],) {}
        2ms GET ([datastore_types.Key.from_path('Image', 348L, _app=u'srv')],) {}

    Этот отладчик очень удобно использовать в Django.

    Динамическая загрузка изображений на сервер

    Вот код, который я использую:

    <form action="." method="post" enctype="multipart/form-data">
        <label>File: </label><input name="file" type="file"><br />
        <input type="submit">
    </form>

    На стороне сервера:

    class Image(db.Model):
        name        = db.StringProperty()
        content     = db.BlobProperty()
    
    class UploadImage(webapp.RequestHandler):
        def post(self):
            if 'file' not in self.request.POST:
                self.error(400)
                self.response.out.write("Файл не указан!")
                return
    
            if (self.request.POST.get('file', None) is None or
               not self.request.POST.get('file', None).filename):
                self.error(400)
                self.response.out.write("Файл не указан!")
                return
    
            file_data = self.request.POST.get('file').file.read()
            file_name = self.request.POST.get('file').filename
    
            im = Image()
            im.name    = file_name
            im.content = file_data
            im.save()
            self.response.out.write("Изображение %r сохранено." % im.name)

    Как определить размер и тип изображения

    Существует реализации функции getImageInfo, которая позволяет определять размер изображения без использования внешних библиотек. Использование очень простое:

    content_type, width, height = getImageInfo(im.content)

    Динамическая работа с изображениями

    По этому поводу существует отдельная статья в официальной документации. Ниже приведен мой упрощенный код, который я использую:

    class ServeImage(webapp.RequestHandler):
        def get(self, key):
            im = db.get(db.Key(key))
            if not im:
                self.error(404)
                return
    
            content_type, width, height = getImageInfo(im.content)
            self.response.headers.add_header("Expires", "Thu, 01 Dec 2014 16:00:00 GMT")
            self.response.headers["Content-Type"] = content_type
            self.response.out.write(im.content)

    Leave a Reply