尼恩说在前面
1.讲一下什么是负载均衡,什么是轮询策略、随机策略、哈希策略 2.讲一下什么是 基于权重的负载均衡 策略? 3 手写一个 基于权重的负载均衡 策略?
最新《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》的PDF,请关注本公众号【技术自由圈】获取,回复:领电子书
本文目录
- 尼恩说在前面
- 1. 为什么需要负载均衡
- 2. Nginx的5大负载均衡策略策略
- 2.1. roundrobin 轮询 策略
- 2.2. 加权 轮循 weighted roundrobin
- 2.3. ip_hash
- 2.4. least_conn
- 2.5. 第三方策略:fair
- 2.6. 第三方策略:url_hash
- 3. 手写一个 基于权重的负载均衡Weighted Round Robin算法
- 3.1. 服务器列表与权重设定
- 3.2. 加权轮询算法
-具体步骤:
- 3.3. 伪代码 实现一个Weighted Round Robin算法:
- 3.4. 基于权重的负载均衡Weighted Round Robin算法示例:
- 4. 手写Java 版本的 Weighted Round Robin算法
- 4.1. 服务器类设计
- 4.2. 加权轮询负载均衡算法
- 4.3. 测试代码
- 4.4. 结果输出
- 4.5. 动态调整与状态维护
- 4.6. 优化与扩展
- 总结
- 说在最后:有问题找老架构取经
1. 为什么需要负载均衡
2. Nginx的5大负载均衡策略策略
#动态服务器组
upstream backend_group{
server localhost:8080; #backend 1
server localhost:8081; #backend 2
server localhost:8082; #backend 3
server localhost:8083; #backend 4
}
192.168.1.1:80:指定后端真实服务器可以是域名或ip,默认是80端口 weight:指定每个后端主机的调度的权重,默认为1 max_conns:指定后端主机最大并发连接数 max_fails:指定后端主机健康检查多少次失败后才将主机标记为不可用(默认1次,0为不做健康检测) fail_timeout:指定后端主机健康检测超时多少时间为一次失败(默认10秒) backup:指定sorry_server,当所有后端主机健康检测失败时,会显示此服务器的页面 down:将当前主机标记为不可用(维护时使用)
#其他页面反向代理到backend 容器
location ~ .*${
index index.jsp index.html;
proxy_pass http://backend_group;
}
轮循 | 默认方式 |
2.1. roundrobin 轮询 策略
fail_timeout | 与max fails结合使用 |
在轮询中,如果服务器down掉了,会自动剔除该服务器。 此策略适合服务器配置相当,无状态且短平快的服务使用。
2.2. 加权 轮循 weighted roundrobin
给高性能的机器分配更高的权重,使其能处理更多的请求, 而性能低、负载高的机器,配置较低的权重,让其处理较少的请求。
#动态服务器组
upstream backend_group{
server localhost:8080 weight=2; #backend 1
server localhost:8081; #backend 2
server localhost:8082; #backend 3
server localhost:8083; #backend 4
}
权重越高分配到需要处理的请求越多。 此策略可以与least_conn和ip_hash结合使用。 此策略比较适合服务器的硬件配置差别比较大的情况。
2.3. ip_hash
#动态服务器组
upstream backend_group{
ip_hash; #保证每个访客固定访问一个后端服务器
server localhost:8080 ; #backend 1
server localhost:8081; #backend 2
server localhost:8082; #backend 3
server localhost:8083 ; #backend 4
}
在nginx版本1.3.1之前,不能在ip_hash中使用权重(weight)。 ip_hash不能与backup同时使用。 此策略适合有状态服务,比如session。 当有服务器需要剔除,必须手动down掉。
2.4. least_conn
#动态服务器组
upstream backend_group{
least_conn; #把请求转发给连接数较少的后端服务器
server localhost:8080 ; #backend 1
server localhost:8081; #backend 2
server localhost:8082; #backend 3
server localhost:8083 ; #backend 4
}
此负载均衡策略适合请求处理时间长短不一造成服务器过载的情况。
2.5. 第三方策略:fair
#动态服务器组
upstream backend_group{
server localhost:8080; #backend 7.0
server localhost:8081; #backend 8.0
server localhost:8082; #backend 8.5
server localhost:8083; #backend 9.0
fair; #实现响应时间短的优先分配
}
2.6. 第三方策略:url_hash
#动态服务器组
upstream backend_group{
hash $request_uri; #实现每个url定向到同一个后端服务器
server localhost:8080; #backend 7.0
server localhost:8081; #backend 8.0
server localhost:8082; #backend 8.5
server localhost:8083; #backend 9.0
}
3. 手写一个 基于权重的负载均衡Weighted Round Robin算法
服务器的权重、 负载均衡策略、 状态维护机制等。
3.1. 服务器列表与权重设定
servers = [
{"ip": "192.168.1.1", "weight": 5, "current_weight": 0},
{"ip": "192.168.1.2", "weight": 3, "current_weight": 0},
{"ip": "192.168.1.3", "weight": 2, "current_weight": 0}
]
3.2. 加权轮询算法
具体步骤:
初始化总权重:计算所有服务器权重的总和。 选择服务器:遍历服务器列表,选出 current_weight
最大的服务器。调整权重
对选择的服务器,将其 current_weight
减去总权重;对所有服务器,将 current_weight
加上各自的权重。
3.3. 伪代码 实现一个Weighted Round Robin算法:
def weighted_round_robin(servers):
total_weight = sum(server['weight'] for server in servers)
selected = None
for server in servers:
# 更新 current_weight
server['current_weight'] += server['weight']
# 选择 current_weight 最大的服务器
if selected_server==null or server['current_weight'] > selected['current_weight']:
selected_server = server
# 将选择的服务器的 current_weight 减去总权重
selected_server['current_weight'] -= total_weight
return selected_server
3.4. 基于权重的负载均衡Weighted Round Robin算法示例:
current_weight
都为 0,负载均衡算法的分配过程如下:请求 | 服务器1权重 | 服务器2权重 | 服务器3权重 | 选择的服务器 |
4. 手写Java 版本的 Weighted Round Robin算法
4.1. 服务器类设计
Server
,包括服务器的 IP、权重、当前权重和当前负载等属性。package com.crazymakercircle.loadbalance;
import lombok.Data;
@Data
public class Server {
private int usedCount; //使用次数
private String ip; //ip
private int weight; //权重
private int currentWeight; //当前权重
public Server(String ip, int weight) {
this.ip = ip;
this.weight = weight;
this.currentWeight = 0;
this.usedCount = 0;
}
public void usedOne() {
System.out.println("选中之后: ip = " + ip + " currentWeight = " + currentWeight + " weight = " + weight);
usedCount++;
}
}
4.2. 加权轮询负载均衡算法
WeightedRoundRobinBalancer
类来实现加权轮询算法,管理多个 Server
实例的请求分发。package com.crazymakercircle.loadbalance;
import java.util.List;
public class WeightedRoundRobinBalancer {
private List<Server> servers; //服务列表
private int totalWeight; //总权重
public WeightedRoundRobinBalancer(List<Server> servers) {
this.servers = servers;
// 初始化总权重
this.totalWeight = servers.stream().mapToInt(Server::getWeight).sum();
System.out.println("totalWeight = " + totalWeight);
}
// 获取当前轮询中要选择的服务器
public Server doSelect() {
Server selectedServer = null;
for (Server server : servers) {
// 增加当前权重
server.setCurrentWeight(server.getCurrentWeight() + server.getWeight());
// 选择 currentWeight 最大的服务器
if (selectedServer == null || server.getCurrentWeight() > selectedServer.getCurrentWeight()) {
selectedServer = server;
}
}
// 将选择的服务器 currentWeight 减去总权重
if (selectedServer != null) {
selectedServer.setCurrentWeight(selectedServer.getCurrentWeight() - totalWeight);
}
selectedServer.usedOne();
return selectedServer;
}
}
4.3. 测试代码
import java.util.Arrays;
public class LoadBalancerTest {
public static void main(String[] args) {
// 创建服务器列表,服务器的权重分别为 5, 3, 2
Server server1 = new Server("192.168.1.1", 5);
Server server2 = new Server("192.168.1.2", 3);
Server server3 = new Server("192.168.1.3", 2);
WeightedRoundRobinBalancer balancer = new WeightedRoundRobinBalancer(
Arrays.asList(server1, server2, server3)
);
// 模拟 10 次请求,观察每次请求分配到的服务器
for (int i = 0; i < 10; i++) {
Server selectedServer = balancer.getServer();
System.out.println("Request " + (i + 1) + " is handled by server: " + selectedServer.getIp());
}
}
}
4.4. 结果输出
totalWeight = 10
选中之后: ip = 192.168.1.1 currentWeight = -5 weight = 5
>>>>>>> 请求 1 路由到: 192.168.1.1
选中之后: ip = 192.168.1.2 currentWeight = -4 weight = 3
>>>>>>> 请求 2 路由到: 192.168.1.2
选中之后: ip = 192.168.1.3 currentWeight = -4 weight = 2
>>>>>>> 请求 3 路由到: 192.168.1.3
选中之后: ip = 192.168.1.1 currentWeight = 0 weight = 5
>>>>>>> 请求 4 路由到: 192.168.1.1
选中之后: ip = 192.168.1.1 currentWeight = -5 weight = 5
>>>>>>> 请求 5 路由到: 192.168.1.1
选中之后: ip = 192.168.1.2 currentWeight = -2 weight = 3
>>>>>>> 请求 6 路由到: 192.168.1.2
选中之后: ip = 192.168.1.1 currentWeight = -5 weight = 5
>>>>>>> 请求 7 路由到: 192.168.1.1
选中之后: ip = 192.168.1.3 currentWeight = -4 weight = 2
>>>>>>> 请求 8 路由到: 192.168.1.3
选中之后: ip = 192.168.1.2 currentWeight = -3 weight = 3
>>>>>>> 请求 9 路由到: 192.168.1.2
选中之后: ip = 192.168.1.1 currentWeight = 0 weight = 5
>>>>>>> 请求 10 路由到: 192.168.1.1
选中之后: ip = 192.168.1.1 currentWeight = -5 weight = 5
>>>>>>> 请求 11 路由到: 192.168.1.1
选中之后: ip = 192.168.1.2 currentWeight = -4 weight = 3
>>>>>>> 请求 12 路由到: 192.168.1.2
选中之后: ip = 192.168.1.3 currentWeight = -4 weight = 2
>>>>>>> 请求 13 路由到: 192.168.1.3
选中之后: ip = 192.168.1.1 currentWeight = 0 weight = 5
>>>>>>> 请求 14 路由到: 192.168.1.1
选中之后: ip = 192.168.1.1 currentWeight = -5 weight = 5
>>>>>>> 请求 15 路由到: 192.168.1.1
选中之后: ip = 192.168.1.2 currentWeight = -2 weight = 3
>>>>>>> 请求 16 路由到: 192.168.1.2
选中之后: ip = 192.168.1.1 currentWeight = -5 weight = 5
>>>>>>> 请求 17 路由到: 192.168.1.1
选中之后: ip = 192.168.1.3 currentWeight = -4 weight = 2
>>>>>>> 请求 18 路由到: 192.168.1.3
选中之后: ip = 192.168.1.2 currentWeight = -3 weight = 3
>>>>>>> 请求 19 路由到: 192.168.1.2
选中之后: ip = 192.168.1.1 currentWeight = 0 weight = 5
>>>>>>> 请求 20 路由到: 192.168.1.1
=======》打印 总次数分布
192.168.1.1:10
192.168.1.2:6
192.168.1.3:4
4.5. 动态调整与状态维护
动态权重调整:根据服务器的实时负载,动态调整权重。比如,监控 CPU 或内存使用率,负载高的服务器权重减少,负载低的权重增加。 健康检查:定期检查服务器状态,将不可用的服务器从列表中移除,以防止请求被发送到已经不可用的服务器。
public void healthCheck() {
// 遍历所有服务器,检查其健康状态
servers.removeIf(server -> !isHealthy(server));
}
private boolean isHealthy(Server server) {
// 实现对服务器的健康检查,返回 true 表示健康
return true; // 简单示例,实际可以发送请求或 ping 检查服务器状态
}
4.6. 优化与扩展
最小连接数优先:在加权轮询的基础上,还可以结合最小连接数策略,优先将请求分配给当前连接数最少的服务器。 一致性哈希:为提高稳定性,避免请求在服务器上下线时大量转移,可以结合一致性哈希算法,使得请求尽可能落在相同的服务器上。
总结
说在最后:有问题找老架构取经
被裁之后, 空窗1年/空窗2年, 如何 起死回生 ?
案例1:42岁被裁2年,天快塌了,急救1个月,拿到开发经理offer,起死回生
案例2:35岁被裁6个月, 职业绝望,转架构急救上岸,DDD和3高项目太重要了
案例3:失业15个月,学习40天拿offer, 绝境翻盘,如何实现?
被裁之后,100W 年薪 到手, 如何 人生逆袭?
100W案例,100W年薪的底层逻辑是什么? 如何实现年薪百万? 如何远离 中年危机?
如何 逆天改命,包含AI、大数据、golang、Java 等
实现职业转型,极速上岸
关注职业救助站公众号,获取每天职业干货
助您实现职业转型、职业升级、极速上岸
---------------------------------
实现架构转型,再无中年危机
关注技术自由圈公众号,获取每天技术千货
一起成为牛逼的未来超级架构师
几十篇架构笔记、5000页面试宝典、20个技术圣经
请加尼恩个人微信 免费拿走
暗号,请在 公众号后台 发送消息:领电子书
如有收获,请点击底部的"在看"和"赞",谢谢