4.4 七层负载均衡
前面介绍四层负载均衡的工作模式都属于“转发”,此时客户端与响应请求的真实服务器维持着同一条 TCP 通道,但七层的负载均衡模式就无法再转发了,由于需要识别应用层协议,就要把 TCP 数据传送到用户态用户程序处理,之后再用代理的方式新建一个请求到真实服务器。如图 4-4 所示,一个七层 HTTP/2 负载均衡器,此时客户端、负载均衡器、真实服务器的工作模式。
图4-4 L7负载均衡
这种情况下,客户端与七层负载均衡器建立一个 HTTP /2 TCP 连接,后续负载均衡器根据负载均衡策略和两个后端建立了连接。当客户端向负载均衡器发送两个 HTTP/2 流(streams )时,stream 1 会被发送到 backend-1,而 stream 2 会被发送到 backend-2。因此,即使不同客户端的请求数量差异巨大,这些请求也可以被高效平衡地分发到后端。七层负载均衡具备检测应用层流量的能力,就能做出更多、更明智的决策,也能玩出更多的花样。
4.1.4 是否还需要 L4 负载均衡
我们前面解释了 L7 负载均衡器对现代协议的重要性,那么是否意味着 L4LB 已经没有用了?肯定不!虽然在 service-to-service 通信中 L7 负载均衡最终会完全取代 L4 负载均衡,但 L4 负载均衡在边缘仍然是非常有用的,因为几乎所有的现代大型分布式架构都是在公网流量入口使用 L4/L7 两级负载均衡架构。在边缘 L7 负载均衡器之前部署 L4 负载均衡器的原因:
- L7LB 承担的更多工作是复杂的分析、变换、以及应用流量路由,他们处理原始流量的能 力(按每秒处理的包数和字节数衡量)比经过优化的 L4 负载均衡器要差。这使得 L4LB 更适合处理特定类型的攻击,例如 SYN 泛洪、通用包(generic packet)泛洪攻击等
- L7LB 部署的更多更频繁,bug 也比 L4LB 多。在 L7 之前加一层 L4LB,可以在调整 L7 部署的时候,对其做健康检查和流量排除(drain),这比(单纯使用)现代 L4LB 要简单的多,后者通常使用 BGP 和 ECMP(后面会介绍)。
- 最后,因为 L7 功能更复杂, 它们的 bug 也会比 L4 多,在前面有一层 L4LB 能及时将有问题的 L7LB 拉出。
4.4.1 Nginx 代理指南
七层负载均衡的实现相信读者们已经非常熟悉,没错,它就是 Nginx。使用 Nginx 七层负载均衡能识别应用层协议,可以通过对 HTTP 头、URL、Cookie 做复杂的逻辑处理,实现更灵活的控制。互联网技术架构中,通常 7 层负载均衡的核心是 Nginx,结合 Lua 插件技术,如 OpenResty,能扩展实现功能丰富且性能较高的网关方案。
Nginx 配置指导
Nginx 的主配置文件是 nginx.conf,这个配置文件一共由三部分组成,分别为全局块、events 块和 http 块。
在 http 块中又包含 http 全局块、多个 server 块。每个 server 块中可以包含 server 全局块和多个 location 块,在同一配置块中嵌套的配置块,各个之间不存在次序关系。
配置文件支持大量可配置的指令,绝大多数指令不是特定属于某一个块的。
同一个指令放在不同层级的块中,其作用域也不同,一般情况下,高一级块中的指令可以作用于自身所在的块和此块包含的所有低层级块。如果某个指令在两个不同层级的块中同时出现,则采用“就近原则”,即以较低层级块中的配置为准。
在本节,将讲解部分重要的配置,以便读者了解 Nginx 性能优化相关的操作。
缓冲(buffer)/缓存(cache)
作为反向代理,缓冲主要是解决后端 Server 与用户网络不对等的情况,比如 Nginx 到 Server 是 100KiB/s, 用户到 Nginx 是 10Kib/s, 这种情况下,如果没有启用 buffer,会导致 Nginx 使用较长的时间处理 用户端与后端 Server 的连接,在高并发的环境下会出现大量的连接积压。
开启代理缓冲后 Nginx 可以用较快的速度尽可能将响应体读取并缓冲到本地内存或磁盘中,然后同时根据客户端的网络质量以合适的网速将响应传递给客户端。
这样既解决了 server 端连接过多的问题也保证了能持续稳定地向客户端传递响应。
Nginx 使用 proxy_buffering 指令启用和禁用缓冲,proxy_buffers 指令设置每个连接读取响应的缓冲区的大小和数量,默认情况下缓冲区大小等于一个内存页,4K 或 8K,具体取决于操作系统。
proxy_buffer_size 可以用来设置后端服务器响应的第一部分存储在单独的缓冲区,此部分通常是相对较小的响应 headers,通常将其设置成小于默认值。
location / { proxy_buffers 16 4k; proxy_buffer_size 2k; proxy_pass http://localhost:8080; }
缓存 Cache
启用缓存后,Nginx 将响应保存在磁盘中,返回给客户端的数据首先从缓存中获取,这样子相同的请求不用每次都发送给后端服务器,减少到后端请求的数量。
启用缓存,需要在 http 上下文中使用 proxy_cache_path 指令,定义缓存的本地文件目录,名称和大小。
缓存区可以被多个 server 共享,使用 proxy_cache 指定使用哪个缓存区。
http { proxy_cache_path /data/nginx/cache keys_zone=mycache:10m; server { proxy_cache mycache; location / { proxy_pass http://localhost:8000; } } }
缓存目录的文件名是 proxy_cache_key 的 MD5 值, proxy_cache_key 默认设置如下
proxy_cache_key $scheme$proxy_host$uri$is_args$args;
当然也可以自定义缓存 key
proxy_cache_key "$host$request_uri$cookie_user";
缓存不应该设置的太敏感,可以使用 proxy_cache_min_uses 设置相同的 key 的请求,访问次数超过指定数量才会被缓存。
proxy_cache_min_uses 5;
缓存设置的示例
http { proxy_cache_path /var/cache/nginx/data keys_zone=mycache:10m; server { location = /html/demo.html { proxy_cache mycache; proxy_cache_valid 200 302 10m; proxy_cache_valid 404 1m; proxy_cache_valid any 5m; proxy_pass http://localhost:8088; } } }
负载均衡
跨多个应用程序实例的负载平衡是一种常用技术,用于优化资源利用率、最大化吞吐量、减少延迟和确保容错配置,Nginx 支持 6 种负载均衡模式
模式 | 介绍 |
---|---|
轮循机制 | 默认机制,以轮循机制方式分发 |
最小连接 | 将下一个请求分配给活动连接数最少的服务器 |
ip-hash | 客户端的 IP 地址将用作哈希键,来自同一个 ip 的请求会被转发到相同的服务器 |
hash | 通用 hash,允许用户自定义 hash 的 key,key 可以是字符串、变量或组合 |
随机 | 每个请求都将传递到随机选择的服务器 |
权重 | 按照 weight 参数进行分配 |
在反向代理中,如果后端服务器在某个周期内响应失败次数超过规定值,Nginx 会将此服务器标记为失败,并在之后的一个周期不再将请求发送给这台服务器。
在 upstream 配置中,通过 fail_timeout来设置检查周期,默认为 10 秒。通过 max_fails来设置检查失败次数,默认为 1 次。
如:
upstream backend { server backend.example.domain max_fails=3 fail_timeout=30s; }
4.4.2 从负载均衡到网关
随着微服务架构、容器编排调度等技术的崛起,现代分布式系统已经越来越动态,这就意味着通过静态文件配置方式早已过时。作为分布式系统的入口,负载均衡器就需要转换角色,不仅仅作为一个代理,而是要承担提供更多现代化的功能。把这些功能统一前移某一层独立支持,实现 API Gateway as a Service,让各个服务直接接入,在管理平台上管理,可视化配置等等,这样就实现了一个全局的视图统一管理这些功能。
而这就是七层负载均衡的升级 – 网关(API Gateway)。
API Gateway 在业内已经有非常多的成熟开源方案,如果按实现语言以及成熟度来讲,主流有以下几个方案。
语言 | 网关 | 特点 |
---|---|---|
Nginx + LuaJIT | OpenResty | 原始 Web 开发平台 |
Nginx + Lua | Kong | 社区活跃、成熟度高、Postgres 存储、二次开发成本高 |
Nginx + Lua | Apache/APISIX | 云原生化、使用 etcd 存储、性能高、二次开发成本低 |
Java | Spring Cloud Alibaba | Spring Cloud 生态、社区成熟度高、国内应用广泛 |
这些现代化的网关方案各有各的特点,实现的功能也非常强大,笔者不再逐一展开,下面简单列举部分实现,以便读者对它“强大功能”有个直观的感受。
-
协议支持 现代网关系统正在显式添加对更多协议的支持。负载均衡器对应用层协议了解的越多, 就可以处理越多更复杂的事情,包括观测输出、高级负载均衡和路由等等。例如,在写作本文时,Envoy 显式支持如下七层协议的解析和路由:HTTP/1、HTTP/2、gRPC、Redis、MongoDB、DynamoDB。
-
动态配置 服务管理的动态配置包括路由、上游服务(Upstream)、SSL证书、消费者等等,数据的替换和和更新不会产生任何中断,从而将线上流量的影响降低到最低。
-
流量治理 在很多微服务架构中,流量治理都需要在七层中进行,譬如超时、重试、限速、熔断、流量镜像、缓存等等。现代网关系统在服务以及流量的管理上可对多业务进行收敛,统一处理,降低多套网关的运维成本。
-
可观测性:在互联网快速发展的今天,基础设施与应用的部署构建都发生了极大变化,特别是基于分布式、微服务的体系架构,传统的监控方式已经无法适应云原生的场景。服务治理的可观测性输出是网关系统提供的最重要的特性。系统度量、分布式跟踪以及自定义日志等功能现在几乎是七层负载均衡解决方案的标配。
-
可扩展性 例如典型的 OpenResty,通过编写可插拔的插件然后加载到负载均衡器,能够轻松地对网关系统实现各种流量处理和分发功能等自定义功能,比如流量限速、日志记录、安全检测、故障注入等等。
-
高可用以及无状态设计:现代网关系统不仅提供数据面实现,还提供控制面实现,二者目标都在朝向无状态设计,可以轻松地实现水平扩展。网关架构整体上也默认高可用,不存在单点故障。
添加评论