2012-12-24 13 views
19

Sto pensando al modo migliore per creare un livello cache nella parte anteriore o come primo livello per le richieste GET alla mia API RESTful (scritta in Ruby).Il modo migliore per memorizzare i risultati dell'API RESTful delle chiamate GET

Non tutte le richieste possono essere memorizzate nella cache, perché anche per alcune richieste GET l'API deve convalidare l'utente/l'applicazione richiedente. Ciò significa che ho bisogno di configurare quale richiesta è memorizzabile nella cache e per quanto tempo ogni risposta in cache è valida. Per alcuni casi ho bisogno di un tempo di scadenza molto breve, ad es. 15 secondi e oltre. E dovrei essere in grado di lasciare scadere le voci della cache dall'applicazione API anche se la data di scadenza non è stata ancora raggiunta.

ho già pensato a molte soluzioni possibili, i miei due migliori idee:

  • primo strato di API (anche prima del routing), la logica di cache da solo (per avere tutte le opzioni di configurazione in mano) , risposte e la data di scadenza memorizzati Memcached

  • un proxy server web (ad alta configurabile), forse qualcosa come Squid, ma non ho mai usato un proxy per un caso come questo prima e io non sono assolutamente sicuro su di esso

Ho pensato anche a una soluzione cache come Varnish, ho usato Varnish per le "solite" applicazioni web ed è impressionante ma la configurazione è piuttosto speciale. Ma lo userei se fosse la soluzione più veloce.

Un'altra idea era quella di memorizzare nella cache l'indice Solr, che sto già utilizzando nel livello dati per non interrogare il database per la maggior parte delle richieste.

Se qualcuno ha un suggerimento o delle buone fonti da leggere su questo argomento, fammi sapere.

risposta

3

memcached è un'ottima opzione e vedo che l'hai già citato come possibile opzione. Anche Redis sembra essere elogiato molto come un'altra opzione a questo livello.

A livello di applicazione, in termini di approccio più granulare alla cache di un file per file e/o modulo base, l'archiviazione locale è sempre un'opzione per gli oggetti comuni che un utente può richiedere più e più volte, anche se semplice come semplicemente rilasciare oggetti di risposta in sessione in modo che possano essere riutilizzati rispetto a fare un'altra chiamata di resto http e codificare in modo appropriato.

Ora la gente va avanti e indietro a discutere di vernice vs calamaro, ed entrambi sembrano avere i loro pro e contro, quindi non posso commentare quale è meglio, ma molte persone dicono che Varnish con un server Apache ottimizzato è ideale per siti Web dinamici.

3

Poiché REST è una cosa HTTP, potrebbe essere che il modo migliore per le richieste di memorizzazione nella cache sia l'uso della memorizzazione nella cache HTTP.

Cerca di utilizzare ETags nelle risposte, controllando l'ETag nelle richieste di risposta con "304 Not Modified" e con Rack :: Cache per fornire dati memorizzati nella cache se gli ETags sono uguali. Funziona perfettamente per il contenuto "pubblico" del controllo della cache.

Rack :: Cache è meglio configurato per utilizzare memcache per le sue esigenze di archiviazione.

ho scritto un post sul blog la scorsa settimana circa il modo interessante che Rack :: Cache utilizza ETags di rilevare e restituire contenuto memorizzato nella cache per i nuovi clienti: http://blog.craz8.com/articles/2012/12/19/rack-cache-and-etags-for-even-faster-rails

Anche se non si sta usando Rails, gli strumenti Rack middleware sono abbastanza buoni per questa roba.

+0

Grazie, ho letto di postare e un po 'di Rack :: Cache. È interessante e darò un'occhiata più da vicino. – maddin2code

+0

Sul tuo blocco ho visto che stai usando Varnish, suppongo che Varnish restituirà il risultato memorizzato nella cache se l'URI e anche "ETag" corrispondono al risultato memorizzato, giusto? – maddin2code

+0

Non ho mai usato Varnish in modo esplicito, ma sembra che Heroku ora stia usando Varnish nel loro stack Cedar per il contenuto nella cache. Tutto il materiale ETag dovrebbe ancora essere applicato –

5

Innanzitutto, creare l'API RESTful per essere RESTful. Ciò significa che gli utenti autenticati possono anche ottenere contenuti memorizzati nella cache in modo da mantenere tutti gli stati nell'URL di cui ha bisogno per contenere i dettagli dell'autenticazione. Ovviamente il tasso di successo sarà inferiore qui, ma è memorabile nella cache.

Con una buona quantità di utenti registrati sarà molto utile avere una sorta di cache del modello dietro una cache a piena pagina, poiché molti modelli sono ancora condivisi anche se alcuni non lo sono (in una buona struttura OOP).

Quindi per una cache a pagina intera è meglio conservare tutte le richieste dal server Web e soprattutto dall'elaborazione dinamica nel passaggio successivo (nel tuo caso Ruby). Il modo più rapido per memorizzare nella cache pagine complete da un normale server Web è sempre un proxy di memorizzazione nella cache di fronte ai server Web.

La vernice è a mio parere buona e facile come si ottiene, ma alcuni preferiscono davvero Squid.

+0

Le informazioni sulla sessione sono memorizzate in un cookie, le controllo sul lato server se il metodo chiamato lo richiede. Se questo non è ancora un modo RESTful, sarei sorpreso, ma dimmi. – maddin2code

+0

Ok, ho trovato una buona discussione su questo argomento [collegamento] (http://stackoverflow.com/questions/319530/restful-authentication) – maddin2code

2

Redis Cache è l'opzione migliore.

È open source. Memorizzazione e memorizzazione di valori-chiave avanzati.

0

Ho usato Redis con successo in questo modo, a mio avviso 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) 

nei miei aggiornamenti modello di eventi, ho definitivamente la cache degli eventi. Questo quasi mai viene eseguita (solo amministratori creano gli eventi, e non che spesso), così ho sempre chiare tutte le cache di eventi

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) 
Problemi correlati