Memcache-Beispiele

Auf dieser Seite finden Sie Python-Codebeispiele zur Verwendung von Memcache. Memcache ist ein leistungsstarkes, verteiltes Speicherobjekt-Caching-System, das schnellen Zugriff auf im Cache gespeicherte Daten bietet. Weitere Informationen zu Memcache finden Sie in der Übersicht über Memcache.

Memcache-Muster

Memcache wird in der Regel nach folgendem Muster verwendet:

  • Die Anwendung erhält eine Anfrage vom Nutzer oder der Anwendung.
  • Die Anwendung überprüft, ob die zur Erfüllung dieser Anfrage erforderlichen Daten in Memcache vorliegen.
    • Wenn die Daten in Memcache vorliegen, verwendet die Anwendung diese Daten.
    • Wenn die Daten nicht in Memcache vorliegen, fragt die Anwendung den Datenspeicher ab und speichert die Ergebnisse für zukünftige Requests in Memcache.

Der unten stehende Pseudocode stellt einen typischen Memcache-Request dar:

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

Memcache wird in ndb intern verwendet, um Abfragen zu beschleunigen. Sie können jedoch auch explizit Memcache-Aufrufe hinzufügen, um die Beschleunigungen besser steuern zu können.

Daten im Cache speichern

Im folgenden Beispiel werden mehrere Methoden zum Festlegen von Werten in Memcache mithilfe der Python API dargestellt.

# Add a value if it doesn't exist in the cache
# with a cache expiration of 1 hour.
memcache.add(key="weather_USA_98105", value="raining", time=3600)

# Set several values, overwriting any existing values for these keys.
memcache.set_multi(
    {"USA_98115": "cloudy", "USA_94105": "foggy", "USA_94043": "sunny"},
    key_prefix="weather_",
    time=3600
)

# Atomically increment an integer value.
memcache.set(key="counter", value=0)
memcache.incr("counter")
memcache.incr("counter")
memcache.incr("counter")

Weitere Informationen zu den Methoden add(), set_multi() und set() finden Sie in der Dokumentation zur Memcache Python API.

guestbook.py für die Verwendung von Memcache ändern

Die Gästebuch-App im Startleitfaden fragt den Datenspeicher bei jedem Request ab. Ein Request erfolgt über ndb, sodass bereits einige Memcache-Beschleunigungen erzielt werden. Sie können die Gästebuch-App so ändern, dass Memcache explizit verwendet wird, bevor auf das Abfragen des Datenspeichers zurückgegriffen wird.

Importieren Sie zuerst das Memcache-Modul und erstellen Sie die Methode, mit der Memcache vor dem Ausführen einer Abfrage überprüft wird.

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

    Args:
      guestbook_name: Guestbook entity group key (string).

    Returns:
      A string of HTML containing greetings.
    """
    greetings = memcache.get('{}:greetings'.format(guestbook_name))
    if greetings is None:
        greetings = self.render_greetings(guestbook_name)
        try:
            added = memcache.add(
                '{}:greetings'.format(guestbook_name), greetings, 10)
            if not added:
                logging.error('Memcache set failed.')
        except ValueError:
            logging.error('Memcache set failed - data larger than 1MB')
    return greetings

Als Nächstes trennen wir das Abfragen und das Erstellen des HTML-Codes für die Seite. Wenn kein Treffer im Cache vorliegt, rufen Sie diese Methode auf, um den Datenspeicher abzufragen und den HTML-String zu erstellen, der in Memcache gespeichert wird.

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

    Args:
      guestbook_name: Guestbook entity group key (string).

    Returns:
      A string of HTML containing greetings
    """
    greetings = ndb.gql('SELECT * '
                        'FROM Greeting '
                        'WHERE ANCESTOR IS :1 '
                        'ORDER BY date DESC LIMIT 10',
                        guestbook_key(guestbook_name))
    output = cStringIO.StringIO()
    for greeting in greetings:
        if greeting.author:
            output.write('<b>{}</b> wrote:'.format(greeting.author))
        else:
            output.write('An anonymous person wrote:')
        output.write('<blockquote>{}</blockquote>'.format(
            cgi.escape(greeting.content)))
    return output.getvalue()

Schließlich aktualisieren wir den MainPage-Handler für den Aufruf der Methode "get_greetings()" und die Anzeige von Statistiken zur Anzahl der Cache Hits bzw. Misses.


import cgi
import cStringIO
import logging
import urllib

from google.appengine.api import memcache
from google.appengine.api import users
from google.appengine.ext import ndb

import webapp2

class Greeting(ndb.Model):
    """Models an individual Guestbook entry with author, content, and date."""
    author = ndb.StringProperty()
    content = ndb.StringProperty()
    date = ndb.DateTimeProperty(auto_now_add=True)

def guestbook_key(guestbook_name=None):
    """Constructs a Datastore key for a Guestbook entity with guestbook_name"""
    return ndb.Key('Guestbook', guestbook_name or 'default_guestbook')

class MainPage(webapp2.RequestHandler):
    def get(self):
        self.response.out.write('<html><body>')
        guestbook_name = self.request.get('guestbook_name')

        greetings = self.get_greetings(guestbook_name)
        stats = memcache.get_stats()

        self.response.write('<b>Cache Hits:{}</b><br>'.format(stats['hits']))
        self.response.write('<b>Cache Misses:{}</b><br><br>'.format(
                            stats['misses']))
        self.response.write(greetings)

        self.response.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>
          <hr>
          <form>Guestbook name: <input value="{}" name="guestbook_name">
          <input type="submit" value="switch"></form>
        </body>
      </html>""".format(urllib.urlencode({'guestbook_name': guestbook_name}),
                        cgi.escape(guestbook_name)))

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

        Args:
          guestbook_name: Guestbook entity group key (string).

        Returns:
          A string of HTML containing greetings.
        """
        greetings = memcache.get('{}:greetings'.format(guestbook_name))
        if greetings is None:
            greetings = self.render_greetings(guestbook_name)
            try:
                added = memcache.add(
                    '{}:greetings'.format(guestbook_name), greetings, 10)
                if not added:
                    logging.error('Memcache set failed.')
            except ValueError:
                logging.error('Memcache set failed - data larger than 1MB')
        return greetings

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

        Args:
          guestbook_name: Guestbook entity group key (string).

        Returns:
          A string of HTML containing greetings
        """
        greetings = ndb.gql('SELECT * '
                            'FROM Greeting '
                            'WHERE ANCESTOR IS :1 '
                            'ORDER BY date DESC LIMIT 10',
                            guestbook_key(guestbook_name))
        output = cStringIO.StringIO()
        for greeting in greetings:
            if greeting.author:
                output.write('<b>{}</b> wrote:'.format(greeting.author))
            else:
                output.write('An anonymous person wrote:')
            output.write('<blockquote>{}</blockquote>'.format(
                cgi.escape(greeting.content)))
        return output.getvalue()

class Guestbook(webapp2.RequestHandler):
    def post(self):
        # We set the same parent key on the 'Greeting' to ensure each greeting
        # is in the same entity group. Queries across the single entity group
        # are strongly consistent. However, the write rate to a single entity
        # group is limited to ~1/second.
        guestbook_name = self.request.get('guestbook_name')
        greeting = Greeting(parent=guestbook_key(guestbook_name))

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

        greeting.content = self.request.get('content')
        greeting.put()
        memcache.delete('{}:greetings'.format(guestbook_name))
        self.redirect('/?' +
                      urllib.urlencode({'guestbook_name': guestbook_name}))

app = webapp2.WSGIApplication([('/', MainPage),
                               ('/sign', Guestbook)],
                              debug=True)