Использование Memcache

Memcache является высокопроизводительным распределенным кэшем в оперативной памяти, который позволяет строить динамичные web-приложения и снижать нагрузку на хранилище. В общем случае Memcache используется для кэширования результатов запросов из хранилища или сгенерированного вашим приложением кода HTML.

Использование кэша

Алгоритм использования сервиса Memcache простой:

  • Приложение получает запрос от пользователя.
  • Проверяем присутствуют ли нужные нам данные в оперативном кэше.
    • Если данные присутствуют, получаем их и возвращаем пользователю
    • Если их нет, выполняем запрос к хранилищу и передаем результат обработки в кэш

Код, показывающий, как происходит типичный запрос к кэшу:

def get_data():
  data = memcache.get("key")
  if data is not None:
    return data
  else:
    data = self.query_for_data()
    memcache.add("key", data, 60)
    return data

Добавление к guestbook.py возможности работы с Memcache

Приложение 'Гостевая книга', рассмотренное в Руководстве для начинающих получает данные из хранилища при каждом запросе пользователя. Мы можем изменить его таким образом, чтобы оно выполняло опрос кэша перед выполнением любого запроса к хранилищу.

Сначала мы должны импортировать модуль memcache и создать новый метод, который будет выполнять запрос к кэшу до обращения к хранилищу.

from google.appengine.api import memcache

def get_greetings(self):
  """get_greetings()
  
  Проверка кэша на предмет наличия объекта greetings
  Если он не обнаружен, то вызывается метод render_greetings и возвращаемый результат помещается в кэш

  Возвращает:
    Строку с HTML кодом, содержащую сообщения.
  """
  greetings = memcache.get("greetings")
  if greetings is not None:
    return greetings
  else:
    greetings = self.render_greetings()
    if not memcache.add("greetings", greetings, 10):
      logging.error("Memcache set failed.")
    return greetings

Теперь необходимо разделить моменты, когда происходит запрос данных и создание отображения страницы. Когда в кэше не будет содержаться требуемых данных, мы вызовем этот метод и произведем запрос к хранилищу для получения сообщений, которые требуется отобразить пользователю.

def render_greetings(self):
  """render_greetings()
  
  Выполняет запрос пользовательских сообщений, добавляет их к переменной 
  results и создает на базе этого HTML.

  Возвращает:
    Строку с HTML кодом, содержащую сообщения
  """
  results = db.GqlQuery("SELECT * "
                        "FROM Greeting "
                        "ORDER BY date DESC").fetch(10)
  output = StringIO.StringIO()
  for result in results:
    if result.author:
      output.write("<b>%s</b> wrote:" % result.author.nickname())
    else:
      output.write("An anonymous person wrote:")
    output.write("<blockquote>%s</blockquote>" %
                  cgi.escape(result.content))
  return output.getvalue()

В завершении необходимо будет обновить обработчик MainPage для вызова нового метода get_greetings() и отображения статистики по результатам работы с кэшем: сколько раз мы получили кэшированные данные.

import cgi
import datetime
import wsgiref.handlers
import logging
import StringIO

from google.appengine.ext import db
from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.api import memcache

logging.getLogger().setLevel(logging.DEBUG)


class Greeting(db.Model):
  author = db.UserProperty()
  content = db.StringProperty(multiline=True)
  date = db.DateTimeProperty(auto_now_add=True)


class MainPage(webapp.RequestHandler):
  def get(self):
    self.response.out.write("<html><body>")
    greetings = self.get_greetings() 
    stats = memcache.get_stats()
    
    self.response.out.write("<b>Cache Hits:%s</b><br>" % stats['hits'])
    self.response.out.write("<b>Cache Misses:%s</b><br><br>" %
                            stats['misses'])
    self.response.out.write(greetings)
    self.response.out.write("""
          <form action="/sign" method="post">
            <div><textarea name="content" rows="3" cols="60"></textarea></div>
            <div><input type="submit" value="Sign Guestbook"></div>
          </form>
        </body>
      </html>""")

  def get_greetings(self):
    """
        get_greetings()
        Проверка кэша на предмет наличия объекта greetings
        Если он не обнаружен, то вызывается метод render_greetings и возвращаемый результат помещается в кэш

        Возвращает:
           Строку с HTML кодом, содержащую сообщения.
    """
    greetings = memcache.get("greetings")
    if greetings is not None:
      return greetings
    else:
      greetings = self.render_greetings()
      if not memcache.add("greetings", greetings, 10):
        logging.error("Memcache set failed.")
      return greetings

  def render_greetings(self):
    """
        render_greetings()
        Выполняет запрос пользовательских сообщений, добавляет их к переменной 
        results и создает на базе этого HTML.

        Возвращает:
           Строку с HTML кодом, содержащую сообщения
    """
    results = db.GqlQuery("SELECT * "
                          "FROM Greeting "
                          "ORDER BY date DESC").fetch(10)
    output = StringIO.StringIO()
    for result in results:
      if result.author:
        output.write("<b>%s</b> wrote:" % result.author.nickname())
      else:
        output.write("An anonymous person wrote:")
      output.write("<blockquote>%s</blockquote>" %
                   cgi.escape(result.content))
    return output.getvalue()  
     
class Guestbook(webapp.RequestHandler):
  def post(self):
    greeting = Greeting()

    if users.get_current_user():
      greeting.author = users.get_current_user()

    greeting.content = self.request.get('content')
    greeting.put()
    self.redirect('/')


application = webapp.WSGIApplication([
  ('/', MainPage),
  ('/sign', Guestbook)
], debug=True)


def main():
  wsgiref.handlers.CGIHandler().run(application)


if __name__ == '__main__':
  main()