2

我正在考虑引入某种缓存机制(如 HTML5 本地存储),以尽可能避免频繁的 RPC 调用。我想获得有关如何在下面的代码中引入缓存而不改变大部分架构(如使用 gwt-dispatch)的反馈。

void getData() {

    /* Loading indicator code skipped */

    /* Below is a gwt-maven plugin generated singleton for SomeServiceAsync */
    SomeServiceAsync.Util.getInstance().getDataBySearchCriteria(searchCriteria, new AsyncCallback<List<MyData>>() {

        public void onFailure(Throwable caught) {
            /* Loading indicator code skipped */
            Window.alert("Problem : " + caught.getMessage());
        }

        public void onSuccess(List<MyData> dataList) {
            /* Loading indicator code skipped */
        }
    });
}

我能想到的一种处理方法是有一个自定义 MyAsyncCallback 类定义 onSuccess/onFailure 方法,然后做这样的事情 -

void getData() {

    AsyncCallback<List<MyData>> callback = new MyAsyncCallback<List<MyData>>;

    // Check if data is present in cache
    if(cacheIsPresent)
        callback.onSuccess(dataRetrievedFromCache);
    else
    //  Call RPC and same as above and of course, update cache wherever appropriate
}

除此之外,我还有一个问题。流行浏览器的 LocalStorage 可用存储的最大大小是多少?浏览器如何管理不同应用程序/URL 的 LocalStorage?任何指针将不胜感激。

4

3 回答 3

2

我建议添加一个处理缓存的委托类。委托类可能如下所示:

public class Delegate {

  private static SomeServiceAsync service = SomeServiceAsync.Util.getInstance();

  private List<MyData> data;

  public static void getData(Callback callback) {
    if (date != null) {
      callback.onSuccess(data);
    } else {
      service.getData(new Callback() {
        public onSuccess(List<MyData> result) {
          data = result;
          callback.onSuccess(result);
      });
    }
  }

}

当然这是一个粗略的示例,您必须改进代码以使其可靠。

于 2012-07-30T05:55:40.120 回答
1

我确实花了太长时间才决定使用哈希映射来缓存结果。

我的策略不是使用单例哈希图,而是使用单例通用对象类存储静态缓存实例。我没有看到加载具有过多哈希树分支级别的单个哈希图的原因。

减少哈希解析量

如果我知道我正在处理的对象是 Employee、Address、Project,我将创建三个静态哈希

final static private Map<Long, Employee> employeeCache =
  new HashMap<Long, Employee>();
final static private Map<Long, Address> addressCache =
 new HashMap<Long, Address>();
final static private Map<String name, Project> projectCache =
 new HashMap<String name, Project>();

public static void putEmployee(Long id, Employee emp){
  employeeCache.put(id, emp);
}
public static Employee getEmployee(Long id){
  return employeeCache.get(id);
}

public static void putEmployee(Long id, Address addr){
  addressCache.put(id, addr);
}
public static Address getEmployee(Long id){
  return addressCache.get(id);
}

public static void putProject(String name, Address addr){
  projectCache.put(name, addr);
}
public static Address getProject(String name){
  return projectCache.get(name);
}

把它全部放在一张地图上会很麻烦。有效访问和存储数据的原则是 - 您确定的有关数据的信息越多,您就越应该利用您拥有的信息来隔离该数据。它将降低访问数据所需的哈希分辨率级别。更不用说需要完成的所有风险和不确定的类型转换。

如果可以,请避免散列

如果您知道 CurrentEmployee 和 NextEmployee 始终只有一个值,请避免将它们存储在 Employee 的散列中。只需创建静态实例

Employee CurrentEmployee, NextEmployee;

这将完全避免需要任何哈希解析。

避免污染全局命名空间

如果可能,将它们保留为类实例而不是静态实例,以避免污染全局命名空间。

为什么要避免污染全局命名空间?因为,由于全局命名空间混淆,多个类会无意中使用相同的名称,从而导致无数错误。

将缓存保持在最接近预期或使用位置的位置

如果可能,如果缓存主要用于某个类,请将缓存保留为该类中的类实例。并为另一个类需要从该缓存中获取数据的任何罕见实例提供事件总线事件。

这样你就会有一个预期的模式

ZZZManager.getZZZ(id);

如果可能,完成缓存

否则/并通过提供推杆和吸气剂将其私有化。不要让另一个类无意中重新实例化缓存,特别是如果有一天你的类变成了一个通用实用程序库。putter 和 getter 也有机会通过直接向缓存提供缓存无法处理的键或值来验证请求,以避免请求清除缓存或将应用程序推入异常。

将这些原则转化为 Javascript 本地存储

GWT 页面说

明智地使用命名约定有助于处理存储数据。例如,在名为 MyWebApp 的 Web 应用程序中,与名为 Stock 的 UI 表中的行关联的键值数据可能具有前缀为 MyWebApp.Stock 的键名。

因此,用相当粗略的代码补充你的类中的 HashMap,

public class EmployeePresenter {
  Storage empStore = Storage.getLocalStorageIfSupported();
  HashMap<Long, Employee> employeeCache;

  public EmployeePresenter(){
    if (empStore==null) {
      employeeCache = new HashMap<Employee>();
    }
  }

  private String getPrefix(){
    return this.getClass()+".Employee";
    //return this.getClass().getCanonicalName()+".Employee";
  }

  public Employee putEmployee(Long id, Employee employee)
    if (empStore==null) {
      stockStore.setItem(getPrefix()+id, jsonEncode(employee));
      return;
    }
    employeeCache.put(id, employee);
  }

  public Employee getEmployee(Long id)
    if (empStore==null) {
      return (Employee) jsonDecode(Employee.class, stockStore.getItem(getPrefix()+id));
    }
    return employeeCache(id);
  }
}

由于 localstore 仅基于字符串,我假设您将编写自己的 json 编码器解码器。另一方面,为什么不直接将 json 从回调中接收到 store 中呢?

内存限制?

我不能自称在这个问题上的专业知识,但我预测哈希图的答案是浏览器上操作系统限制的最大内存。减去浏览器、插件和 javascript 等已经消耗的所有内存等开销。

对于 HTML5 本地存储,GWT 页面说

“LocalStorage:每个浏览器每个应用程序 5MB。根据 HTML5 规范,用户可以在需要时增加此限制;但是,只有少数浏览器支持此功能。”

“SessionStorage:仅受系统内存限制”

于 2012-07-30T06:05:11.817 回答
0

由于您使用的是 gwt-dispath,因此这里的一个简单解决方案是将 gwt-dispatch Response 对象与 Request 对象再次缓存为 Map 中的键。它易于实现并且与类型无关。您将需要覆盖 Request - equals() 方法以查看请求是否已在缓存中。如果是,则从缓存返回响应,否则通过调用访问服务器。

IMO - 如果您只需要在会话缓存中以提高性能,则此处不需要 LocalStorage。本地存储仅适用于离线应用程序。

你可以看看这个 - http://turbomanage.wordpress.com/2010/07/12/caching-batching-dispatcher-for-gwt-dispatch/

于 2012-07-30T16:18:43.947 回答