My favorites | 中文(简体) | Sign in

使用 Memcache

Memcache 是一个高性能的分布式内存对象缓存系统,主要用于快速访问缓存的数据库查询结果。

Memcache 模式

Memcache 通常与以下模式配合使用:

  • 应用程序将接收来自用户或应用程序的查询。
  • 应用程序检查满足该查询所需的数据是否在 Memcache 中。
    • 如果数据在 Memcache 中,应用程序将使用该数据。
    • 如果数据不在 Memcache 中,应用程序将查询数据库并将结果存储在 Memcache 中以便将来提出请求。

以下伪代码表示一个典型的 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。

首先,我们将导入 Memcache 模块,并创建在运行查询前检查 Memcache 的方法。

from google.appengine.api import memcache

def get_greetings(self):
  """get_greetings()
  
  Checks the cache to see if there are cached greetings.
  If not, call render_greetings and set the cache

  Returns:
    A string of HTML containing greetings.
  """
  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

然后,我们将分出查询和页面 HTML 的创建。如果缓存未命中,我们将调用此方法来查询数据库并构建将存储在 Memcache 中的 HTML 字符串。

def render_greetings(self):
  """render_greetings()
  
  Queries the database for greetings, iterate through the
  results and create the HTML.

  Returns:
    A string of HTML containing greetings
  """
  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 logging
import StringIO

from google.appengine.ext import db
from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
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()
        Checks the cache to see if there are cached greetings.
        If not, call render_greetings and set the cache

        Returns:
           A string of HTML containing greetings.
    """
    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()
        Queries the database for greetings, iterate through the
        results and create the HTML.

        Returns:
           A string of HTML containing greetings
    """
    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():
  run_wsgi_app(application)


if __name__ == '__main__':
  main()