1 常见负载均衡的分类
说到常见负载均衡的分类,我们先来看看什么是 OSI 七层模型。我们常说的二层/三层/四层交换机,就是基于 OSI 七层模型所对应的层来命名的。我刚开始接触网络设备的时候,第一次听说二层交换机、三层交换机时,还以为交换机的硬件厚度有二层、有三层,后来才知道,其实是按照 OSI 七层模型来区别不同交换机的原理功能。OSI 七层模型主要作为一个通用模型来做理论分析,它是一个理论模型。而 TCP/IP 通信协议模型(四层)是互联网的实际通信协议,两者一般做映射对比及分析。
OSI 七层模型及对应的传输协议
而常见的负载均衡有很多,有出名的硬件负载均衡,如 F5、Netscaler;还有常见开源的负载均衡,比如 LVS、HAProxy、Nginx,甚至 Apache 也能做负载均衡(不推荐)。我们常说的七层负载均衡、四层负载均衡,也是基于 OSI 七层模型的。基于 OSI 七层模型的底层原理,我们把常见的负载均衡具体划分为以下几个类型:二层、三层、四层、七层。最后再加上一个除 OSI 七层模型以外的 DNS,通过 DNS 做负载均衡的场景也是非常常见的。
负载均衡的分类
当前二层、三层负载均衡的实现只有 LVS 能做到。而相比在四层、七层的负载均衡应用得要更为广泛,比如硬件负载均衡、Nginx、HAProxy 热门的中间件都支持四层、七层的负载均衡。
2 负载均衡开山鼻祖—LVS 的工作原理
前面已介绍了 LVS 的应用场景及特性,但在介绍二层/三层/四层/七层负载均衡对比之前,有必要说一下 LVS 的原理。在运维领域,相信大家对 LVS 并不陌生,几乎提到负载均衡,提到运维,都会提到 LVS。在实际负载均衡实践应用中,我看到了一些有意思的现象,接下来跟大家详细分享一下相关的技术实践。在此之前,先来看看负载均衡技术相关术语缩写。
负载均衡技术相关术语缩写
LVS 负载均衡技术的实现,主要由 IPVS 和 Ipvsadm 实现。
IPVS:是 LVS 集群系统的核心部分,是基于 Linux Netfilter 框架实现的一个内核模块,主要工作于内核空间的 INPUT 链上。其钩子函数分别 HOOK 在 LOCAL_IN 和 FORWARD 两个 HOOK 点。
需要特别注意的是,IPVS 是直接作用在内核空间进行数据包的修改及转发的。而 Nginx/HAProxy 作用在用户空间,这使得 LVS 的性能更为强悍(能够赶上硬件负载均衡的性能),而 Nginx/HAProxy 根本不是一个量级。并且现在 IPVS 已经是 Linux 内核标准的一部分,在早期的 Linux 系统版本中,安装 LVS 还需要重新编译内核,当然现在已经不需要了。
内核LVS数据包流向
Ipvsadm:而 Ipvsadm 是工作在用户空间,主要用于用户定义和管理集群服务的工具。所以实际在安装配置 LVS 时,主要是安装配置 Ipvsadm。比如在 Redhat 中安装一条简单命令来实现:
yum install ipvsadm
LVS 原理架构
在 Director Server 上,IPVS 虚拟出一个对外提供访问的 IP 地址,用户必须通过这个虚拟的 VIP 地址访问服务器。由于 LVS 是采用 VIP(三层 IP 地址)作为请求入口的,这也是很多人喜欢把 LVS 统称为 IP 负载均衡的原因,即也是三层负载均衡。但 LVS 有 DR/IP TUN/NAT3 种模式,每种模式的核心原理分别作用在二层/三层/四层,所以把 LVS 称为二层/三层/四层负载均衡更为恰当些。
具体数据包的走向如下:
访问请求首先经过 VIP 到达负载调度器的内核空间。
PREROUTING 链在接收到用户请求后,会判断目标 IP,确定是本机 IP,将数据包发往 INPUT 链。
当用户请求到达 INPUT 时,IPVS 会将用户请求和 Ipvsadm 定义好的规则进行对比。如果用户请求的就是定义的集群服务,那么此时 IPVS 会强行修改数据包,并将新的数据包发往 POSTROUTING 链。
POSTROUTING 链接收数据包后发现目标 IP 地址刚好是自己的后端服务器,最终将数据包发送给后端的服务器。对于数据包的修改,基于修改方式的不同,形成了 LVS 的 3 种模式。
LVS 三种模式
3 二层负载均衡
当前的负载均衡界,只有 LVS 实现了二层负载均衡。所以二层负载均衡实践主要为 LVS 的 DR 模式实践。
二层负载均衡数据包走向原理
以下是对二层负载均衡数据包走向原理的详细说明:
客户端请求数据包报文的源地址是 CIP,目标地址是 VIP。
负载均衡会将客户端请求数据包报文的源 MAC 地址改为自己 DIP 的 MAC 地址,目标 MAC 改为了 RIP 的 MAC 地址,并将此包发送给后端服务器。这里要求所有后端服务器和负载均衡所在服务器只能在一个 VLAN(局域网)里面,即不能跨 VLAN。根据二层原理我们可以看到,在后端服务器中能直接获取客户端的源 IP 地址,netstat -n 能直接查看客户端请求通信的源 IP。示例代码如下:># netstat -nActive Internet connections (w/o servers)Proto Recv-Q Send-Q Local Address Foreign Address Statetcp 0 0 192.168.3.33:80 47.100.187.71:13537 ESTABLISHEDtcp 0 0 192.168.3.33:80 115.29.209.104:49435 ESTABLISHEDtcp 0 0 192.168.3.33:80 115.19.244.124:45418 TIME_WAITtcp 0 0 192.168.3.33:80 115.29.233.224:59310 ESTABLISHED
后端服务器发现请求数据包报文中的目的 MAC 是自己,会将数据包报文接收下来。由于数据包的 MAC 地址被修改,因此后端服务器需要在 lo 网口绑定 VIP,这样数据包才会有“归属感”。处理完请求报文后,将响应报文通过 lo 接口送给 eth0 网卡直接发送给客户端。由于数据包由后端服务器直接返回给客户端,因此也会要求后端服务器必须绑定公网 IP。
4 三层负载均衡
同样,在当前负载均衡界,只有 LVS 实现了三层负载均衡。所以三层负载均衡实践主要为 LVS 的 IP-TUN 模式实践。
三层负载均衡数据包走向原理
以下是对三层负载均衡数据包走向原理的详细说明:
客户端请求数据包报文的源地址是 CIP,目标地址是 VIP。
负载均衡将客户端请求数据包报文首部再封装一层 IP 报文,将源地址改为 DIP,目标地址改为 RIP,并将此数据包发送给后端服务器。与二层负载均衡不同的是,包通信通过 TUNNEL 模式实现,因此不管是内网还是外网都能通信,所以不需要 LVS VIP 与后端服务器在同一个网段内,即能跨 VLAN。但三层负载均衡原理导致在后端服务器中不能直接获取客户端的源 IP 地址,netstat -n 能查看到的是和负载均衡的通信 IP。示例代码如下:># netstat -nActive Internet connections (w/o servers)Proto Recv-Q Send-Q Local Address Foreign Address Statetcp 0 0 192.168.3.33:80 172.16.2.2:13537 ESTABLISHEDtcp 0 0 192.168.3.33:80 172.16.2.2:49435 ESTABLISHEDtcp 0 0 192.168.3.33:80 172.16.2.2:45418 TIME_WAITtcp 0 0 192.168.3.33:80 172.16.2.2:59310 ESTABLISHED
后端服务器收到请求报文后,会首先拆开第一层封装,然后发现里面还有一层 IP 首部的目标地址是自己 lo 接口上的 VIP,所以会处理次请求报文,并将响应报文通过 lo 接口发送给 eth0 网卡直接发送给客户端。
TUNNEL 模式走的隧道模式,运维起来比较困难,在实际应用中不常用。
5 四层负载均衡
LVS 的 NAT 模式、阿里云的四层 SLB、Nginx/HAProxy 的四层,虽然都是四层负载均衡,但是它们的底层原理有很大差异,这就导致它们的功能特性也有很大区别。
5.1 LVS-NAT 下的四层负载均衡
四层负载均衡 LVS-NAT 数据包走向原理如下图所示。以下是对四层负载均衡 LVS-NAT 数据包走向原理的详细说明。
客户端请求数据包报文的源地址是 CIP,目标地址是 VIP。
负载均衡将客户端请求数据包报文的目标地址改为 RIP 地址,并将此数据包发送给后端服务器。同样要求所有的后端服务器和负载均衡所在服务器只能在一个 VLAN(局域网)里面,即不能跨 VLAN。同样,根据 LVS 的 NAT 模式修改数据包报文的原理,在后端服务器中能直接获取客户端的源 IP 地址,netstat -n 能直接查看客户端请求通信的源 IP。
报文送到后端服务器后,目标服务器会响应该请求,并将响应数据包报文返还给负载均衡。每台内部的后端服务器的网关地址必须是负载均衡所在服务器的内网地址,即要配置 SNAT,这样数据包才能经过 LVS 返回给客户端。
然后负载均衡将此数据包报文的源地址修改为本机并发送给客户端。
四层负载均衡 LVS-NAT 数据包走向原理
我们可以看到,目标服务器处理完数据包返回给客户端的时候,会经过 LVS,然后再把数据包回给客户端。由此可以看到,LVS NAT 模式存在很大的性能瓶颈(就是在于 LVS 这一端),而相比于 DR 及 IP-TUN 模式,数据包后端服务器直接返回给客户端就不存在这个问题。在实际运用中,我也遇到过这个真实的性能瓶颈案例。之前工作过的一家公司主要从事电商领域,在一年一度的双十一备战中,流量高峰期客户端出现严重的卡顿、丢包和延时。当时负载均衡采用的就是 LVS 的 NAT 模式,我们发现高并发高流量模式下,内网的流量可达到千兆。那时紧急切换到 LVS DR 模式,故障很快消除了。
5.2 阿里云 SLB 下的四层负载均衡
阿里云四层负载均衡针对 LVS 的一些问题进行了定制和优化,新增转发模式 FULLNAT。
阿里云四层负载均衡对 LVS 的优化
由此可见基于 LVS,阿里云四层负载均衡实现了跨 VLAN、具备防 DDoS 攻击的能力、采用集群方式部署。这 3 个方面相比原生态的 LVS,功能上做了很大的改进。
实践应用中,我们发现使用阿里云的四层负载均衡,数据包的走向跟后端的 ECS 有没有绑定公网 IP 有直接关系(仅限经典网络)。
当后端 ECS 不绑定公网的时候,负载均衡转发数据包给后端 ECS。后端 ECS 处理完将返回数据包给负载均衡,负载均衡再返回数据包给客户端。这种方式类似 LVS 的 NAT 模式。
当后端经典网络的 ECS 绑定公网的时候(只有经典网络的 ECS 绑定的公网 IP 会单独分配公网网卡 eth1),负载均衡转发数据包给后端 ECS。后端 ECS 处理完将数据包直接通过公网网卡返回给客户端。这种方式类似 LVS 的 DR 模式。在 ECS 的公网网卡监控中看不到这块的流量明细,这块流量内容直接归并到 SLB 中计算了。但我们通过 Zabbix 相关监控,可以看到 eth1 公网网卡流量,并能抓到相应明细。有时候实际公网网卡流量甚至远远超过公网带宽峰值,由此可以看到,这部分返回给客户端的数据包走公网网卡,不受后端 ECS 绑定的公网带宽限制,且不纳入 ECS 的流量计费,单独放在 SLB 流量计费中了。
如果 ECS 有公网 IP 和私网 IP,禁用公网网卡就会影响负载均衡服务,因为在有公网网卡的情况下,默认路由会走公网,如禁用就无法回包,因此负载均衡服务会受影响。建议不要禁止,如一定要这么做,需要修改默认路由为私网才会不影响服务。但需要考虑业务是否对公网有依赖,如通过公网访问 RDS 等。
5.3 Nginx/HAProxy 下的四层负载均衡
四层负载均衡 Nginx/HAProxy 数据包走向原理如下图所示。以下是对四层负载均衡 Nginx/HAProxy 数据包走向原理的详细说明。
四层负载均衡 Nginx/HAProxy 数据包走向原理
需要重点注意的是,这里已经跟 LVS 有本质的区别。LVS 的 NAT 模式对外是以虚拟 VIP 作为请求入口(IP 层为三层),然后在三层负载均衡的基础之上用 IP+PORT 接收请求,再转发到对应后端服务器,所以 LVS 的 NAT 模式是三层负载均衡+四层负载均衡。而 Nginx/HAProxy 的四层对外直接暴露的是 DIP+TCP/UDP IP 端口服务。
客户端请求数据包报文的源地址是 CIP,访问目标地址是 DIP+IP 端口。
负载均衡在用户空间接收数据包,并且负载均衡和后端服务器发起新的 TCP 三次握手,建立新的 TCP 连接。所以这时候是负载均衡代替客户端与后端发起新的 TCP 请求连接。请求数据包为一个新的请求数据包,报文的源地址是 DIP,目标地址是 RIP。所以在后端服务器中,netstat -n 查看到的是和负载均衡的通信 IP。但负载均衡和后端服务器是建立新的 TCP 连接,所以后端服务器和负载均衡所在服务器可以进行跨 VLAN(局域网)通信。
报文发送到后端服务器后,服务器响应该请求,并将响应数据包报文返还给负载均衡。相比 LVS 的 NAT 模式,Nginx/HAProxy 下的四层负载均衡无须将每台内部的后端服务器的网关地址设为负载均衡所在服务器的内网地址,即无须配置 SNAT。因为是负载均衡和后端服务器建立了新的 TCP 连接,不必担心数据包不会返回给负载均衡。
然后负载均衡将此数据包报文的响应内容进行重新打包并返回给客户端。
6 七层负载均衡
在常见负载均衡分类中,其实应用最为广泛的还是七层和四层负载均衡。而七层和四层负载均衡最主要的区别是,在七层负载均衡中能获取用户请求的 HTTP 头信息。具体什么是 HTTP 头信息,我们以 nginx.conf 配置中定义的 Nginx 日志文件的格式内容从侧面进行说明:
log_format '$remote_addr - $remote_user [$time_local] "$request" ''$status $body_bytes_sent "$http_referer" ''"$http_user_agent" "$http_x_forwarded_for"';
然后我们在对应的日志文件中可以看到具体客户端的请求头内容信息,如下:
45.127.220.171 - - [02/Dec/2018:14:10:17 +0800] "GET https://qiaobangzhu.cn ( https://qiaobangzhu.cn ) HTTP/1.1" 301 184 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36"
可以看到在七层负载均衡中,我们能获取客户端访问的 IP、HTTP 请求类型(GET/POST)、访问的域名主机地址及请求的 URL 明细、浏览器的类型等,这便是 HTTP 请求头信息。而七层负载均衡的原理就是根据 HTTP 请求头来做判断转发,在七层负载均衡中应用最为广泛的当数虚拟主机功能。其实虚拟主机功能的核心就是获取 HTTP 请求头中的 HOST 字段来对应匹配转发。
七层负载均衡数据包走向原理
以下是对七层负载均衡数据包走向原理的详细说明。
和 Nginx/HAProxy 的四层一样,客户端请求数据包报文的源地址是 CIP。只不过在实际应用中,这里访问的目标地址并不是 DIP+IP 端口,而是 URL。
同样和 Nginx/HAProxy 的四层一样,负载均衡在用户空间接收数据包,并且负载均衡和后端服务器发起新的 TCP 三次握手,建立新的 TCP 连接。所以这时候是负载均衡代替客户端与后端发起新的 TCP 请求连接。请求数据包是新的,报文的源地址是 DIP,目标地址是 RIP,并且还有客户端请求的目标 URL(这里可以看到,对于七层负载均衡,目标地址可能一直在变,但访问的目标 URL 始终不变,除非修改了客户端请求内容)。
报文送到后端服务器后,服务器响应该请求,并将响应数据包报文返还给负载均衡。跟 Nginx/HAProxy 一样,这里无须额外配置后端服务器的网关,即 SNAT 相关配置。
然后负载均衡将此数据包报文的响应内容进行重新打包并返回给客户端。
综上所述,因为七层中能获取应用层 HTTP 的请求内容,所以七层负载均衡有如下常见应用场景:
七层作用在 HTTP 应用层,所以七层的负载均衡只能跟 Tomcat、PHP 等 Web 服务做负载均衡。
可以根据请求的域名来做转发。比如请求者访问 A 域名,转发到后端 A 服务器;请求访问 B 域名,转发到后端 B 服务器。这个功能,在七层叫虚拟主机功能,是七层应用中最为热门的应用实践。
可以根据请求的 URL 来做转发。比如请求者访问的 URL 包含 A 目录,就转发到 A 服务器;请求访问的 URL 包含 B 目录,就转发到 B 服务器。
可以根据请求的浏览器类型来做转发。比如请求者使用的浏览器类型是 Chrome,就转发到 A 服务器;请求者使用的浏览器类型是 Firefox,就转发到 B 服务器。
而四层只能获取访问的目标/源 IP 地址和端口。所以四层的负载均衡,单纯地是将请求轮询转发到后端目标服务器。并不能跟七层一样,做相应的逻辑判断,然后最终再转发给符合要求的后端目标服务器。相比七层,四层的应用场景包括:
对 MySQL、LDAP、Redis、Memcache 等四层应用做负载均衡。
对七层 HTTP 的 Web 类应用做负载均衡,如 Tomcat、PHP。
对七层 Nginx、HAProxy 的负载均衡做负载均衡(具体在实践中,四层和七层怎么来搭配使用,详见第二篇中负载均衡的相关章节)。
但四层中没办法跟七层一样,做虚拟主机的应用。我曾经在面试中问过这个问题,就是 LVS 能不能识别域名来做转发。比如请求者访问 A 域名,转发到后端 A 服务器;请求访问 B 域名,转发到后端 B 服务器。有意思的是,有一些经验丰富的运维人员对这个问题还回答不出来,这就需要好好看一下底层原理和对应负载均衡的应用场景了。答案肯定是不行的,因为 LVS 在四层和二层,没办法识别封装在七层中的数据包内容。再举个七层负载均衡实践的例子,要访问我们驻云内部的某个系统,要求客户端必须使用 Chrome 浏览器,可参考以下核心配置:
location / {if ( $http_user_agent !~ ^.Chrome/5|6. ){rewrite ^(.)$ https://qiaobangzhu.cn/error.html ( https://qiaobangzhu.cn/error.html ) permanent;}proxy_pass http://192.168.2.2:81/ ( http://192.168.2.2:81/ );proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;}
实现的核心配置就是获取七层头信息里面的数据来做对比判断,如果符合,执行对应的操作即可。这个是四层/二层负载均衡无法实现的功能。
7 负载均衡的“一次连接”和“两次连接”核心总结
7.1 LVS 二层/四层的“一次连接”
LVS 的 DR 模式、NAT 模式对数据包的处理都仅做“一次连接”,即负载均衡对数据包仅做转发。
LVS 的三次握手数据包走向
LVS 能够做到“一次连接”的本质原因是 LVS 工作在内核空间。LVS3 种模式都是工作在内核空间,数据包的处理也仅在内核空间,这也是 LVS 轻量高效、高性能的最为本质的原因。
LVS 能够做到“一次连接”,所以通过四层 SLB,我们能直接在后端服务器 ECS 中 netstat -n 查看到客户端通信的源 IP 地址。
LVS 请求数据包走向
7.2 Nginx/HAProxy 四层的“二次连接”
相比于 LVS,Nginx/HAProxy 四层要建立“二次连接”。
Nginx/HAProxy 四层三次握手数据包走向
客户端在向负载均衡进行 TCP 三次握手后,负载均衡会马上发起新的 TCP 连接,即为“二次连接”。由于是负载均衡与后端建立新的 TCP 三次握手及转发客户端请求的数据,所以在后端服务器 netstat -n 查看到的请求通信的 IP 是负载均衡的 IP。而相比于 LVS,Nginx/HAProxy 四层工作在用户空间,对数据包的处理是在用户空间完成的,数据包的流转及处理过程增多,这也是 Nginx/HAProxy 的性能和达不到 LVS 这个量级的本质原因。
Nginx/HAProxy 请求数据包走向
7.3 Nginx/HAProxy 七层的“二次连接”
相比于 Nginx/HAProxy 四层的二次连接,而 Nginx/HAProxy 七层的二次连接有些不一样。Nginx/HAProxy 四层的二次连接,是客户端和负载均衡进行 TCP 三次握手后,负载均衡和后端服务器马上进行新的 TCP 三次握手。而 Nginx/HAProxy 七层的二次连接,在客户端和负载均衡进行 TCP 三次握手后,还需要等客户端 Pushdata 传输数据,之后负载均衡和后端服务器才会建立新的 TCP 三次握手。由此可见,Nginx/HAProxy 四层的二次连接转发效率会更高。加上 Nginx/HAProxy 七层会进行一些 Rewrite 规则的判断,会损耗一些 CPU 和内存的性能。所以相较而言,Nginx/HAProxy 四层的性能要高许多。同样,由于是负载均衡跟后端建立新的 TCP 三次握手及转发客户端请求的数据,所以在后端服务器 netstat -n 查看到的请求通信的 IP 是负载均衡的 IP。所以在后端服务器中,HTTP 头的 remote_addr 虽然代表客户端的 IP,但实际值是负载均衡的 IP。为了避免这个情况,七层负载均衡通常会增加一个叫作 x_forwarded_for 的头信息,把连接它的客户端 IP(上网机器 IP)加到这个头信息里,这样就能保障后端服务器可以获取到客户端真实的 IP。但实际上,我们会遇到后端服务器再把请求转发给到下一个目标服务器的情况,即:“客户端请求>>> Nginx >>> Nginx1 >>> Nginx2”的结构,我们在 Nginx1 服务器上通过 x_forwarded_for 的头信息获取到了客户端的真实源 IP,那如何在 Nginx2 上进一步获取客户端的源 IP 呢?在 Nginx1 上的转发规则中,配置如下代码:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
这样会让 Nginx1 转发给后端 HTTP 头中的 x_forwarded_for 信息中保存客户的真实源 IP。
Nginx/HAProxy 七层三次握手数据包走向
Nginx 七层能够进行“二次连接”,所以通过七层 SLB,我们在后端服务器 ECS 中 netstat -n 查看到和后端服务器通信的 IP 为 SLB 的 IP 地址,而不能直接获取客户端通信的源 IP。
8 DNS 负载均衡
从 OSI 七层模型来看,DNS 的域名解析其实作用在第七层应用层。
DNS 请求流向
在图中,本质上 DNS 的负载均衡还算不上七层负载均衡,因为 DNS 解析是在 TCP/IP 通信之前进行的。即如果在应用中用到了域名,需要先去请求 DNS 服务器获得目标 IP 地址,才能建立 TCP/IP 通信。相较于应用广泛的四层和七层的负载均衡,DNS 做负载均衡的场景就没那么常见了。DNS 做负载均衡会带来两个很大的问题,两个问题分别如下:
实践中,通过 DNS 解析配置多个 A 记录地址。不同的客户端来请求 DNS,返回 A 记录配置的不同的源 IP(也可以是负载均衡的 VIP 地址),从而达到负载均衡的效果。但我们发现,设置 DNS 的负载均衡,落到不同源 IP(也可以是负载均衡的 VIP 地址)的请求流量往往分布得很不均匀。有可能是某个后端地址的请求量很大,而另一个后端地址的请求量却很小。
由于客户端往往有 DNS 相应缓存,如若 DNS 解析的某个源 IP 服务异常,一般它不会主动剔除这个有异常的源 IP 解析。这可能会导致部分客户的解析访问还是这个有异常的服务地址。虽然现在 DNS 有智能解析的高级功能,能主动监测后端服务的可用性。但是我们唯一不能把控的就是客户端的 DNS 缓存,大部分客户端的电脑 DNS 都有缓存。有可能是 DNS 已经解析到最新的 IP,但这时候客户端的 DNS 缓存还是会获取解析到的旧的 IP,这会导致这个客户端可能一段时间内一直解析访问到那个有异常服务的 IP。
DNS 的负载均衡,一般在超大规模的应用中,特别是跨地域的分布式应用中运用得非常广泛,常规的中小型、中大型应用,不是特别推荐尝试 DNS 的负载均衡。DNS 作为负载均衡的应用场景将会在第二篇中详细介绍。
9 总结
不同负载均衡的类型的功能特性汇总。