【问题标题】:Enabling caching for GWT RPC asynchronous calls为 GWT RPC 异步调用启用缓存
【发布时间】:2012-07-27 17:15:47
【问题描述】:

我正在考虑引入某种缓存机制(如 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?任何指针将不胜感激。

【问题讨论】:

    标签: gwt caching local-storage


    【解决方案1】:

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

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

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

    【讨论】:

      【解决方案2】:

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

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

      减少哈希解析量

      如果我知道我正在处理的对象是 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从回调中接收到存储中呢?

      内存限制?

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

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

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

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

      【讨论】:

      • HTML5 本地存储以字符串形式将未加密的数据保存在常规浏览器缓存中。这不是安全存储。它不应用于敏感数据,例如社会保险号、信用卡号、登录凭据等。
      【解决方案3】:

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

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

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

      【讨论】:

      • 正如我在问题中提到的,我为此探索 gwt-dispatch。
      • 即使没有 gwt-dispatch,您也可以很好地将这种方法应用于您的代码。在这种情况下,使用 HashMap 将 searchCriteria 对象存储为键,将 ListData 对象存储为值。这种方法非常通用,并且可以很好地扩展以缓存各种类型的数据请求和结果。您可以选择使用自己的 Cacheable vs. Result 接口来创建通用系统。
      • 是的,但这个问题与如何设计/实现缓存无关。更多的是关于阿德里安的事情。 B回答。还是谢谢。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多