作者:大小大空间_566 | 来源:互联网 | 2023-10-12 20:28
ConsistentHashLoadBalance原理图第一步:基于网络地址hash构建虚拟一致性hash表获取接口与方法名每个方法构建一致性hash选择器通过选择器选择一个In
ConsistentHashLoadBalance 原理图 第一步: 基于网络地址hash构建虚拟一致性hash表
获取接口与方法名 每个方法构建一致性hash选择器 通过选择器选择一个Invoker
基于调用方法参数值hash获取hash结果
选择器选择Invoker的依据: 方法的参数值hash以及参与hash的方法参数数量 默认只有第一个参数的参数值参与hash
protected < T > Invoker < T > doSelect ( List < Invoker < T > > invokers, URL url, Invocation invocation) { 获取接口与方法名String methodName = RpcUtils . getMethodName ( invocation) ; String key = invokers. get ( 0 ) . getUrl ( ) . getServiceKey ( ) + "." + methodName; 每一个方法一个一致性hash选择器int invokersHashCode = invokers. hashCode ( ) ; ConsistentHashSelector < T > selector = ( ConsistentHashSelector < T > ) selectors. get ( key) ; if ( selector == null || selector. identityHashCode != invokersHashCode) { selectors. put ( key, new ConsistentHashSelector < T > ( invokers, methodName, invokersHashCode) ) ; selector = ( ConsistentHashSelector < T > ) selectors. get ( key) ; } 通过选择器选择一个Invoker return selector. select ( invocation) ; }
获取一致性hash选择器 获取方法配置的结点数,默认160 获取需要进行hash的参数数组索引,默认对第一个参数进行hash 构建一致性hash表,大小为replicaNumber*Invoker数量 private static final class ConsistentHashSelector < T > { private final TreeMap < Long , Invoker < T > > virtualInvokers; private final int replicaNumber; private final int identityHashCode; private final int [ ] argumentIndex; ConsistentHashSelector ( List < Invoker < T > > invokers, String methodName, int identityHashCode) { this . virtualInvokers = new TreeMap < Long , Invoker < T > > ( ) ; this . identityHashCode = identityHashCode; URL url = invokers. get ( 0 ) . getUrl ( ) ; 获取方法配置的结点数,默认160 this . replicaNumber = url. getMethodParameter ( methodName, HASH_NODES, 160 ) ; 获取需要进行hash的参数数组索引,默认对第一个参数进行String methodParameter = url. getMethodParameter ( methodName, HASH_ARGUMENTS, "0" ) ; String [ ] index = COMMA_SPLIT_PATTERN. split ( methodParameter) ; 构建参数hash的参数数组元信息argumentIndex = new int [ index. length] ; for ( int i = 0 ; i < index. length; i++ ) { argumentIndex[ i] = Integer . parseInt ( index[ i] ) ; } 构建一致性hash表for ( Invoker < T > invoker : invokers) { String address = invoker. getUrl ( ) . getAddress ( ) ; for ( int i = 0 ; i < replicaNumber / 4 ; i++ ) { byte [ ] digest = md5 ( address + i) ; for ( int h = 0 ; h < 4 ; h++ ) { long m = hash ( digest, h) ; virtualInvokers. put ( m, invoker) ; } } } }
负载实现 根据前面配置的argumentIndex,判断取几个参数进行一致性hash 默认argumentIndex 大小为1,数组值为0;表示取第一个参数进行hash public Invoker < T > select ( Invocation invocation) { 根据前面配置的argumentIndex, 判断取几个参数进行一致性hashString key = toKey ( invocation. getArguments ( ) ) ; byte [ ] digest = md5 ( key) ; return selectForKey ( hash ( digest, 0 ) ) ; } private String toKey ( Object [ ] args) { 默认argumentIndex 大小为1 , 数组值为0 表示取第一个参数进行hashStringBuilder buf = new StringBuilder ( ) ; for ( int i : argumentIndex) { if ( i >= 0 && i < args. length) { buf. append ( args[ i] ) ; } } return buf. toString ( ) ; }
根据hash值取virtualInvokers一致性hash表上的节点 兜底逻辑: 溢出则取第一个 private Invoker < T > selectForKey ( long hash) { Map. Entry < Long , Invoker < T > > entry = virtualInvokers. ceilingEntry ( hash) ; if ( entry == null ) { entry = virtualInvokers. firstEntry ( ) ; } return entry. getValue ( ) ; }