2010-08-23 3 views
1

J'essaie de configurer correctement un cache côté serveur et je suis à la recherche de critiques constructives sur l'installation que j'ai actuellement. Le cache est chargé lorsque la servlet démarre et ne change plus jamais, c'est donc un cache en lecture seule. Il doit évidemment rester en mémoire pour la durée de vie du Servlet. Voici comment je l'ai configurationConfiguration correcte d'un simple cache côté serveur

private static List<ProductData> _cache; 
private static ProductManager productManager; 

private ProductManager() { 
    try { 
     lookup(); 
    } catch (Exception ex) { 
     _cache = null; 
    } 
} 

public synchronized static ProductManager getInstance() { 
    if (productManager== null) { 
     productManager= new ProductManager(); 
    } 
    return productManager; 
} 

Le cache est configuré par le Servlet comme ci-dessous:

private ProductManager productManager; 

public void init(ServletConfig config) throws ServletException { 
    productManager = ProductManager.getInstance(); 
} 

Et enfin, voici comment je y accéder:

public static ProductData lookup(long id) throws Exception { 
    if (_cache != null) { 
     for (int i = 0; i < _cache.size(); i++) { 
      if (_cache.get(i).id == id) { 
       return _cache.get(i); 
      } 
     } 
    } 

    // Look it up in the DB. 
} 

public static List<ProductData> lookup() throws Exception { 
    if (_cache != null) { 
     return _cache; 
    } 

    // Read it from the DB. 

    _cache = list; 
    return list; 
} 

Répondre

1

Vous êtes ce faire à la dure. Un motif aromatisé au singleton est complètement inutile. Il suffit d'implémenter un ServletContextListener pour avoir un hook sur le démarrage webapp (et shutdown), de sorte que vous pouvez simplement charger et stocker les données dans la portée de l'application lors du démarrage de la webapp.

public class Config implements ServletContextListener { 

    private static final String ATTRIBUTE_NAME = "com.example.Config"; 
    private Map<Long, Product> products; 

    @Override 
    public void contextInitialized(ServletContextEvent event) { 
     ServletContext context = event.getServletContext(); 
     context.setAttribute(ATTRIBUTE_NAME, this); 
     String dbname = context.getInitParameter("dbname"); 
     products = Database.getInstance(dbname).getProductDAO().map(); 
    } 

    @Override 
    public void contextDestroyed(ServletContextEvent event) { 
     // NOOP. 
    } 

    public static Config getInstance(ServletContext context) { 
     return (Config) context.getAttribute(ATTRIBUTE_NAME); 
    } 

    public Map<Long, Product> getProducts() { 
     return products; 
    } 

} 

qui vous vous inscrivez à web.xml comme suit:

<listener> 
    <listener-class>com.example.Config</listener-class> 
</listener> 

De cette façon, vous pouvez l'obtenir dans un servlet comme suit:

Config config = Config.getInstance(getServletContext()); 
Map<Long, Product> products = config.getProducts(); 
// ... 
0

I vous recommande de faire le cache comme hashmap:

private static HashMap<Long,ProductData> _cache = new HashMap<Long,ProductData>(); 


// lookup by id becomes 
return _cache.get(id); 

// lookup of the complete collection of ProductData : 
return _cache.values(); 

Vous pouvez transformer le cache en champ statique dans la classe ProductData afin d'obtenir moins de pièces de couplage et de déplacement. Avec un hashmap cherchant par id sera essentiellement temps constant tandis que la recherche avec la boucle for augmentera lorsque le nombre de ProductData augmente au fil du temps.

1

Quelques choses qui viennent à l'esprit:

  • Stockez vos instances ProductData mises en cache dans une carte afin que vous pouvez rechercher une instance mise en cache en temps constant plutôt que d'avoir à chercher dans une liste.
  • Les méthodes lookup ne sont pas adaptées aux threads.
  • Seul le lookup() chargera les valeurs de la base de données: ne voulez-vous pas que l'autre méthode lookup charge également le cache (s'il n'est pas déjà chargé) pour accélérer la récupération des instances ProductData?
+0

Comment, en particulier, dois-je aller sur le fil sécurité? Blocs synchronisés et/ou classes de collection simultanées? – bluedevil2k

+0

Les collections simultanées ne vous aideront pas parce que je crois que vous chargez le cache une fois et après ce point, il ne change pas. La deuxième méthode 'lookup' - qui charge réellement le cache - doit être' synchronisée'.Autrement, deux threads pourraient tous les deux trouver que '_cache' est' null' et peuvent tous deux essayer de charger à partir de la base de données. –

0

Veuillez vous reporter à la section Suppliers.memoizeWithExpiration() et MapMaker de Guava. Les deux (dans mon cas, je pense que MapMaker est plus pertinent) vous permettra de créer une fonction de mise en cache en quelques minutes.

Epargnez-vous des maux de tête, utilisez une API :)

Questions connexes