Dubbo一致性哈希遇到的坑
访问量:
关于dubbo的负载均衡策略已经有太多blog介绍了,这里说一下我在使用dubbo一致性哈希负载均衡策略时候遇到的一个坑。
在使用dubbo2.8.4版本consistenthash策略的时候,我们根据用户uid做哈希,理论上同一个用户的访问应该打到同一个provider节点上, 但实际上我们发现不同consumer节点过来的同一个uid的请求并不能打到同一个provider节点上。这是因为dubbo2.8.4版本 的一致哈希策略存在问题。如何解决?
-
dubbo2.8.4实际上是2015年的一个老版本,对应的是当当的开源项目dubbox,我们可以将其升级为dubbo2.6.x(看着是版本号减小啊, 实际上是升级,感兴趣的可以研究一下dubbo版本号~),dubbo2.6.x已经修复了这个问题,但是问题是 实际升级并不会如想象般简单,由于dubbo服务众多,而且版本的不兼容(比如序列化/反序列化的不兼容),升级可能会困难重重。
-
另一种方法是我们实现dubbo的LoadBalance接口,提供一个新的一致哈希实现,我们先看一下dubbo2.8.4的一致性哈希的实现问题在哪。
public ConsistentHashSelector(List<Invoker<T>> invokers, String methodName, int identityHashCode) { this.virtualInvokers = new TreeMap<Long, Invoker<T>>(); this.identityHashCode = System.identityHashCode(invokers); URL url = invokers.get(0).getUrl(); this.replicaNumber = url.getMethodParameter(methodName, "hash.nodes", 160); String[] index = Constants.COMMA_SPLIT_PATTERN.split(url.getMethodParameter(methodName, "hash.arguments", "0")); argumentIndex = new int[index.length]; for (int i = 0; i < index.length; i ++) { argumentIndex[i] = Integer.parseInt(index[i]); } for (Invoker<T> invoker : invokers) { for (int i = 0; i < replicaNumber / 4; i++) { byte[] digest = md5(invoker.getUrl().toFullString() + i); for (int h = 0; h < 4; h++) { long m = hash(digest, h); virtualInvokers.put(m, invoker); } } } }
上面代码中通过md5计算digest的时候使用的invoker的url实际包含了和provider相关的参数”&pid=”以及和时间相关的参数”×tamp=”参数, 所以fix这个问题最直接的方法就是去掉这些参数影响,只要稍作修改即可。
byte[] digest = md5(invoker.getUrl().getAddress() + i);
源码中的ConsistentHashLoadBalance全部实现保持不变,但我们将类名修改为ConsistentHashLoadBalance2
public class ConsistentHashLoadBalance2 extends AbstractLoadBalance {
...
}
然后我们在resources/META-INF/dubbo中定义com.alibaba.dubbo.rpc.cluster.LoadBalance文件,内容为
consistenthash2=xxx=com.xxx.ConsistentHashLoadBalance2
这样只要在consumer侧引入相应的修改,配置loadblance=”consistenthash2”即可。