2009-03-31 16 views
22

मैं एक जावा वेब एप्लिकेशन विकसित कर रहा हूं जो इसे वेब सेवा से लोड की गई बड़ी XML कॉन्फ़िगरेशन फ़ाइलों के माध्यम से व्यवहार करता है। चूंकि इन फ़ाइलों को वास्तव में तब तक आवश्यक नहीं किया जाता है जब तक कि एप्लिकेशन के किसी विशेष भाग तक पहुंचा नहीं जाता है, वे आलसी लोड होते हैं। जब इन फ़ाइलों में से एक की आवश्यकता होती है, तो संबंधित फ़ाइल को पुनर्प्राप्त करने के लिए webservice को एक क्वेरी भेजी जाती है। चूंकि कुछ कॉन्फ़िगरेशन फ़ाइलों को अधिक से अधिक उपयोग करने की संभावना है, अन्य लोगों की तुलना में अधिक बार मैं एक ही फ़ाइल को अनुरोध करने से बचने के लिए किसी प्रकार की कैशिंग (शायद 1 घंटे की समाप्ति समय के साथ) सेट करना चाहता हूं।जावा वेब एप्लीकेशन: कैशिंग तकनीकों को कैसे कार्यान्वित करें?

वेब सेवा द्वारा लौटाई गई फाइलें सभी सत्रों के सभी उपयोगकर्ताओं के लिए समान हैं। मैं जेएसपी, जेएसएफ या किसी अन्य फैंसी ढांचे का उपयोग नहीं करता, सिर्फ सादे सर्वलेट्स।

मेरा सवाल है, जावा वेब एप्लिकेशन के भीतर ऐसे वैश्विक, स्थैतिक कैश को लागू करने के लिए सबसे अच्छा अभ्यास क्या माना जाता है? क्या सिंगलटन वर्ग उचित है, या जे 2 ईई कंटेनरों के कारण अजीब व्यवहार होगा? क्या मुझे जेएनडीआई के माध्यम से कहीं कुछ खुलासा करना चाहिए? मैं क्या करूँगा ताकि क्लस्टर वातावरण में मेरा कैश खराब न हो (यह ठीक है, लेकिन जरूरी नहीं है, प्रति क्लस्टर सर्वर पर एक कैश है)?

उपरोक्त सूचनाओं को देखते हुए, क्या यह एक ऑब्जेक्ट को ServletContext विशेषता के रूप में कैशिंग के लिए ज़िम्मेदार रखने के लिए एक सही कार्यान्वयन होगा?

नोट: मैं स्टार्टअप पर उन सभी को लोड करने के लिए और क्योंकि यह के साथ किया जा नहीं करना चाहती है कि होगा

1)। जब भी मेरा एप्लिकेशन शुरू होता है तब webservice को अधिभारित करें
2)। मेरे एप्लिकेशन चलते समय फ़ाइलें बदल सकती हैं, इसलिए मुझे उन्हें
3) की आवश्यकता होगी। मुझे अभी भी एक वैश्विक रूप से सुलभ कैश की आवश्यकता होगी, इसलिए मेरा प्रश्न अभी भी

अद्यतन: कैशिंग प्रॉक्सी (जैसे स्क्विड) का उपयोग करना एक अच्छा विचार हो सकता है, लेकिन webservice के लिए प्रत्येक अनुरोध पोस्ट में बड़ी XML क्वेरी भेज देगा डेटा, जो हर बार अलग हो सकता है। केवल वेब एप्लिकेशन वास्तव में जानता है कि webservice के लिए दो अलग-अलग कॉल वास्तव में समकक्ष हैं।

अपने मदद के लिए धन्यवाद

उत्तर

11

आपके प्रश्न में एक साथ कई अलग-अलग प्रश्न हैं। आइए धीरे-धीरे शुरू करें। ServletContext एक अच्छी जगह है जहां आप अपने कैश में हैंडल स्टोर कर सकते हैं। लेकिन आप प्रति सर्वर उदाहरण कैश करके भुगतान करते हैं। यह कोई समस्या नहीं होनी चाहिए। यदि आप विस्तृत सीमा में कैश पंजीकृत करना चाहते हैं तो इसे जेएनडीआई में पंजीकृत करने पर विचार करें।

कैशिंग के साथ समस्या। असल में, आप webservice के माध्यम से एक्सएमएल पुनर्प्राप्त कर रहे हैं। यदि आप HTTP के माध्यम से इस webservice को accesing कर रहे हैं तो आप अपने पक्ष में सरल HTTP प्रॉक्सी सर्वर स्थापित कर सकते हैं जो xml की कैशिंग को संभालता है। अगला चरण कुछ प्रकार के स्थानीय ऑब्जेक्ट कैश में हल किए गए XML की कैशिंग होगी। यह कैश बिना किसी समस्या के प्रति सर्वर मौजूद हो सकता है। इस दूसरे मामले में ईएचसीएच सही काम करेगा। इस मामले में प्रसंस्करण की श्रृंखला इस 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 के रूप में एक ही हार्डवेयर पर चलने से ही ऑब्जेक्ट सम्मिलित हैं।
  • एक्सएमएल फाइलों के लिए नए http प्रॉक्सी जोड़ने के बिना वेबैप स्केल करने की संभावना।

विपक्ष:

  • बुनियादी ढांचे के अगले स्तर
  • +1 विफलता के बिंदु (http प्रॉक्सी)
  • अधिक जटिल तैनाती

अद्यतन: करने के लिए हमेशा के लिए मत भूलना कैश अद्यतित है यह सुनिश्चित करने के लिए प्रॉक्सी में HTTP HEAD अनुरोध भेजें।

8

विकल्प # 1: इस तरह के ehcache के रूप में एक ओपन सोर्स कैशिंग पुस्तकालय का उपयोग करें

अपने खुद के कैश को लागू न करें जब वहाँ अच्छा खुला स्रोत विकल्पों की एक संख्या हैं कि आप ड्रॉप और उपयोग शुरू कर सकते हैं। अपने स्वयं के कैश को कार्यान्वित करना अधिकांश लोगों के एहसास से कहीं अधिक जटिल है और यदि आपको पता नहीं है कि आप wrt threading क्या कर रहे हैं तो आप आसानी से पहिया को फिर से शुरू करना शुरू कर देंगे और कुछ मुश्किल समस्याओं का समाधान करेंगे।

मैं ईएचसीएच की सिफारिश करता हूं कि यह अपाचे लाइसेंस के तहत है। आप the EHCace code samples पर एक नज़र डालना चाहेंगे।

विकल्प # 2: उपयोग विद्रूप

एक तुम्हारी समस्या को और भी आसान समाधान विद्रूप उपयोग करने के लिए ... प्रक्रिया है कि डेटा कैश हो जाने और प्रणाली अनुरोध करने का अनुरोध करता है के बीच में रख विद्रूप होगा : http://www.squid-cache.org/

+0

धन्यवाद, लेकिन यह वास्तव में मेरे प्रश्न का उत्तर नहीं देता है। यदि मैं ईएचसीएच का उपयोग करने का फैसला करता हूं, तो मैं इसे जावा ई कंटेनर के अंदर कैसे सेट करूं ताकि यह सत्रों, विभिन्न क्लासलोडर गड़बड़ी और क्लस्टर वातावरण में ठीक से काम करे? – LordOfThePigs

+0

मुझे विश्वास नहीं है कि स्क्विड सही समाधान होगा। यह बहुत चालाक लगता है, हालांकि मुझे विश्वास नहीं है कि webservices "अगर संशोधित" का सम्मान करेंगे – monksy

1

कुछ और खुद को देखने के बाद, ऐसा लगता है कि मुझे जो चाहिए (प्राप्त करने की आवश्यकताओं और स्वीकार्य सीमाओं के भीतर) प्राप्त करने का सबसे आसान तरीका, मेरे कैशिंग ऑब्जेक्ट को सर्वलेट संदर्भ में जोड़ना होगा, और इसे देखकर (या इसे चारों ओर गुजरना) जहां आवश्यक हो।

मैं बस अपने कॉन्फ़िगरेशन लोडर को ServletContextListener से तुरंत चालू करता हूं, और संदर्भ में प्रारंभिक() विधि के भीतर, मैं इसे ServletContext.setAttribute() का उपयोग करके ServletContext में संग्रहीत करता हूं। Request.getSession()। GetServletContext()। GetAttribute() प्राप्त करते हुए स्वयं servlets से इसे देखना आसान है।

मुझे लगता है कि यह वसंत या किसी अन्य निर्भरता इंजेक्शन ढांचे के बिना इसे करने का उचित तरीका है।

+0

क्या आप अमरूद कैश के बारे में जानते हैं? सेटअप नहीं होने पर प्राप्त करें या पुनर्प्राप्त करें? https://code.google.com/p/guava-libraries/wiki/CachesExplained – maress

35

यहां एह कैश के साथ कैशिंग का एक उदाहरण दिया गया है। विज्ञापन कोड कैशिंग को लागू करने के लिए इस कोड का उपयोग कई परियोजनाओं में किया जाता है।

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) कैश उदाहरण पुनः प्राप्त जब आप इसकी आवश्यकता है। एक सर्वलेट से अर्थात:

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) इसके अलावा जब उचित कैश की गई वस्तुओं रद्द करने के लिए मत भूलना।

आप अधिक नमूने here

1

Bref पा सकते हैं, तो आप इस के लिए तैयार वसंत ehcache विन्यास

1- ehcache.xml उपयोग कर सकते हैं: ehcache की वैश्विक विन्यास दिखा।

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xsi:noNamespaceSchemaLocation="./ehcache.xsd" updateCheck="false" monitoring="autodetect" dynamicConfig="true" name="myCacheManager"> 

    <!-- 
    see ehcache-core-*.jar/ehcache-fallback.xml for description of elements 
    Attention: most of those settings will be overwritten by hybris 
    --> 
    <diskStore path="java.io.tmpdir"/> 

</ehcache> 

2- ehcache-spring.xml: EhCacheManagerFactoryBean और EhCacheFactoryBean पैदा करते हैं।

<bean id="myCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" 
     scope="singleton"> 
     <property name="configLocation" value="ehcache.xml" /> 
     <property name="shared" value="true" /> 

    </bean> 

<bean id="myCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean" scope="singleton"> 
     <property name="cacheManager" ref="myCacheManager" /> 
     <property name="cacheName" value="myCache" /> 
     <property name="maxElementsInMemory" value="1000" /> 
     <property name="maxElementsOnDisk" value="1000" /> 
     <property name="eternal" value="false" /> 
     <property name="diskPersistent" value="true" /> 
     <property name="timeToIdle" value="600" /> 
     <property name="timeToLive" value="1200" /> 
     <property name="memoryStoreEvictionPolicy" value="LRU" /> 
     <property name="statisticsEnabled" value="true" /> 
     <property name="sampledStatisticsEnabled" value="true" /> 
    </bean> 

3- इंजेक्षन अपने बिजनेस क्लास में "myCache" सेम, निम्नलिखित उदाहरण हो रही है और अपने कैश में एक वस्तु डाल के साथ आरंभ करने के लिए देखें।

@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; 

} 
0

मुझे ServletContext के अंदर कैश किए गए ऑब्जेक्ट उदाहरण को डालने में कोई समस्या नहीं थी। इस ऑब्जेक्ट्स के setAttributes विधियों के साथ अन्य 2 विकल्प (अनुरोध स्कोप, सत्र स्कोप) को न भूलें। वेबकैंटेनर्स और जे 2 ई सर्विसर्स के अंदर मूल रूप से समर्थित कुछ भी अच्छा है (अच्छा मतलब है कि यह विक्रेता विक्रेता है, और वसंत की तरह भारी जे 2 लाइब्रेरीज़ के बिना)। मेरी सबसे बड़ी आवश्यकता यह है कि सर्वर 5-10 सेकेंड में उठते हैं और चलते हैं।

मैं वास्तव में सभी कैशिंग समाधान को नापसंद करता हूं, यह स्थानीय मशीन पर काम करना इतना आसान है, और इसे उत्पादन मशीनों पर काम करना मुश्किल है। EHCACHE, Infinispan आदि .. जब तक आप क्लस्टर व्यापक प्रतिकृति/वितरण की आवश्यकता नहीं है, जावा पारिस्थितिक तंत्र के साथ कड़ाई से एकीकृत, आप RedIS (NOSQL डेटाबेस) या nodejs का उपयोग कर सकते हैं ... HTTP इंटरफेस के साथ कुछ भी करेगा। विशेष रूप से

कैशिंग वास्तव में आसान हो सकता है, और यहाँ शुद्ध जावा समाधान (कोई चौखटे) है:

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(); 
    } 
    } 
} 

आगे सुधार के लिए इस link को देखें।

संबंधित मुद्दे