Лучший способ кэшировать результаты RESTful API вызовов GET

Я думаю о лучшем способе создания слоя кэша спереди или в качестве первого слоя для запросов GET к моему RESTful API (написанному на Ruby).

Не каждый запрос может быть кэширован, потому что даже для некоторых запросов GET API должен проверить запрашивающего пользователя / приложение. Это означает, что мне нужно настроить, какой запрос кэшируется и как долго каждый кэшированный ответ действителен. Для нескольких случаев мне нужно очень короткое время истечения, например, 15s и ниже. И я должен быть в состоянии дать записи кэша истекают приложением API, даже если срок действия еще не достигнут.

Я уже думал о многих возможных решениях, мои две лучшие идеи:

  • первый уровень API (даже до маршрутизации), логика кэша сама по себе (чтобы иметь все параметры конфигурации в моей руке), ответы и срок действия, хранящиеся в Memcached

  • прокси-сервер веб-сервера (высокий настраиваемый), возможно, что - то вроде Squid, но я никогда не использовал прокси для такого случая раньше, и я абсолютно не уверен в этом

Я также думал о решении кэша, таком как лак, я использовал лак для "обычных" веб-приложений, и это впечатляет, но конфигурация особенная. Но я бы использовал его, если это самое быстрое решение.

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

Если кто-то имеет подсказка или хорошие источники, чтобы прочитать об этой теме, дайте мне знать.

5 ответов


memcached-отличный вариант, и я вижу, что вы уже упоминали об этом как о возможном варианте. Также Redis, похоже, очень хвалят как еще один вариант на этом уровне.

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

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


во-первых, создайте свой RESTful API, чтобы быть RESTful. Это означает, что аутентифицированные пользователи также могут получить кэшированное содержимое, чтобы сохранить все состояние в URL-адресе, который должен содержать данные аутентификации. Конечно, скорость попадания здесь будет ниже,но она достижима.

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

тогда для a полный кэш страниц вам лучше всего держать все запросы вне веб-сервера и особенно вдали от динамической обработки на следующем шаге (в вашем случае Ruby). Самый быстрый способ кэширования полных страниц с обычного веб-сервера - это всегда кэширующий прокси перед веб-серверами.

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


поскольку REST-это HTTP-вещь, возможно, лучший способ кэширования запросов-использовать HTTP-кэширование.

посмотрите на использование ETags в ваших ответах, проверяя ETag в запросах на ответ с "304 не изменено" и имея Rack::Cache для обслуживания кэшированных данных, если ETags одинаковы. Это отлично подходит для "публичного" контента cache-control.

Rack:: Cache лучше всего настроить для использования memcache для его потребностей хранения.

Я написал пост в блоге на прошлой неделе о интересный способ, которым Rack:: Cache использует ETags для обнаружения и возврата кэшированного содержимого новым клиентам: http://blog.craz8.com/articles/2012/12/19/rack-cache-and-etags-for-even-faster-rails

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


Redis Cache-лучший вариант. регистрация здесь.

Это с открытым исходным кодом. Расширенный кэш и хранилище значений ключей.


Я успешно использовал redis таким образом в моем представлении REST:

from django.conf import settings
import hashlib
import json
from redis import StrictRedis
from django.utils.encoding import force_bytes

def get_redis():
    #get redis connection from RQ config in settings
    rc = settings.RQ_QUEUES['default']
    cache = StrictRedis(host=rc['HOST'], port=rc['PORT'], db=rc['DB'])
    return cache



class EventList(ListAPIView):
    queryset = Event.objects.all()
    serializer_class = EventSerializer
    renderer_classes = (JSONRenderer, )


    def get(self, request, format=None):
        if IsAdminUser not in self.permission_classes:  # dont cache requests from admins


            # make a key that represents the request results you want to cache
            #  your requirements may vary
            key = get_key_from_request()

            #  I find it useful to hash the key, when query parms are added
            #  I also preface event cache key with a string, so I can clear the cache
            #   when events are changed
            key = "todaysevents" + hashlib.md5(force_bytes(key)).hexdigest()        

            # I dont want any cache issues (such as not being able to connect to redis)
            #  to affect my end users, so I protect this section
            try:
                cache = get_redis()
                data = cache.get(key)
                if not data:
                    #  not cached, so perform standard REST functions for this view
                    queryset = self.filter_queryset(self.get_queryset())
                    serializer = self.get_serializer(queryset, many=True)
                    data = serializer.data

                    #  cache the data as a string
                    cache.set(key, json.dumps(data))

                    # manage the expiration of the cache 
                    expire = 60 * 60 * 2  
                    cache.expire(key, expire)
                else:
                    # this is the place where you save all the time
                    #  just return the cached data 
                    data = json.loads(data)

                return Response(data)
            except Exception as e:
                logger.exception("Error accessing event cache\n %s" % (e))

        # for Admins or exceptions, BAU
        return super(EventList, self).get(request, format)

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

class Event(models.Model):

...

    def clear_cache(self):
        try:
            cache = get_redis()
            eventkey = "todaysevents"
            for key in cache.scan_iter("%s*" % eventkey):
                cache.delete(key)
        except Exception as e:
            pass


    def save(self, *args, **kwargs):
        self.clear_cache()
        return super(Event, self).save(*args, **kwargs)