2

可能な限り頻繁な RPC 呼び出しを回避するために、ある種のキャッシュ メカニズム (HTML5 ローカル ストレージなど) を導入することを考えています。アーキテクチャの多くを変更せずに (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 */
        }
    });
}

これに対処するために私が考えることができる 1 つの方法は、onSuccess/onFailure メソッドを定義するカスタム MyAsyncCallback クラスを用意してから、次のようなことを行うことです -

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
}

これとは別に、もう1つ質問がありました。一般的なブラウザの 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 であることがわかっている場合、3 つの静的ハッシュを作成します。

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

すべてを 1 つのマップにまとめるのは大変です。データの効率的なアクセスと保存の原則は、データについて決定した情報が多ければ多いほど、その情報を使用してそのデータを分離して活用する必要があるということです。これにより、データへのアクセスに必要なハッシュ解決のレベルが低下します。実行する必要があるすべての危険で不明確な型キャストは言うまでもありません。

可能であればハッシュ化を避ける

CurrentEmployee と NextEmployee の値が常に 1 つだけであることがわかっている場合は、それらを Employee のハッシュに格納しないでください。静的インスタンスを作成するだけ

Employee CurrentEmployee, NextEmployee;

これにより、ハッシュ解決がまったく必要なくなります。

グローバル名前空間の汚染を避ける

また、可能であれば、それらを静的インスタンスではなくクラス インスタンスとして保持し、グローバル名前空間を汚染しないようにします。

グローバル名前空間の汚染を避けるのはなぜですか? なぜなら、複数のクラスが誤って同じ名前を使用して、グローバルな名前空間の混乱が原因で数え切れないほどのバグが発生するからです。

予想される場所または使用される場所に最も近いキャッシュを保持する

可能であれば、キャッシュが主に特定のクラス用である場合は、キャッシュをそのクラス内のクラス インスタンスとして保持します。また、別のクラスがそのキャッシュからデータを取得する必要があるまれなインスタンスにイベントバス イベントを提供します。

期待できるパターンになるように

ZZZManager.getZZZ(id);

可能であればキャッシュをファイナライズし

それ以外の場合は、パターとゲッターを提供して民営化します。別のクラスが誤ってキャッシュを再インスタンス化することを許可しないでください。特に、ある日、クラスが一般的なユーティリティ ライブラリになる場合は注意してください。また、パターとゲッターは、キャッシュが処理できないキーまたは値をキャッシュに直接提示することにより、リクエストがキャッシュを一掃したり、アプリを例外にプッシュしたりすることを回避するために、リクエストを検証する機会があります。

これらの原則を 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 をコールバックから受け取った瞬間にストアに直接書き込んでみませんか?

メモリの制約?

この質問の専門知識を公言することはできませんが、ハッシュマップの答えは、ブラウザーの OS によって制限される最大メモリであると予測しています。ブラウザー、プラグイン、JavaScript などのオーバーヘッドによって既に消費されているすべてのメモリを差し引いたものです。

HTML5ローカルストレージの場合、GWTページには次のように記載されています

「LocalStorage: ブラウザごとのアプリごとに 5MB。HTML5 仕様によると、この制限は必要に応じてユーザーが増やすことができます。ただし、これをサポートするブラウザはごくわずかです。」

「SessionStorage: システム メモリによってのみ制限されます」

于 2012-07-30T06:05:11.817 に答える
0

gwt-dispathを使用しているので、ここでの簡単な解決策は、gwt-dispatchResponseオブジェクトをMapのキーとしてRequestオブジェクトに対してキャッシュすることです。実装と入力にとらわれないのは簡単です。Requestがすでにキャッシュにあるかどうかを確認するには、Request --equals()メソッドをオーバーライドする必要があります。はいの場合はキャッシュからの応答を返し、そうでない場合はサーバーに呼び出しをヒットします。

IMO-パフォーマンスのために必要なのがセッションキャッシュにある場合、LocalStorageはここでは必要ありません。ローカルストレージは、オフラインアプリにのみ必要です。

あなたはこれを調べるかもしれません-http://turbomanage.wordpress.com/2010/07/12/caching-batching-dispatcher-for-gwt-dispatch/

于 2012-07-30T16:18:43.947 に答える