2009-03-31 40 views
22

Tôi đang phát triển một ứng dụng web Java dựa trên hành vi của nó thông qua các tệp cấu hình XML lớn được tải từ một dịch vụ web. Vì các tệp này không thực sự cần thiết cho đến khi một phần cụ thể của ứng dụng được truy cập, chúng được tải một cách lười biếng. Khi một trong các tệp này được yêu cầu, truy vấn được gửi đến webservice để truy xuất tệp tương ứng. Vì một số tệp cấu hình có khả năng được sử dụng nhiều, thường xuyên hơn nhiều so với những người khác tôi muốn thiết lập một số loại bộ nhớ đệm (có thể là thời gian hết hạn 1 giờ) để tránh yêu cầu cùng một tệp nhiều lần.Ứng dụng web Java: Cách triển khai các kỹ thuật lưu bộ nhớ đệm?

Các tệp được dịch vụ web trả về giống nhau cho tất cả người dùng trên tất cả các phiên. Tôi không sử dụng JSP, JSF hay bất kỳ khung công tác ưa thích nào khác, chỉ là các servlet đơn giản.

Câu hỏi của tôi là, những gì được coi là thực tiễn tốt nhất để triển khai bộ nhớ cache tĩnh toàn cục như vậy trong một ứng dụng web java? Là một lớp singleton thích hợp, hoặc sẽ có những hành vi kỳ lạ do các container J2EE? Tôi có nên phơi bày điều gì đó ở đâu đó thông qua JNDI không? Tôi phải làm gì để bộ nhớ cache của tôi không bị vướng vào môi trường nhóm (OK, nhưng không cần thiết, để có một bộ nhớ cache cho mỗi máy chủ nhóm)?

Với thông tin ở trên, nó sẽ là một thực hiện chính xác để đặt một đối tượng chịu trách nhiệm về bộ nhớ đệm như là một thuộc tính ServletContext?

Lưu ý: Tôi không muốn tải tất cả chúng khi khởi động và được thực hiện với nó vì điều đó sẽ

1). quá tải webservice bất cứ khi nào ứng dụng của tôi khởi động
2). Các tệp có thể thay đổi trong khi ứng dụng của tôi đang chạy, vì vậy, tôi sẽ phải yêu cầu lại chúng
3). Tôi vẫn cần có bộ nhớ cache có thể truy cập toàn cầu, vì vậy câu hỏi của tôi vẫn giữ

Cập nhật: Sử dụng proxy caching (như mực) có thể là một ý tưởng hay, nhưng mỗi yêu cầu vào webservice sẽ gửi truy vấn XML khá lớn trong bài đăng Dữ liệu, có thể khác nhau mỗi lần. Chỉ có ứng dụng web thực sự biết rằng hai cuộc gọi khác nhau tới webservice thực sự là tương đương.

Nhờ sự giúp đỡ của bạn

Trả lời

11

Câu hỏi của bạn chứa một số câu hỏi riêng biệt cùng nhau. Hãy bắt đầu từ từ. ServletContext là nơi tốt, nơi bạn có thể lưu trữ xử lý vào bộ nhớ cache của bạn. Nhưng bạn trả tiền bằng cách có bộ nhớ cache cho mỗi trường hợp máy chủ. Nó không phải là vấn đề. Nếu bạn muốn đăng ký bộ nhớ cache trong phạm vi rộng hơn hãy xem xét đăng ký nó vào JNDI.

Sự cố khi lưu vào bộ nhớ cache. Về cơ bản, bạn đang truy xuất xml thông qua webservice. Nếu bạn đang truy cập dịch vụ web này qua HTTP, bạn có thể cài đặt máy chủ proxy HTTP đơn giản ở bên cạnh bạn, xử lý bộ nhớ đệm của xml. Bước tiếp theo sẽ là bộ nhớ đệm của xml đã giải quyết trong một số loại bộ nhớ cache đối tượng cục bộ. Bộ nhớ cache này có thể tồn tại trên mỗi máy chủ mà không có bất kỳ sự cố nào. Trong trường hợp thứ hai này, EHCache sẽ thực hiện công việc hoàn hảo. Trong trường hợp này, chuỗi xử lý sẽ như thế này Client - http request -> servlet -> look into local cache - if not cached -> look into http proxy (xml files) -> do proxy job (http to webservice).

Ưu điểm:

  • bộ nhớ cache địa phương mỗi trường hợp máy chủ, mà chỉ chứa các đối tượng từ XMLs yêu cầu
  • Một http proxy chạy trên cùng một phần cứng như webapp của chúng tôi.
  • Khả năng mở rộng ứng dụng web mà không cần thêm proxy http mới cho các tệp xml.

Nhược điểm:

  • mức tiếp theo của cơ sở hạ tầng
  • 1 điểm lỗi (http proxy)
  • triển khai phức tạp hơn

Cập nhật: đừng quên luôn gửi yêu cầu HEAD HTTP vào proxy để đảm bảo rằng bộ nhớ cache được cập nhật.

8

Lựa chọn # 1: Sử dụng một mã nguồn mở Thư viện Caching Chẳng hạn như EHCache

Đừng thực hiện bộ nhớ cache riêng của bạn khi có một số lựa chọn thay thế mã nguồn mở tốt mà bạn có thể ghé vào và bắt đầu sử dụng. Thực hiện bộ nhớ cache của riêng bạn phức tạp hơn nhiều so với hầu hết mọi người nhận ra và nếu bạn không biết chính xác những gì bạn đang thực hiện, bạn sẽ dễ dàng bắt đầu phát minh lại bánh xe và giải quyết một số vấn đề rất khó khăn.

Tôi khuyên bạn nên sử dụng EHCache theo giấy phép Apache. Bạn sẽ muốn xem the EHCace code samples.

Lựa chọn # 2: Sử dụng Squid

Một giải pháp dễ dàng hơn cho vấn đề của bạn sẽ được sử dụng Squid ... Đặt Squid ở giữa quá trình mà yêu cầu các dữ liệu được lưu trữ và hệ thống đưa ra yêu cầu : http://www.squid-cache.org/

+0

Cảm ơn bạn, nhưng điều đó không thực sự trả lời câu hỏi của tôi. Nếu tôi quyết định sử dụng EHCache, làm thế nào để tôi thiết lập nó trong một thùng chứa java ee để nó hoạt động bình thường trong các phiên làm việc, các rối loạn bộ nạp lớp khác nhau và trong môi trường nhóm? – LordOfThePigs

+0

Tôi không tin rằng Squid sẽ là giải pháp đúng đắn. Nó có vẻ rất thông minh, tuy nhiên tôi không tin rằng các dịch vụ web sẽ tôn trọng "nếu sửa đổi" – monksy

1

Sau khi thực hiện một số thao tác khác, có vẻ như cách dễ nhất để đạt được những gì tôi cần (trong các yêu cầu và giới hạn chấp nhận được mô tả trong câu hỏi) sẽ thêm đối tượng bộ nhớ đệm của tôi vào ngữ cảnh Servlet, và tìm kiếm nó (hoặc đi qua nó) khi cần thiết.

Tôi chỉ cần khởi tạo trình tải cấu hình của mình từ ServletContextListener và trong phương thức contextInitialized(), tôi chỉ lưu trữ nó vào ServletContext bằng ServletContext.setAttribute(). Sau đó, bạn dễ dàng tìm kiếm nó từ các servlet bằng cách sử dụng request.getSession(). GetServletContext(). GetAttribute().

Tôi cho rằng đây là cách thích hợp để thực hiện điều đó mà không giới thiệu mùa xuân hoặc bất kỳ khung tiêm phụ thuộc nào khác.

+0

Bạn có biết về bộ nhớ đệm ổi không? lấy hoặc lấy nếu không thiết lập? https://code.google.com/p/guava-libraries/wiki/CachesExplained – maress

35

Đây là ví dụ về bộ nhớ đệm với EhCache. Mã này được sử dụng trong một số dự án để triển khai bộ nhớ đệm đặc biệt.

1) Đặt bộ nhớ cache của bạn trong bối cảnh chung. (Đừng quên thêm người nghe vào 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) Lấy ví dụ bộ nhớ cache khi bạn cần nó. ví dụ: từ một servlet:

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

3) Truy vấn bộ nhớ cache ngay trước khi bạn làm một hoạt động tốn kém.

 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) Cũng đừng quên để làm mất hiệu lực đối tượng lưu trữ khi cần thiết.

Bạn có thể tìm thêm mẫu here

1

BREF, bạn có thể sử dụng sẵn sàng cấu hình này mùa xuân ehcache

1- ehcache.xml: hiển thị cấu hình toàn cầu của 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: tạo EhCacheManagerFactoryBean và 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- Tiêm "myCache" đậu trong lớp doanh nghiệp của bạn, xem các dụ sau đây để bắt đầu với nhận và đưa một đối tượng trong bộ nhớ cache của bạn.

@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

Tôi không gặp bất kỳ vấn đề nào khi đặt đối tượng được lưu trong bộ nhớ cache bên trong ServletContext. Đừng quên 2 tùy chọn khác (phạm vi yêu cầu, phạm vi phiên) với các phương thức setAttributes của các đối tượng này. Bất cứ điều gì được hỗ trợ tự nhiên bên trong webcontainers và j2ee phục vụ là tốt (tốt, tôi có nghĩa là nó là nhà cung cấp independed, và không có nặng j2ee librarires như mùa xuân). Yêu cầu lớn nhất của tôi là các máy chủ được thiết lập và chạy trong 5-10 giây.

Tôi thực sự không thích tất cả giải pháp lưu vào bộ nhớ cache, thật dễ dàng để làm cho nó hoạt động trên máy cục bộ và khó làm cho nó hoạt động trên các máy sản xuất. EHCACHE, Infinispan vv .. Trừ khi bạn cần sao chép/phân phối rộng cluster, tích hợp chặt chẽ với hệ sinh thái Java, bạn có thể sử dụng REDIS (NOSQL datatabase) hoặc nodejs ... Bất cứ điều gì với giao diện HTTP sẽ làm. Đặc biệt

Caching có thể được thực sự dễ dàng, và đây là giải pháp tinh khiết java (không khung):

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

Vui lòng tham khảo link này để cải thiện hơn nữa.

Các vấn đề liên quan