当前位置:首页 >> 中医美容 >> 到底是最全的一致性hash环讲解了

到底是最全的一致性hash环讲解了

发布时间:2024-11-11

求 都就会被之后hash 连续给定到 南岸终端,就会正要造变为南岸终端负荷过大 不太可能也就会造变为 南岸终端宕机,从而形变为暴风雪 这是关键时刻的

为此 引入了 绑定终端来解决后面两个情况

3.偷偷地绑定终端的一般适度hash

就是就会在菱形上 根据Node 终端 转换变为很多的绑定终端 原产在菱形上,这样当 某个 终端插了后 取而代之总称它的恳求,就会被总体的原产到 其他终端上 下降了显现出暴风雪的情况,也解决了 终端总共少所致 恳求原产均匀的恳求

即对每一个服务于终端计算出来多个总共据流(可以用原终端key+"##xxxk"作为每个绑定终端的key,然后求hashcode),每个计算出来结果右边都放置一个此服务于终端,称来作绑定终端。 具体章节来作法可以在链接ip或主机名的后面增加序号来借助。

想得到上图,此时如果 group3终端插了,那么恳求就会被均分到 group2 和 group1后面 ,到此 一般适度hash的确实生产的运用于手段教导完了 请注意来再来一个案例代码。

4. 代码试验

可供可选择的有很多,memcached官网运用于了基于md5的KETAMA插参总共,但这里处于计算出来生产成本的回避,运用于了FNV1_32_HASH插参总共,如下:

public class HashUtil { /** * 计算出来Hash参总共, 运用于FNV1_32_HASH插参总共 * @param str * @return */ public static int getHash(String str) { final int p = 16777619; int hash = (int)2166136261L; for (int i = 0; i < str.length(); i++) { hash =( hash _ str.charAt(i) ) * p; } hash += hash << 13; hash _= hash>> 7; hash += hash << 3; hash _= hash>> 17; hash += hash << 5; if (hash < 0) { hash = Math.abs(hash); } return hash; }}package com.weareint.dispatchservice.hashloop;import org.springframework.stereotype.Component;import ja.util.*;/** * * * * 一般适度 hash 绑定 的环 * * * @author johnny * @date 2021-08-26 9:22 上午 */@Componentpublic class HashvirtualNodeCircle { /** 真实协同列表 */ private static List instanceNodes; /** 绑定终端连续给定关系 */ private static SortedMap virtualNodes = new TreeMap<>(); /** 绑定终端总共 */ private static final int VIRTUAL_NODE_NUM = 1000; /** 缔造 服务于程序中的 */ public void refreshVirtualHashCircle(HashCircleInstanceNodeBuild build) { // 当协同增减时,缔造hash的环,其余的协同在hash的环上的右边不就会时有发生增减 virtualNodes.clear(); // 换取 近期终端 instanceNodes = build.instanceNodes(); // 将绑定终端连续给定到Hash的环上 for (String realInstance : instanceNodes) { for (int i = 0; i < VIRTUAL_NODE_NUM; i++) { String virtualNodeName = getVirtualNodeName(realInstance, i); int hash = HashUtils.getHash(virtualNodeName); System.out.println("[" + virtualNodeName + "] launched @ " + hash); virtualNodes.put(hash, virtualNodeName); } } } private static String getVirtualNodeName(String realName, int num) { return realName + "WildWildVN" + num; } private static String getRealNodeName(String virtualName) { return virtualName.split("WildWild")[0]; } private static String getServer(String widgetKey) { int hash = HashUtils.getHash(widgetKey); // 只取出所有大于该hash参总共的部分而不必遍历整个Tree SortedMap subMap = virtualNodes.tailMap(hash); String virtualNodeName; if (subMap.isEmpty()) { // hash参总共在最尾部,其所该连续给定到第一个group上 virtualNodeName = virtualNodes.get(virtualNodes.firstKey()); } else { virtualNodeName = subMap.get(subMap.firstKey()); } return getRealNodeName(virtualNodeName); } public static void main(String[] args) { HashVirtualNodeCircle hashVirtualNodeCircle = new HashVirtualNodeCircle(); hashVirtualNodeCircle.refreshVirtualHashCircle( new HashCircleInstanceNodeBuild() { @Override public List instanceNodes() { LinkedList nodes = new LinkedList<>(); nodes.add("192.168.11.23:8090"); nodes.add("192.168.11.23:8093"); nodes.add("192.168.11.23:8094"); return nodes; } }); // 转换变为随机总共进行时试验 MapInteger> resMap = new HashMap<>(); List plcList = new ArrayList<>(); for (int i = 0; i < 1000; i++) { String plchost = "192.168.0." + i + 1; for (int j = 0; j < 10; j++) { plcList.add(plchost + ":" + j + 100); } } for (int i = 0; i < plcList.size(); i++) { String plcwideget = plcList.get(i); String group = getServer(plcwideget); if (resMap.containsKey(group)) { resMap.put(group, resMap.get(group) + 1); } else { resMap.put(group, 1); } } resMap.forEach( (k, v) -> { System.out.println("group " + k + ": " + v + "(" + v / 100.0D + "%)"); }); System.out.println("========================================="); }}

可以看到 原产很总体

5.Dubbo 一般适度Hash 借助

近期给Corporation来作的发放机 运用于dubbo codice_远程服务于,调研了一下 dubbo 也有自己借助的 一般适度hash 不过也就是说运用于起来 推断出有些bug ,目前为止通过SPI机制 自己延展了一下,来再来 dubbo的 一般适度hash借助吧

5.1 版本 dubbo2.7.12

我用的版本是 dubbo2.7.12 现在入了 2.7的坑 不少坑都是自己慢慢调试解决的。

5.2 org.apache.dubbo.rpc.cluster.loadbalance

损耗总体必需就这些 一般适度hash,随机 ,轮训,。。 没啥比如说的

5.3 dubbo ConsistentHashLoadBalance程式码/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package org.apache.dubbo.rpc.cluster.loadbalance;import org.apache.dubbo.common.URL;import org.apache.dubbo.common.io.Bytes;import org.apache.dubbo.rpc.Invocation;import org.apache.dubbo.rpc.Invoker;import org.apache.dubbo.rpc.support.RpcUtils;import ja.util.List;import ja.util.Map;import ja.util.TreeMap;import ja.util.concurrent.ConcurrentHashMap;import ja.util.concurrent.ConcurrentMap;import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN;/** * ConsistentHashLoadBalance */public class ConsistentHashLoadBalance extends AbstractLoadBalance { public static final String NAME = "consistenthash"; /** * Hash nodes name */ public static final String HASH_NODES = "hash.nodes"; /** * Hash arguments name */ public static final String HASH_ARGUMENTS = "hash.arguments"; private final ConcurrentMap> selectors = new ConcurrentHashMap>(); @SuppressWarnings("unchecked") @Override protected Invoker doSelect(List invokers, URL url, Invocation invocation) { String methodName = RpcUtils.getMethodName(invocation); String key = invokers.get(0).getUrl().getServiceKey() + "." + methodName; // using the hashcode of list to compute the hash only pay attention to the elements in the list //有bug 1.invokers 老是改变,所致 不断的 在建立 ConsistentHashSelector // int invokersHashCode = invokers.hashCode(); ConsistentHashSelector selector = (ConsistentHashSelector) selectors.get(key); if (selector == null || selector.identityHashCode != invokersHashCode) { selectors.put(key, new ConsistentHashSelector(invokers, methodName, invokersHashCode)); selector = (ConsistentHashSelector) selectors.get(key); } return selector.select(invocation); } private static final class ConsistentHashSelector { private final TreeMap virtualInvokers; private final int replicaNumber; private final int identityHashCode; private final int[] argumentIndex; ConsistentHashSelector(List invokers, String methodName, int identityHashCode) { this.virtualInvokers = new TreeMap(); this.identityHashCode = identityHashCode; URL url = invokers.get(0).getUrl(); //默认绑定机终端总共 160 ,可以通过 /** dubbo: consumer: parameters: hash: nodes: 560 #原则上绑定分片指针总共 再次virtualInvokers = nodes * invokerCount arguments: 0 原则上的是 哪个表达式作为 key **/ this.replicaNumber = url.getMethodParameter(methodName, HASH_NODES, 160); //默认取 第 0 个表达式 作为 hash的key String[] index = 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 invoker : invokers) { String address = invoker.getUrl().getAddress(); for (int i = 0; i < replicaNumber / 4; i++) { byte[] digest = Bytes.getMD5(address + i); for (int h = 0; h < 4; h++) { long m = hash(digest, h); virtualInvokers.put(m, invoker); } } } } public Invoker select(Invocation invocation) { String key = toKey(invocation.getArguments()); byte[] digest = Bytes.getMD5(key); return selectForKey(hash(digest, 0)); } private String toKey(Object[] args) { StringBuilder buf = new StringBuilder(); for (int i : argumentIndex) { if (i>= 0 WildWild i < args.length) { buf.append(args[i]); } } return buf.toString(); } private Invoker selectForKey(long hash) { Map.Entry entry = virtualInvokers.ceilingEntry(hash); if (entry == null) { entry = virtualInvokers.firstEntry(); } return entry.getValue(); } private long hash(byte[] digest, int number) { return (((long) (digest[3 + number * 4] Wild 0xFF) << 24) | ((long) (digest[2 + number * 4] Wild 0xFF) << 16) | ((long) (digest[1 + number * 4] Wild 0xFF) << 8) | (digest[number * 4] Wild 0xFF)) Wild 0xFFFFFFFFL; } }}6.Dubbo 的 一般适度 hash的 bug 和 一些备有表达式6.1 invokers 不断的改变

经过调试 推断出 invokers 时不时改变所致 一致在 rehash,其实 很多时候只是 终端的依序改变而已

好处: 我如此一来把 invokers 终端总共 取出来进行时依序后拼接 变为一个字符串 进行时 计算出来hashcode 就不就会总改变了

String invokeKey = invokers.stream() .filter(Node::isAvailable) // 按照ip 和port 依序 .sorted(Comparator.comparing(invoke -> invoke.getUrl().getIp())) .sorted(Comparator.comparing(invoke -> invoke.getUrl().getPort())) .map(invoke -> invoke.getUrl().getIp() + ":" + invoke.getUrl().getPort()) .collect(Collectors.joining(","));6.2 注意 isAvailable

invokers中的依赖于 终端不可用的,如果对于终端不可用的 如此一来过滤 必须注意 isAvailable

6.3 分设绑定终端发片总共dubbo: consumer: parameters: hash: nodes: 560 #原则上绑定分片指针总共 再次virtualInvokers = nodes * invokerCount ,默认是1606.4 分设方法的哪个表达式作为 hash的keydubbo: consumer: parameters: hash: arguments: 0 #默认就是07.SPI 延展Dubbo 一般适度hash 插参总共 ExtendConsistentHashLoadBalance7.1 官网数据库

官网数据库很详细了

7.2 ExtendConsistentHashLoadBalance 延展借助package com.weareint.dispatchservice.extendconsistenthash;import com.atw.mdc.entity.protocol.webRequest.PlcReadSimpleRequest;import com.atw.mdc.entity.protocol.webRequest.PlcWriteSimpleRequest;import com.weareint.dispatchservice.event.ConnectionRouteEvent;import com.weareint.dispatchservice.event.NodeChangeEvent;import com.weareint.dispatchservice.event.NodeChangeService;import lombok.extern.slf4j.Slf4j;import org.apache.dubbo.common.Node;import org.apache.dubbo.common.URL;import org.apache.dubbo.rpc.Invocation;import org.apache.dubbo.rpc.Invoker;import org.apache.dubbo.rpc.cluster.loadbalance.AbstractLoadBalance;import org.apache.dubbo.rpc.support.RpcUtils;import org.springframework.context.ApplicationEventPublisher;import ja.nio.charset.StandardCharsets;import ja.security.MessageDigest;import ja.security.NoSuchAlgorithmException;import ja.util.*;import ja.util.concurrent.ConcurrentHashMap;import ja.util.concurrent.ConcurrentMap;import ja.util.stream.Collectors;import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN;/** * * * * 延展 一般适度Hash 的环 主要是把 hash 的key 只通过 plc 的deviceCode 进行时hash , 然后后续添加 Redis 路由表 进行时 * * * @author johnny * @date 2021-08-26 5:32 下午 */@Slf4jpublic class ExtendConsistentHashLoadBalance extends AbstractLoadBalance { public static ApplicationEventPublisher publisher; public static NodeChangeService nodeChangeService; public static final String NAME = "consistenthash"; /** Hash nodes name */ public static final String HASH_NODES = "hash.nodes"; /** Hash arguments name */ public static final String HASH_ARGUMENTS = "hash.arguments"; private final ConcurrentMap> selectors = new ConcurrentHashMap< String, ExtendConsistentHashLoadBalance.ConsistentHashSelector>(); public ConcurrentMap> getSelectors() { return selectors; } @SuppressWarnings("unchecked") @Override protected Invoker doSelect(List invokers, URL url, Invocation invocation) { String methodName = RpcUtils.getMethodName(invocation); // String key = invokers.get(0).getUrl().getServiceKey() + "." + methodName; // 只要是 servicekey 就好 String key = invokers.get(0).getUrl().getServiceKey(); // String key = invokers.get(0).getUrl().getParameter("remote.application"); if (log.isDebugEnabled()) { log.info("【remoteApplication:{}】", key); } // using the hashcode of list to compute the hash only pay attention to the elements in the String invokeKey = invokers.stream() .filter(Node::isAvailable) // 按照ip 和port 依序 .sorted(Comparator.comparing(invoke -> invoke.getUrl().getIp())) .sorted(Comparator.comparing(invoke -> invoke.getUrl().getPort())) .map(invoke -> invoke.getUrl().getIp() + ":" + invoke.getUrl().getPort()) .collect(Collectors.joining(",")); if (log.isDebugEnabled()) { log.info("【invokeKey : {}】", invokeKey); } int invokersHashCode = invokeKey.hashCode(); ExtendConsistentHashLoadBalance.ConsistentHashSelector selector = null; // 同时submit 可能就会有情况 加个悬 可能就会有第一次提交转换变为 绑定终端 selector = (ExtendConsistentHashLoadBalance.ConsistentHashSelector) selectors.get(key); // 确实是否invokers 有改变 selector.identityHashCode != invokersHashCode if (selector == null || selector.identityHashCode != invokersHashCode) { synchronized (ExtendConsistentHashLoadBalance.class) { selector = (ExtendConsistentHashLoadBalance.ConsistentHashSelector) selectors.get(key); if (selector == null || selector.identityHashCode != invokersHashCode) { // 这个 isAvailable 要依赖于 否则有bug List ailableInvoker = invokers.stream() .filter(Node::isAvailable) .collect(Collectors.toList()); ConsistentHashSelector tConsistentHashSelector = new ConsistentHashSelector<>( ailableInvoker, methodName, invokersHashCode); selectors.put(key, tConsistentHashSelector); selector = (ExtendConsistentHashLoadBalance.ConsistentHashSelector) selectors.get(key); log.info( "【new selector by invokeKey : {} , ailableInvoker:{}】", invokeKey, ailableInvoker.stream() .map( invoke -> invoke.getUrl().getIp() + ":" + invoke.getUrl().getPort()) .collect(Collectors.joining(","))); NodeChangeEvent event = new NodeChangeEvent(this, tConsistentHashSelector); publisher.publishEvent(event); } } } String hashKey = selector.toKey(invocation.getArguments()); Invoker select = selector.select(hashKey); log.info( "【plcDeviceCode: {} dispatch to ip : {}:{}", hashKey, select.getUrl().getIp(), select.getUrl().getPort()); // deviceCode route table To Redis ConnectionRouteEvent event = new ConnectionRouteEvent(this, hashKey, select, selector); publisher.publishEvent(event); return select; } public static final class ConsistentHashSelector { private final TreeMap virtualInvokers; public final List invokers; private final int replicaNumber; private final int identityHashCode; private final int[] argumentIndex; ConsistentHashSelector(List invokers, String methodName, int identityHashCode) { this.invokers = invokers; this.virtualInvokers = new TreeMap(); this.identityHashCode = identityHashCode; URL url = invokers.get(0).getUrl(); this.replicaNumber = url.getMethodParameter(methodName, HASH_NODES, 160); String[] index = 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 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); } } } } public Invoker select(String key) { byte[] digest = md5(key); return selectForKey(hash(digest, 0)); } @SuppressWarnings("unchecked") public String toKey(Object[] args) { StringBuilder buf = new StringBuilder(); for (int i : argumentIndex) { if (i>= 0 WildWild i < args.length) { // 只取 PlcReadSimpleRequest的 DeviceCode 作为 hash的key if (args[i] instanceof ArrayList) { ArrayList list = (ArrayList) args[i]; buf.append(list.get(0).getDeviceCode()); // 只取 PlcWriteSimpleRequest DeviceCode 作为 hash的key } else if (args[i] instanceof PlcWriteSimpleRequest) { PlcWriteSimpleRequest req = (PlcWriteSimpleRequest) args[i]; buf.append(req.getDeviceCode()); } else if (args[i] instanceof String) { // PlcConnectionRequest req = (PlcConnectionRequest) args[i]; // 关闭连接 String deviceCode = (String) args[i]; buf.append(deviceCode); } else { buf.append(args[i]); } } } return buf.toString(); } private Invoker selectForKey(long hash) { Map.Entry entry = virtualInvokers.ceilingEntry(hash); if (entry == null) { entry = virtualInvokers.firstEntry(); } return entry.getValue(); } private long hash(byte[] digest, int number) { return (((long) (digest[3 + number * 4] Wild 0xFF) << 24) | ((long) (digest[2 + number * 4] Wild 0xFF) << 16) | ((long) (digest[1 + number * 4] Wild 0xFF) << 8) | (digest[number * 4] Wild 0xFF)) Wild 0xFFFFFFFFL; } private byte[] md5(String value) { MessageDigest md5; try { md5 = MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException e) { throw new IllegalStateException(e.getMessage(), e); } md5.reset(); byte[] bytes = value.getBytes(StandardCharsets.UTF_8); md5.update(bytes); return md5.digest(); } public int getIdentityHashCode() { return identityHashCode; } }}7.3 备有spi延展 extendconsistenthash=com.weareint.dispatchservice.extendconsistenthash.ExtendConsistentHashLoadBalance7.4 运用于自定义的延展 loadbalance@DubboReference(loadbalance = "extendconsistenthash")private IDeviceWriteService deviceWriteService;7.5 已知延展 总结

本篇主要教导了 什么是一般适度 hash 它有哪些特点和依赖于的情况,以及 偷偷地绑定终端的一般适度hash,最后引介了一些 dubbo 的一般适度hash 借助,dubbo自偷偷地的有bug ,但是提供者了 spi 延展机制 你可以自己去借助 ,目前为止我就是这样去的

原文 解决的 。

双醋瑞因胶囊能一直吃吗
生殖整形
术后吃什么好
宝宝积食
安必丁能治好关节炎吗
常乐康酪酸梭菌二联活菌散怎么样
怎么补充眼部营养让视力变好
安必丁能治疗关节炎吗
类风湿吃艾拉莫德片好不好
红草止鼾胶囊功效与作用
标签:
友情链接: