2.3 负载均衡
简介
Wing
内置的负载均衡器支持的算法有轮询
、加权轮询
、最小连接数
、一致性哈希
。
轮询:将请求按顺序轮流的分配到后端服务器上,它均衡的对待后端的每一台服务器,而不关心服务器实际的连接数和当前的系统负载。
加权轮询:不同的后端服务器可能机器的配置和当前系统的负载并不相同,因此它们的抗压能力也不相同。给配置高、负载低的机器配置更高的权重,让其处理更多的请求。而配置低、负载高的机器,给其分配较低的权重,降低其系统负载,加权轮询能很好地处理这一问题,并将请求顺序且按照权重分配到后端。
最小连接数:最小连接数算法比较灵活和智能,由于后端服务器的配置不尽相同,对于请求的处理有快有慢,它是根据后端服务器当前的连接情况,动态地选取其中当前积压连接数最少的一台服务器来处理当前的请求,尽可能地提高后端服务的利用效率,将请求合理地分流到每一台服务器。
一致性哈希:一致性哈希算法在1997年由麻省理工学院提出,是一种特殊的哈希算法,目的是解决分布式缓存的问题。 在移除或者添加一个服务器时,能够尽可能小地改变已存在的服务请求与处理请求服务器之间的映射关系。一致性哈希解决了简单哈希算法在分布式哈希表( Distributed Hash Table,DHT) 中存在的动态伸缩等问题。
为什么要使用内置的负载均衡器?
我们知道nginx
是实现服务器负载均衡的利器,但是,它针对的是服务端的负载均衡。即所有的请求到达nginx
后,由nginx
服务器进行请求路由的分发,从而实现负载均衡。而对于微服务,我们需要一种更简单、更高效的实现负载均衡的方式,那就是客户端(服务调用方)的负载均衡算法, Wing
内置的负载均衡器实现了该功能。即由客户端主动拉取注册中心的服务列表,然后通过负载均衡算法获取一个可用的服务实例发起请求,从而实现了微服务之间的负载均衡调用。
轮询
创建3个服务名都是
Wing.Demo_2.3
的Web Api项目,(点击查看完整示例代码2.3-1),(点击查看完整示例代码2.3-2),(点击查看完整示例代码2.3-3)3个服务都添加相同的负载均衡算法配置为
RoundRobin
{
"Consul": {
"Service": {
"LoadBalancer": {
"Option": "RoundRobin",
},
}
}
}
- 示例
2.3-1
Test方法代码
[HttpGet]
public string Test()
{
return "我是示例 2.3-1";
}
- 示例
2.3-2
Test方法代码
[HttpGet]
public string Test()
{
return "我是示例 2.3-2";
}
- 示例
2.3-3
Test方法代码
[HttpGet]
public string Test()
{
return "我是示例 2.3-3";
}
- 创建调用服务
Wing.Demo_2.3
的Web Api项目(点击查看完整示例代码2.2),添加LoadBalance方法,对服务Wing.Demo_2.3
发起3次请求,代码如下:
private static int count1 = 0;
private static int count2 = 0;
private static int count3 = 0;
[HttpGet]
public void LoadBalance()
{
Parallel.For(0, 3, async x =>
{
await _serviceFactory.InvokeAsync("Wing.Demo_2.3", async serviceAddr =>
{
var client = _httpClientFactory.CreateClient();
client.BaseAddress = new Uri(serviceAddr.ToString());
var response = await client.GetAsync("/WeatherForecast/Test");
if (serviceAddr.Port == 2311)
{
Interlocked.Increment(ref count1);
}
else if (serviceAddr.Port == 2312)
{
Interlocked.Increment(ref count2);
}
else
{
Interlocked.Increment(ref count3);
}
_logger.LogInformation($"请求示例2.3-1次数:{count1},请求示例2.3-2次数:{count2},请求示例2.3-3次数:{count3}");
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
_logger.LogInformation($"第{x + 1}次请求,结果:{await response.Content.ReadAsStringAsync()}");
}
else
{
_logger.LogInformation($"第{x + 1}次请求,状态码:{response.StatusCode}");
}
});
});
}
- 分别启动被调用的示例服务
2.3-1
、2.3-2
、2.3-3
,启动调用服务2.2
,浏览器访问 http://localhost:2210/weatherforecast/loadbalance ,可以看到我们发起的3次请求中,3个服务各被调用1次
加权轮询
参照轮询示例
示例
2.3-1
配置权重为1
{
"Consul": {
"Service": {
"LoadBalancer": {
"Option": "WeightRoundRobin",
//权重
"Weight": 1
},
}
}
}
- 示例
2.3-2
配置权重为2
{
"Consul": {
"Service": {
"LoadBalancer": {
"Option": "WeightRoundRobin",
//权重
"Weight": 2
},
}
}
}
- 示例
2.3-3
配置权重为3
{
"Consul": {
"Service": {
"LoadBalancer": {
"Option": "WeightRoundRobin",
//权重
"Weight": 3
},
}
}
}
- 示例
2.2
LoadBalance方法调整为发起6次请求
private static int count1 = 0;
private static int count2 = 0;
private static int count3 = 0;
[HttpGet]
public void LoadBalance()
{
Parallel.For(0, 6, async x =>
{
await _serviceFactory.InvokeAsync("Wing.Demo_2.3", async serviceAddr =>
{
var client = _httpClientFactory.CreateClient();
client.BaseAddress = new Uri(serviceAddr.ToString());
var response = await client.GetAsync("/WeatherForecast/Test");
if (serviceAddr.Port == 2311)
{
Interlocked.Increment(ref count1);
}
else if (serviceAddr.Port == 2312)
{
Interlocked.Increment(ref count2);
}
else
{
Interlocked.Increment(ref count3);
}
_logger.LogInformation($"请求示例2.3-1次数:{count1},请求示例2.3-2次数:{count2},请求示例2.3-3次数:{count3}");
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
_logger.LogInformation($"第{x + 1}次请求,结果:{await response.Content.ReadAsStringAsync()}");
}
else
{
_logger.LogInformation($"第{x + 1}次请求,状态码:{response.StatusCode}");
}
});
});
}
- 分别启动被调用的示例服务
2.3-1
、2.3-2
、2.3-3
,接着启动调用服务2.2
,浏览器访问 http://localhost:2210/weatherforecast/loadbalance ,可以看到我们发起的6次请求中,示例2.3-1
权重为1的被调用1次,示例2.3-2
权重为2的被调用2次,示例2.3-3
权重为3的被调用3次,如下图:
最小连接数
参照轮询示例
3个服务都添加相同的负载均衡算法配置
LeastConnection
{
"Consul": {
"Service": {
"LoadBalancer": {
"Option": "LeastConnection"
},
}
}
}
- 示例
2.3-1
Test方法代码,阻塞10秒
[HttpGet]
public string Test()
{
Thread.Sleep(10000);
return "我是示例 2.3-1";
}
- 示例
2.3-2
Test方法代码,阻塞6秒
[HttpGet]
public string Test()
{
Thread.Sleep(6000);
return "我是示例 2.3-2";
}
- 示例
2.3-3
Test方法代码
[HttpGet]
public string Test()
{
return "我是示例 2.3-3";
}
- 示例
2.2
LoadBalance方法调整为发起500次请求
private static int count1 = 0;
private static int count2 = 0;
private static int count3 = 0;
[HttpGet]
public void LoadBalance()
{
Parallel.For(0, 500, async x =>
{
await _serviceFactory.InvokeAsync("Wing.Demo_2.3", async serviceAddr =>
{
var client = _httpClientFactory.CreateClient();
client.BaseAddress = new Uri(serviceAddr.ToString());
var response = await client.GetAsync("/WeatherForecast/Test");
if (serviceAddr.Port == 2311)
{
Interlocked.Increment(ref count1);
}
else if (serviceAddr.Port == 2312)
{
Interlocked.Increment(ref count2);
}
else
{
Interlocked.Increment(ref count3);
}
_logger.LogInformation($"请求示例2.3-1次数:{count1},请求示例2.3-2次数:{count2},请求示例2.3-3次数:{count3}");
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
_logger.LogInformation($"第{x + 1}次请求,结果:{await response.Content.ReadAsStringAsync()}");
}
else
{
_logger.LogInformation($"第{x + 1}次请求,状态码:{response.StatusCode}");
}
});
});
}
- 分别启动被调用的示例服务
2.3-1
、2.3-2
、2.3-3
,接着启动调用服务2.2
,浏览器访问 http://localhost:2210/weatherforecast/loadbalance ,可以看到我们发起的500次请求中,示例2.3-3
获得最多请求,结果如下图:
一致性哈希
- 配置
{
"Consul": {
"Service": {
"LoadBalancer": {
"Option": "ConsistentHash",
},
}
}
}
- 调用示例
跟其他服务调用不同是需要传多一个参数key
,这个key
的取值可以是客户端IP或者用户ID等
await _serviceFactory.InvokeAsync("Wing.Demo_2.3","127.0.0.1", async serviceAddr =>
{
})