1. 从单点瓶颈到流量分发:为什么我们需要负载均衡
如果你负责的线上服务,在某个促销活动开始后的五分钟内,因为流量激增导致服务器直接宕机,用户页面一片空白,你会怎么办?这几乎是每个后端工程师或运维人员都可能遇到的噩梦场景。问题的根源往往不在于代码逻辑,而在于架构的瓶颈——单台服务器的处理能力是有限的。无论是CPU、内存、磁盘I/O还是网络带宽,总有一个会成为压垮骆驼的最后一根稻草。这时候,负载均衡就不再是一个可选项,而是保障服务高可用、高性能的基石。
简单来说,负载均衡就像是一个经验丰富的交通指挥员。当大量车辆(用户请求)涌向一个路口(单台服务器)时,指挥员会根据各条道路(多台服务器)的实时拥堵情况,智能地将车辆分流到不同的道路上,确保整个交通系统(服务集群)畅通无阻。它的核心价值在于:提升吞吐量、避免单点故障、实现无缝横向扩展。
在众多负载均衡解决方案中,Nginx凭借其高性能、高稳定性和配置灵活的特点,成为了业界最广泛使用的软件负载均衡器之一。它不仅能处理海量的HTTP/HTTPS请求,还支持TCP/UDP协议的负载均衡。更重要的是,Nginx的配置清晰直观,学习曲线相对平缓,使得从初创团队到大型企业都能快速上手并部署。今天,我们就抛开那些空洞的理论,直接切入实战,看看如何用Nginx搭建一个可靠、高效的负载均衡层,并分享那些官方文档里不会写的“踩坑”经验。
2. Nginx负载均衡的核心机制与算法选择
在动手配置之前,我们必须先理解Nginx是如何做决策的。它不是一个简单的“随机分配器”,而是内置了多种智能算法,可以根据你的业务场景选择最合适的分流策略。理解这些算法,是进行有效配置的前提。
2.1 主流负载均衡算法深度解析
Nginx的upstream模块支持以下几种核心算法,你需要根据后端服务器的性能和业务特点来抉择。
轮询 (Round Robin)这是默认算法,也是最好理解的。Nginx将进入的请求按顺序逐一分配到不同的后端服务器。假设你有三台服务器A、B、C,第一个请求给A,第二个给B,第三个给C,第四个又回到A,如此循环。
- 适用场景:后端服务器硬件配置、处理性能几乎完全相同,且每个请求的处理耗时相差不大的无状态服务。例如,提供静态图片、API接口的服务集群。
- 配置示例:无需特殊声明,默认即是。
upstream backend_servers { server 192.168.1.101:8080; server 192.168.1.102:8080; server 192.168.1.103:8080; }
加权轮询 (Weighted Round Robin)这是轮询算法的增强版。你可以为每台服务器分配一个权重值,权重越高,被分配到的请求比例就越大。这完美解决了后端服务器性能不均等的问题。
- 适用场景:后端服务器配置不一致。比如,你有两台新采购的高配服务器和一台老旧的备用服务器,就可以给高配服务器设置更高的权重(如
weight=5),给低配服务器设置较低的权重(如weight=1)。 - 配置示例与计算逻辑:
Nginx内部会维护一个动态的权重计数器。在多次请求分配中,101服务器被选中的概率理论上是102的1.5倍,是103的3倍。这比简单轮询能更充分地利用硬件资源。upstream backend_servers { server 192.168.1.101:8080 weight=3; # 高性能服务器 server 192.168.1.102:8080 weight=2; # 中等性能服务器 server 192.168.1.103:8080 weight=1; # 低性能服务器 }
IP哈希 (IP Hash)该算法根据客户端IP地址计算出一个哈希值,然后将这个请求固定地映射到某台后端服务器。只要客户端的IP不变,它后续的请求就总会落到同一台服务器上。
- 适用场景:需要会话保持(Session Persistence)的应用。例如,用户的购物车信息、登录状态等存储在单台服务器的内存中(而非集中式Redis),就必须确保同一用户的请求始终访问同一台后端,否则会出现状态丢失。
- 重要限制:如果后端服务器数量发生变化(增删节点),大部分哈希映射会失效,导致会话中断。因此,在采用此方案时,后端服务器的扩容缩容需要格外谨慎,或配合一致性哈希等更高级的方案。
- 配置示例:
upstream backend_servers { ip_hash; server 192.168.1.101:8080; server 192.168.1.102:8080; server 192.168.1.103:8080; }
最少连接 (Least Connections)Nginx会实时跟踪每个后端服务器当前正在处理的活跃连接数,并将新请求发送给连接数最少的服务器。这是一种动态的、更公平的分配策略。
- 适用场景:后端服务器处理能力相近,但单个请求的处理时间长短不一、波动较大的场景。例如,有些请求是简单的查询(快),有些是复杂的报表生成(慢)。最少连接算法可以避免某个服务器因为接到几个“慢请求”而堆积更多请求,实现负载的实时均衡。
- 配置示例:
upstream backend_servers { least_conn; server 192.168.1.101:8080; server 192.168.1.102:8080; server 192.168.1.103:8080; }
加权最少连接 (Weighted Least Connections)顾名思义,这是最少连接算法和加权因子的结合。Nginx在考虑连接数的同时,也会参考服务器的权重,做出更精细的决策。
- 适用场景:后端服务器性能差异大,且请求处理时间不均等。这是生产环境中最推荐使用的算法之一,因为它同时兼顾了服务器的静态处理能力(权重)和动态负载情况(连接数)。
- 配置示例:
upstream backend_servers { least_conn; server 192.168.1.101:8080 weight=3; server 192.168.1.102:8080 weight=2; server 192.168.1.103:8080 weight=1; }
2.2 算法选型实战心得:别凭感觉,看数据
选择哪种算法,不能靠猜。这里分享一个我经历过的真实案例:我们有一个用户画像计算服务,初期使用默认轮询,上线后发现三台服务器CPU使用率分别是90%、30%、85%,极不均衡。排查发现,因为部分大客户的数据量巨大,计算耗时很长,导致接到这些“大请求”的服务器瞬间被压垮,而轮询算法对此无能为力。
我们的排查和优化过程如下:
- 监控先行:首先,我们在Nginx和后端服务器上都部署了监控,收集请求响应时间、服务器连接数、CPU/内存使用率等指标。
- 分析请求模式:通过日志分析,我们发现请求的处理时间分布非常不均匀,从几十毫秒到几十秒都有,且与客户端IP无强关联(无需会话保持)。
- 切换算法:我们将算法从
round robin改为least_conn。 - 观察效果:切换后,三台服务器的连接数趋于一致,但CPU使用率依然有较大差距,因为服务器本身性能不同。
- 最终方案:我们采用了
加权最少连接,并根据服务器的实际基准测试性能(如每秒处理请求数)设定了权重。调整后,各服务器的CPU使用率都稳定在70%-80%的合理区间,整体吞吐量提升了约40%。
核心建议:在重要的生产环境变更负载均衡算法前,务必在测试环境进行压测和对比。使用
ab、wrk或jmeter等工具模拟真实流量,观察不同算法下的响应时间分布、错误率和服务器资源利用率。
3. 手把手搭建Nginx负载均衡:从配置到上线
理解了原理,我们开始实战。假设我们要为一个名为myapp的Web应用配置负载均衡,它运行在三台服务器上(192.168.1.101-103:8080),Nginx负载均衡器安装在192.168.1.100上。
3.1 基础配置骨架
首先,在Nginx的配置文件(通常是/etc/nginx/nginx.conf或/etc/nginx/conf.d/default.conf)中,我们需要定义一个upstream块和一个server块。
http { # 定义后端服务器组,命名为 backend_servers upstream backend_servers { # 使用加权最少连接算法 least_conn; # 定义三台后端服务器,并设置权重 server 192.168.1.101:8080 weight=3 max_fails=3 fail_timeout=30s; server 192.168.1.102:8080 weight=2 max_fails=3 fail_timeout=30s; server 192.168.1.103:8080 weight=1 max_fails=3 fail_timeout=30s; } server { listen 80; # 负载均衡器监听80端口 server_name myapp.example.com; # 你的域名 location / { # 将流量代理到上面定义的服务器组 proxy_pass http://backend_servers; # 以下是一组至关重要的代理参数设置 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 连接超时设置 proxy_connect_timeout 5s; proxy_send_timeout 60s; proxy_read_timeout 60s; # 启用缓冲,提升性能(针对大响应) proxy_buffering on; proxy_buffer_size 4k; proxy_buffers 8 4k; } } }3.2 关键配置参数拆解与避坑指南
上面配置中每一行都不是多余的,理解它们能帮你避开很多坑。
1. 健康检查 (max_fails与fail_timeout)这是生产环境的生命线。max_fails=3和fail_timeout=30s是组合拳。
- 作用:在
30秒内,如果Nginx向某台后端服务器发起的请求失败次数达到3次,Nginx会标记该服务器为“不可用”,并在接下来的30秒内不再向其分发请求。 - “失败”的定义:Nginx与后端服务器建立连接、发送请求或读取响应头时发生超时或网络错误。注意:后端返回
5xx错误码(如500内部错误)在默认的被动健康检查中不算“失败”。如果需要检查业务状态,需要启用主动健康检查或结合proxy_next_upstream指令。 - 避坑点:
fail_timeout有两个含义:一是判定失败的时间窗口长度,二是服务器被标记为不可用后的“冷却时间”。设置太短会导致服务器因网络抖动被误剔除;设置太长则意味着真正的故障服务器会长时间接收流量。根据业务容忍度调整,一般建议5-30秒。
2. 代理头信息传递 (proxy_set_header)这是最容易被忽略但问题最多的地方。Nginx作为反向代理,默认会修改或丢失一些原始的客户端请求信息。
Host $host:将原始请求的Host头传递给后端。很多Web框架(如Spring Boot、Django)依赖这个头来生成正确的URL或进行虚拟主机路由。如果丢失,后端应用可能无法正常工作。X-Real-IP $remote_addr:将客户端的真实IP放在X-Real-IP头中传给后端。这样后端日志记录的就是用户IP,而不是Nginx的IP。X-Forwarded-For $proxy_add_x_forwarded_for:这是最重要的头之一。它记录了请求经过的所有代理服务器的IP链。如果Nginx前面还有CDN或其它代理,这个头能确保后端拿到最原始的客户端IP。常见坑:后端应用需要从X-Forwarded-For中取第一个IP或最后一个IP(取决于信任链配置),如果没配置,后端获取的IP永远是Nginx的地址。X-Forwarded-Proto $scheme:告诉后端客户端原始请求是http还是https。如果你的Nginx负责SSL卸载(即用户用HTTPS访问Nginx,Nginx用HTTP访问后端),这个头必须传,否则后端生成的跳转链接可能是错误的HTTP。
3. 超时控制 (proxy_connect_timeout,proxy_send_timeout,proxy_read_timeout)
proxy_connect_timeout:Nginx与后端服务器建立TCP连接的超时时间。如果网络不稳定或后端服务器宕机,这个设置能防止Nginx工作进程长时间阻塞。建议3-5秒。proxy_send_timeout:Nginx向后端发送请求的超时时间。如果请求体很大,或者网络慢,可以适当调大。proxy_read_timeout:Nginx从后端读取响应的超时时间。这是最关键的。如果你的应用有长时间处理的接口(如文件导出、复杂计算),必须将此值调大,否则Nginx会在超时后断开连接,并向用户返回502错误,而后端进程可能还在继续运行,浪费资源。需要根据业务接口的最大耗时来设定。
4. 上游服务器域名解析在upstream中,我们使用了IP地址。你也可以使用域名,但这里有一个巨坑:Nginx只在启动或重载配置时解析一次域名,并将其缓存直到下次重启或重载。如果后端服务器的IP地址发生变化(比如在Kubernetes或动态云环境中),Nginx将无法感知,导致流量无法到达新IP。
- 解决方案:在
upstream块内使用resolver指令指定DNS服务器并设置解析有效期。
这样,Nginx会定期刷新域名解析结果。注意,upstream backend_servers { resolver 8.8.8.8 valid=30s; # 使用Google DNS,缓存30秒 server backend1.example.com:8080; server backend2.example.com:8080; }resolver必须放在upstream块内,且对块内的所有server域名生效。
4. 高可用与进阶配置:超越基础
一个健壮的负载均衡方案,不能只满足于“把请求分出去”。我们还需要考虑后端故障转移、流量精细化管理、性能优化和安全。
4.1 故障转移与备份服务器
Nginx可以设置备份服务器,当所有主服务器都不可用时,流量会降级到备份服务器。
upstream backend_servers { server 192.168.1.101:8080; server 192.168.1.102:8080; server 192.168.1.103:8080 backup; # 标记为备份服务器 }备份服务器通常配置较低,仅用于展示维护页面或提供最基本的只读服务,保证业务不彻底中断。
4.2 使用Zone实现共享内存与状态同步
在多个Nginx工作进程的场景下,upstream中服务器的状态(如连接数、失败次数)需要在进程间共享。这需要通过zone指令定义一块共享内存。
upstream backend_servers { zone backend_zone 64k; # 分配64KB共享内存 least_conn; server 192.168.1.101:8080; server 192.168.1.102:8080; }zone对于least_conn和hash类算法是必须的,它能确保所有工作进程对后端负载的认知是一致的。内存大小(如64k)通常足够,除非你有非常大量的上游服务器。
4.3 基于条件的请求重试 (proxy_next_upstream)
当请求转发到一台后端服务器失败时,你可以定义在何种情况下,Nginx应该尝试下一台服务器。
location / { proxy_pass http://backend_servers; proxy_next_upstream error timeout http_500 http_502 http_503 http_504; }上述配置表示,如果遇到网络错误、超时、或者后端返回500、502、503、504状态码,就尝试下一个上游服务器。注意:对于POST等非幂等请求,重试可能导致数据重复提交(如订单重复支付),需要谨慎设置。通常只对GET、HEAD等幂等请求启用重试,或者在后端应用层实现幂等性。
4.4 连接池与长连接优化 (keepalive)
为每个请求都创建新的TCP连接到后端是非常消耗资源的。启用上游连接池可以大幅提升性能。
upstream backend_servers { server 192.168.1.101:8080; server 192.168.1.102:8080; keepalive 32; # 为每个Nginx工作进程保持最多32个空闲长连接 } server { location / { proxy_pass http://backend_servers; proxy_http_version 1.1; # 必须使用HTTP/1.1以支持keep-alive proxy_set_header Connection ""; } }keepalive指令指定了每个工作进程可缓存的空闲连接数上限。设置proxy_set_header Connection “”是为了清除客户端请求中的Connection头,防止其干扰Nginx与后端的长连接。这个优化在高并发场景下效果显著,能降低延迟,减少TCP握手和慢启动的开销。
5. 生产环境排坑实录:那些让你熬夜的问题
配置写完,测试通过,上线后却可能遇到各种光怪陆离的问题。下面分享几个典型的排查案例。
5.1 案例一:负载不均,部分服务器压力巨大
现象:采用轮询算法,但监控显示Server A的QPS远高于Server B和C。排查:
- 检查Nginx配置,确认权重设置无误。
- 检查后端服务器日志,发现Server A收到了大量
POST请求,而B和C多是GET请求。 - 根因:客户端的某些爬虫或移动端APP,在请求失败后进行了快速重试。由于Nginx的默认行为,在极短时间内来自同一客户端的连续请求,可能因为TCP连接复用或调度瞬时状态,被分配到同一台后端服务器。虽然轮询长期看是均匀的,但短时间窗口内可能不均匀。
- 解决方案:对于需要更均匀分布的场景,可以尝试使用
least_conn算法。或者,如果怀疑是客户端行为导致,可以在Nginx层对异常高频的IP进行限速 (limit_req模块)。
5.2 案例二:获取不到用户真实IP (X-Forwarded-For失效)
现象:后端应用日志记录的访问IP全是Nginx负载均衡器的内网IP(如192.168.1.100)。排查:
- 确认Nginx配置中已正确设置
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for。 - 在Nginx的访问日志格式
log_format中添加$proxy_add_x_forwarded_for变量,查看Nginx是否确实接收并生成了该头信息。 - 根因:请求可能经过了多层代理(如:用户 -> CDN -> WAF -> Nginx -> 后端)。
$proxy_add_x_forwarded_for变量会不断追加IP。后端应用默认可能只信任直接连接它的上一跳IP(即Nginx的IP),并从这个头中提取最后一个IP(即Nginx追加的客户端IP)。但如果后端应用配置错误,比如错误地提取了第一个IP,而第一个IP可能是CDN的IP,导致问题。 - 解决方案:
- 后端修正:确保后端应用(如Nginx、Apache、Tomcat或应用代码)从
X-Forwarded-For头中正确解析IP。通常,需要配置信任的代理链,然后取最后一个非信任的IP作为真实IP。例如,在Nginx后端中,可以使用real_ip_header X-Forwarded-For; set_real_ip_from 192.168.1.100;来信任负载均衡器,并提取真实IP。 - 使用
X-Real-IP:在负载均衡器层,如果确定前面只有一层可信代理(如公司统一的网关),可以直接将真实IP写入X-Real-IP头,后端直接读取这个头,更简单可靠。
- 后端修正:确保后端应用(如Nginx、Apache、Tomcat或应用代码)从
5.3 案例三:502 Bad Gateway 间歇性出现
现象:服务偶尔返回502错误,但后端服务器监控显示一切正常。排查:
- 查看Nginx错误日志 (
error.log),通常会有类似upstream timed out (110: Connection timed out)或connect() failed (111: Connection refused)的记录。 - 连接拒绝 (Connection refused):说明在Nginx尝试建立连接的瞬间,后端服务器的端口无进程监听。可能原因是后端应用进程崩溃后重启,在重启的短暂间隙,请求到达。优化应用的重启策略,或使用
backup服务器顶替。 - 连接超时 (Connection timed out):说明TCP握手失败。可能原因:
- 后端服务器网络问题或防火墙规则阻止。
- 更常见的原因:后端服务器的连接数已满(
netstat查看),无法接受新连接。这可能是后端应用并发处理能力不足,或数据库连接池耗尽等连带效应。 - Nginx与后端服务器之间的网络延迟过高,超过了
proxy_connect_timeout的设置(默认60秒,通常不会触发)。
- 解决方案:
- 适当增加后端服务器的
net.core.somaxconn等内核参数。 - 优化后端应用性能,减少请求处理时间,释放连接。
- 检查是否有慢查询拖垮数据库,进而拖垮应用。
- 在Nginx层面,可以稍微调大
proxy_connect_timeout,但更重要的是设置合理的proxy_next_upstream和max_fails,让Nginx能快速剔除故障节点。
- 适当增加后端服务器的
5.4 案例四:重载配置后,部分长连接请求失败
现象:在修改Nginx配置并执行nginx -s reload平滑重载后,监控到有一小撮请求失败。排查:nginx -s reload会启动新的工作进程加载新配置,然后优雅关闭旧进程。优雅关闭会等待旧进程处理完已建立的连接。根因:如果某些客户端到Nginx,或Nginx到后端的连接是长连接(Keep-Alive),并且正在处理一个耗时很长的请求(如下载大文件),这个旧连接可能会存活很长时间。旧进程关闭后,这些连接会被强制中断,导致请求失败。解决方案:
- 对于非常重要的服务,可以考虑在低峰期进行重启,而非重载。
- 在
upstream配置中,为server指令添加slow_start参数。这样,当一台服务器从故障中恢复或被重新加入集群时,权重会从0逐渐增加到设定值,避免瞬间涌入大量请求将其再次打垮。但这不能完全解决重载中断问题。 - (进阶)通过API动态管理上游服务器,而不是通过重载整个配置文件。可以使用Nginx Plus的商业功能,或者开源方案如
nginx-upsync-module结合Consul等配置中心。
6. 性能调优与监控:让负载均衡器本身不再是瓶颈
Nginx本身性能很强,但不当的配置也会成为瓶颈。以下是一些关键调优点。
6.1 系统层面优化
- 文件描述符限制:Nginx每个连接都会消耗一个文件描述符。使用
ulimit -n查看,确保其值足够大(如65535或更高)。需要在/etc/security/limits.conf中为Nginx进程的用户设置。nginx soft nofile 65535 nginx hard nofile 65535 - 网络内核参数:调整
/etc/sysctl.conf中的参数。
修改后执行net.core.somaxconn = 65535 # 提高连接队列长度 net.ipv4.tcp_tw_reuse = 1 # 允许重用TIME_WAIT状态的socket net.ipv4.tcp_fin_timeout = 30 # 减少FIN_WAIT_2状态时间sysctl -p生效。
6.2 Nginx配置优化
- 工作进程与连接数:
worker_processes auto; # 通常设置为CPU核心数 events { worker_connections 10240; # 每个工作进程的最大连接数 use epoll; # Linux下使用epoll高效事件模型 multi_accept on; # 一个工作进程同时接受多个新连接 }worker_connections乘以worker_processes就是Nginx能处理的最大并发连接数。确保这个值大于你的最大预期并发。 - 缓冲与缓存:如前面配置所示,合理设置
proxy_buffer_size和proxy_buffers。对于响应体很大的代理场景(如文件下载),适当调大这些值可以减少磁盘I/O。但也不宜过大,会占用过多内存。 - 禁用访问日志:对于压力极大的负载均衡器,如果不需要记录每一条访问日志,可以关闭或只记录错误日志,能节省大量磁盘I/O和CPU。
access_log off; # 在特定的location或server块中关闭
6.3 监控指标与告警
一个健康的负载均衡器需要被持续监控。关键指标包括:
- Nginx自身状态:通过
ngx_http_stub_status_module模块暴露基础状态。
访问location /nginx_status { stub_status on; access_log off; allow 127.0.0.1; # 只允许本机访问 deny all; }http://your-nginx-server/nginx_status会得到类似以下输出:Active connections: 291 server accepts handled requests: 16630948 16630948 31070465 Reading: 6 Writing: 179 Waiting: 106Active connections:当前活跃客户端连接数。accepts/handled/requests:总接受连接数、总处理连接数、总请求数。正常情况下accepts应等于handled,若不等于说明有连接被丢弃。Reading:正在读取请求头的连接数。Writing:正在向客户端写入响应的连接数。Waiting:空闲的Keep-Alive连接数。
- 上游服务器状态:使用商业版Nginx Plus或开源第三方模块(如
nginx-upsync-module或vts模块)来监控每个upstream中服务器的健康状态、响应时间、流量分布。 - 系统资源:监控负载均衡器服务器的CPU、内存、网络带宽和磁盘I/O。
- 业务指标:监控经过负载均衡器的总QPS、平均响应时间、错误率(特别是5xx和4xx比例)。
将这些指标接入Prometheus+Grafana或商业监控系统,并设置告警规则(如:上游服务器失败节点超过50%、平均响应时间大于1秒、5xx错误率超过0.1%),你就能在用户感知之前发现问题。
7. 架构演进:从单Nginx到高可用集群
单台Nginx负载均衡器本身也成为了一个单点故障。因此,生产环境需要构建高可用的负载均衡层。常见的方案有:
1. 主备模式 (Keepalived + VIP)使用Keepalived实现虚拟IP (VIP) 的漂移。两台Nginx服务器一主一备,共享一个VIP。客户端访问VIP。Keepalived通过心跳检测主节点健康,一旦主节点故障,VIP自动漂移到备节点,实现秒级切换。配置相对简单,但备机资源闲置。
2. DNS轮询在DNS层面配置多个A记录,将域名解析到多个Nginx服务器的IP上。客户端会随机或轮询选择IP。这种方法成本低,但故障切换依赖DNS TTL,时效性差(分钟级),且无法感知服务器真实健康状态。
3. 云服务商负载均衡器 (SLB/ALB/ELB)直接使用阿里云SLB、AWS ALB/ELB等云产品。它们提供开箱即用的高可用、自动伸缩、强大的监控和WAF集成。对于在云上部署的业务,这是最省心、最推荐的方式。你只需要将后端服务器挂载到负载均衡实例下即可。
4. 多活集群 (BGP+Anycast)在大型全球业务中,可以使用BGP协议在多个数据中心宣告相同的IP段(Anycast),用户流量通过路由协议自动导向最近的数据中心。每个数据中心的入口都是一组Nginx集群。这是最复杂也是扩展性最好的方案。
对于大多数中小型业务,主备模式或直接使用云负载均衡器是性价比最高的选择。随着业务增长,架构也需要相应演进。Nginx作为负载均衡器,无论是作为独立的软件,还是作为更大流量调度体系中的一环,其核心价值和配置思想都是相通的。理解它,不仅能解决眼前的分流问题,更能为你构建更复杂、更健壮的分布式系统打下坚实的基础。