请求穿过后端系统 点图看注解 · F5、华为 CCE、Redis、GaussDB
Learning Note · 后端入门

请求怎么穿过你的后端系统

一条线往下走:用户发出请求,负载均衡接住并分流,容器集群处理,数据层供给数据,然后原路返回。点图里任意一块看它是什么,点虚线框里的标签看这个区域的不同形态。完全没接触过后端,先看下面的「01 名词表」。

点击图中的模块,查看它的注解;点虚线框里的标签,切换这个区域的形态

浏览器 手机 App 其他系统 HTTPS 请求 接入层 VS IP 整张图唯一的公网地址 F5 部署形态 单机 双机热备 F5 / 迪普 单台设备 一台设备扛全部流量;它一挂,整个入口就断了 F5(主) F5(备) 心跳 主备互盯;主机挂了,VS IP 漂到备机,用户基本无感 Self IP 从它往下,全是内网地址 应用层 · 华为 CCE 集群入口 · 对外暴露方式 NodePort Ingress NodePort 四层(传输层) 做法:每个节点开一个端口(默认 30000–32767),用「节点 IP : 端口」直接访问 Ingress 七层(应用层) 做法:统一入口,按域名和路径把请求分给不同的服务 Service ClusterIP Node 节点 Deployment Pod 1 Pod 2 Pod 3 读:先问 Redis 数据层 Redis 部署形态 单机 主从 集群 Redis 一台机器;它重启或宕机的那段时间,缓存全部失效,请求会瞬间压到数据库 Redis(主) Redis(从) 同步 主负责写、从负责读;主挂了由哨兵把从提成新的主 —— 读多写少的场景最常用 Redis 1 Redis 2 Redis 3 数据按规则切成几份分散在多台机器上(每一份通常还有副本),容量和吞吐量一起往上加 未命中 / 写 回填 GaussDB 部署形态 主备 分布式 GaussDB(主) GaussDB(备) 同步 一主一备,数据实时同步;主库故障切到备库,一份数据都不会丢 GaussDB 1 GaussDB 2 GaussDB 3 数据按规则拆到多个数据节点上,单表几亿行也扛得住 —— 代价是运维更复杂
浏览器 手机 App 其他系统 HTTPS 请求 接入层 VS IP 整张图唯一的公网地址 F5 部署形态 单机 双机热备 F5 / 迪普 单台设备 一台设备扛全部流量;它一挂,整个入口就断了 F5(主) 心跳 F5(备) 主备互盯;主机挂了,VS IP 漂到备机,用户基本无感 Self IP 从它往下,全是内网地址 应用层 · 华为 CCE 集群入口 · 对外暴露方式 NodePort Ingress NodePort 四层(传输层) 做法:每个节点开一个端口(默认30000–32767),用「节点 IP : 端口」直接访问 Ingress 七层(应用层) 做法:统一入口,按域名和路径把请求分给不同的服务 Service ClusterIP Node 节点 Deployment Pod 1 Pod 2 Pod 3 读:先问 Redis 数据层 Redis 部署形态 单机 主从 集群 Redis 一台机器;它重启或宕机的那段时间,缓存全部失效,请求会瞬间压到数据库 Redis(主) Redis(从) 同步 主负责写、从负责读;主挂了由哨兵把从提成新的主 —— 读多写少的场景最常用 Redis 1 Redis 2 Redis 3 数据按规则切成几份分散在多台机器上(每一份通常还有副本),容量和吞吐量一起往上加 未命中 / 写 回填 GaussDB 部署形态 主备 分布式 GaussDB(主) GaussDB(备) 同步 一主一备,数据实时同步;主库故障切到备库,一份数据都不会丢 GaussDB 1 GaussDB 2 GaussDB 3 数据按规则拆到多个数据节点上,单表几亿行也扛得住 —— 代价是运维更复杂

← 左右滑动查看完整长图 →

图 1 · 请求从上往下走。图里有 4 个虚线框,每个框都是「这里有好几种形态」:接入层的 F5(单机 / 双机热备)、应用层的集群入口(NodePort / Ingress)、Redis(单机 / 主从 / 集群)、GaussDB(主备 / 分布式)。点框里的标签,只切换这个框里画的东西。点任意模块,注解从右侧滑出。

图 2 · 一次真实请求(查数据 / 写数据)

图 1 讲的是「系统里有哪些东西」,这一张讲「一次请求具体怎么走」。横着看是五台角色(浏览器 → F5 → CCE → Redis → GaussDB),竖着看是时间:箭头越往下越晚发生。蓝色实心箭头是请求,虚箭头是往回走的响应。图上的标签只说「这一步在干什么」;想细看这一步在网络上真正长什么样——谁寄给谁、地址怎么变的——点那一步就行。

点上面的标签切换三种走向;点图里的任意一步,看这一步在网络上到底长什么样

这次请求是: 查数据 · 缓存命中 查数据 · 未命中 写数据 · 转账 ① 浏览器发起:GET /api/balance(查余额) ② F5 挑一台健康的后端,源地址换成 Self IP ④ 查缓存:GET balance:user:1001 ⑤ 命中!直接返回余额(约 0.2 毫秒) ⑥ 组装响应 ⑦ 原路返回给浏览器 整条链路约 5 毫秒,数据库一次都没被打扰。用户全程只知道 203.0.113.100 这一个地址。 这就是缓存存在的意义:绝大多数查询止步于 Redis,GaussDB 只在第一次接活。 ① 浏览器发起:GET /api/balance(查余额) ② F5 挑一台健康的后端,源地址换成 Self IP ④ 查缓存:GET balance:user:1001 ⑤ 未命中:Redis 里没有这个键 ⑥ 这才去查库:SELECT … WHERE user_id=1001 ⑦ 返回这一行数据 ⑧ 顺手回填:SET balance:user:1001 … EX 300 ⑨ 组装响应 ⑩ 原路返回给浏览器 这条才是「第一次」:要真的查库、还要回填,几十毫秒很正常。 慢只慢第一次——同样的请求再来一次,全部走上面那条「命中」的路径。 ① 浏览器发起:POST /api/transfer(转 100 元) ② F5 挑一台健康的后端,源地址换成 Self IP ④ 写库:BEGIN → UPDATE → COMMIT(一个事务) ⑤ 提交成功:两个账户一起变了 ⑥ 删缓存:DEL balance:user:1001 ⑦ 组装响应 ⑧ 原路返回给浏览器 写操作不查缓存,直接写库;写完把缓存删掉,让下一次读重新回填。 为什么是「删」不是「改」:改要同时改两处、还可能改错;删掉最省事,下次自然读到新值。 时间 浏览器 / App F5 CCE 集群 Redis GaussDB ③ 集群内部:入口 → Service → 选中一个 Pod

← 左右滑动查看完整时序图 →

图 2 · 怎么看这张图:横着五个角色(浏览器 → F5 → CCE 集群 → Redis → GaussDB),竖着是时间,越往下发生得越晚。实线箭头是「请求往下走」,虚线箭头是「响应往回走」。图上只留了「这一步在干什么」,具体的地址和报文收进了抽屉——点任意一步就能看到那一步真实的 源地址:端口 → 目的地址:端口、带回来的数据,以及这一步为什么要这么走。两种查询的区别只在下半张图:命中走到 Redis 就返回了,未命中才继续往下查库、并把结果写回 Redis(这叫「回填」)。写数据走的是另一条路——不查缓存,直接写库,写完把缓存删掉。

这一路上的地址,都是些什么?

先分清最容易懵的一处:哪些是公网地址,哪些是内网地址。整条链路上只有两个公网地址——用户自己的出口 IP,和 F5 对外的 VS IP;从 F5 往里,全都是内网地址,公网根本访问不到。正文里凡是出现地址的地方,都按下面这套颜色标出来了。

网段性质谁在用外面能不能访问到
203.0.113.0/24 公网 用户的出口 IP、F5 对外的 VS IP 能。这就是留给互联网访问的那一个地址
10.20.30.0/24 内网 F5 自己的口,也就是 Self IP 所在网段 不能
192.168.10.0/24 内网 CCE 的 节点(Node) 不能
10.96.0.0/16 内网 K8s Service 的虚拟地址(ClusterIP) 不能。只在集群内部有效,连同一局域网里的别的机器都访问不到
10.244.0.0/16 内网 Pod 的地址,集群里每个容器一个 不能。只在集群内部有效
10.20.40.0/24
10.20.50.0/24
内网 Redis / GaussDB 所在网段 不能。数据层是最该被隔离的地方

203.0.113.100公网 10.244.1.7内网 正文和抽屉里的地址,都按这个颜色标。

一句话记住:用户能看见的自始至终只有 VS IP 这一个地址。F5 往里——Self IP、节点地址、Service 地址、Pod 地址——一层比一层深,全是内网。这也是为什么后端日志里永远看不到用户的真实 IP(要看得额外配 X-Forwarded-For),以及为什么「内网穿透」「端口映射」这类词会存在。
下面这张表把图里每一跳的地址集中列一遍。地址都是编的——公网用了 RFC 5737 专门留给文档的 203.0.113.0/24,内网用了常见私有段,安全、不会撞上真实机器;换成你们环境里的实际值即可。

这一跳源地址目的地址在干什么 / 要注意什么
浏览器 → F5 203.0.113.45:51423 203.0.113.100:443 用户出口的公网地址和它随手开的端口 → 域名的地址。203.0.113.100 就是 VS IP:用户只认这一个地址。
这一跳还没碰到 F5,两端都是公网、地址一个没改——它是整条链路里唯一走公网的一段。你电脑上其实是内网地址,出你家路由器时才被换成了左边这个公网地址。
F5 → CCE 节点 10.20.30.11:51423 192.168.10.21:30443 源地址被换掉了——变成 F5 自己的 Self IP。所以后端服务器看到的「客户端」是 F5,不是用户。
节点 → Pod 10.96.0.10:8080
(Service 的 ClusterIP)
10.244.1.7:8080 入口先进 Service,Service 再从背后几个 Pod 里挑一个。10.96.x.x 这类地址只在集群内部有效,外面连不上。
Pod → Redis 10.244.1.7 10.20.40.21:6379 应用代码直接连缓存。6379 是 Redis 的默认端口,看到这个端口号基本就能认出是 Redis。
Pod → GaussDB 10.244.1.7 10.20.50.31:8000 只有未命中写操作才走这一跳。这一步最慢,所以前面的缓存才有价值。
返回浏览器 203.0.113.45:51423 响应原路返回,出口处地址又换回 VS IP。用户从头到尾只知道 203.0.113.100,对里面的 10.x 一无所知。

为什么地址会变?是怎么变的?

这是整张图里最该弄懂的一处。把第 1 跳和第 2 跳并排放在一起看,三个位置变了,一个位置没变

源 IP 203.0.113.4510.20.30.11 把「谁寄的」换成 F5 自己。不改的话,内网的后端根本没有路把回包送回公网。这个动作叫 SNAT(源地址转换)。
目的 IP 203.0.113.100192.168.10.21 把「寄给谁」从对外的门牌号,换成真正干活的那台机器。这个动作叫 DNAT(目的地址转换)。
目的端口 44330443 443 是对外的标准 HTTPS 端口,30443 是集群给这个服务开的大门编号(NodePort)。端口也可以跟着一起换。
源端口 5142351423 唯独这一位原样不动。它是这条连接的「身份证号」——F5 后面就靠它,把回包和这次的请求对上号。

怎么变的?F5 收到包以后,把报文头拆开,改掉上面那三个位置,再原样转发出去。这个动作叫 NAT(网络地址转换)——不只是 F5,路由器、防火墙、家用宽带网关都在干同一件事,只是规模不同。

与此同时,F5 在内存里记一张会话表,一行就是一条连接:

客户端                F5 改写后             转发给                    状态
203.0.113.45:51423  →  10.20.30.11:51423  →  192.168.10.21:30443  →  10.244.1.7:8080   ESTABLISHED

回包回来时,F5 拿「源端口 51423」去查这张表,认出这是刚才那条连接,于是反着改回去:源地址改回 203.0.113.100:443,目的地址改回 203.0.113.45:51423。用户收到的回包,看起来就像是 203.0.113.100:443 亲手发的一样。

一句话:去的时候改一遍,回的时候改回来;用户全程只认识 VS IP,中间那些内网地址全被抹掉了。 正因为所有流量都从 F5 这一条固定通路进出,它才能顺手做健康检查、会话保持,也才能让 VS IP 在双机热备时从主机「漂移」到备机——而这,正是双机热备用户无感的原因。

VS IP 和 Self IP 到底差在哪

这两个词最容易混,其实一句话就能分清:一个是对外挂牌的,一个是对内自报家门的。

VS IP · 对外的门牌号

Virtual Server IP,虚拟服务器地址。它不属于任何一台真实服务器,是 F5 拿出来「挂牌营业」的地址。客户端连的是它,而它背后挂几台机器、机器换了没有,客户端完全不知道——换后端不用改客户端,这就是它存在的全部意义。

在上面那张图里:203.0.113.100。域名 app.example.com 解析到的就是它。

Self IP · 对内自己的地址

F5 这台设备自己在内网里的地址,用来跟后面的服务器说话。F5 转发时会把自己的 Self IP 填进报文的「源地址」——所以后端服务器看到的「客户端」永远是 F5。

在上面那张图里:10.20.30.11。它是第 2 跳的源地址。

为什么要换源地址?因为后端只认识内网,回包得能找回来。如果不换,后端要回一个公网地址,回程还得再绕一圈——换了之后,请求和响应都走 F5 这一条固定通路,F5 才好做健康检查、会话保持这些事。这也是双机热备能生效的前提:VS IP 是一个可以「漂移」的地址,主机挂了它挪到备机上,用户那边毫无感觉。

01

名词表 · 零基础版

完全没接触过后端的话,先扫一遍这一节。这里只解释最基础的词,全部用生活里的东西打比方,不讲具体产品。后面的长图和注解都会用到这些词。

怎么用这一节:不用背。往下读正文(或点开抽屉)时遇到看不懂的词,回这里查一下就行。名词表按「它属于哪一类」分组——机器与运行、网络、数据、动作、工具。

机器与运行 —— 东西跑在哪

服务器
一台长期开着、专门等着别人连过来的电脑。它没有显示器,放在机房里,你通过命令行远程操作它。
集群
把好几台机器当成一台来用的一组机器。对外看不出里面有几台,坏掉一两台也不影响使用。
节点
集群里的「一台机器」。容器最终都跑在节点上。
容器
把程序和它需要的一切(依赖库、配置)打包在一起,搬到任何机器上都能一模一样地跑起来。可以想成自带全部家当的标准集装箱
镜像
容器的「安装包 / 模板」。同一个镜像可以起出许多个一模一样的容器。
Pod
K8s 里最小的调度单位,一个或几个必须待在一起的容器。
命名空间
集群里的一块「分区」。不同团队、不同项目的资源各放各的,名字互不冲突。

网络 —— 请求怎么找到你

IP 地址
网络里一台机器的门牌号,比如 192.168.10.21
端口
同一台机器上的不同「窗口」。一台机器可以同时跑很多服务,靠端口区分:80 是网页、6379 是 Redis、5432 是数据库。
域名
给 IP 起的名字(app.example.com)。人记名字,机器只认 IP。
DNS
把域名翻译成 IP 的那套系统,相当于全网的电话簿。
请求 / 响应
你向服务器要东西 = 请求;服务器把结果还给你 = 响应。就是一问一答。
HTTP / HTTPS
浏览器和服务器之间说话的规矩。带 S 的是加密版,别人在中间截获也看不懂内容。
虚拟 IP
不属于任何一台真实机器的地址,只存在于负载均衡设备上。用户访问它,由设备决定转给谁。相当于公司总机号码
内网 / 外网
内网是机房里自己人互相访问的网络,外面进不来;外网就是公网,谁都能访问。

数据 —— 东西存在哪

内存
断电就没了、但极快的存储。像办公桌面:随手就能拿到,但下班一关灯,桌上东西全清空。
磁盘
断电还在、但慢得多的存储。像文件柜:要走过去翻,但东西一直在。
缓存
把常要用的数据先抄一份放在快的地方(内存)。下次要用,直接拿抄件,不用再跑一趟慢的。
数据库
专门用来长期、可靠地存放数据的软件。数据的正式归宿。
表 / 行 / 列
数据在数据库里的摆放方式,跟 Excel 一样:一张表有很多(每条记录)和(每个字段)。
SQL
跟数据库说话用的语言,比如「把 id=5 那一行的名字查出来」。
事务
一组必须一起成功、一起失败的操作。像转账:钱从 A 扣了,就必须给 B 加上,不能只做一半。
主从 / 副本
同样一份数据,在另一台机器上再存一份,坏一台不丢。通常主库负责写、从库负责读。

动作 —— 平时都在干嘛

部署 / 发版
把新版本的程序放到服务器上跑起来。
扩容 / 缩容
多开几个实例 / 少开几个。忙了加人,闲了减人。
回滚
新版本出了问题,退回上一个正常版本。
灰度
新版本先只给一小部分用户用,没问题再逐步放开到全部。
健康检查
定期探一下某个服务还活着没有,死了就不再往它那里发请求。
高可用
一台机器坏了,服务照样能用,用户感觉不到。
单点故障
整个系统里只有这一处,它一坏就全盘皆输。架构设计里最想消灭的东西。

工具 —— 用什么东西去看

命令行 / 终端
打字的方式操作电脑,而不是点鼠标。运维的大部分时间都在这里。
kubectl
操作 K8s 集群的命令行工具——K8s 世界的遥控器。
redis-cli
连上 Redis 敲命令的工具。
gsql
连上 GaussDB 敲 SQL 的工具(和 PostgreSQL 的 psql 用法一样)。
tmsh
F5 的命令行工具。网页界面能看的东西,它基本都能看和改。
日志
程序自己记的「今天干了啥」。出了问题时,第一个要看的就是它。
02

概念速览

每条一句话,用来快速回忆。有看不懂的词,回 01 名词表 查。想深入就看图里点开的抽屉——那里有例子、有生活类比,也有能直接照着敲的命令。

接入层 —— 请求先到这里

F5 / 迪普
负载均衡设备(ADC)。对外只暴露一个 VS IP,对内从 Pool 里挑一台后端转发。
VS IP
Virtual Server IP。用户访问的就是它,一个不属于任何真实服务器的虚拟地址。
Self IP
F5 自己网卡上的真实地址。转发时源地址换成它,所以后端看到的请求来自 F5,不是用户
Pool / Pool Member
F5 里的一组后端服务器,以及组里的每一台。
健康检查
定期探测池子里的成员还活着没有,挂了就踢出去不再分流量。
双机热备
两台负载均衡设备一主一备,之间连一根心跳线互相盯着。主机挂了,VS IP 漂到备机接管,用户基本无感。图里那个虚线框可以切单机 / 双机热备看对比。

应用层 —— 应用跑在哪、怎么找到它

Node 节点
集群里真正跑容器的机器。Pod 都跑在节点上。
Pod
最小的运行单元,一个或多个容器共用网络和存储。随时会被销毁重建,别往里存数据。
Deployment
声明「我要 3 个一模一样的 Pod」,K8s 负责永远维持这个数字。
Service
给一组 Pod 一个固定地址。Pod 换了一批又一批,Service 的地址不变。
ClusterIP
Service 的默认类型,集群内部的虚拟地址,外面进不来。

对外暴露的两种方式 —— 二选一

NodePort
四层(传输层)做法。在每个节点上开一个端口(默认 30000–32767),集群外直接用「节点 IP : 端口」访问到 Service。简单直接,但端口难记、也不好按域名分流。
Ingress
七层(应用层)做法。集群的统一入口,按域名和路径把请求分给不同的 Service。多个服务共用一个端口,是生产上更常见的做法。
关系
两者不是前后两道关卡,而是两条可选的路:要么用 NodePort 直接把服务暴露出去,要么用 Ingress 做统一入口。图上「集群入口」那个虚线框里可以切换看这两条路。

数据层 —— 一个管快,一个管准

缓存命中 / 未命中
数据在 Redis 里叫命中,直接返回;不在叫未命中,才往下查库。
TTL
缓存的过期时间,到点自动删。没有它,缓存会一直涨到撑爆内存。
回填
查库的结果写回 Redis,下次同样的请求就直接命中了。
Redis 的三种形态
单机(一台,最简单也最脆)/ 主从(主写从读,主挂了从顶上)/ 集群(数据分片到多台,容量和性能一起涨)。图里那个虚线框可以三种切换看。

数据库

主备 / 副本
数据在多台机器上各留一份,坏一台不丢数据。
事务
一组操作要么全成功、要么全失败,不会只做一半。
GaussDB 的两种形态
主备(一主一备,实时同步,挂了切备)/ 分布式(数据拆到多个数据节点,单表很大也能扛)。图里那个虚线框可以两种切换看。

四层(传输层)/ 七层(应用层)—— 最容易记混的一对

「四层(传输层)」「七层(应用层)」说的是在第几层做转发——数字越大,能看到的越多,也越费性能。

OSI 里的名字这一层能看到什么对应哪些设备
7应用层HTTP 内容:域名、路径、请求头、CookieIngress、F5 的七层模式
6表示层日常几乎不提,先忽略
5会话层日常几乎不提,先忽略
4传输层TCP / UDP:只有 IP 和端口F5 的四层模式、NodePort
3网络层IP 地址路由器

网络层是第 3 层,不是第 7 层。七层对应的是应用层(HTTP 那一层),四层对应的是传输层(TCP/IP 那一层)。

再提醒一次别搞混:我们这张长图上的「接入层 / 应用层 / 数据层」是按职责分的——说的是"这块东西负责干什么";OSI 的四层 / 七层是按协议层次分的——说的是"它能看到数据包的哪一层内容"。两套分法各说各的,图上那个装容器的「应用层」和 OSI 第七层的「应用层」只是重名,不是一回事。

贯穿全局的两个说法

VIP
虚拟 IP,泛指"对外暴露的那个地址"。F5 上的 VS IP 就是一种 VIP。
无状态 / 有状态
无状态的实例可以随便增减替换(Pod);有状态的数据握在自己手里(Redis、GaussDB)。

最容易搞混的两个地方

Pod 里不能存数据
Pod 随时会被销毁重建,写在容器本地的东西跟着一起没。临时状态给 Redis,正式数据给 GaussDB。
Redis 不是更快的数据库
它内存有限、默认不保证不丢,是可以重新生成的副本,不是数据的最终归宿。
03

跟着图走一遍

把图上的箭头按顺序念一遍,就是一次完整的请求。想看得更细,对照上面的 图 2 · 一次真实请求——那里有真的 URL、真的命令、真的耗时。

1

用户点了一下按钮

域名解析成一个公网地址,请求发出去。用户不知道也不关心后面有几台机器。

2

请求打到 F5 的 VS IP

F5 查健康检查表,挑一个还活着的 Pool Member 转过去。转发时源地址换成 Self IP。

3

请求进入 CCE 集群

按集群入口的走法(NodePort 或 Ingress 二选一)找到 Service,Service 再从 ClusterIP 背后挑一个 Pod。

4

代码先问 Redis

命中就返回,一毫秒的事,数据库完全没被打扰。

5

没命中才去查库

去 GaussDB 查,查到之后顺手回填 Redis,下次就命中了。

6

响应原路返回

F5 只管转发、不看内容,所以它能扛住大流量。

04

配套能力

前三章讲的是一条请求怎么穿过去。真实系统里光「能穿过去」远远不够——还得能扛住量、能查出问题、能改配置、能从故障里恢复。这一章把主流系统基本都会配的东西补齐,按「它堵的是哪个窟窿」分成五组,可以跳读。

第一组 · 请求进门之前——加密与拦截

请求到达 F5 之前,还有两道和「安全」有关的动作。它们不在图上的箭头里,但每个对外系统都有。

TLS / 证书
浏览器地址栏那个小锁就是它。开头的几下往返叫 TLS 握手:双方协商出一个只有彼此知道的密钥,之后所有内容都加密传输。证书是这中间的身份证,由权威机构签发,用来证明「你连的确实是这家公司」,而不是有人冒充。它装在哪儿是个关键选择:装在 F5 上,F5 到后端那一段就变回明文(业内叫「SSL 卸载」),后端程序轻松、排查也方便;装在容器里,全程加密更安全,但每个 Pod 都得管证书、每次续期都得重启。图 2 里 443 那个端口,就是 TLS 的入口。
WAF
Web 应用防火墙。TLS 管的是「别被人偷看」,WAF 管的是「别被人打坏」——它看的是解密之后的 HTTP 内容,专门拦 SQL 注入、跨站脚本、恶意爬虫这类有明显特征的攻击。所以它必须放在解密之后,通常就挨着 F5 或者干脆是 F5 上的一个模块。注意它不是万能的:拦得住已知套路,拦不住逻辑漏洞,代码该做的校验一样不能少。

第二组 · 让集群自己维持住

第一、二章里 F5 会「健康检查」后端。到了 K8s 内部,同一件事换个名字继续做——而且做得更细。

健康探针
K8s 定期问 Pod 两个不同的问题。存活探针问的是「你是不是卡死了,要不要重启你」;就绪探针问的是「你现在准备好接流量了吗,能不能把你加进 Service」。两者的区别很关键:卡死了该重启,而启动慢只是暂时不该给流量——这时候重启反而让它更慢。没有就绪探针,K8s 会在应用还没加载完数据时就把请求打进去,用户看到的就是一片报错。
弹性伸缩 HPA
手动扩容得有人盯着、有人操作。HPA 让 K8s 自己看 CPU、内存或自定义指标,超过阈值就自动加 Pod,降下来再减回去。它生效的前提是应用无状态——Pod 里存了数据就没法随便增减,这也是「别往 Pod 里写东西」这条规矩的实际价值。
配置中心
数据库地址、功能开关、限流阈值这类东西不该写死在代码里。ConfigMap 存普通配置,Secret 存密码和密钥。改完之后重新挂载、或滚动重启一次就能生效,不用重新打镜像、重新发版。有个常见误解要说清楚:Secret 只是做了 base64 编码,那是编码不是加密,谁拿到都能解开——真正的保护靠集群权限控制。
灰度发布
滚动更新是默认姿势:一批一批换 Pod,换的过程中旧的还在服务,用户基本无感。灰度更保守:新版本先只放 1% 的流量,盯着错误率和耗时,没问题再逐步放量,出事立刻把流量切回旧版本。它和滚动更新解决的不是同一件事——滚动更新保证不停服,灰度保证出问题也是小范围

第三组 · 出事了得能查

这三样合起来叫「可观测性」。判断标准很简单:线上出问题时,你能不能在五分钟内说出「哪里、什么时候、为什么」。

日志
程序自己记的流水账。K8s 里 Pod 随时会被销毁重建,容器里的日志跟着一起没,所以生产上必须把日志收集到外面去(常见做法是每台 Node 上跑一个采集器,统一送进日志平台)。日志一般分级别:DEBUG 自己调试用、INFO 正常流水、WARN 要注意、ERROR 出错了。排查问题的第一动作,永远是先按时间点搜日志。
指标监控
日志记的是「发生了什么」,监控记的是「现在是什么状态」——QPS、响应时间、错误率、CPU、内存、连接数。把这些画成曲线,再配上阈值规则,超了就发消息、打电话。它真正的价值是让你比用户先知道。没有它,你的故障发现机制就是「用户来投诉」。
链路追踪
一个请求穿过了 F5、网关、应用 A、应用 B、Redis、GaussDB,结果它慢了 2 秒。是哪一段慢的?链路追踪给每个请求发一个全局 ID,沿途每一段都记一笔耗时,最后拼成一棵调用树,一眼就能看出瓶颈在哪一跳。没有它,跨服务的性能问题基本只能靠猜。

第四组 · 扛不住的时候先自保

流量总会超过你的容量,下游总会偶尔变慢。这一组的共同思路是:与其一起崩,不如主动放弃一部分。

限流
系统能处理的请求量有上限,限流就是主动设一道闸,超过就拒绝(返回 429)或者排队。原则是宁可让一部分用户慢一点,也不能让所有用户一起崩。常见算法有令牌桶、漏桶、滑动窗口。位置越靠外越好——配在 F5 或网关上,请求根本进不到应用里就被挡住了。
熔断
下游某个服务挂了,如果还一直往它发请求,调用方会被拖住:线程全卡在等它返回,最后自己也被拖死,故障顺着调用链一路蔓延。熔断就是发现失败率超过阈值,先断开一段时间,请求直接返回兜底结果、不再白等;过一会儿再试探性放一点流量过去,通了就恢复。它和限流防的不是一回事:限流防自己被打挂,熔断防被下游拖死
降级
熔断或限流之后,总得给用户一个交代。降级就是放弃非核心功能,保住主干:推荐位加载不出来就显示默认内容,评论数查不到就先不显示,但商品详情照常打开、下单照常能走。它最难的从来不是技术,而是提前想清楚哪些功能可以没有
消息队列 MQ
有些活又慢又不急:发短信、生成报表、写行为日志。同步做完,用户就要平白多等两秒;丢进消息队列就立刻返回,后台的消费者慢慢处理。它还顺带解决两件事——削峰(突然涌来的流量先堆在队列里,后端按自己的速度消费)和解耦(生产者只管丢进去,不用关心谁在处理、有几个在处理)。代价是系统变复杂了,而且「消息有没有真的被处理」需要额外的机制去保证。

第五组 · 数据库这层的进阶话题

图 2 里 GaussDB 只是「查一下、返回」。真跑起来,这一层的问题最多,也最值得先学。

连接池
建立一条数据库连接要经过网络握手和身份认证,成本不低。连接池就是提前建好一批连接反复用,用完还回去而不是关掉。这是最容易被忽略、又最容易出事的配置之一:池子太小,请求排在池子外面等;池子太大,数据库自己被压垮。看到「平时好好的,一上量就卡住」,先怀疑它。
慢查询
数据库大多支持把「执行超过 N 秒的 SQL」记下来。看到慢查询,第一反应是看它有没有走索引——没走索引的查询会一行行扫完整张表,数据量一涨就慢得离谱。这是后端最经典、也最值得优先掌握的一类性能问题,往往一条索引就能把几秒压到几毫秒。
读写分离
主库负责写、从库负责读,读请求分给多个从库。大部分业务是读多写少,这么一来读的压力就被摊开了。代价是主从之间有一小段同步延迟——刚写完立刻去读从库,可能读到旧数据。所以「注册完立刻跳个人中心」这类场景要小心。
备份与恢复
前面讲的主备、副本,防的是机器坏;备份防的是数据被误改、被误删——这类错误会在几毫秒内同步到所有副本上,副本救不了你。要定期做全量加增量的备份,而且必须真的演练过恢复。一条行业铁律:没验证过能恢复的备份,等于没有备份。

这一章不用背。记住一个判断方法就够:任何一套后端系统,都要回答「怎么扛住量、怎么看见状态、怎么安全地改、怎么从故障里回来」这四个问题——上面每一样东西,都是在回答其中之一。下一章把整篇压缩成三条线索。

05

三条记忆线索

1
三站路:分诊台 → 后厨 → 仓库

把整条链路想成一次进餐厅吃饭。分诊台决定你去哪个诊室(F5 分给哪台后端),后厨真正做菜(CCE 里的应用),仓库取原料(Redis 是手边的取件柜、GaussDB 是后面的档案室)。记住这三站,每层挂了会怎样也就能顺出来:分诊台挂了全堵在门口,后厨挂了用户直接看到报错,仓库挂了数据就真没了。

2
越往上越快,也越容易没

按速度排一遍:浏览器缓存 > Redis > GaussDB。越快的容量越小、越可以不保证不丢。看到任何新组件,先问它快在哪、丢了会怎样。

3
先问「它有没有状态」

无状态的(Pod)可以随便增减、随便替换;有状态的(Redis、GaussDB)数据握在自己手里,扩容迁移都得单独设计。凡是出现「主从」「副本」「切换」字眼的,都是有状态的。

待确认与后续

待确认(确认完就能把图定死)

图里那 4 个虚线框,目前每种形态都画出来了,但你们环境实际用的是哪一种还需要你确认。确认后可以只保留实际的那种,图会更干净。

  • 接入层:F5 是单机还是双机热备
  • 应用层:CCE 的对外入口走的是 NodePort,还是云上挂了 ELB / Ingress
  • 数据层:Redis 是单机 / 主从 / 集群?GaussDB 是主备还是分布式
  • F5 前面还有没有别的东西(防火墙 / WAF / IPS)?有的话该画在哪一层。
  • 请求是从路由器直接打到 F5,还是云上挂了别的入口设备?

要继续补的图

  • 图二 · 时序图 —— 已完成,见上面的 图 2 · 一次真实请求。三种走向可切换;图里每一步都能点开,抽屉里给出那一步的报文、地址为什么变、以及背后的规则。
  • 图三 · CCE 内部放大:Node、Pod、Deployment、Service、Ingress 的包含关系。
  • 图四 · F5 视角:VS IP、Pool、Pool Member、Self IP、健康检查 之间怎么连。
  • 图五 · 数据分工图:读写怎么走、缓存怎么保持一致、Redis 挂了会怎样(现在只有图 2 里的三种走向,还不够细)。