24

我正在考虑在前面创建缓存层或作为对我的 RESTful API(用 Ruby 编写)的 GET 请求的第一层的最佳方法。

并非每个请求都可以缓存,因为即使对于某些 GET 请求,API 也必须验证请求用户/应用程序。这意味着我需要配置哪个请求是可缓存的,以及每个缓存答案的有效期。在少数情况下,我需要非常短的到期时间,例如 15 秒及以下。即使尚未达到到期日期,我也应该能够让 API 应用程序的缓存条目到期。

我已经考虑了许多可能的解决方案,我最好的两个想法:

  • API 的第一层(甚至在路由之前),自己缓存逻辑(掌握所有配置选项),答案和到期日期存储到 Memcached

  • 一个网络服务器代理(高度可配置),可能类似于 Squid,但我以前从未在这样的情况下使用过代理,我对此完全不确定

我还考虑过像 Varnish 这样的缓存解决方案,我将 Varnish 用于“普通”的 Web 应用程序,它令人印象深刻,但配置有点特别。但如果它是最快的解决方案,我会使用它。

另一个想法是缓存到 Solr 索引,我已经在数据层中使用它来不为大多数请求查询数据库。

如果有人有关于此主题的提示或好的资源可以阅读,请告诉我。

4

5 回答 5

5

memcached 是一个很好的选择,我看到你已经提到了这个作为一个可能的选择。此外,Redis 似乎在这个级别上作为另一种选择受到了很多赞扬。

在应用程序级别,就在逐个文件和/或模块的基础上缓存的更精细的方法而言,本地存储始终是用户可能一遍又一遍地请求的常见对象的一种选择,即使只是简单地丢弃响应对象进入会话,以便可以重用,而不是进行另一个 http 休息调用和适当编码。

现在人们来回争论 varnish 和 squid,两者似乎各有利弊,所以我无法评论哪个更好,但很多人说 Varnish 和经过调整的 apache 服务器非常适合动态网站。

于 2012-12-24T22:16:43.463 回答
5

首先,将您的 RESTful API 构建为 RESTful。这意味着经过身份验证的用户还可以获得缓存内容,以将所有状态保留在 URL 中,它需要包含身份验证详细信息。当然这里的命中率会低一些,但是是可以缓存的。

对于大量登录用户来说,在完整页面缓存后面拥有某种模型缓存将非常有益,因为即使有些模型不共享(在良好的 OOP 结构中),许多模型仍然是共享的。

然后,对于完整的页面缓存,您最好将所有请求远离 Web 服务器,尤其是远离下一步中的动态处理(在您的情况下为 Ruby)。从普通 Web 服务器缓存完整页面的最快方法始终是在 Web 服务器前面使用缓存代理。

Varnish 在我看来就像它得到的一样好和容易,但有些人确实更喜欢 Squid。

于 2012-12-25T14:00:27.817 回答
4

由于 REST 是一种 HTTP 事物,因此缓存请求的最佳方式可能是使用 HTTP 缓存。

考虑在您的响应中使用 ETag,检查请求中的 ETag 以使用“304 Not Modified”回复,如果 ETag 相同,则使用 Rack::Cache 提供缓存数据。这对于缓存控制“公共”内容非常有用。

Rack::Cache 最好配置为使用 memcache 来满足其存储需求。

上周我写了一篇关于 Rack::Cache 使用 ETags 检测缓存内容并将其返回给新客户端的有趣方式的博客文章:http: //blog.craz8.com/articles/2012/12/19/rack-cache- and-etags-for-even-faster-rails

即使您不使用 Rails,Rack 中间件工具也非常适合这些东西。

于 2012-12-25T06:37:53.437 回答
1

Redis 缓存是最好的选择。 检查这里。

它是开源的。高级键值缓存和存储。

于 2015-05-06T06:55:36.233 回答
0

我在 REST 视图中以这种方式成功使用了 redis:

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)
于 2018-01-11T22:52:12.200 回答