<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Technology@Work &#187; Google App Engine</title>
	<atom:link href="http://techwork.ru/tag/google-app-engine/feed/" rel="self" type="application/rss+xml" />
	<link>http://techwork.ru</link>
	<description>Русскоязычная информация о Cloud Computing и других технологиях</description>
	<lastBuildDate>Wed, 16 Sep 2009 09:34:43 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>PHP код теперь можно запускать под Google App Engine</title>
		<link>http://techwork.ru/2009/04/15/php-on-google-app-engine/</link>
		<comments>http://techwork.ru/2009/04/15/php-on-google-app-engine/#comments</comments>
		<pubDate>Wed, 15 Apr 2009 06:37:43 +0000</pubDate>
		<dc:creator>techworkru</dc:creator>
				<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://techwork.ru/?p=647</guid>
		<description><![CDATA[Недавний анонс поддержки Java в App Engine от Google породил целую волну экспериментов с реализацией возможности запуска кода под другие языки &#8211; Clojure, Beanshell, Groovy, Javascript, Ruby, Scala, Scheme и другие (если они имеют соответствующие JVM-реализации).
Напомню, что проект или приложение, размещенное на Google App Engine, может хоститься бесплатно, если оно вписывается в стандартную квоту, которая [...]]]></description>
			<content:encoded><![CDATA[<p>Недавний анонс поддержки Java в App Engine от Google породил целую волну экспериментов с реализацией возможности запуска кода под другие языки &#8211; Clojure, Beanshell, Groovy, Javascript, Ruby, Scala, Scheme и другие (если они имеют соответствующие JVM-реализации).</p>
<p>Напомню, что проект или приложение, размещенное на Google App Engine, может хоститься бесплатно, если оно вписывается в стандартную квоту, которая в принципе достаточна для сайтов с 5 млн хитов в месяц. В AppEngine для хранения данных используется специальный интерфейс к собственной высокомасштабируемой платформе <a href="http://googleappengine.ru/docs/whatisgoogleappengine.html#The_Application_Environment" target="_blank">BigTable</a>, которая кардинально отличается от СУБД на основе SQL.</p>
<p>Так как подавляющее большинство web-разработчиков в своей работе использует язык PHP, им будет интересно узнать, что теперь и их приложения <a href="http://www.webdigi.co.uk/blog/2009/run-php-on-the-google-app-engine/" target="_blank">возможно запустить под App Engine</a>. Для этого используется JVM-движок Quercus, который прозрачно выполняет перекомпиляцию в Java-байткод. Примечательно, что по тестам многих разработчиков скорость выполнения такого PHP кода гораздо выше работы стандартного движка без использования дополнительных механизмов ускорения.</p>
<p>Остается вопрос: как правильно работать из PHP с фирменными API от Google: Datastore, Mail, Users и прочими? Если есть желание провести эксперименты, <a href="http://groups.google.com/group/google-appengine-ru/browse_thread/thread/5f5b9c898861d5a8" target="_blank">присоединяйтесь к русскоязычной группе проекта</a></p>
]]></content:encoded>
			<wfw:commentRss>http://techwork.ru/2009/04/15/php-on-google-app-engine/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Google App Engine: Обновление roadmap</title>
		<link>http://techwork.ru/2009/02/07/google-app-engine-new-roadmap/</link>
		<comments>http://techwork.ru/2009/02/07/google-app-engine-new-roadmap/#comments</comments>
		<pubDate>Sat, 07 Feb 2009 07:32:16 +0000</pubDate>
		<dc:creator>techworkru</dc:creator>
				<category><![CDATA[Google App Engine]]></category>

		<guid isPermaLink="false">http://techwork.ru/?p=637</guid>
		<description><![CDATA[Разработчики Google App Engine вновь приоткрыли завесу над планами по развитию платформы и решили опубликовать список будущих нововведений, которые должны быть реализованы в ближайшие подгода. Из многих вкусностей стоит отметить:

Поддержка запуска задач по расписанию (а-ля cron)
Возможность с помощью специальных команд запускать процессы, выполняющиеся вне контекста пользовательских сессий (так называемые background tasks)
Возможность получения и обработки входящих [...]]]></description>
			<content:encoded><![CDATA[<p>Разработчики Google App Engine вновь приоткрыли завесу над планами по развитию платформы и решили опубликовать список будущих нововведений, которые должны быть реализованы в ближайшие подгода. Из многих вкусностей стоит отметить:</p>
<ul>
<li>Поддержка запуска задач по расписанию (а-ля cron)</li>
<li>Возможность с помощью специальных команд запускать процессы, выполняющиеся вне контекста пользовательских сессий (так называемые background tasks)</li>
<li>Возможность получения и обработки входящих сообщений по электронной почте</li>
<li>Поддержка отправки и приема сообщений через протокол XMPP (всем известный Jabber).</li>
</ul>
<p>Кроме того, в мае этого года ожидается очередная конференция разработчиков Google I/O, видео с которой как обычно будут выложены на канале Google Developers на Youtube.</p>
<p>P.S. А я все никак не могу дождаться выхода в свет поддержки Java в GAE <img src='http://techwork.ru/wp-includes/images/smilies/icon_sad.gif' alt=':-(' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://techwork.ru/2009/02/07/google-app-engine-new-roadmap/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Хранение пользовательских профилей</title>
		<link>http://techwork.ru/2008/08/01/saving-user-profiles/</link>
		<comments>http://techwork.ru/2008/08/01/saving-user-profiles/#comments</comments>
		<pubDate>Fri, 01 Aug 2008 07:31:27 +0000</pubDate>
		<dc:creator>techworkru</dc:creator>
				<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://techworkru.wordpress.com/?p=75</guid>
		<description><![CDATA[Оригинал статьи http://blog.appenginefan.com/2008/04/saving-user-specific-data.html
В одной из веток форума по App Engine развернулась дискуссия о том, как можно обеспечить уникальность каким-то данным. Одним из примеров, где необходимо будет использовать этот подход, является сохранение нового профиля пользователя в хранилище, только в том числе, если он еще не существует. Я хочу предложить способ сделать это для конкретного рассматриваемого случая, [...]]]></description>
			<content:encoded><![CDATA[<p>Оригинал статьи http://blog.appenginefan.com/2008/04/saving-user-specific-data.html</p>
<p>В <a href="http://groups.google.com/group/google-appengine/browse_thread/thread/a4d1019e31edfb28/3d3b1df220146cf2" target="_blank">одной из веток форума по App Engine</a> развернулась дискуссия о том, как можно обеспечить уникальность каким-то данным. Одним из примеров, где необходимо будет использовать этот подход, является сохранение нового профиля пользователя в хранилище, только в том числе, если он еще не существует. Я хочу предложить способ сделать это для конкретного рассматриваемого случая, и если считаете, что существует более правильный вариант &#8211; дайте знать.</p>
<p>Представим, что существует несколько наборов данных для каждого пользователя (настройки, статистика, история работы с сайтом и тому подобное). Лучшим способом, который я знаю, будет использование уникальной комбинации для первичного ключа модели, в нашем случае это будет пользователь и категория данных:</p>
<pre><code>key_name = '%s||%s' % (user.email(), category)</code></pre>
<p>В нашем примере существует проблема, что пользователь может в любой момент сменить свой адрес электронной почты. Несмотря на то, что реально это происходит не так часто, но все-таки эту проблему нельзя снимать со счетов (особенно если приложение работает с аккаунтами доменов, обслуживаемых в <a href="http://www.google.com/a" target="_blank">Службах Google</a>). Допустим сотрудница может поменять свою фамилию, когда выходит замуж или разводится. Так как после смены адреса первичный ключ уже не будет указывать на нужный нам объект, можно воспользоваться GQL запросом, который вернет нужную информацию.</p>
<p>Код, представленный ниже, занимается обработкой таких ситуаций. Он создает расширенную модель, которая позволяет работать с данными даже при смене адреса электронной почты. Модель содержит два метода-помощника: Load() и Modify() для удобного доступа к данным и созданию при необходимости нового объекта, К примеру, обновление счетчика по данным, как часто пользователь заходил на определенную страницу, будет выглядеть так:</p>
<pre><code>
def modify(x):
    x.count = getattr(x, 'count', 0) + 1
updated_stats = UserData.Modify(modify, 'stats')
</code></pre>
<p>Алгоритм получения и создания объекта состоит из трех шагов (для примера смотрите код метода Load(), приведенный ниже):</p>
<ul>
<li>попытка загрузки объекта с использованием его &laquo;стандартного&raquo; первичного ключа</li>
</ul>
<ul>
<li>если объект не был найден, выполнить GQL запрос (обработка изменений адреса электронной почты пользователя)</li>
</ul>
<ul>
<li>если и в этом случае ничего не найдено &#8211; создается новый объект, ассоциированный с пользователем, с использованием стандартного первичного ключа (методом get_or_create во избежание конфликтов)</li>
</ul>
<p>Пример реализации приведен ниже:</p>
<pre><code>
from google.appengine.api import users
from google.appengine.ext import db

"""Хранение пользовательских профилей."""

class UserData(db.Expando):
 """Расширенная модель, которая хранит определенный набор
    данных заданного пользователя. Содержит два обязательных
    свойства - объект, ассоциированный с пользователем и категория
    хранимых данных (к примеру, "settings"). Класс использует
    первичный ключ из этих обоих свойств для создания уникального
    набора данных с помощью внутренних методов Load и Modify.
 """
 user = db.UserProperty(required=True)
 category = db.StringProperty(required=True)

 @staticmethod
 def Load(category='None', user=None):
   """Загрузка пользовательских данных определенной категории
      (например, "settings") из хранилища. Если такие данные не
      существуют, то будет создан новый объект
   """

   # Если не передан пользователь, берем параметры текущего.
   # Если текущий не авторизован, функция возвращает None
   if not user:
     user = users.GetCurrentUser()
   if not user:
     return None

   # В большинстве случаев достаточно выполнить загрузку по ключу
   key_name = '%s||%s' % (user.email(), category)
   data = UserData.get_by_key_name(key_name)
   if data:
     return data

   # Если это не сработало - делаем GQL запрос, в тех случаях, если
   # адрес изменился и мы не можем найти объект
   data = UserData.gql('WHERE user=:1 AND category=:2',
       user, category).get()
   if data:
     return data

   # Ok, в хранилище нужных нам данных не обнаружилось, Мы будем
   # создавать новый объект при помощи переданного функции ключа.
   # В теории существует вероятность что произойдет попытка создания
   # дублирующегося объекта (если пользователь изменил адрес в ту же
   # секунду, когда мы создаем новый объект для этого пользователя),
   # но шанс что это случится - ничтожно малый.
   data = UserData.get_or_insert(key_name,
       user=user, category=category)
   return data

 @staticmethod
 def Modify(logic, category='None', user=None):
   """Запрашивает объект с данными, вносит в него изменения с помощью
      транзакции и возвращает объект. Параметр logic должен быть функцией,
      возвращающей объект.
   """

   # Попытка загрузки объекта из хранилища;
   data = UserData.Load(category, user)
   if not data:
     return None
   key = data.key().name()
   if not key:
     raise 'Ошибка: невозможно найти объект'

   # Переключаемся в режим транзакции
   def modify():
     data = UserData.get_by_key_name(key)
     logic(data)
     data.put()
     return data
   return db.run_in_transaction(modify)
</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://techwork.ru/2008/08/01/saving-user-profiles/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Запуск нескольких приложений под одним идентификатором App Engine</title>
		<link>http://techwork.ru/2008/07/31/multiplexing/</link>
		<comments>http://techwork.ru/2008/07/31/multiplexing/#comments</comments>
		<pubDate>Thu, 31 Jul 2008 03:13:55 +0000</pubDate>
		<dc:creator>techworkru</dc:creator>
				<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[domains]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://techworkru.wordpress.com/?p=72</guid>
		<description><![CDATA[Оригинал статьи http://blog.appenginefan.com/2008/04/multiplexing-and-namespaces-running.html
До тех пор, пока не завершился предварительный период тестирования платформы, разработчик может создать только 10 своих приложений. Последний раз, когда я заходил в панель управления своего приложения, я обнаружил что мой URL Shortlinker практически не использует для своей работы системных ресурсов. Это происходит на мой взгляд по трем причинам и соответствует ситуации у [...]]]></description>
			<content:encoded><![CDATA[<p>Оригинал статьи http://blog.appenginefan.com/2008/04/multiplexing-and-namespaces-running.html</p>
<p>До тех пор, пока не завершился предварительный период тестирования платформы, разработчик может создать только 10 своих приложений. Последний раз, когда я заходил в панель управления своего приложения, я обнаружил что мой URL Shortlinker практически не использует для своей работы системных ресурсов. Это происходит на мой взгляд по трем причинам и соответствует ситуации у других разработчиков:</p>
<ul>
<li>сайт практически никем не посещается (пока слишком мало на него внешних ссылок)</li>
</ul>
<ul>
<li>приложение оптимизировано для повышения производительности (так как я знал, что для этого можно сделать)</li>
</ul>
<ul>
<li>на самом деле это простое приложение и не требует для работы много ресурсов</li>
</ul>
<p>На сегодняшний день разработчики некоторых приложений уже должны беспокоиться о превышении квот, однако, готов поспорить, для многих других (таких как я) это пока не является проблемой. Таким образом, если мы имеем целый набор приложений, код которых не требует много места, то почему бы не опубликовать их вместе?</p>
<p>Так появится возможность использовать общие ресурсы одного идентификатора для запуска нескольких маленьких приложений. К примеру, вы имеете базу данных ссылок, гостевую книгу, счетчик посещаемости сайта и генератор случайных чисел. Объедините их вместе, назовите приложение &laquo;Инструменты для сайта&raquo; и запустите его как один экземпляр на App Engine. К сожалению, процесс объединения может оказаться довольно сложным (что если эти приложения используют классы моделей с одинаковыми именами или названия их файлов совпадают?), поэтому придется немного поработать. Я убежден, что возможно использовать часть волшебства языка Python и поместить код каждого приложения в свой каталог и создать единый корневой обработчик-диспетчер для всех обращений к сайту.</p>
<p>Второй вариант &#8211; задействовать возможность работы с мажорными версиями. Я могу опубликовать два разных приложения с одним и тем же идентификатором, одно из них будет иметь основной номер версии &laquo;1&#8243;, а другое &#8211; версию &laquo;2&#8243;. Они оба будут работать независимо друг от друга, до тех пор, пока их объекты в хранилище не будут пересекаться.</p>
<p>Пример:</p>
<p>Следующий код является простым каталогом закладок:</p>
<pre>import wsgiref.handlers

from google.appengine.ext import webapp

class MainPage(webapp.RequestHandler):
  def get(self):
    if self.request.get('url'):
      self.redirect(self.request.get('url'))
      return
    self.response.out.write('Link scrubber')

def main():
  application = webapp.WSGIApplication([('/.*', MainPage)], debug=True)
  wsgiref.handlers.CGIHandler().run(application)

if __name__ == "__main__":
  main()</pre>
<p>Его можно открыть, перейдя по ссылке <a href="http://2.2.aef.appspot.com/" target="_blank">http://2.2.aef.appspot.com/</a>, которая находится в том же самом месте, что и приложение URL shortlinker. Таким образом shortlinker является версией 1 этой программы, а представленный выше каталог ссылок (совершенно другое приложение) определен как версия 2.</p>
<p>Хотя этот подход будет работать, я не рекомендую его для использования. Это кратковременное удобство для развертывания может затем выльиться в головную боль после того, как любое из этих приложений станет популярным у пользователей. Любое минимальное изменение и обновление кода на сервере приведет к тому, что каталог ссылок будет изменять номер своей минорной версии. Также вы не сможете привязать к нему домен, обслуживаемый Службами Google. Другими словами, этот способ имеет больше ограничений, чем преимуществ.</p>
<p>Третьим вариантом является тот способ, который зачастую может быть использован при наименьшем изменении кода и позволяет привлечь к приложению дополнительных пользователей: возможность работы одного и того же приложения с разными данными с помощью использования имени домена в качестве области имен в хранилище.</p>
<p>Одно единственное приложение App Engine может быть привязано к большому числу разных доменов. Простое определение значения os.environ['SERVER_NAME'] в приложении, укажет на то, в какой области имен оно сейчас работает. Если мы заранее позаботимся о алгоритме разделения данных, то сможем предоставить возможность пользователям сделать свои данные уникальными для каждого из них.</p>
<p>Давайте рассмотрим конкретный пример: загрузите код wiki-приложения с сайта <a href="http://google-app-engine-samples.googlecode.com/files/cccwiki_20080409.tar.gz" target="_blank">code.google.com</a>. Оно представляет из себя довольно неплохой движок, но не имеет возможности разделять данные по доменам. Представим, что мы имеем три различных продукта (база закладок, редиректор, хостинг страниц) и хотим добавить wiki для каждого из них.</p>
<p>Если вы посмотрите на основной файл, wiki.py, то увидите, что реально только в двух местах кода выполняется задание имена объекта для чтения или записи данных в хранилище:</p>
<pre>def save(self):
...
 entity['name'] = self.name
...
 datastore.Put(entity)

def load(name):
...
 query = datastore.Query('Page')
 query['name ='] = name
 entities = query.Get(1)</pre>
<p>Не будем заострять внимание на том, что разработчик использует кривой метод доступа к данным без моделей или запросов GQL. Мы видим, что код использует параметр &laquo;name&raquo;, являющийся именем wiki-страницы, в качестве уникального ключа для загрузки и записи данных в хранилище. Достаточно добавить значение нашего домена к коду, и мы получим разделение области имен для создания сколь угодного числа одинаковых страниц с различным содержимым:</p>
<pre>import os
def GetDomain():
return os.environ['SERVER_NAME']

...

def save(self):
...
 entity['name'] = GetDomain() + "|" +self.name # ИЗМЕНЕНО
...
 datastore.Put(entity)

def load(name):
...
 query = datastore.Query('Page')
 query['name ='] = GetDomain() + "|" + name # ИЗМЕНЕНО
 entities = query.Get(1)</pre>
<p>С помощью выполнения несложных изменений мы обеспечили возможность использования этого приложения множеством пользователей независимо без необходимости создания для каждого из них собственного экземпляра приложения. Все это также будет работать с другим более сложным кодом, как можно увидеть на этом <a href="http://google-app-engine-samples.googlecode.com/files/cccwiki_20080409.tar.gz" target="_blank">примере</a>. Однако, чем выше будет уровень сложности приложения, тем гораздо труднее будет вносить подобные изменения.</p>
<h3>В завершении</h3>
<p>Даже без выполнения изменения кода, есть возможность использовать один идентификатор для работы множества приложений параллельно. С использованием домена в качестве области поиска объектов, становится возможным разбивать данные каждого пользователя в хранилище для одного приложения. Оба метода увеличивают полезное использование одного приложения, но пока еще достаточно сыры. Я верю что есть и другой лучший метод выполнения совместной работы, но пока не знаю его. Ниже описаны мысли по этому поводу:</p>
<p>* приложение wiki в этом примере использует для работы с хранилищем простой интерфейс и могло бы задействовать оба метода доступа к данным: язык запросов GQL и классы модели. В зависимости от того, какая высокоуровневая реализация интерфейса работает с доступом к данным хранилища, можно разработать патч для их автоматического разделения по имени домена или подкаталогу. Если это удатся, то все проблемы с конфликтами имен объектов для выполнения нескольких приложения под одним идентификатором, будут сняты.</p>
<p>* SDK написан на языке Python. В нем содержится класс, который в зависимости от содержимого конфигурационного файла app.yaml, принимает решение, какому из скриптов приложения необходимо передать управление. Возможно, используя этот код, создать свой обработчик верхнего уровня, который будет знать к какому из подкаталогов с приложением следует направить запрос на основании информации из файла app.yaml.</p>
]]></content:encoded>
			<wfw:commentRss>http://techwork.ru/2008/07/31/multiplexing/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Эффективная работа с глобальными счетчиками</title>
		<link>http://techwork.ru/2008/07/30/%d1%8d%d1%84%d1%84%d0%b5%d0%ba%d1%82%d0%b8%d0%b2%d0%bd%d0%b0%d1%8f-%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%b0-%d1%81-%d0%b3%d0%bb%d0%be%d0%b1%d0%b0%d0%bb%d1%8c%d0%bd%d1%8b%d0%bc%d0%b8-%d1%81%d1%87%d0%b5/</link>
		<comments>http://techwork.ru/2008/07/30/%d1%8d%d1%84%d1%84%d0%b5%d0%ba%d1%82%d0%b8%d0%b2%d0%bd%d0%b0%d1%8f-%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%b0-%d1%81-%d0%b3%d0%bb%d0%be%d0%b1%d0%b0%d0%bb%d1%8c%d0%bd%d1%8b%d0%bc%d0%b8-%d1%81%d1%87%d0%b5/#comments</comments>
		<pubDate>Wed, 30 Jul 2008 08:54:40 +0000</pubDate>
		<dc:creator>techworkru</dc:creator>
				<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://techworkru.wordpress.com/?p=67</guid>
		<description><![CDATA[Оригинал статьи &#8211; http://blog.appenginefan.com/2008/06/efficient-global-counters.html
Одним из примеров, как не нужно работать с глобальными счетчиками &#8211; тот наивный подход, который я использовал в прошлом (сохранять счетчик в объекте и обновлять его в транзакции) является бутылочным горлышком, который реально уменьшает производительность приложения. Вместо этого рекомендуется использовать механизм по разделению  счетчика на несколько составляющих, относящихся к разным группам объектов. [...]]]></description>
			<content:encoded><![CDATA[<p>Оригинал статьи &#8211; http://blog.appenginefan.com/2008/06/efficient-global-counters.html</p>
<p>Одним из примеров, как не нужно работать с глобальными счетчиками &#8211; тот наивный подход, который я использовал в прошлом (сохранять счетчик в объекте и обновлять его в транзакции) является бутылочным горлышком, который реально уменьшает производительность приложения. Вместо этого рекомендуется использовать механизм по разделению  счетчика на несколько составляющих, относящихся к разным группам объектов. Примерный код может выглядеть так:</p>
<pre><code>from google.appengine.ext import db
import random

SHARDS_PER_COUNTER = 20

class CounterShard(db.Model):
  name = db.StringProperty(required=True)
  count = db.IntegerProperty(default=0)

def GetCount(nameOfCounter):
  result = 0
  for shard in CounterShard.gql('WHERE name=:1', nameOfCounter):
    result += shard.count
  return result

def ChangeCount(nameOfCounter, delta):
  shard_id = '/%s/%s' % (
     nameOfCounter, random.randint(1, SHARDS_PER_COUNTER))
  def update():
   shard = CounterShard.get_by_key_name(shard_id)
   if shard:
     shard.count += delta
   else:
     shard = CounterShard(
         key_name=shard_id, name=nameOfCounter, count=delta)
   shard.put()
  db.run_in_transaction(update)
</code></pre>
<p>Очень интересное решение, но можно сделать еще лучше с помощью использования интерфейса <a href="http://googleappengine.ru/docs/memcache/" target="_blank">Memcache API</a>, путем сохранения значения счетчика в памяти и обновления его при необходимости. Следующий пример показывает, как можно установить период устаревания значения счетчика на одну минуту:</p>
<pre><code>def GetCount(nameOfCounter):
  memcache_id = '/CounterShard/%s' %  nameOfCounter
  result = memcache.get(memcache_id)
  if not (result == None):
    return result
  result = 0
 for shard in CounterShard.gql('WHERE name=:1', nameOfCounter):
   result += shard.count
  memcache.set(memcache_id, result, 60)
  return result
</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://techwork.ru/2008/07/30/%d1%8d%d1%84%d1%84%d0%b5%d0%ba%d1%82%d0%b8%d0%b2%d0%bd%d0%b0%d1%8f-%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%b0-%d1%81-%d0%b3%d0%bb%d0%be%d0%b1%d0%b0%d0%bb%d1%8c%d0%bd%d1%8b%d0%bc%d0%b8-%d1%81%d1%87%d0%b5/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Потоковый загрузчик данных для App Engine</title>
		<link>http://techwork.ru/2008/07/29/bulk-loader-for-app-engine/</link>
		<comments>http://techwork.ru/2008/07/29/bulk-loader-for-app-engine/#comments</comments>
		<pubDate>Tue, 29 Jul 2008 06:51:52 +0000</pubDate>
		<dc:creator>techworkru</dc:creator>
				<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[unicode]]></category>

		<guid isPermaLink="false">http://techworkru.wordpress.com/?p=64</guid>
		<description><![CDATA[Оригинал статьи http://www.wooji-juice.com/blog/appengine-bulkloader.html
Недавно я провел много времени ожидая, когда AppEngine Bulk Loader наконец-то закончит свою работу. Пока в вашем приложении нет никаких данных, вы спокойно можете добавлять, изменять и удалять новые объекты в хранилище через Административную консоль, однако когда их становится очень много, работать с таким интерфейсом начинается тяжело.
Официальным инструментом для массовой загрузки данных на [...]]]></description>
			<content:encoded><![CDATA[<div class="byline"><span class="date">Оригинал статьи http://www.wooji-juice.com/blog/appengine-bulkloader.html</span></div>
<p>Недавно я провел много времени ожидая, когда AppEngine Bulk Loader наконец-то закончит свою работу. Пока в вашем приложении нет никаких данных, вы спокойно можете добавлять, изменять и удалять новые объекты в хранилище через Административную консоль, однако когда их становится очень много, работать с таким интерфейсом начинается тяжело.</p>
<p>Официальным инструментом для массовой загрузки данных на сервер является <a href="http://googleappengine.ru/articles/bulkload.html" target="_blank">Bulk Loader</a>. Несмотря на то, что он плохо документирован, он является мощным (и медленным) инструментом.</p>
<p>Как описано в статье, на которую я еще сошлюсь, суть его в том, что вы можете создать CSV файл с данными и передать его скрипту <var>bulkload_client.py</var>. Для корректной работы на сервере необходимо установить обработчик, который будет принимать строки в формате CSV и помещать данные в хранилище.</p>
<p>Чтобы сделать это, ваш код должен вызывать метод <var>bulkload.main()</var>, передавая ему по одному объекту каждого типа (потомок <var>db.Model</var>). К примеру, если у вас есть объекты моделей <var>Album</var> и <var>Track</var>, необходимо выполнить следующее:</p>
<p><code> </code></p>
<pre>if __name__=="__main__":
    bulkload.main(AlbumLoader(), TrackLoader())</pre>
<p>В коде указаны потомки классов <var>bulkload.Loader</var> — хотя вы можете не описывать свои новые классы, достаточно будет создать экземпляры класса <var>bulkload.Loader</var> и передать ему все параметры в конструктор. Кстати о параметрах, сначала указывается имя объекта, который загружается в приложение, затем список кортежей, каждый из которых содержит имя свойства модели и тип значения (либо стандартный тип языка Python, либо стандартный тип для хранилища, <em>но не</em> тип самого свойства — например, допускается <var>str</var>, но не <var>db.StringProperty()</var>).</p>
<p>Это все, в обычном случае все предельно просто.<a name="1"></a></p>
<h3>Монстроидальный Cookie</h3>
<p>Первой недокументированной проблемой, с которой вы столкнетесь, будет безопасность. К примеру, если вы захотите, как это и покажется логичным, ограничить доступ к обработчику только для администраторов приложения, то обнаружите, что не существует способа передать пароль администратора скрипту <var>bulkload_client.py</var>. Вместо этого вам нужно будет указать <em>очень, очень длинный</em> cookie. Как сделать это?</p>
<p>Авторизируйтесь в вашем приложении через свой браузер и перейдите по адресу, к которому привязан обработчик загрузки данных. Затем, выполните запрос <var>GET</var>, вместо используемого <var>POST</var> и увидите cookie, который необходимо будет использовать.</p>
<h3><a name="2">Адский набор символов</a></h3>
<p>Если вы попытаетесь загрузить данные в кодировке UTF-8, то это может привести утилиту из среды разработки SDK в шок. Это происходит потому, что обработчик пытается привести полученные данные из UTF-8 в родной тип <var>unicode</var> еще до того, как передает его загрузчику, но класс <var>BulkLoad.Load</var> считает эти данные все еще закодированными (точнее модуль языка Python, отвечающий за работу с CSV ожидает тип <var>str</var>, а переданный ему тип <var>unicode</var> будет повторно декодировать их при помощи кодека ascii, что и приводит к порче данных).</p>
<p>Надеюсь эту проблему скоро исправят, а пока временно приходится использовать обходное решение: создавать собственный класс, наследник класса <var>bulkload.BulkLoad</var>, который перекодирует данные:</p>
<p><code></p>
<pre>class BulkLoad(bulkload.BulkLoad):
    def Load(self, kind, data):
        return bulkload.BulkLoad.Load(self, kind, data.encode('utf-8'))</pre>
<p></code><br />
Для определения собственной версии загрузчика, вам необходимо целиком скопировать код функции <var>bulkload.main</var> в свой модуль и отредактировать его — пока я не обнаружил другого удобного способа. Я надеюсь, что эту ошибку исправят довольно скоро.</p>
<p>Винсент Исамбарт указал мне на то, что при инициализации экземпляра класса необходимо упомянуть, что вы используете utf-8. Я определил функцию, которая выполняет это преобразование:</p>
<pre><code>def utf8string(s):
    return unicode(s, 'utf-8') </code></pre>
<p>&#8230;и затем использовать ее вместо типов <var>str</var> и <var>unicode</var> в конструкторе:<br />
<code> </code></p>
<pre>class UserLoader(bulkload.Loader):
    def __init__(self):
        bulkload.Loader.__init__(self, 'User', [ ('name', utf8string), ])</pre>
<h3>Загадочный метод HandleEntity</h3>
<p>В статье был приведен пример реализации метода <var>HandleEntity</var>, но никаких объяснений, как его можно использовать не было. Это очень полезное дополнение для обработки данных. По существу, он получает данные объекта и может изменить их так, как необходимо разработчику, после чего обратно вернуть его. Есть несколько интересных моментов:</p>
<ul>
<li>Передаваемый объект является экземпляром класса <var>datastore.Entity</var>, а не <var>db.Model</var>. По своей сути он является обычным словарем языка Python с дополнительными опциями, может быть использован как обычный словарь <var>dictionary['item']</var>, если имеется необходимость изменения данных перед записью их в хранилище.</li>
<li>Если в процессе обработки появится необходимость создать дополнительный объект, то вы можете сделать это, передав название модели в качестве строки и опционально указать параметры <var>name</var> (ключ объекта) и его родителя <var>parent</var> (должен быть типа <var>Key</var>.)</li>
<li>Основной причиной, по которой вы можете создать еще один объект, будет возможность вернуть любое количество новых объектов из функции <var>HandleEntity</var>, таким образом каждая отдельная строка CSV файла может порождать большие массивы данных.</li>
<li>Пожалуй, наиболее интересной является возможность вообще не создавать объекты. Таким образом Поточный загрузчик превращается в Поточный универсальный обработчик. Если метод не возвращает объектов, то ничего нового в хранилище не создается, и модель данных, переданная функции становится нерелевантной. Вы можете использовать такой загрузчик для выполнения практически любой повторяющейся функции, которая принимает в качестве параметров значения из CSV файла.</li>
</ul>
<p>К примеру, ниже приведен код Bulk Deleter, который получает список ключей объектов для удаления их из CSV файла:</p>
<pre><code>class BulkDeleter(bulkload.Loader):
    def __init__(self):
        bulkload.Loader.__init__(self, 'BulkDelete', [ ('key', str), ])

    def HandleEntity(self, entity):
        key = entity['key'] entity = db.get(db.Key(key))
        if entity:
            entity.delete()
            return []</code></pre>
<h3>Другие проблемы</h3>
<p>Все это хорошо, но, к сожалению, медленно и ненадежно. Может быть мне не повезло, но я часто видел ошибки <var>502 Bad Gateway</var> и <var>500 Server Error</var>, которые прерывали работу загрузчика. Один из параметров, которые можно передать программе <var>bulkload_client.py</var>, позволяет указать количество строк CSV файла, которые единовременно передаются за один раз. Его уменьшение может помочь решить проблему, но это не кажется правильным методом. Каждый раз вы запускаете процесс загрузки данных с самого начала.</p>
<p>Я <a href="http://code.google.com/p/googleappengine/issues/detail?id=511" target="_blank">отправил патч</a> для скрипта, запускаемого на стороне клиента, который выполняет пропуск заданного количества строк от начала файла, таким образом можно просто возобновить прерванную загрузку данных. Он также сообщает, сколько строк с данными было загружено на сервер, до того как произошла ошибка, и таким образом вы всегда будете знать, с какого места нужно возобновить загрузку. Возможно вы захотите, чтобы обработка данных происходила индемпотентно, так как не можете быть всегда уверены, где произойдет ошибка. Поэтому возможно захотите выполнять проверку существования объектов до их добавления к хранилищу, <em>в противном случае</em> придется указывать точное значение их ключей и тогда они будут перезаписывать объекты со старыми данными с помощью метода <var>put()</var>.</p>
<p>Надеюсь, этот материал окажется полезным! Хотя&#8230; нет, подождите. Я надеюсь что он окажется бесполезным, так как это будет означать что Bulk Loader стал работать как надо. <img src='http://techwork.ru/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  В любом случае, надеюсь, что ваши данные будут загружены без проблем!</p>
]]></content:encoded>
			<wfw:commentRss>http://techwork.ru/2008/07/29/bulk-loader-for-app-engine/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Pagination: постраничный вывод в App Engine</title>
		<link>http://techwork.ru/2008/07/28/pagination-app-engine/</link>
		<comments>http://techwork.ru/2008/07/28/pagination-app-engine/#comments</comments>
		<pubDate>Mon, 28 Jul 2008 07:31:54 +0000</pubDate>
		<dc:creator>techworkru</dc:creator>
				<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://techworkru.wordpress.com/?p=59</guid>
		<description><![CDATA[Разбиение вывода информации на страницы легко осуществимо в приложениях Google App Engine, для чего достаточно использовать обычный метод query.fetch(), подробнее описанный в документации:
Небольшой пример части кода, как можно организовать загрузку объектов со статьями из хранилища по-странично, выглядит следующим образом:

def generate_stories(self, query, template_values={}):
  """ Создание страницы с помощью переданной строки запроса объектов Stories.

  Мы [...]]]></description>
			<content:encoded><![CDATA[<p>Разбиение вывода информации на страницы легко осуществимо в приложениях Google App Engine, для чего достаточно использовать обычный метод query.fetch(), подробнее описанный в <a href="http://googleappengine.ru/docs/datastore/queryclass.html#Query" target="_blank">документации</a>:</p>
<p>Небольшой пример части кода, как можно организовать загрузку объектов со статьями из хранилища по-странично, выглядит следующим образом:</p>
<pre><code>
def generate_stories(self, query, template_values={}):
  """ Создание страницы с помощью переданной строки запроса объектов Stories.

  Мы работаем с различными типами вывода информации (форматы Atom, HTML и другие),
  таким образом страницы, которые выдают целиком списки доступных статей не должны
  воспроизводить эту логику.

  Параметры функции:
  query: созданный объект запроса данных, который извлекает список статей
            template_values: дополнительные значения для передачи в шаблон
  """
  # Определяем рамки получаемых значений с помощью параметров start и num,
  #  переданных в адресной строке
  start = self.get_arg_range('start', minvalue=0, maxvalue=1000, default=0)
  num = self.get_arg_range('num', minvalue=1, maxvalue=100, default=10)

  # включаем в выборку еще один дополнительный объект для определения существования
  #  следующей страницы - этот метод быстрее, чем выполнение второго запроса
  # для проверки существования следующей страницы
  stories = query.fetch(num+1, start)
  # был ли извлечен дополнительный объект? Если да, то это не последняя страница
  more_stories = len(stories) &gt; num
  # убираем этот дополнительный объект из результатов
  stories = stories[:num-1]
  # если переменная не равна нулю, то также имеется предыдущая страница
  prev_stories = start &gt; 0
  # и передаем реальное количество объектов, которые будут отображены
  disp_end = max(len(stories), 0) + start + 1
  disp_start = start + 1
</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://techwork.ru/2008/07/28/pagination-app-engine/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Небольшие изменения</title>
		<link>http://techwork.ru/2008/07/25/some-small-updates/</link>
		<comments>http://techwork.ru/2008/07/25/some-small-updates/#comments</comments>
		<pubDate>Fri, 25 Jul 2008 05:37:12 +0000</pubDate>
		<dc:creator>techworkru</dc:creator>
				<category><![CDATA[Google App Engine]]></category>

		<guid isPermaLink="false">http://techworkru.wordpress.com/?p=38</guid>
		<description><![CDATA[Мы рады сообщить, что в платформе Google App Engine произошли некоторые изменения. Наиболее значительные из них:

Доступно больше приложений: Хотите создать больше, чем 3 приложения в одном аккаунте App Engine? Теперь их можно сделать до 10!


Шкала времени в графиках панели управления: Появилась возможность задавать период в панели для получения требуемой детализации графика. Можно выбирать период в [...]]]></description>
			<content:encoded><![CDATA[<p>Мы рады сообщить, что в платформе Google App Engine произошли некоторые изменения. Наиболее значительные из них:</p>
<ul>
<li>Доступно больше приложений: Хотите создать больше, чем 3 приложения в одном аккаунте App Engine? Теперь их можно сделать до 10!</li>
</ul>
<ul>
<li>Шкала времени в графиках панели управления: Появилась возможность задавать период в панели для получения требуемой детализации графика. Можно выбирать период в 24, 12 и 6 часов.</li>
</ul>
<ul>
<li>Экспорт журналов событий: Теперь можно использовать программу appcfg.py для того, чтобы загрузить на локальный компьютер журналы в текстовом формате. Для дополнительной информации запустите appcfg.py с параметром &#8212;-help.</li>
</ul>
<ul>
<li>Отправка писем с почтовым адресом авторизованного пользователя: Если вы используете Users API, то теперь появилась возможность отправлять сообщение с обратным адресом текущего авторизованного пользователя.</li>
</ul>
<p><a href="http://googleappengine.ru/downloads.html" target="_blank">Обновите SDK</a> и прочитайте полный список произошедших изменений. Какие изменения вы хотите еще видеть в App Engine? Напишите это в <a href="http://groups.google.com/group/google-appengine-ru" target="_blank">группе Google проекта</a>!</p>
]]></content:encoded>
			<wfw:commentRss>http://techwork.ru/2008/07/25/some-small-updates/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>App Engine Launcher для Mac OS X</title>
		<link>http://techwork.ru/2008/07/24/app-engine-launcher-mac-osx/</link>
		<comments>http://techwork.ru/2008/07/24/app-engine-launcher-mac-osx/#comments</comments>
		<pubDate>Thu, 24 Jul 2008 05:50:11 +0000</pubDate>
		<dc:creator>techworkru</dc:creator>
				<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[mac os]]></category>

		<guid isPermaLink="false">http://techworkru.wordpress.com/?p=35</guid>
		<description><![CDATA[Пока я занимался написанием тестов для платформы Google App Engine, у меня появилась следующая идея: какие из приложений: BBEdit, Dreamweaver, CSSEdit или TextMate могут помочь в разработке приложений для App Engine? Как это глупо не звучит, я задумался о своей рабочей среде. Я использовал:

несколько приложений на Python, CSS, HTML и JavaScript.
командную строку для их запуска [...]]]></description>
			<content:encoded><![CDATA[<p>Пока я занимался написанием тестов для платформы Google App Engine, у меня появилась следующая идея: какие из приложений: BBEdit, Dreamweaver, CSSEdit или TextMate могут помочь в разработке приложений для App Engine? Как это глупо не звучит, я задумался о своей рабочей среде. Я использовал:</p>
<ul>
<li>несколько приложений на Python, CSS, HTML и JavaScript.</li>
<li>командную строку для их запуска и просмотра журнала ошибок.</li>
<li>браузер для проведения локального тестирования и доступа к локальной консоли разработчика.</li>
<li>командную строку для публикации приложения.</li>
<li>браузер для просмотра панели управления развернутого приложения.</li>
</ul>
<p>Я задумался о том, какой недружественной пользователю может оказаться командная строка, и как много повторяющихся действий приходится выполнять для того, чтобы опубликовать приложение. С этими мыслями я занялся совместной работой с <a href="http://googlemac.blogspot.com/2008/05/app-engine-launcher-for-mac-os-x.html" target="_blank">Джоном Грабовски</a> из команды Google Mac и <a href="http://www.youtube.com/watch?v=bfgO-LXGpTM" target="_blank">Бреттом Слаткином</a>, инженером-разработчиком App Engine, над проектом <a href="http://googleappengine.ru/downloads.html" target="_blank">Google App Engine Launcher</a> для Mac OS X.</p>
<p>App Engine Launcher не является заменителем вашего редактора кода или интегрированной среды разработки IDE. Она предоставляет удобный способ разработчику управлять списком его приложений. За несколько кликов можно запустить, просмотреть, опубликовать приложение или перейти к его журналу ошибок. Также в ней содержатся вкусные возможности вроде drag and drop, интеграции с редактором и быстрый переход к локальной консоли разработчика и панели управления опубликованного приложения.</p>
<p><a href="http://googleappengine.ru/downloads.html">Загрузите</a> его прямо сейчас. Если у вас появится предложение по добавлению в него новых возможностей (например, скриптинг, версии для Windows и Linux, искусственный интеллект <img src='http://techwork.ru/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> , напишите об этом в <a href="http://groups.google.com/group/google-appengine-ru" target="_blank">группе обсуждения</a>.</p>
<p style="text-align:center;"><a href="http://techwork.ru/wp-content/uploads/2008/07/launcher1.png"><img class="alignnone size-medium wp-image-34" src="http://techwork.ru/wp-content/uploads/2008/07/launcher1.png?w=300" alt="" width="300" height="213" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://techwork.ru/2008/07/24/app-engine-launcher-mac-osx/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Подсказки по созданию масштабируемых приложений</title>
		<link>http://techwork.ru/2008/07/23/tips_on_writing_scalable_apps/</link>
		<comments>http://techwork.ru/2008/07/23/tips_on_writing_scalable_apps/#comments</comments>
		<pubDate>Wed, 23 Jul 2008 06:34:18 +0000</pubDate>
		<dc:creator>techworkru</dc:creator>
				<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[highload]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://techworkru.wordpress.com/?p=18</guid>
		<description><![CDATA[Google App Engine позволяет создавать масштабируемые приложения намного проще, чем это делается на традиционных платформах, и правильно спроектированные приложения будут одинаково хорошо обслуживать от одного пользователя до миллиона. Что означает фраза, что приложения Google App Engine должны быть &#171;правильно спроектированы&#187;? Мы приводим несколько простых подсказок, при использовании которых, вы будете уверены, что ваше приложение надежно [...]]]></description>
			<content:encoded><![CDATA[<p>Google App Engine позволяет создавать масштабируемые приложения намного проще, чем это делается на традиционных платформах, и правильно спроектированные приложения будут одинаково хорошо обслуживать от одного пользователя до миллиона. Что означает фраза, что приложения Google App Engine должны быть &laquo;правильно спроектированы&raquo;? Мы приводим несколько простых подсказок, при использовании которых, вы будете уверены, что ваше приложение надежно справится с высокой нагрузкой.</p>
<ul>
<li><strong>Избегайте конфликтов при работе с объектами хранилища.</strong> Если каждый запрос вашего приложения выполняет чтение и запись определенного объекта, то латентность приложения будет возрастать по мере того, как увеличивается его трафик, так как чтение и запись одного объекта происходит последовательно. Одним из таких примеров является использование глобального счетчика, то есть объекта, который содержит число обращений к странице и обновляется при каждом запросе. Есть несколько интересных способов реализации других алгоритмов по работе с такими объектами, одним из них является кэширование, которое будет рассмотрено ниже.</li>
<li><strong>Избегайте использования больших <a href="http://googleappengine.ru/docs/datastore/keysandentitygroups.html#Entity_Groups_Ancestors_and_Paths" target="_blank">групп объектов</a>.</strong> Любые два объекта, которые имеют одного объекта-предка будут принадлежать к одной группе. Все операции записи в группы объектов являются последовательными, таким образом использование больших групп приведет к тому, что производительность приложения при работе с популярными объектами одной группы быстро деградирует. Вместо этого возьмите за правило использовать небольшие локализованные группы объектов.</li>
<li><strong>Сохраняйте умеренно.</strong> Операция записи отнимает в системе больше ресурсов, чем операция чтения; примите это к сведению при работе с моделями данных. Если в какой-то части приложения вы можете избежать сохранения данных, так и стоит поступить.</li>
<li><strong>Определите функцию <code>main()</code> для повторного использования кода.</strong> Экземпляры вашего приложения остаются запущенными какое-то время после получения запроса пользователя, поэтому есть шанс, что другой запрос будет отправлен в рабочий экземпляр приложения. В коде приложения можно определить функцию <code>main</code>, как показано в следующем примере:
<div class="wp_syntax" style="padding-bottom:0;">
<div class="code">
<pre class="python">def main():
    application = webapp.WSGIApplication(_URLS, debug=True)
    wsgiref.handlers.CGIHandler().run(application)

if __name__ == '__main__':
    main()</pre>
</div>
</div>
<p>Приложение может повторно использовать глобальные переменные, таким образом несмотря на то, что не будет гарантии, что следующий запрос попадет на запущенный экземпляр приложения, вы получите преимущества по кэшированию объектов и вычисляемых выражений. Этот пример работает просто: App Engine обнаружит определение функции <code>main()</code> и вызовет ее вместо проведения повторного импортирования модуля.</li>
<li><strong><a href="http://googleappengine.ru/kb/commontasks.html#profiling" target="_blank">Профилируйте свой код</a>.</strong> Вы можете использовать профайлер языка Python для того, чтобы изучить производительность своего кода.</li>
</ul>
<p>Есть еще какие-то предложения по оптимизации web-приложения под App Engine? Добавьте их к этому посту!</p>
]]></content:encoded>
			<wfw:commentRss>http://techwork.ru/2008/07/23/tips_on_writing_scalable_apps/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
