Caching Layer nelle librerie: qual è il design più corretto?

1

Introduzione

Supponiamo che tu abbia un design simile nella tua applicazione:

- application  (laravel)
  - web scraper (used by application)
    - query builder (used by web scraper)

L'applicazione è ciò che effettivamente usa il Web Scraper per recuperare i dati dal web, tuttavia poiché utilizziamo il Web Scraper come motore di ricerca , costruiamo la stringa di query utilizzata dal Web Scraper basato sull'input dell'utente.

Il Web Scraper è la libreria che si occupa effettivamente dell'estrazione e dell'analisi dei file HTML presi dai siti in cui lavoriamo.

Query Builder utilizza alcune API remote (ad esempio google maps per le coordinate basate sull'input dell'utente) da utilizzare nel Web Scraper.

Esempio di codice

class WebsiteScraper 
{
    public function city($city)
    {
        $this->parameters['city'] = CityIdentifierFactory::retrieve($city);
    }
}
class CityIdentifier
{
    const API_URL = "http://maps.google.com/maps/api/geocode/json";

    public function __construct(\GuzzleHttp\Client $client)
    {
        $this->client = $client;
    }

    public function retrieve($city)
    {
        try
        {
            $coordinates = $this->getCoordinates($city);

            if (count($coordinates) <= 0) {
                return [];
            }

            return $coordinates;
        } catch (Exception $e) {
            throw new QueryBuilderCityFetchingException(
                "Whoops! Something wrong happened."
            );
        }
    }

    private function getCoordinates($city)
    {
        $response = $this->client->get(self::API_URL, [
            'query' => [
                'address' => $city,
                'region'  => 'IT'
            ]
        ]);

        $body = json_decode($response->getBody()->getContents());

        return [[
            'city'     => $city,
            'lat'      => $body->results[0]->geometry->location->lat,
            'lng'      => $body->results[0]->geometry->location->lng,
            'location' => $city
        ]];
    }
}
class CityIdentifierFactory
{
    public static function retrieveFromDisk($name)
    {
        $identifier = new FilesystemCityIdentifier(__DIR__ . "/../../assets/provinces.json");
        return $identifier->retrieve($name);
    }

    public static function retrieve($name)
    {
        $identifier = new CityIdentifier(new Client);
        return $identifier->retrieve($name);
    }
}

Problema

Il problema è che il Web Scraper viene eseguito migliaia di volte al giorno e le API di Google Maps hanno dei limiti che non dovremmo raggiungere (anche se li abbiamo già raggiunti! :(). Vorrei implementare un livello di memorizzazione nella cache in il componente CityIdentifier di Query Builder, in modo da evitare di fare migliaia di query relative alla stessa città.

Domanda

Dove devo implementare questo livello di memorizzazione nella cache, sapendo che tutte queste cose verranno inserite per iniezione di dipendenza in Laravel (4.2)?

Dovrebbe essere nel raschietto Web, nel generatore di query o nel componente CityIdentifier effettivo?

Dove dovrebbe essere salvata la cache? In una cartella interna del generatore di query o in una cartella di applicazioni configurata nella libreria?

    
posta GiamPy 06.04.2017 - 12:39
fonte

1 risposta

3

Prefazione: so che stai chiedendo di PHP. Non ho esperienza di PHP, ma qui è quello che vorrei fare come sviluppatore Java. Non so quanto questo sia rilevante per la mia risposta.

Hai scritto:

I would like to implement a caching layer in the QueryBuilder

Questo suona come un buon caso per un decoratore. Qui racchiudere un QueryBuilder "regolare" e aggiungere il comportamento di memorizzazione nella cache. Il codice potrebbe essere simile a questo:

class CachingQueryBuilder implements QueryBuilder {

    QueryBuilder basicQueryBuilder;
    Cache cache;

    @Override Query buildQuery(String input) {

        if(cache.contains(input)) {
            //returned cached contents when possible
            return cache.get(input);
        } else {
            //build query manually, cache it, and return it
            Query q = basicQueryBuilder.buildQuery(input);
            cache.put(q);
            return q;
        } 
    }
}

L'API Guava di Google (una risorsa Java) ha alcune ottime cache su: cache di guava .

    
risposta data 06.04.2017 - 14:24
fonte

Leggi altre domande sui tag