2012-12-24 115 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)