2012-12-24 81 views
19

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

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

我已经想过很多方法,我的两个最好的想法:

  • 通过自己的API(甚至在路由),高速缓存逻辑的

    第一层(有我手里的所有配置选项) ,答案和到期日期存储到Memcached的

  • Web服务器代理(高配置),也许像鱿鱼,但我从来没有使用代理像这之前的情况下,我完全不知道这件事

我也考虑过像Varnish这样的高速缓存解决方案,我使用Varnish来处理“平常”的Web应用程序,但它的性能令人印象深刻,但配置有点特别。但如果它是最快的解决方案,我会使用它。

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

如果有人有提示或好消息来了解这个话题,请告诉我。

回答

3

memcached是一个很好的选择,我看到你已经提到这是一个可能的选项。另外Redis在这个级别似乎也被称赞为另一种选择。

在应用程序级别上,根据文件和/或模块对文件进行缓存的更细粒度的方法,本地存储始终是用户可能一遍又一遍请求的常见对象的选项,即使是简单就像将响应对象放入会话中一样,以便可以重复使用,并相应地进行另一个http休息调用和编码。

现在人们来回讨论关于清漆vs squid的问题,两者似乎都有其优点和缺点,所以我不能评论哪一个更好,但很多人说使用调整好的apache服务器的清漆非常适合动态网站。

3

由于REST是一个HTTP事件,因此缓存请求的最佳方式是使用HTTP缓存。

在你的响应中使用ETags进行研究,检查ETag中的请求是否使用'304 Not Modified'进行回复,如果ETags相同,则让Rack :: Cache提供缓存数据。这对缓存控制“公共”内容非常有用。

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

我上周写了一篇博客有关机架有趣的方式:: Cache使用ETag来检测并返回缓存的内容,新的客户端:即使你不使用Rails的http://blog.craz8.com/articles/2012/12/19/rack-cache-and-etags-for-even-faster-rails

,机架中间件工具对这个东西来说相当好。

+0

谢谢你,我看了你发布和一些关于机架::缓存。这很有趣,我会仔细看看。 – maddin2code

+0

在你的块上我看到你也在使用Varnish,如果URI和“ETag”与存储的结果匹配,我猜Varnish会返回缓存的结果,对吗? – maddin2code

+0

我从来没有明确使用Varnish,但看起来Heroku现在在Cedar堆栈中使用Varnish来缓存内容。所有的ETag的东西仍然应用 –

5

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

随着大量登录用户,在完整页面缓存后面放置某种模型缓存将会非常有用,因为即使有些模型仍然没有共享(在一个良好的OOP结构中),仍然可以共享许多模型。

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

在我看来,清漆很好很容易,但有些人更喜欢鱿鱼。

+0

会话信息存储在cookie中,如果被调用方法需要,我在服务器端检查它们。如果这还不是一个RESTful的方式,我会感到惊讶,但告诉我。 – maddin2code

+0

好吧,我发现了关于这个主题的一个很好的讨论[链接](http://stackoverflow.com/questions/319530/restful-authentication) – maddin2code

2

Redis缓存是最佳选择。

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

0

我已经成功地使用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)