HAProxy简介
HAProxy是用C语言开发的,是一款开源的自由软件。它具备高可用性、负载均衡功能,并能代理TCP和HTTP应用程序。在负载均衡速度和并发处理能力方面,HAProxy比Nginx更胜一筹。众多大型网站架构中,HAProxy常被选用,确保网站高效运作。
单点问题应对
负载均衡器有单一故障点,一旦发生故障,整个系统可能受到影响。为了防止这种情况,我们可以使用Nginx搭配Keepalived或HAproxy搭配Keepalived的方案。这样的组合能在负载均衡器出现问题时快速切换,确保系统持续运行,降低因故障点造成的损失。
网关鉴权设置
直接将所有流量导入server存在安全风险。为了解决这个问题,我们可以在流量进入server之前,先进行一次身份验证,这个环节就是网关。为了防止出现故障点,网关应以集群模式运行。网关的验证功能可以过滤掉非法流量,增强系统的安全保障。
负载均衡转发
设备接收到客户端的SYN请求,便运用负载均衡算法选择最合适的服务器。随后,它将报文中的目标IP地址更改为后端服务器的IP,并直接进行转发。在某些特定情况下,为了确保服务器能够准确回包,设备还会对报文原有的源地址进行相应调整。通过这种方式,可以实现流量在各服务器之间的合理分配。
静态资源部署
流量大时,将静态资源部署在CDN上是个不错的选择。CDN会自动挑选离用户最近的节点来提供资源。若不利用CDN,服务器在高流量下可能承受不住,进而影响用户的使用体验。采用CDN进行资源分发,能有效提高网站的响应速度。
架构部署选择
流量较少时,使用多台Nginx是可行的。然而,LVS作为Linux内核模块,运行于内核层,而Nginx则运行在用户层,相对较重。因此,在性能和稳定性方面,Nginx不及LVS。所以,通常采用LVS与Nginx结合的部署模式,以实现更佳的性能表现。
public interface LoadBalance {
/**
* 路由
*
* @param serverMap 服务列表
* @return 选择到的一个服务
*/
String route(Map<String, Integer> serverMap);
}
请求分配算法
public class IpMap {
/**
* 待路由的Ip列表,Key代表Ip,Value代表该Ip的权重
*/
public static Map<String, Integer> serverWeightMap = new HashMap<>();
static {
serverWeightMap.put("192.168.1.100", 1);
serverWeightMap.put("192.168.1.101", 1);
serverWeightMap.put("192.168.1.102", 2);
serverWeightMap.put("192.168.1.103", 2);
serverWeightMap.put("192.168.1.104", 3);
}
}
通过访问URL的哈希值来分配请求,可以让每个URL都指向同一个后端服务器。这种方法在后端服务器进行缓存时特别有用。它能确保缓存得到充分利用,降低重复计算的需求。例如,大型电商网站的商品页面缓存就是利用这种方法来提升响应速度的。
public class LoadBalanceUtil {
private static String requestIp;
/**
* 统计路由结果
*
* @param routingMap 记录路由结果的Map
*/
public static void countRoutingMap(Map<String, Integer> routingMap) {
// 路由总体结果
for (Map.Entry<String, Integer> entry : routingMap.entrySet()) {
System.out.println("IP:" + entry.getKey() + ",次数:" + entry.getValue());
}
}
/**
* 模拟路由调用
*
* @param loadBalance 负载均衡策略
* @param requestCount 请求次数
* @return 负载均衡的记录
*/
public static Map<String, Integer> imitateRouting(LoadBalance loadBalance, int requestCount) {
Map<String, Integer> serverMap = new ConcurrentHashMap<>(IpMap.serverWeightMap.size());
for (int i = 0; i < requestCount; i++) {
String server = loadBalance.route(IpMap.serverWeightMap);
Integer count = serverMap.getOrDefault(server, 0);
serverMap.put(server, ++ count);
}
return serverMap;
}
/**
* 获取Ip地址
*
* @return IP地址
*/
public static String getIp() {
try {
InetAddress ip4 = Inet4Address.getLocalHost();
return ip4.getHostAddress();
} catch (UnknownHostException e) {
e.printStackTrace();
}
return null;
}
/**
* 获取请求的 Ip地址 (模拟)
*
* @return IP地址
*/
public static String getRequestIp() {
return requestIp;
}
/**
* 设置请求的IP 模拟
*
* @param requestIp 请求IP
*/
public static void setRequestIp(String requestIp) {
LoadBalanceUtil.requestIp = requestIp;
}
}
缓存处理问题
public class RandomLoadBalance implements LoadBalance {
/**
* 路由
*
* @param serverMap 服务列表
* @return 选择到的一个服务
*/
@Override
public String route(Map<String, Integer> serverMap) {
// 复制遍历用的集合,防止操作中集合有变更
List<String> serverList = new ArrayList<>(serverMap.size());
serverList.addAll(serverMap.keySet());
// 随机数随机访问
int randomInt = new Random().nextInt(serverList.size());
return serverList.get(randomInt);
}
}
服务器一旦出现故障,与之关联的对象缓存便会失效。这时,需将故障服务器的缓存清除,导致缓存服务器总数减少至N减1台。同时,映射计算方式也将调整为hash(object)对(N - 1)取余。这一变动将扰乱既有的缓存策略,进而对系统性能造成影响。
/**
* 测试 随机
*/
@Test
public void testRandom() {
Map<String, Integer> routingMap = LoadBalanceUtil.imitateRouting(new RandomLoadBalance(), 20000);
// 统计路由结果
LoadBalanceUtil.countRoutingMap(routingMap);
}
一致性哈希算法
IP:192.168.1.100,次数:3959
IP:192.168.1.101,次数:4015
IP:192.168.1.102,次数:4017
IP:192.168.1.103,次数:4009
IP:192.168.1.104,次数:4000
为了克服数据分布不均的问题,一致性哈希算法采用了虚拟节点的方法。这种方法会为每个服务节点生成多个哈希值,然后在相应的位置放置一个代表该节点的虚拟节点。这样做可以使数据分布更加均衡,从而提升系统的整体性能。
public class RoundRobinLoadBalance implements LoadBalance {
private static volatile Integer index = 0;
/**
* 路由
*
* @param serverMap 服务列表
* @return 选择到的一个服务
*/
@Override
public String route(Map<String, Integer> serverMap) {
// 复制遍历用的集合,防止操作中集合有变更
List<String> serverList = new ArrayList<>(serverMap.size());
serverList.addAll(serverMap.keySet());
synchronized (RoundRobinLoadBalance.class) {
index++;
if (index == serverList.size()) {
index = 0;
}
return serverList.get(index);
}
}
}
虚拟节点示例
/**
* 测试 轮询
*/
@Test
public void testRoundRobin() {
Map<String, Integer> routingMap = LoadBalanceUtil.imitateRouting(new RoundRobinLoadBalance(), 20000);
// 统计路由结果
LoadBalanceUtil.countRoutingMap(routingMap);
}
为每台服务器设定三个虚拟节点,具体为“Node A#1”“Node A#2”“Node A#3”和“Node B#1”“Node B#2”“Node B#3”,并计算它们的哈希值。这样得到六个虚拟节点。这些节点能更精确地分配数据,有助于降低数据不平衡现象。
IP:192.168.1.100,次数:4000
IP:192.168.1.101,次数:4000
IP:192.168.1.102,次数:4000
IP:192.168.1.103,次数:4000
IP:192.168.1.104,次数:4000
在实际的架构部署过程中,众人对哪种负载均衡技术更偏爱?不妨在评论区留下您的看法,同时别忘了点赞和转发这篇文章!
public class WeightRandomLoadBalance implements LoadBalance {
/**
* 路由
*
* @param serverMap 服务列表
* @return 选择到的一个服务
*/
@Override
public String route(Map<String, Integer> serverMap) {
Map<String, Integer> tempMap = new HashMap<>(serverMap.size());
tempMap.putAll(serverMap);
List<String> serverList = new ArrayList<>();
for (String server : tempMap.keySet()) {
// 按照权重比例添加服务节点(权重高,节点数量多)
for (int i = 0; i < serverMap.get(server); i++) {
serverList.add(server);
}
}
int randomInt = new Random().nextInt(serverList.size());
return serverList.get(randomInt);
}
}
/**
* 测试 加权随机
*/
@Test
public void testWeightRandom() {
Map<String, Integer> routingMap = LoadBalanceUtil.imitateRouting(new WeightRandomLoadBalance(), 20000);
// 统计路由结果
LoadBalanceUtil.countRoutingMap(routingMap);
}