負荷均衡拡張インターフェース再構成


自分の会社でのブログに移行します.
http://pt.alibaba-inc.com/wp/experience_1003/loadbalance_refactor.
プロジェクトの中の一つの再構成の過程と理由は、知会チームのメンバーに使用され、ここで一つを準備します.
RPC遠隔呼び出しフレームには多くのオプションの負荷均衡ポリシーがあります.
例えば:ランダム、サイクル、最小接続など、
この時はSPIの拡張点が必要です.今後新たな戦略を追加することが可能です.
再構成前:
元のインターフェースの形式は以下の通りです.

public class LoadBalance<T> {

	//        ,          
	int select(T[] resources, int[] weights);

}
問題1:
下記の符号を返して、インターフェースの入出力が一致しないようにして、資源の包装を制限しました.

public class LoadBalance {

	//        ,      
	<T> T select(T[] resources, int[] weights);

}
問題二:
拡張インターフェースとして、すなわち汎型を使用して、ポリシーがリソースタイプを制限しないことを実現することを表し、インターフェース自体が汎型を定義することは意味がない.

public class LoadBalance {

	//        ,      
	<T> T select(T[] resources, int[] weights);

}
問題三:
拡張インターフェースとしては、重み情報の伝達が特殊化しすぎ、
例えば、現在の最小接続数戦略は現在のアクティブ接続数を使います.
新しいパラメータのactivesを追加したいです.

public class LoadBalance {

	//        ,      
	<T> T select(T[] resources, int[] weights, int[] actives);

}
問題四:
上のように、後に何のパラメータが入るかは分かりません.インターフェースの契約は広がりません.
もう一つの方法は、最小接続数ポリシーの実現において、TをRpcInvokerインターフェースに強制的に変換し、次いでgetActive()を呼び出し、
しかし、activeの数はget側で得られます.なぜweightは他のパラメータを通して入ってきたのですか?明らかに違います.
また、ここでの強制的な転換は、従来の期待通りにはいかない戦略の実現にもつながります.
一つのフレームの拡張点としては、一般的な意味は大きくなく、通用すればするほど使いにくくなります.
直接RpcInvokerをパラメータとして使用すると、契約の完全性が保証されます.

public class LoadBalance {

	// RpcInvoker    getWeight(), getActive()       
	//        ,      
	RpcInvoker select(RpcInvoker[] resources);

}
共通性の需要があれば、もう一つのインターフェースを抽出することも考えられます.

public class LoadBalance {

	//        ,      
	Selectable select(Selectable[] resources);

}
問題5:
クライアントの一貫性に基づいて、RpcInvokerのいずれかを選択して実行するのではなく、すべてのRpcInvokerを実行する必要がある.
既存の実現は、それを特例として、コードの中に書き込んだもので、上のLoadBalanceインターフェースに基づいて、
導入されたRpcInvoker[]は全部RpcInvokerに包装できます.中はfor循環ですべての呼び出しを委任します.
w+r>n(書き込みノード+読み出しノード>全体ノード)の一貫性のある要求があれば、対応する方法で処理しても良いし、いくつかの構成項目を追加するだけでよい.

public class AllLoadBalance {

	public RpcInvoker select(RpcInvoker[] resources) {
		return new AllRpcInvoker(resources);
	}

}
class AllRpcInvoker implements RpcInvoker {

	private RpcInvoker[] invokers;
	
	public AllRpcInvoker(RpcInvoker[] invokers) {
		this.invokers = invokers;
	}
	
	// Delegate all methods to invokers
	
}
問題六:
ある策略の実現は状態があるので、例えばサイクル戦略は順番を記録しなければなりません.
つまり、インスタンスだけでLoadBalanceを使用することはできません.
これはフレームのメンテナンスに非常に不利で、後から来るメンテナに地雷を埋めやすいです.
また、同じresource集合は、同じLoadBalanceのインスタンスを使用しなければならない.
すなわち、LoadBalanceの例の変更は、resourceの集合に従う変更であり、
すなわち、リソースセットの設定は、select()の前に決定されてもよい.

public class LoadBalance {

	//        
	void init(RpcInvoker[] resources);

	//       
	RpcInvoker select();

}
このような利点は、LoadBalanceの実現はinit()の時に前処理とキャッシュをすることができ、
例えばランダムポリシーは、総重みを統計する必要があります.init()方法で統計すると、
select()の時に一回のforサイクルを減らすことができます.
また、単一のLoadBalanceの例は、init()方法を反復して呼び出すことによって多重化されてもよい.
もちろん、LoadBalanceの実現はスレッドの安全性を確保する必要があります.
--------------
Dubbo設計共有シリーズ:
いくつかの設計上の基本常識
汎化式の拡張と組合せ式の拡張を話します。