当前位置: 首页 > wzjs >正文

嘉兴网站制作百度seo词条优化

嘉兴网站制作,百度seo词条优化,他达拉非说明书,公司网页设计思路在分布式系统架构中,服务间的调用与通信是最基础的需求。Apache Dubbo 作为国内流行的高性能微服务框架,为服务调用提供了丰富的负载均衡策略,帮助系统实现流量合理分配、服务高可用和性能优化。本文将深入解析 Dubbo 负载均衡的实现原理&…

在分布式系统架构中,服务间的调用与通信是最基础的需求。Apache Dubbo 作为国内流行的高性能微服务框架,为服务调用提供了丰富的负载均衡策略,帮助系统实现流量合理分配、服务高可用和性能优化。本文将深入解析 Dubbo 负载均衡的实现原理,结合源码分析其核心机制,并通过实际案例展示如何在项目中合理运用这些策略。

一、Dubbo 负载均衡概述

1.1 负载均衡在微服务中的作用

在微服务架构下,一个服务通常会部署多个实例来提高系统的可用性和处理能力。当消费者调用服务时,如何从多个提供者实例中选择一个合适的实例进行调用,就是负载均衡需要解决的问题。合理的负载均衡策略可以:

  • 避免单点压力过大,提高系统整体稳定性
  • 优化资源利用,提升系统吞吐量
  • 实现服务的高可用,当某个实例故障时能自动切换到其他实例

1.2 Dubbo 负载均衡的基本概念

Dubbo 的负载均衡是在客户端实现的,消费者客户端维护了一份提供者列表,每次调用时会根据配置的负载均衡策略从列表中选择一个提供者。Dubbo 提供了多种负载均衡策略,默认使用随机负载均衡。

Dubbo 负载均衡的实现基于 SPI (Service Provider Interface) 机制,这使得我们可以方便地扩展自定义的负载均衡策略。

二、Dubbo 负载均衡核心源码解析

2.1 负载均衡接口设计

Dubbo 负载均衡的核心接口是org.apache.dubbo.rpc.cluster.LoadBalance,定义如下

@SPI(RandomLoadBalance.NAME)
public interface LoadBalance {/*** 从多个invokers中选择一个* * @param invokers 服务提供者列表* @param url 消费者url* @param invocation 调用信息* @return 选中的提供者*/@Adaptive("loadbalance")<T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
}

这是一个典型的 SPI 接口,默认实现是RandomLoadBalance。接口中定义了select方法,用于从多个服务提供者中选择一个。

2.2 抽象实现类

Dubbo 提供了一个抽象实现类AbstractLoadBalance,实现了一些公共逻辑:

public abstract class AbstractLoadBalance implements LoadBalance {/*** Calculate the weight according to the uptime proportion of warmup time* the new weight will be within 1(inclusive) to weight(inclusive)** @param uptime the uptime in milliseconds* @param warmup the warmup time in milliseconds* @param weight the weight of an invoker* @return weight which takes warmup into account*/// 计算预热期的权重static int calculateWarmupWeight(int uptime, int warmup, int weight) {// 预热期权重计算公式:uptime / warmup * weightint ww = (int) ( uptime / ((float) warmup / weight));return ww < 1 ? 1 : (Math.min(ww, weight));}@Overridepublic <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {if (CollectionUtils.isEmpty(invokers)) {return null;}// 如果只有一个提供者,直接返回if (invokers.size() == 1) {return invokers.get(0);}// 由子类实现的具体选择逻辑return doSelect(invokers, url, invocation);}protected abstract <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation);/*** Get the weight of the invoker's invocation which takes warmup time into account* if the uptime is within the warmup time, the weight will be reduce proportionally** @param invoker    the invoker* @param invocation the invocation of this invoker* @return weight*///计算权重的方法int getWeight(Invoker<?> invoker, Invocation invocation) {// 获取服务提供者配置的权重int weight = invoker.getUrl().getMethodParameter(invocation.getMethodName(), WEIGHT_KEY, DEFAULT_WEIGHT);if (weight > 0) {// 获取服务提供者启动时间long timestamp = invoker.getUrl().getParameter(REMOTE_TIMESTAMP_KEY, 0L);if (timestamp > 0L) {// 计算服务提供者运行时间int uptime = (int) (System.currentTimeMillis() - timestamp);// 获取预热时间int warmup = invoker.getUrl().getParameter(WARMUP_KEY, DEFAULT_WARMUP);// 如果服务运行时间小于预热时间,则计算预热后的权重if (uptime > 0 && uptime < warmup) {weight = calculateWarmupWeight(uptime, warmup, weight);}}}return Math.max(weight, 0);}
}

这个抽象类实现了两个重要功能

  1. 权重计算:考虑了服务预热期,避免新启动的服务因为性能未完全发挥而承受过大压力
  2. 基础选择逻辑:如果只有一个提供者直接返回,否则调用子类的doSelect方法

2.3 四种负载均衡策略实现

Dubbo 内置了四种负载均衡策略,下面分别解析它们的实现原理和源码。

2.3.1 随机负载均衡 (RandomLoadBalance)

随机负载均衡是 Dubbo 的默认策略,它根据权重随机选择提供者。实现代码如下:

@Activate(group = {Constants.PROVIDER, Constants.CONSUMER}, value = Constants.LOADBALANCE_KEY)
public class RandomLoadBalance extends AbstractLoadBalance {public static final String NAME = "random";@Overrideprotected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {int length = invokers.size(); // 提供者数量boolean sameWeight = true; // 是否所有提供者权重相同int[] weights = new int[length]; // 每个提供者的权重int firstWeight = getWeight(invokers.get(0), invocation);weights[0] = firstWeight;int totalWeight = firstWeight; // 总权重for (int i = 1; i < length; i++) {int weight = getWeight(invokers.get(i), invocation);weights[i] = weight;totalWeight += weight; // 累加总权重if (sameWeight && weight != firstWeight) {sameWeight = false; // 如果有不同权重,标记为false}}// 如果总权重大于0且权重不完全相同if (totalWeight > 0 && !sameWeight) {// 随机生成一个[0, totalWeight)之间的数int offset = ThreadLocalRandom.current().nextInt(totalWeight);// 根据随机数确定提供者for (int i = 0; i < length; i++) {offset -= weights[i];if (offset < 0) {return invokers.get(i);}}}// 如果总权重为0或所有提供者权重相同,随机选择一个return invokers.get(ThreadLocalRandom.current().nextInt(length));}
}

随机负载均衡的核心思想是:

  • 计算所有提供者的总权重

  • 如果权重不同,根据权重比例随机选择

  • 如果权重相同或总权重为 0,简单随机选择

2.3.2 轮询负载均衡 (RoundRobinLoadBalance)

轮询负载均衡按顺序依次选择提供者,实现代码如下:

@Activate(group = {Constants.PROVIDER, Constants.CONSUMER}, value = Constants.LOADBALANCE_KEY)
public class RoundRobinLoadBalance extends AbstractLoadBalance {public static final String NAME = "roundrobin";// 保存每个方法的轮询状态private final ConcurrentMap<String, AtomicPositiveInteger> sequences = new ConcurrentHashMap<String, AtomicPositiveInteger>();@Overrideprotected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();int length = invokers.size();// 获取当前方法的最大权重int maxWeight = 0;// 获取当前方法的最小权重int minWeight = Integer.MAX_VALUE;final LinkedHashMap<Invoker<T>, IntegerWrapper> invokerToWeightMap = new LinkedHashMap<Invoker<T>, IntegerWrapper>();int weightSum = 0;// 计算每个提供者的权重,并找出最大和最小权重for (int i = 0; i < length; i++) {int weight = getWeight(invokers.get(i), invocation);maxWeight = Math.max(maxWeight, weight);minWeight = Math.min(minWeight, weight);if (weight > 0) {invokerToWeightMap.put(invokers.get(i), new IntegerWrapper(weight));weightSum += weight;}}// 获取当前方法的轮询序列AtomicPositiveInteger sequence = sequences.get(key);if (sequence == null) {sequences.putIfAbsent(key, new AtomicPositiveInteger());sequence = sequences.get(key);}// 当前序列值int currentSequence = sequence.getAndIncrement();// 如果权重不相等且总权重大于0,使用加权轮询if (maxWeight > 0 && minWeight < maxWeight) {int mod = currentSequence % weightSum;for (int i = 0; i < maxWeight; i++) {for (Map.Entry<Invoker<T>, IntegerWrapper> each : invokerToWeightMap.entrySet()) {final Invoker<T> k = each.getKey();final IntegerWrapper v = each.getValue();if (mod == 0 && v.getValue() > 0) {return k;}if (v.getValue() > 0) {v.decrement();mod--;}}}}// 如果权重相等或总权重为0,使用普通轮询return invokers.get(currentSequence % length);}// 用于包装权重值的内部类private static final class IntegerWrapper {private int value;public IntegerWrapper(int value) {this.value = value;}public int getValue() {return value;}public void setValue(int value) {this.value = value;}public void decrement() {this.value--;}}
}

轮询负载均衡的核心思想是:

  • 维护一个方法级别的轮询序列

  • 如果权重不同,按权重比例进行轮询

  • 如果权重相同,简单按顺序轮询

这种策略适合服务提供者性能相近的场景,但如果提供者性能差异较大,可能导致性能好的提供者资源利用率不足。

2.3.3 最少活跃调用数负载均衡 (LeastActiveLoadBalance)

最少活跃调用数负载均衡会选择当前活跃调用数最少的提供者,如果有多个提供者的活跃调用数相同,则使用随机策略选择。实现代码如下:

@Activate(group = {Constants.PROVIDER, Constants.CONSUMER}, value = Constants.LOADBALANCE_KEY)
public class LeastActiveLoadBalance extends AbstractLoadBalance {public static final String NAME = "leastactive";@Overrideprotected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {int length = invokers.size();// 最小活跃数int leastActive = -1;// 最小活跃数的提供者数量int leastCount = 0;// 最小活跃数的提供者索引int[] leastIndexes = new int[length];// 每个提供者的权重int[] weights = new int[length];// 最小活跃数提供者的总权重int totalWeight = 0;// 第一个最小活跃数提供者的权重int firstWeight = 0;// 是否所有最小活跃数提供者的权重相同boolean sameWeight = true;// 找出最小活跃数的提供者for (int i = 0; i < length; i++) {Invoker<T> invoker = invokers.get(i);// 获取当前提供者的活跃数int active = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()).getActive();// 获取当前提供者的权重int afterWarmup = getWeight(invoker, invocation);weights[i] = afterWarmup;// 如果是第一个提供者或者当前提供者的活跃数更小if (leastActive == -1 || active < leastActive) {// 更新最小活跃数leastActive = active;// 更新最小活跃数提供者数量leastCount = 1;// 记录当前提供者索引leastIndexes[0] = i;// 重置总权重totalWeight = afterWarmup;// 记录第一个提供者权重firstWeight = afterWarmup;// 重置权重相同标志sameWeight = true;} // 如果当前提供者的活跃数与最小活跃数相同else if (active == leastActive) {// 记录当前提供者索引leastIndexes[leastCount++] = i;// 累加总权重totalWeight += afterWarmup;// 判断权重是否相同if (sameWeight && i > 0 && afterWarmup != firstWeight) {sameWeight = false;}}}// 如果只有一个最小活跃数的提供者,直接返回if (leastCount == 1) {return invokers.get(leastIndexes[0]);}// 如果有多个最小活跃数的提供者,且权重不相同if (!sameWeight && totalWeight > 0) {// 根据权重随机选择int offsetWeight = ThreadLocalRandom.current().nextInt(totalWeight);for (int i = 0; i < leastCount; i++) {int leastIndex = leastIndexes[i];offsetWeight -= weights[leastIndex];if (offsetWeight < 0) {return invokers.get(leastIndex);}}}// 如果有多个最小活跃数的提供者,且权重相同,随机选择return invokers.get(leastIndexes[ThreadLocalRandom.current().nextInt(leastCount)]);}
}

最少活跃调用数负载均衡的核心思想是:

  • 维护每个提供者的活跃调用数(正在处理的请求数)

  • 优先选择活跃调用数最少的提供者

  • 如果有多个提供者活跃调用数相同,则根据权重随机选择

这种策略能更好地反映提供者的实际负载情况,活跃调用数少的提供者往往处理能力更强或负载更低,因此能更合理地分配请求。

2.3.4 一致性哈希负载均衡 (ConsistentHashLoadBalance)

一致性哈希负载均衡可以保证相同参数的请求总是发到同一提供者,实现代码如下:

@Activate(group = {Constants.PROVIDER, Constants.CONSUMER}, value = Constants.LOADBALANCE_KEY)
public class ConsistentHashLoadBalance extends AbstractLoadBalance {public static final String NAME = "consistenthash";// 方法名到一致性哈希选择器的映射private final ConcurrentMap<String, ConsistentHashSelector<?>> selectors = new ConcurrentHashMap<String, ConsistentHashSelector<?>>();@SuppressWarnings("unchecked")@Overrideprotected <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;// 获取invokers的标识数int identityHashCode = System.identityHashCode(invokers);// 获取一致性哈希选择器ConsistentHashSelector<T> selector = (ConsistentHashSelector<T>) selectors.get(key);// 如果选择器不存在或者invokers列表已变更,创建新的选择器if (selector == null || selector.identityHashCode != identityHashCode) {selectors.put(key, new ConsistentHashSelector<T>(invokers, methodName, identityHashCode));selector = (ConsistentHashSelector<T>) selectors.get(key);}// 使用选择器选择提供者return selector.select(invocation);}private static final class ConsistentHashSelector<T> {// 虚拟节点到提供者的映射private final TreeMap<Long, Invoker<T>> virtualInvokers;// 每个提供者的虚拟节点数private final int replicaNumber;// invokers列表的标识哈希值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();// 获取虚拟节点数,默认为160this.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) {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<T> select(Invocation invocation) {// 将参数转换为字符串String key = toKey(invocation.getArguments());// 计算参数的MD5值byte[] digest = md5(key);// 计算哈希值,选择提供者return selectForKey(hash(digest, 0));}private String toKey(Object[] args) {StringBuilder buf = new StringBuilder();for (int i : argumentIndex) {if (i >= 0 && i < args.length) {buf.append(args[i]);}}return buf.toString();}private Invoker<T> selectForKey(long hash) {// 查找第一个大于等于该哈希值的虚拟节点Map.Entry<Long, Invoker<T>> 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] & 0xFF) << 24)| ((long) (digest[2 + number * 4] & 0xFF) << 16)| ((long) (digest[1 + number * 4] & 0xFF) << 8)| (digest[0 + number * 4] & 0xFF))& 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();}}
}

一致性哈希负载均衡的核心思想是:

  • 为每个提供者创建多个虚拟节点,并将它们分布在一个哈希环上

  • 根据请求的参数计算哈希值,找到哈希环上对应的位置

  • 顺时针找到环上的第一个虚拟节点,该节点对应的提供者即为选中的提供者

这种策略适合有状态服务,如缓存服务,能保证相同参数的请求总是发到同一提供者,提高缓存命中率。

三、Dubbo 负载均衡实践指南

3.1 不同场景下的策略选择建议

  1. 随机负载均衡 (RandomLoadBalance):适用于大多数场景,实现简单且能在长时间内达到均衡效果。

  2. 轮询负载均衡 (RoundRobinLoadBalance):适合服务提供者性能相近的场景,但如果性能差异较大,可能导致资源分配不均。

  3. 最少活跃调用数负载均衡 (LeastActiveLoadBalance):适合服务提供者性能差异较大的场景,能更好地反映提供者的实际负载情况。

  4. 一致性哈希负载均衡 (ConsistentHashLoadBalance):适合有状态服务,如缓存服务,能保证相同参数的请求总是发到同一提供者。

3.2自定义负载均衡策略

Dubbo 的 SPI 机制使得我们可以方便地扩展自定义的负载均衡策略。步骤如下:

  1. 创建自定义负载均衡类,实现LoadBalance接口:
public class MyLoadBalance implements LoadBalance {@Overridepublic <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {// 实现自己的负载均衡逻辑// ...}
}

  1. META-INF/dubbo目录下创建org.apache.dubbo.rpc.cluster.LoadBalance文件,内容为:
myloadbalance=com.example.MyLoadBalance
  1. 使用自定义负载均衡策略:
<dubbo:reference interface="com.example.UserService" loadbalance="myloadbalance" />
    http://www.dtcms.com/wzjs/148842.html

    相关文章:

  1. 做租车行网站优化的意思
  2. 建筑工程网站哪个好买链接网站
  3. java script 做网站网站关键词排名优化推广软件
  4. 英文网站 正文字体大小网络营销的种类有哪些
  5. 专业建设家电维修网站公司市场营销的八个理论
  6. 网络广告营销的好处seo按照搜索引擎的
  7. 免费购物平台都有哪些广州seo团队
  8. wordpress mediaseo网站诊断方案
  9. 品牌网站建设坚持大蝌蚪广告推广平台网站
  10. 建设门户网站申请网页制作工具有哪些
  11. 程序员免费自学网站百度做免费推广的步骤
  12. 商业空间设计案例ppt模板seo外包推广
  13. 珠海免费网站建设上海平台推广的公司
  14. 多用户智能网站建设源码湖南seo快速排名
  15. wordpress主题高仿雷锋网广州seo优化外包服务
  16. 无锡装修公司做网站新站seo快速排名 排名
  17. 做定制的网站宁波seo推广联系方法
  18. 做设计的网站商家入驻免费注册个人网站不花钱
  19. 杭州做网站找力果升华网络推广软件
  20. 政府网站建设发展规划中山口碑seo推广
  21. 营销型网站建设页面关键词是指什么
  22. 德州做网站建设的公司哪家好微信seo排名优化软件
  23. 网站广告代理如何做免费永久个人域名注册
  24. 企业网站建立流程的第一步是什么网络服务商电话
  25. 遵义建设厅网站官网建立网站
  26. 网站后台管理水印怎么做黄金网站软件app大全下载
  27. 做企业网站怎么接活全球搜效果怎么样
  28. 有哪些做西点及烘焙的网站制作网站费用
  29. 小型购物网站模板网站制作的费用
  30. 网站建设公司济南2022磁力链接搜索引擎推荐