负载均衡扩展接口重构

转于自己在公司的Blog:
[url]http://pt.alibaba-inc.com/wp/experience_1003/loadbalance_refactor.html[/url]

项目中的一个重构的过程及理由,用于知会团队成员,在这里备一个。

RPC远程调用框架中有很多可选的负载均衡策略,
比如:随机,轮循,最少连接等等,
这个时候就需要一个SPI扩展点,为后续增加新的策略提供可能,

[b]重构前:[/b]
原接口形式,如下:
[code]
public class LoadBalance<T> {

// 给定资源和权重,返回选中资源的下标号
int select(T[] resources, int[] weights);

}
[/code]

[b]问题一:[/b]
返回下标号,使接口输入输出不一致,并且限制了资源的包装,如:
[code]
public class LoadBalance {

// 给定资源和权重,返回选中资源
<T> T select(T[] resources, int[] weights);

}
[/code]

[b]问题二:[/b]
作为扩展接口,即然使用泛型,表示策略实现不限制资源类型,接口本身定义泛型没有意义,泛型声明应该定义在方法上,如:
[code]
public class LoadBalance {

// 给定资源和权重,返回选中资源
<T> T select(T[] resources, int[] weights);

}
[/code]

[b]问题三:[/b]
作为扩展接口,权重信息的传递过于特殊化,
比如现在最小连接数策略要用到当前活跃连接数,
需增加新的参数actives,如:
[code]
public class LoadBalance {

// 给定资源和权重,返回选中资源
<T> T select(T[] resources, int[] weights, int[] actives);

}
[/code]

[b]问题四:[/b]
但像上面这样,你并不清楚后续还有什么参数要加入,接口的契约不容扩展,
另一种办法是,在最小连接数策略实现中将T强制转型成RpcInvoker接口,然后调用getActive(),
但即然active数通过get方获取,为什么weight却通过另一个参数传入,明显的不一致,
而且这里的强制转型,也会导致策略的实现并不能像原来期望的通用,
作为一个框架的扩展点,通用意义并不大,越通用越难用,上面的泛型T有过度设计之嫌,
直接用RpcInvoker作为参数,更能保证契约的完备性,如:
[code]
public class LoadBalance {

// RpcInvoker接口中有getWeight(), getActive()等获取参数方法
// 给定资源和权重,返回选中资源
RpcInvoker select(RpcInvoker[] resources);

}
[/code]
如果有通用性需求,也可以考虑再抽取一个接口,如:
[code]
public class LoadBalance {

// 给定资源和权重,返回选中资源
Selectable select(Selectable[] resources);

}
[/code]

[b]问题五:[/b]
有一种需求是基于客户端一致性的,要求执行所有RpcInvoker,而不是选其中一个RpcInvoker执行,
原有实现,是将其作为特例,写死在代码中的,基于上面的LoadBalance接口,
完全可以将传入的所有RpcInvoker[]包装成一个总的RpcInvoker,里面用for循环委派所有调用。
如果有w + r > n(写节点 + 读节点 > 总节点)的一致性需求,也可以用相应方法处理,只是增加一些配置项,如:
[code]
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

}
[/code]

[b]问题六:[/b]
有的策略实现是带状态的,比如轮循策略需记录轮循序号,
也就是并不能单实例使用LoadBalance实例,
这对框架的维护非常不利,容易给后来的维护者埋下地雷,
而且,同一个resources集合,必需用同一个LoadBalance实例,
也就是LoadBalance实例的变更是跟随resources集合的变更,
即然如此,资源集合的设定,可以在select()之前确定,如:
[code]
public class LoadBalance {

// 初始化资源集合
void init(RpcInvoker[] resources);

// 返回选中资源
RpcInvoker select();

}
[/code]
这样的好处是,LoadBalance的实现可以在init()时做预处理及缓存,
比如随机策略,需要统计总权重,如果在init()方法中统计,
select()时可以减少一次for循环,
而且,可以通过重复调用init()方法,复用单一LoadBalance实例,
当然,LoadBalance的实现需确保线程安全性。

------------------------
Dubbo设计分享系列:
[url=http://javatar.iteye.com/blog/706098]一些设计上的基本常识[/url]
[url=http://javatar.iteye.com/blog/690845]谈谈泛化式扩展与组合式扩展[/url]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值