Java Web应用程序:如何实现缓存技术?

我正在开发一个Java Web应用程序,它通过从Web服务加载的大型XML配置文件来实现它的行为。 由于在访问应用程序的特定部分之前实际上不需要这些文件,因此它们会被懒惰地加载。 当需要其中一个文件时,会向Web服务发送查询以检索相应的文件。 由于某些配置文件可能会被使用得多,比其他配置文件更频繁,我想设置某种缓存(可能有1小时的到期时间),以避免一遍又一遍地请求相同的文件。

对于所有会话中的所有用户,Web服务返回的文件都是相同的。 我不使用JSP,JSF或任何其他花哨的框架,只是普通的servlet。

我的问题是,在Java Web应用程序中实现这样一个全局静态缓存的最佳实践是什么? 单例类是否合适,或者由于J2EE容器会有奇怪的行为吗? 我应该通过JNDI在某处暴露某些东西吗? 我该怎么做才能使我的缓存不会在集群环境中搞砸(每个集群服务器有一个缓存可以,但不是必需的)?

鉴于上面的信息,将一个负责缓存的对象作为ServletContext属性是否是一个正确的实现?

注意:我不想在启动时加载所有这些并完成它,因为那会

1)。 每当我的应用程序启动时,都会重载webservice
2)。 我的应用程序运行时文件可能会更改,所以无论如何我都必须重新查询它们
3)。 我仍然需要一个全局可访问的缓存,所以我的问题仍然存在

更新:使用缓存代理(例如squid)可能是个好主意,但是每个对webservice的请求都会在post Data中发送相当大的XML查询,每次都可能不同。 只有Web应用程序才真正知道对Web服务的两个不同调用实际上是等效的。

谢谢你的帮助

您的问题包含几个单独的问题。 让我们慢慢开始。 ServletContext是一个可以存储缓存句柄的好地方。 但是你需要为每个服务器实例提供缓存。 应该没问题。 如果要在更大范围内注册缓存,请考虑将其注册到JNDI中。

缓存的问题。 基本上,您是通过webservice检索xml。 如果您通过HTTP访问此Web服务,您可以在您的一侧安装简单的HTTP代理服务器来处理xml的缓存。 下一步将是在某种本地对象缓存中缓存已解析的xml。 每个服务器可以存在此缓存而没有任何问题。 在第二种情况下,EHCache将做得很好。 在这种情况下,处理链将像这样的Client - http request -> servlet -> look into local cache - if not cached -> look into http proxy (xml files) -> do proxy job (http to webservice)

优点:

  • 每个服务器实例的本地缓存,仅包含来自请求的xmls的对象
  • 一个http代理在与我们的webapp相同的硬件上运行。
  • 可以在不添加xml文件的新http代理的情况下扩展webapp。

缺点:

  • 下一级基础设施
  • +1点失败(http代理)
  • 部署更复杂

更新:不要忘记始终将HTTP HEAD请求发送到代理中以确保缓存是最新的。

以下是使用EhCache进行缓存的示例。 此代码在多个项目中用于实现临时缓存。

1)将缓存放在全局上下文中。 (不要忘记在WEB.XML中添加监听器)。

 import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; public class InitializationListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { ServletContext ctx = sce.getServletContext(); CacheManager singletonManager = CacheManager.create(); Cache memoryOnlyCache = new Cache("dbCache", 100, false, true, 86400,86400); singletonManager.addCache(memoryOnlyCache); cache = singletonManager.getCache("dbCache"); ctx.setAttribute("dbCache", cache ); } } 

2)在需要时检索缓存实例。 即来自servlet:

cache = (Cache) this.getContext().getAttribute("dbCache");

3)在执行昂贵的操作之前查询缓存。

  Element e = getCache().get(key); if (e != null) { result = e.getObjectValue(); // get object from cache } else { // Write code to create the object you need to cache, then store it in the cache. Element resultCacheElement = new Element(key, result); cache.put(resultCacheElement); } 

4)也不要忘记在适当的时候使缓存的对象无效。

你可以在这里找到更多样品

选项#1:使用开源缓存库,如EHCache

当有许多可以插入并开始使用的好的开源替代品时,不要实现自己的缓存。 实现自己的缓存要比大多数人意识到的要复杂得多,如果你不确切知道自己在做什么,那么你就可以轻松地重新开发轮子并解决一些非常棘手的问题。

我推荐EHCache它是在Apache许可下。 您需要查看EHCace代码示例 。

选项#2:使用Squid

更容易解决问题的方法是使用Squid …将Squid置于请求缓存数据的进程和发出请求的系统之间: http : //www.squid-cache.org/

在做了一些自我调查之后,似乎最简单的方法来实现我需要的东西(在问题中描述的要求和可接受的限制内),将我的缓存对象添加到Servlet上下文中,并查找它(或者在需要的地方传递它。

我只是从ServletContextListener实例化我的配置加载器,并且在contextInitialized()方法中,我只是使用ServletContext.setAttribute()将它存储到ServletContext中。 然后使用request.getSession()。getServletContext()。getAttribute()从servlet本身查找它很容易。

我认为这是在不引入spring或任何其他dependency injection框架的情况下执行此操作的正确方法。

Bref,你可以使用这个现成的spring ehcache配置

1- ehcache.xml :显示Ehcache的全局配置。

     

2- ehcache-spring.xml :创建EhCacheManagerFactoryBean和EhCacheFactoryBean。

                   

3-在您的业务类中注入“myCache”bean,请参阅以下示例以开始获取并将对象放入缓存中。

 @Resource("myCache") private net.sf.ehcache.Cache myCache; @Resource("myService") private Service myService; public byte[] getFromCache(final String code) { // init Cache final StringBuilder builder = new StringBuilder(); // key to identify a entry in cache map final String key = code; // get form the cache final Element element = myCache.get(key); if (element != null && element.getValue() != null) { return (byte[]) element.getValue(); } final byte[] somethingToBeCached = myService.getBy(code); // store in the cache myCache.put(new Element(key, somethingToBeCached)); return somethingTobeCached; } 

将缓存的对象实例放在ServletContext中没有任何问题。 不要忘记使用此对象的setAttributes方法的其他2个选项(请求范围,会话范围)。 webcontainers和j2ee服务器本身支持的任何东西都是好的(好的我的意思是它的供应商独立,没有像Spring这样的重型j2ee库)。 我最大的要求是服务器在5-10秒内启动并运行。

我真的不喜欢所有的缓存解决方案,因为它很容易让它在本地机器上运行,并且很难让它在生产机器上运行。 EHCACHE,Infinispan等。除非你需要集群范围的复制/分发,与Java生态系统紧密集成,你可以使用REDIS(NOSQL数据库)或nodejs ……任何具有HTTP接口的东西都可以。 特别

缓存可以非常简单,这里是纯java解决方案(没有框架):

 import java.util.*; /* ExpirableObject. Abstract superclass for objects which will expire. One interesting design choice is the decision to use the expected duration of the object, rather than the absolute time at which it will expire. Doing things this way is slightly easier on the client code this way (often, the client code can simply pass in a predefined constant, as is done here with DEFAULT_LIFETIME). */ public abstract class ExpirableObject { public static final long FIFTEEN_MINUTES = 15 * 60 * 1000; public static final long DEFAULT_LIFETIME = FIFTEEN_MINUTES; protected abstract void expire(); public ExpirableObject() { this(DEFAULT_LIFETIME); } public ExpirableObject(long timeToLive) { Expirer expirer = new Expirer(timeToLive); new Thread(expirer).start(); } private class Expirer implements Runnable { private long _timeToSleep; public Expirer (long timeToSleep){ _timeToSleep = timeToSleep; } public void run() { long obituaryTime = System.currentTimeMillis() + _timeToSleep; long timeLeft = _timeToSleep; while (timeLeft > 0) { try { timeLeft = obituaryTime - System.currentTimeMillis(); if (timeLeft > 0) { Thread.sleep(timeLeft); } } catch (InterruptedException ignored){} } expire(); } } } 

请参阅此链接以获得进一步改进。