4、负载均衡入门:负载均衡的作用、常见算法(轮询、最小连接、IP哈希)、会话保持问题
好,咱们今天聊聊负载均衡。说实话,这是弹性伸缩里最基础、也最容易被忽视的一环。
我见过不少团队,业务一上量就急着加机器,结果加完发现流量全怼到一台上了。你说气不气人?
负载均衡要解决的,说白了就三个字:分流量。把用户请求均匀地分给后端多台服务器,别让某台机器累死,其他机器闲得发慌。
负载均衡到底干了啥?
我习惯把负载均衡比作「前台接待」。你想想看,一个公司前台,要负责把来访的客人引导到不同的会议室。如果前台只认一个会议室,那其他会议室空着,这个会议室挤爆了——这就是没做负载均衡。
具体来说,负载均衡干这几件事:
- 分发请求:把用户请求按策略分给后端服务器
- 健康检查:定期检查后端服务器是否活着,死了就踢出集群
- 故障转移:某台挂了,自动把流量切到其他机器
- 扩展能力:加机器就加进来,减机器就摘出去,对用户无感
核心要点:负载均衡不是简单的「一人一个」,而是要考虑后端服务器的实际负载能力、连接状态、甚至用户身份。
常见算法,我一个个说
算法这东西,没有绝对的好坏,只有合不合适。我挑三个最常用的讲讲。
轮询(Round Robin)
这个最简单。就像发扑克牌,一人一张,轮流来。
假设你有3台服务器:A、B、C。第一个请求给A,第二个给B,第三个给C,第四个又给A……循环往复。
# 伪代码示意
servers = [A, B, C]
index = 0
def get_server():
server = servers[index]
index = (index + 1) % len(servers)
return server
优点:实现简单,无状态,适合服务器配置差不多的场景。
缺点:不考虑服务器当前负载。如果A是8核16G,B是4核8G,轮询还是各分一半,B很快就扛不住了。
我的经验:我在早期做一个小型电商站时就用轮询。当时三台机器配置一样,效果还行。后来加了台高配机器,轮询就明显不合理了——高配机器闲得慌,低配机器忙得要死。
最小连接(Least Connections)
这个就聪明一点了。它不看「谁该轮到谁」,而是看「谁现在最闲」。
每台服务器都维护一个当前活跃连接数。新请求来了,就分配给连接数最少的那台。
# 伪代码示意
servers = {
A: {connections: 5},
B: {connections: 2},
C: {connections: 8}
}
def get_server():
# 找到连接数最少的服务器
return min(servers, key=lambda s: servers[s]['connections'])
优点:能动态适应后端负载,适合长连接场景(比如WebSocket、数据库连接池)。
缺点:需要维护连接数状态,稍微复杂一点。而且如果请求处理时间差异很大,连接数少不代表真的闲。
注意:我曾经遇到过一个坑——某台服务器处理请求特别慢,连接数一直很高,但其他服务器连接数低,结果新请求全涌到其他服务器,导致那几台也变慢。最后发现是那台慢服务器有内存泄漏。所以最小连接算法不能替代健康检查。
IP哈希(IP Hash)
这个算法有点意思。它根据客户端的IP地址计算一个哈希值,然后决定把请求发给哪台服务器。
同一个IP的请求,永远会被发到同一台服务器。
# 伪代码示意
def get_server(client_ip):
hash_value = hash(client_ip)
index = hash_value % len(servers)
return servers[index]
优点:解决了会话保持问题(后面会细说)。同一个用户始终访问同一台服务器,session数据不用跨机器同步。
缺点:如果某台服务器挂了,这个IP的所有用户都会受影响。而且如果客户端IP变化(比如手机切换WiFi和4G),哈希结果就变了。
我的建议:IP哈希适合内部系统或者对会话一致性要求高的场景。但对外部互联网用户,我一般不太推荐——因为很多用户共享同一个出口IP(比如公司网络),会导致流量倾斜。
会话保持问题,这是个老大难
好,咱们重点聊聊会话保持。为什么单独拿出来说?因为这是我在项目中踩坑最多的一个点。
什么叫会话保持?简单说就是:同一个用户的请求,尽量都发给同一台服务器。
为什么会这样?因为很多应用把用户登录状态、购物车数据存在服务器的内存里(session)。如果用户第一次请求到了A服务器,登录了,第二次请求被轮询到了B服务器——B上没有这个用户的session,用户就得重新登录。
你想想看,用户刚登录完,点个页面又让登录,是不是想骂人?
解决方案有哪些?
我按从简单到复杂的顺序说:
- IP哈希:上面讲过了,同一个IP固定到同一台服务器。简单粗暴,但不够灵活。
- Cookie植入:负载均衡器在用户第一次请求时,在Cookie里写一个标记(比如服务器ID),后续请求根据这个标记转发。这个比较常用。
- Session共享:把session数据存到外部存储(Redis、数据库),所有服务器都从同一个地方读取。这样就不需要会话保持了,任何服务器都能处理任何用户的请求。
我的推荐:如果条件允许,我强烈建议用Session共享方案。虽然架构复杂一点,但彻底解决了会话保持的痛点。我在做一个日活百万的SaaS系统时,就是用的Redis存session,效果非常好。
会话保持的坑
嗯,这里要特别提醒一下。会话保持虽然解决了session问题,但带来了新的问题:
- 负载不均:某些用户请求量大,导致某台服务器压力大
- 故障影响面大:某台服务器挂了,所有绑定到这台服务器的用户都受影响
- 扩容困难:加了新服务器,旧用户的请求还是往老服务器跑,新服务器利用率低
避坑指南:我曾经在一个项目中,用了Cookie植入做会话保持。结果用户清除了Cookie,所有请求都重新分配,导致session丢失。后来我们加了一层「如果Cookie失效,就用IP哈希兜底」,才算稳定下来。
小结一下
负载均衡不是选个算法就完事了。你得想清楚:
- 你的服务器配置是否一致?不一致就别用轮询
- 你的应用是长连接还是短连接?长连接用最小连接更合适
- 你的用户是否需要会话保持?需要的话,考虑IP哈希或Session共享
说白了,没有银弹。每个场景都有自己的最优解。我个人的习惯是:先做Session共享,再用最小连接算法。这样既解决了会话问题,又能动态适应负载。
下一章咱们聊聊自动扩容的触发条件,到时候会用到今天讲的这些知识。