- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
负载均衡(Load Balance),简称 LB,就是将并发的用户请求通过规则后平衡、分摊到多台服务器上进行执行,以此达到压力分摊、数据并行的效果。常见的算法也有许多随机、轮询、加权等,今天我们就使用 C# 来实现这几种算法,并讲解在实际项目中的使用。
负载均衡算法在开发层面,使用场景其实并不多。通常在项目重构、转型、上线大版本新功能等,为了避免上线出现 Bug 应用功能 100% 的挂掉。可以在程序中使用负载均衡,将部分 HTTP 流量,打入项目中新的功能模块,然后进行监控,出现问题可以及时进行调整。
这样 AB 测试的场景,也可以在运维或者网关等其他层面实现流量分配。但现实是大多数公司项目因为一些原因没有这样的支持,这时开发就可以在项目中使用代码进行实现。
有这样一个需求,电商系统中,有一个预估运费的微服务(ShippingCharge )。此时上面领导来了需求,预估运费要改版,开发预估了一下改动不小。经过两周的奋斗 ShippingCharge 需求终于开发测试好了,此时要上线,但是掐指一算,万一有问题不就死翘翘了,而且还和钱相关。
此时负载均衡算法就派上用场了,我们可以让 10% 的流量打入这次的改动,可以先进行监控,可以再全部切过来。实际项目中,使用的肯定是权重的,后面随机、轮询也简单进行介绍一下其实现。
假设在改动 ShippingCharge 时,没有修改旧的功能,是在 controller 下面,对 call business 层换成了这次需求的,这样我们就可以使用负载均衡,让 10% 的流量打入新的 business,其余的依然走老的 business。
这里不会说的太精细,会将核心实现代码做介绍,实际项目中使用需要自己进行一下结合,举一反三哈
下面定义了一个 ServiceCenterModel 主要用作承载需要负载均衡的对象信息,可以是 call 下游的 url,也可以是程序内的某一算法标识
public class ServiceCenterModel
{
/// <summary>
/// Service
/// 1. call 下游 server,可以放 url
/// 2. 在同一个程序内,可以放一个业务标识
/// </summary>
public string Service { get; set; }
public int Weight { get; set; }
}
随机算法的先对来讲,较为简单一些,主要根据 Random 与 ServiceList 的数量结合实现。如下:
/// <summary>
/// 随机
/// </summary>
public class RandomAlgorithm
{
/// <summary>
/// Random Function
/// </summary>
private static readonly Random random = new Random();
/// <summary>
/// serviceList
/// </summary>
/// <param name="serviceList">service url set</param>
/// <returns></returns>
public static string Get(List<ServiceCenterModel> serviceList)
{
if (serviceList == null)
return null;
if (serviceList.Count == 1)
return serviceList[0].Service;
// 返回一个小于所指定最大值的非负随机数
int index = random.Next(serviceList.Count);
string url = serviceList[index].Service;
return url;
}
}
模拟 10 次 http request,可以看到对OldBusiness、NewBusiness进行了随机的返回
public static void Main(string[] args)
{
// 模拟从配置中心读取 Service
var serviceList = new List<ServiceCenterModel>()
{
new ServiceCenterModel { Service ="OldBusiness"},
new ServiceCenterModel { Service ="NewBusiness"},
};
// 模拟 Http 请求次数
for (int i = 0; i < 10; i++)
{
Console.WriteLine(RandomAlgorithm.Get(serviceList));
}
}
轮询的实现思路,将每次读取 ServiceList 的 Index 放到静态全局变量中,当到 ServiceList 最后一个时从0开始读取。如下:
/// <summary>
/// 轮询
/// </summary>
public class PollingAlgorithm
{
private static Dictionary<string, int> _serviceDic = new Dictionary<string, int>();
private static SpinLock _spinLock = new SpinLock();
/// <summary>
/// Get URL From Service List
/// </summary>
/// <param name="serviceList">Service URL Set</param>
/// <param name="serviceName">Service Name</param>
/// <returns></returns>
public static string Get(List<ServiceCenterModel> serviceList, string serviceName)
{
if (serviceList == null || string.IsNullOrEmpty(serviceName))
return null;
if (serviceList.Count == 1)
return serviceList[0].Service;
bool locked = false;
_spinLock.Enter(ref locked);//获取锁
int index = -1;
if (!_serviceDic.ContainsKey(serviceName)) // Not Exist
_serviceDic.TryAdd(serviceName, index);
else
_serviceDic.TryGetValue(serviceName, out index);
string url = string.Empty;
++index;
if (index > serviceList.Count - 1) //当前索引 > 最新服务最大索引
{
index = 0;
url = serviceList[0].Service;
}
else
{
url = serviceList[index].Service;
}
_serviceDic[serviceName] = index;
if (locked) //释放锁
_spinLock.Exit();
return url;
}
}
模拟 10 次 http request,可以看到对OldBusiness、NewBusiness进行了轮询返回
public static void Main(string[] args)
{
// 模拟从配置中心读取 Service
var serviceList = new List<ServiceCenterModel>()
{
new ServiceCenterModel { Service ="OldBusiness"},
new ServiceCenterModel { Service ="NewBusiness"},
};
// 模拟 Http 请求次数
for (int i = 0; i < 10; i++)
{
Console.WriteLine(PollingAlgorithm.Get(serviceList, "ShippingChargeBusiness"));
}
}
权重的实现思路,将配置权重的 Service 按照数量放置在一个集合中,然后按照轮询的方式进行读取,需要注意的是这的 weight 只能配置大于 0 的整数。如下:
/// <summary>
/// 权重
/// </summary>
public class WeightAlgorithm
{
private static ConcurrentDictionary<string, WeightAlgorithmItem> _serviceDic = new ConcurrentDictionary<string, WeightAlgorithmItem>();
private static SpinLock _spinLock = new SpinLock();
public static string Get(List<ServiceCenterModel> serviceList, string serviceName)
{
if (serviceList == null)
return null;
if (serviceList.Count == 1)
return serviceList[0].Service;
bool locked = false;
_spinLock.Enter(ref locked);//获取锁
WeightAlgorithmItem weightAlgorithmItem = null;
if (!_serviceDic.ContainsKey(serviceName))
{
weightAlgorithmItem = new WeightAlgorithmItem()
{
Index = -1,
Urls = new List<string>()
};
BuildWeightAlgorithmItem(weightAlgorithmItem, serviceList);
_serviceDic.TryAdd(serviceName, weightAlgorithmItem);
}
else
{
_serviceDic.TryGetValue(serviceName, out weightAlgorithmItem);
weightAlgorithmItem.Urls.Clear();
BuildWeightAlgorithmItem(weightAlgorithmItem, serviceList);
}
string url = string.Empty;
++weightAlgorithmItem.Index;
if (weightAlgorithmItem.Index > weightAlgorithmItem.Urls.Count - 1) //当前索引 > 最新服务最大索引
{
weightAlgorithmItem.Index = 0;
url = serviceList[0].Service;
}
else
{
url = weightAlgorithmItem.Urls[weightAlgorithmItem.Index];
}
_serviceDic[serviceName] = weightAlgorithmItem;
if (locked) //释放锁
_spinLock.Exit();
return url;
}
private static void BuildWeightAlgorithmItem(WeightAlgorithmItem weightAlgorithmItem, List<ServiceCenterModel> serviceList)
{
serviceList.ForEach(service => //有几个权重就加几个实例
{
for (int i = 0; i < service.Weight; i++)
{
weightAlgorithmItem.Urls.Add(service.Service);
}
});
}
}
public class WeightAlgorithmItem
{
public List<string> Urls { get; set; }
public int Index { get; set; }
}
模拟 10 次 http request,可以看到对 OldBusiness 返回了 9 次,NewBusiness 返回了一次
public static void Main(string[] args)
{
// 模拟从配置中心读取 Service
var serviceList = new List<ServiceCenterModel>()
{
new ServiceCenterModel { Service ="OldBusiness",Weight = 9 },
new ServiceCenterModel { Service ="NewBusiness",Weight = 1 },
};
// 模拟 Http 请求次数
for (int i = 0; i < 10; i++)
{
Console.WriteLine(WeightAlgorithm.Get(serviceList, "ShippingChargeBusiness"));
}
}
在 Web 应用程序架构设计期间,我必须从概念上计算我的服务器之一可以服务多少个当前客户端。然后我可以预算它。 那么,有什么公式可以遵循吗?或者,你如何计算这个?或者,通常,一个 httpd/tomc
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 要求我们推荐或查找书籍、工具、软件库、教程或其他场外资源的问题对于 Stack Overflow 来说是
我正在使用 Angular 5,我正在尝试在加载 div 的背景图像时获取加载图标。 如果它是一个普通的 img,我对此没有问题,但如果我尝试将它作为背景,它就不起作用。 这里是一些示例代码 app.
我们怎么知道我们的程序在 CPU 上有多少负载? 我尝试使用 htop 找到它。但是 htop 不会给 cpu 负载。它实际上给出了我程序的 cpu 利用率(使用 pid)。 我正在使用 C 编程,L
我们发现从Spark 1.3到当前的Spark 2.0.1以来,从Oracle数据库使用Spark的API加载数据一直很慢。典型的代码在Java中是这样的: Map options =
我有时会收到 mnesia overloaded主要使用时的错误消息 async_dirty查询和 ram_copies表。所以为了了解发生了什么,我想获得更多关于 mnesia 状态的信息,例如每秒
对于通常使用很少 CPU 的程序来说,内核 CPU 非常高。 Linux 机器在状态之间交替。大多数时候,程序使用低 CPU 正常执行。在 CPU“激增”期间,程序使用 100% 可用 CPU 使用高
我正在使用 Raspberry Pi 2 来路由 wifi-eth 连接。因此,从 eth 方面来看,我有一台可以使用 Pi wifi 连接到互联网的计算机。在 Raspberry 上我启动 htop
基本上我有一个网页,其中有一个 iframe 可以从不同的域加载另一个网页。它移动得很慢,我想证明整个页面很慢只是因为 iframe 内的页面。 有什么方法可以测量总页面负载以及总页面负载中有多少%来
我们有一个基于 Spring 的应用程序,它充当使用其他 Rest API 的编排层。我只想测试这个组件的性能,而不测试正在使用的下游 api。 我正在寻找有关如何完成此操作的任何架构建议? 当前的方
我正在学习 hibernate 。为了进行测试,我使用无效 key 调用了 session.load 。当我在调试器(JB Idea)中跨过该行后,没有任何反应 - 我预计会得到 ObjectNotF
我正在开发一个小型的待办事项 PHP 应用程序。我正在使用 jQuery 构建 HTML。其中一个是一个按钮,用于启动一个模式,允许用户编辑该项目。我很好奇加载数据时更好的方法是什么: 1) 在初始加
我尝试在 twitch 播放器中使用 angular 作为覆盖标记。 我将 ng-repear 与(键,值)结合使用。 //player is here 设置是一个全局对象。但是当我尝试加载页面
我即将了解 C 语言中的特定进程如何在特定时间范围内加载 CPU。该进程可能会在运行时切换处理器核心,因此我也需要处理这个问题。 CPU为ARM处理器。 我研究了从标准顶部获取负载的不同方法,perf
这个问题在这里已经有了答案: XMLHttpRequest Origin null is not allowed Access-Control-Allow-Origin for file:/// t
您好,我正在用 Java 开发负载平衡算法。在我的系统中将有一个主节点和 n 个从节点。主节点将接收查询分发给它的从节点。但是在将查询分发到其从节点之一之前,我想测量从节点中的当前负载,以检查特定从节
我正在渲染由大约 50 万个三角形组成的相当重的对象。我使用 opengl 显示列表,在渲染方法中只调用 glCallList。我认为一旦图形基元被编译成显示列表,cpu 的工作就完成了,它只是告诉
我正在尝试加密 Sipdroid,为此我必须在 RTP 数据包获得编码的音频负载后对其进行加密。我在 RTP 数据包类中使用这个函数: public byte[] getPayload() {
我正在尝试解析以下 JSON 负载: { "results":[ [ 298.648132, 280.68692, 356.54
在动画期间 cpu 负载非常高(高达 75%) 是否有优化代码以降低 CPU 负载的方法? 我的代码: ImageView myImageView = (ImageView)findViewById(
我是一名优秀的程序员,十分优秀!