开发面试准备(2026)
PHP
include和require的区别
在PHP中,include和require都是用于在代码中包含文件的语句,但它们有一些重要的区别。
- 错误处理方式:
include:如果文件未找到或者包含过程中出现错误,include会产生一个警告,但脚本会继续执行。require:如果文件未找到或者包含过程中出现错误,require会产生一个致命错误,并停止脚本的执行。
- 返回值:
include:如果包含文件成功,include语句返回1;如果失败,返回FALSE。require:如果包含文件成功,require语句返回1;如果失败,返回FALSE。
- 使用场景:
include用于包含可选的文件,即使文件不存在或包含失败,脚本仍然会继续执行。require用于包含必须的文件,如果文件不存在或包含失败,脚本会停止执行,这在必须依赖某个文件的情况下很有用。 总的来说,选择使用include还是require取决于你对脚本执行的期望和对错误处理的需求。如果文件的存在对程序的正常运行不是必须的,那么使用include可能更合适。如果文件的存在对程序是必须的,那么使用require会更适合,因为它能在必要时中止脚本的执行。
预定义变量
魔术常量
使用 PHP 函数对变量 $x 进行比较
常用函数
PHP支持多线程吗?
PHP 本身的标准版本并不直接支持真正的多线程。PHP 的主要运行模型是基于请求-响应的,每个请求都在一个独立的进程中处理。在这个模型下,PHP 是单线程运行的,每个请求都有自己的上下文,不共享数据。
然而,PHP 提供了一些扩展和库,以支持并发编程或者异步操作,使得可以在某种程度上模拟多线程的效果。以下是一些与并发和异步相关的 PHP 扩展和库:
- Swoole: 是一个基于 C 语言开发的高性能异步、协程并发网络通信引擎。Swoole 提供了协程和异步 I/O 支持,可以用于编写高并发的服务器程序。
虽然上述方法可以用于处理并发任务,但需要注意的是,PHP 不是为真正的多线程编程而设计的。对于需要更严格的多线程控制和共享内存等需求,其他语言如 Java 或 C++ 更为适用。
MySQL
更多 MySQL 基础与性能优化内容,请查看:MySQL 基础笔记。
操作系统
Linux常用命令
ls: 列出目录中的文件和子目录。cd: 切换当前工作目录。pwd: 显示当前工作目录的路径。cp: 复制文件或目录。mv: 移动或重命名文件或目录。rm: 删除文件或目录。mkdir: 创建新目录。rmdir: 删除空目录。cat: 查看或连接文件。more/less: 分屏显示文件内容,逐页查看。head/tail: 显示文件的开头或结尾部分。touch: 更新文件的访问和修改时间,或者创建新文件。chmod: 修改文件或目录的权限。chown: 修改文件或目录的所有者。ps: 显示当前运行的进程。kill: 终止进程。df: 显示磁盘空间使用情况。du: 显示文件和目录的磁盘使用情况。top: 实时显示系统资源使用情况。grep: 在文件中搜索指定模式。ping: 测试网络连接并测量往返时间。tar: 打包、压缩和解压文件。
进程和线程有什么区别
进程(Process)和线程(Thread)是操作系统中用于执行程序的两个基本概念,它们有一些重要的区别:
定义:
- 进程: 一个独立的执行环境,包括程序、数据和系统资源。每个进程都有独立的内存空间,进程之间相互独立。
- 线程: 在进程内部执行的轻量级执行单元。同一进程内的线程共享相同的内存空间和资源,可以更方便地进行通信。
资源占用:
- 进程: 占用独立的内存空间和系统资源,相对较重量级。
- 线程: 共享进程的内存空间,相对轻量级。
通信和同步:
- 进程: 进程之间通信需要特殊机制,如管道、消息队列、共享内存等。同步需要使用进程同步机制,如信号量、互斥锁等。
- 线程: 线程可以通过共享内存直接进行通信,同步相对容易,可以使用线程同步机制,如互斥锁、条件变量等。
切换开销:
- 进程: 进程切换开销较大,需要保存和恢复整个进程的上下文。
- 线程: 线程切换开销较小,因为线程共享相同的地址空间,只需保存和恢复寄存器、程序计数器等少量数据。
独立性:
- 进程: 进程是相互独立的执行单元,一个进程的崩溃不会影响其他进程。
- 线程: 线程共享相同的地址空间,一个线程的错误可能会影响整个进程,但不会影响其他进程。
创建和销毁:
- 进程: 创建和销毁进程相对较慢,涉及资源的分配和释放。
- 线程: 创建和销毁线程相对较快,因为它们共享相同的资源。
适用场景:
- 进程: 适用于需要独立执行环境、数据隔离、稳定性较强的应用。
- 线程: 适用于需要轻量级、资源共享、响应速度快的应用。
Nginx
更多 Nginx内容,请查看:Nginx 面试高频问题。
网络
IP是什么?
IP(Internet Protocol)是计算机网络中的一种协议,用于在互联网上识别和定位设备。IP地址是用来标识网络上的设备的,例如,IPv4地址通常以形如192.168.0.1的格式表示。
192.168.0.1/16 是什么意思?
192.168.0.1/16 是一个表示IP地址和子网掩码的标记,用于表示一个IP地址范围。这个标记中的 "192.168.0.1" 是网络的起始地址,而 "/16" 表示网络的子网掩码,指定了网络部分有16位,因此IP地址的前16位是网络部分。在IPv4中,一个IP地址总共有32位,因此剩下的 32 - 16 = 16 位用于表示主机。这意味着在这个特定的网络中,可以有2^16个不同的主机地址。
IPv4 与 IPv6 区别
ipv4和ipv6的区别本质在于它们的二进制表示位数,ipv4是用32位0/1序列来表示的,而ipv6使用128位0/1序列来表示的。 ipv4用32位,为了方便人类记录和阅读,我们通常将ipv4的32位0/1分成4段8位序列,并用10进制来表示每一段(这样,一段的范围就是0到255),段与段之间以“.”分隔。在IPv6的设计过程中除了一劳永逸地解决了地址短缺问题以外,还考虑了在IPv4中解决不好的其它问题,主要有端到端IP连接、服务质量(QoS)、安全性、多播、移动性、即插即用等。
DNS 主要作用是什么?
DNS 是域名系统 (Domain Name System) 的缩写,它是由解析器和域名服务器组成的,又名“域名解析服务器”.域名服务器是指保存有该网络中所有主机的域名和对应IP地址,并具有将域名转换为IP地址功能的服务器。其中域名必须对应一个IP地址,而IP地址不一定有域名.在Internet上域名与IP地址之间是一对一(或者多对一)的.
DNS工作原理
过程:浏览器缓存 -> 系统缓存 -> 路由器缓存 -> IPS服务器缓存 -> 根域名服务器缓存 -> 顶级域名服务器 缓存 -> 主域名服务器缓存。查到后会将结果缓存至本地系统。
DNS负载均衡策略
原理是在 DNS 服务器中为同一个主机名配置多个 IP 地址,在应答 DNS 查询时,DNS 服务器对每个查询 将以 DNS 文件中主机记录的 IP 地址按顺序返回不同的解析结果,将客户端的访问引导到不同的机器上 去,使得不同的客户端访问不同的服务器,从而达到负载均衡的目的。例如可以根据每台机器的负载 量,该机器离用户地理位置的距离等等。
分布式环境下 Session 如何处理?
分布式环境下,客户端请求经过负载均衡,可能会分配到不同的服务器上,假如一个用户的请求两次没有落到同一台服务器上,那么在新的服务器上就没有记录用户状态的 Session。
可以使用 Redis 等分布式缓存来存储 Session,在多台服务器之间共享。
从浏览器地址栏输入 URL 到显示主页的过程发生了什么?
- DNS解析:将域名解析为对应的 IP 地址
1.1. DNS 查找过程:浏览器缓存、本地DNS路由缓存、DNS解析服务)
1.2. DNS 解析服务:1.2.1. 请求根服务器,返回顶级域名服务器
1.2.2. 请求顶级域名服务器(例如 .com ),返回权威域名服务器
1.2.3. 请求权威域名服务器(例如 baidu.com ),返回对应的 IP 地址 - TCP连接:与服务器进行三次握手,建立 TCP 连接
- 向服务器发送 HTTP(S) 请求
- 服务器处理请求,返回 HTTP(S) 响应
- 浏览器解析并渲染页面
- 断开连接:TCP四次挥手,连接结束
TCP 和 UDP 的区别
最根本区别:TCP 是面向连接,而 UDP 是无连接。
- UDP 传输不可靠。
- TCP 采用字节流传输;UDP 采用数据段报文
- UDP 传输效率比 TCP 快
- UDP 所需资源比 TCP 少
- TCP 主要应用场景:文件传输、邮件传输;UDP 主要应用场景:即时通讯、域名转换
- TCP 首部字节 20-60;UDP 首部 8 字节
简单总结一下:UDP协议是无连接方式的协议,它的效率高,速度快,占资源少,对服务器的压力比较 小。但是其传输机制为不可靠传送,必须依靠辅助的算法来完成传输控制。
HTTP和HTTPS有什么区别?
HTTP(HyperText Transfer Protocol)和HTTPS(HyperText Transfer Protocol Secure)是用于在客户端和服务器之间传输数据的两种协议,它们之间有一些关键的区别:
安全性:
- HTTP: 是明文传输的协议,数据在传输过程中是未加密的。这使得HTTP容易受到中间人攻击,例如窃听、篡改或劫持。
- HTTPS: 使用了TLS/SSL协议进行数据加密,确保在数据传输的过程中,中间人无法轻易窃听或篡改传输的数据。因此,HTTPS提供了更高的安全性。
端口号:
- HTTP: 默认使用端口号80。
- HTTPS: 默认使用端口号443。因为HTTPS引入了加密层,因此需要使用不同的端口号以确保安全通信。
加密方式:
- HTTP: 以明文方式传输数据,不提供加密。
- HTTPS: 使用TLS/SSL协议对数据进行加密,确保传输的数据在客户端和服务器之间是安全的。
证书要求:
- HTTP: 不需要证书。
- HTTPS: 需要服务器端使用SSL证书,该证书由受信任的证书颁发机构(CA,Certificate Authority)签发。这确保客户端与服务器之间建立的连接是可信任的。
使用场景:
- HTTP: 适用于不涉及敏感信息传输的场景,如浏览网页、阅读新闻等。
- HTTPS: 建议用于涉及敏感信息传输的场景,如登录、支付、个人数据提交等,以确保数据的安全性。
性能:
- HTTP: 由于不涉及加密解密过程,通常比HTTPS在性能上更快。
- HTTPS: 由于加密解密的过程,可能会引入一些额外的性能开销。然而,现代的硬件和优化手段使得这种差异相对较小。
在今天的互联网环境中,推荐在涉及用户隐私和安全性的场景中使用HTTPS,以确保数据的保密性和完整性。大多数网站已经迁移到了HTTPS,以提供更安全的用户体验。
GET和POST有什么区别
GET请求和POST请求是HTTP协议中两种常见的请求方法,它们在数据传输、安全性和用途等方面有一些区别:
数据传输方式:
- GET请求: 将参数附加在URL的末尾,以查询字符串的形式发送给服务器。例如,
http://example.com/page?name=value&age=25。 - POST请求: 将参数包含在请求的消息体中,而不是直接附加在URL上。数据通过请求头以及请求体传输给服务器。
- GET请求: 将参数附加在URL的末尾,以查询字符串的形式发送给服务器。例如,
安全性:
- GET请求: 参数在URL上明文可见,不适合传输敏感信息,因为这些信息会被保存在浏览器的历史记录、服务器日志等地方。
- POST请求: 参数在请求体中,相对于GET请求更安全,因为请求体的内容不会被保存在浏览器历史记录中,但仍然可能被记录在服务器日志中。
数据长度限制:
- GET请求: 对数据传输的长度有限制,因为数据附加在URL上,URL的长度受到浏览器和服务器的限制。
- POST请求: 通常允许传输更大量的数据,因为数据不附加在URL上,而是放在请求体中。
请求的幂等性:
- GET请求: 通常是幂等的,即对同一个URL的多次请求应该返回相同的结果。
- POST请求: 不一定是幂等的,它可能会引起服务器端的状态改变,每次请求的结果可能不同。
缓存:
- GET请求: 可以被浏览器缓存,可以被书签保存,也可以被浏览器历史记录保存。
- POST请求: 不会被浏览器缓存,不适合被保存在书签或历史记录中。
使用场景:
- GET请求: 适用于获取数据,如浏览器中的页面跳转、链接、搜索等。
- POST请求: 适用于提交表单数据、上传文件等,具有更多的数据传输选项。
总体而言,选择使用GET还是POST取决于具体的需求和用途。GET用于获取数据,而POST用于提交数据。GET请求更适合幂等和无副作用的操作,而POST请求更适合有副作用的操作,例如向服务器提交表单数据。
GET、POST、PUT、DELETE 区别
这四个是 HTTP 的主要请求方式,对应 RESTful 风格中的常见操作:
GET
- 用途:获取资源(查询)。
- 特点:参数附在 URL;无副作用(应为幂等);可以被缓存。
- 示例:
GET /api/users/123→ 获取用户信息。
POST
- 用途:创建资源(或提交数据)。
- 特点:参数放在请求体;非幂等(重复请求可能创建多个);常用于表单提交、登录等。
- 示例:
POST /api/users+ 请求体{ "name": "张三" }→ 新建用户。
PUT
- 用途:更新资源(整体替换)。
- 特点:请求体包含完整资源,幂等(多次相同请求结果一致)。
- 示例:
PUT /api/users/123+ 全量数据 → 替换该用户信息。
DELETE
- 用途:删除资源。
- 特点:幂等;只要资源存在,就删除;再次删除结果相同(资源不存在)。
- 示例:
DELETE /api/users/123→ 删除用户。
| 方法 | 常见作用 | 参数位置 | 幂等性 |
|---|---|---|---|
| GET | 查询/获取资源 | URL/query | ✅ |
| POST | 创建/提交 | 请求体 | ❌ |
| PUT | 更新(全量) | 请求体 | ✅ |
| DELETE | 删除 | URL/请求体可选 | ✅ |
HTTP状态码
下面是常见的HTTP状态码:200 - 请求成功;301 - 资源(网页等)被永久转移到其它URL;404 - 请求的资源(网页等)不存在;500 - 内部服务器错误。
| 分类 | 分类描述 |
|---|---|
| 1** | 信息,服务器收到请求,需要请求者继续执行操作 |
| 2** | 成功,操作被成功接收并处理 |
| 3** | 重定向,需要进一步的操作以完成请求 |
| 4** | 客户端错误,请求包含语法错误或无法完成请求 |
| 5** | 服务器错误,服务器在处理请求的过程中发生了错误 |
常用端口
FTP (File Transfer Protocol):
- 用途: 用于在客户端和服务器之间传输文件。
- 端口号: 21
SSH (Secure Shell):
- 用途: 安全外壳协议,用于在网络中加密传输数据。
- 端口号: 22
HTTP (HyperText Transfer Protocol):
- 用途: 用于在Web浏览器和Web服务器之间传输超文本数据,通常用于访问网页。
- 端口号: 80
HTTPS (HyperText Transfer Protocol Secure):
- 用途: 加密的HTTP协议,用于在Web浏览器和Web服务器之间安全地传输超文本数据。
- 端口号: 443
SMTP (Simple Mail Transfer Protocol):
- 用途: 用于发送电子邮件的协议。
- 端口号: 25
POP3 (Post Office Protocol version 3):
- 用途: 用于接收电子邮件的协议。
- 端口号: 110
IMAP (Internet Message Access Protocol):
- 用途: 用于接收电子邮件的协议,支持在多个设备上同步邮件状态。
- 端口号: 143
DNS (Domain Name System):
- 用途: 用于域名解析,将域名映射为对应的IP地址。
- 端口号: 53
MySQL:
- 用途: 用于MySQL数据库的通信。
- 端口号: 3306
Redis:
- 用途: 用于缓存、消息队列等的开源内存数据库。
- 端口号: 6379(默认端口,可根据配置修改)
PHP-FPM:
- 用途: PHP FastCGI 进程管理器,用于处理PHP脚本的高性能进程管理。
- 端口号: 9000(默认端口,可根据配置修改)
SSL (Secure Sockets Layer):
- 用途: 用于在网络中提供加密安全通信。
- 端口号: 443(常用于HTTPS)
Cookie和Session的区别
Cookies(Cookie)和Session(会话)是用于在Web应用中跟踪用户状态和维护用户数据的两种主要机制,但它们在实现方式和使用场景上有一些区别:
1. 存储位置:
- Cookie: 存储在客户端(用户浏览器)上,以文本文件的形式保存在用户的计算机上。
- Session: 存储在服务器上,通常以服务器上的一个文件或数据库记录的形式保存。
2. 数据安全性:
- Cookie: 相对不安全,因为存储在用户的计算机上,用户可以查看和编辑。
- Session: 相对安全,因为数据存储在服务器上,用户无法直接访问或修改。
3. 生命周期:
- Cookie: 可以设置过期时间,可以是会话级的(浏览器关闭后失效)或长时间的。
- Session: 通常在用户关闭浏览器或一定时间不活动后过期,取决于服务器的配置。
4. 存储内容:
- Cookie: 存储在客户端的小型文本数据,通常用于跟踪用户偏好、识别用户等。
- Session: 存储在服务器上,可用于存储较大的数据,通常包括用户的会话信息、状态等。
5. 跨页面访问:
- Cookie: 可以在不同页面之间共享,因为存储在客户端。
- Session: 通常通过在页面之间传递会话标识符(session ID)来实现跨页面访问。
6. 资源消耗:
- Cookie: 存储在客户端,不占用服务器资源。
- Session: 存储在服务器上,可能占用服务器资源,特别是在用户较多的情况下。
7. 使用场景:
- Cookie: 适用于存储少量且对安全性要求不高的数据,如用户偏好设置、广告跟踪等。
- Session: 适用于存储较大、对安全性要求较高的数据,如用户登录状态、购物车信息等。
总体而言,Cookie和Session是Web开发中常用的两种机制,它们通常一起使用,以实现在Web应用中跟踪用户状态和维护用户数据的目的。选择使用哪种机制取决于应用的具体需求和安全考虑。
NoSQL
缓存击穿、缓存穿透、缓存雪崩
更多关于缓存问题的深入解析,请阅读文章:缓存击穿、缓存穿透与缓存雪崩(详解)。
Redis
Redis 数据类型
更多关于 Redis 数据类型与选型的详尽指南,请参阅:Redis 数据类型对比。
Redis持久化
Redis 持久化是一种将内存中的数据存储到硬盘上以防止数据丢失的机制。在 Redis 中,有两种主要的持久化方式:RDB(Redis Database Backup)和AOF(Append-Only File)。
RDB 持久化
触发方式: 手动或定期触发。
工作原理: 将当前内存中的数据快照保存到一个二进制文件中。这个文件包含了 Redis 在某个时间点的数据。
优点: 适合用于备份和恢复,文件较小,恢复速度较快。
缺点: 可能会有一定的数据丢失,因为 RDB 是定期执行的。
配置示例:
save 900 1 # 在 900 秒内,如果至少有 1 个 key 发生变化,则执行一次快照
save 300 10 # 在 300 秒内,如果至少有 10 个 key 发生变化,则执行一次快照
save 60 10000 # 在 60 秒内,如果至少有 10000 个 key 发生变化,则执行一次快照
AOF 持久化
- 触发方式: 始终处于开启状态,数据发生变化时即时记录。
- 工作原理: 记录每个写命令(写操作)到一个文件中,以追加的方式记录,该文件可以用于恢复数据。
- 优点: 提供更好的持久性,可以降低数据丢失的可能性。
- 缺点: 文件较大,恢复速度可能较慢。
配置示例:
appendonly yes # 开启 AOF 持久化
appendfsync everysec # 每秒钟同步一次 AOF 文件,可以选择 always 或 no。
持久化选择和使用场景
选择 RDB 持久化:
- 适用于对数据一定程度的数据丢失可以接受的场景。
- 适合用于备份和恢复。
选择 AOF 持久化:
- 需要更高的数据安全性,对数据的可靠性要求较高的场景。
- 对于需要追踪数据变更历史的场景。
NOTE
- 也可以同时使用 RDB 和 AOF 持久化,以兼顾备份和数据恢复的需求。
- 持久化的方式可以根据具体的业务需求和对数据安全性的要求进行调整。
RDB 和 AOF恢复优先级对比
AOF 恢复优先于 RDB:
- 如果同时启用了 AOF(Append-Only File)和 RDB(Redis Database Backup)持久化,Redis 在启动时会优先使用 AOF 文件进行数据恢复。AOF 文件包含了每个写命令的记录,能够提供更高的数据安全性。
AOF 文件的同步方式影响恢复优先级:
- 如果 AOF 文件的同步方式为
always(始终同步),则 Redis 在启动时会强制将 AOF 文件中的数据同步到内存中,确保数据的完整性。这时,即使存在 RDB 快照,AOF 仍然优先。 - 如果 AOF 文件的同步方式为
everysec(每秒同步),则 Redis 在启动时会尽量使用 AOF 文件进行恢复,但可能会根据同步的时间点有一定的数据丢失。此时,如果存在 RDB 文件,则会考虑使用 RDB 进行恢复。
- 如果 AOF 文件的同步方式为
RDB 文件作为备用:
- RDB 文件通常用于备份和恢复,可以在需要时手动加载或用于定期备份。当 AOF 文件无法使用或需要降低数据恢复时间时,RDB 可作为一个备用的、更快速的恢复方式。
启用 AOF 时的 RDB 文件恢复:
- 如果 AOF 文件存在但损坏,Redis 在启动时会检查是否存在有效的 RDB 文件。如果 RDB 文件存在且有效,可以用于恢复数据。
总体而言,AOF 文件提供了更强的数据安全性,但在某些情况下可能导致恢复时间较长。RDB 文件则提供了较快的恢复速度,但在保存快照的过程中可能存在一定的数据丢失。根据业务需求和对数据安全性的要求,可以选择合适的持久化配置。
RabbitMQ
更多RabbitMQ的深入解析,请参阅:RabbitMQ 面试高频 20 问。
设计模式
设计模式的目标不是“为了用模式而用模式”,而是降低耦合、提升可维护性和可测试性。无论是 PHP、Java、Go、Node.js、Python 还是 .NET,选型时都可以遵循同一套原则:
- 先看变化点:哪里最可能频繁变化,就把那里抽象出来。
- 先看调用方成本:优先让业务调用简单、稳定。
- 先小后大:能用函数和模块解决,就不要过早引入复杂模式。
常用模式(工程实战版)
工厂模式(Factory)
- 适用场景:对象创建逻辑复杂,且存在多种实现(如支付渠道、消息发送器、存储驱动)。
- 核心收益:调用方只依赖抽象接口,不关心实例化细节。
- 落地建议:用“配置 + 工厂”替代大量
if/else创建代码。
单例模式(Singleton)
- 适用场景:全局只需一个实例(如配置中心、连接池管理器、日志中心)。
- 核心收益:统一资源入口,避免重复初始化开销。
- 风险提示:单例容易变成“全局状态污染”;优先通过依赖注入容器管理生命周期。
模板方法模式(Template Method)
- 适用场景:流程骨架固定,但局部步骤可替换(如订单处理、数据导入、审核流水线)。
- 核心收益:复用主流程,减少重复代码,保证步骤顺序一致。
- 落地建议:把可变步骤留给子类或回调实现,不要把所有逻辑都写进基类。
策略模式(Strategy)
- 适用场景:同一业务在不同条件下有多种算法(如优惠计算、路由选择、风控规则)。
- 核心收益:把“规则选择”和“规则实现”解耦,便于扩展和 A/B 测试。
- 落地建议:配合工厂/注册表做策略发现,新增策略尽量不改老代码。
适配器模式(Adapter)
- 适用场景:接入第三方 SDK 或历史系统时接口不一致(如多云存储、多短信网关)。
- 核心收益:对外暴露统一接口,隔离外部差异。
- 落地建议:适配层只做协议转换,不要混入业务决策。
代理模式(Proxy)
- 适用场景:需要在不改原逻辑的前提下增加访问控制、鉴权、缓存、限流、审计。
- 核心收益:把横切关注点与核心业务分离。
- 落地建议:网关层和中间件层优先用代理思想,业务层保持纯粹。
装饰器模式(Decorator)
- 适用场景:需要按需叠加功能(如促销叠加、请求处理链、输出格式增强)。
- 核心收益:比继承更灵活,组合优于层层子类。
- 落地建议:保证每个装饰器只做一件事,避免“大而全装饰器”。
外观模式(Facade)
- 适用场景:子系统复杂,希望对业务提供稳定的统一入口(如下单聚合服务)。
- 核心收益:降低调用复杂度,收敛跨模块依赖。
- 落地建议:外观层负责编排,不承载过重业务规则。
设计模式使用边界
- 当代码规模较小、变化较少时,优先保持简单实现。
- 当需求频繁变更、分支逻辑持续增长时,再引入匹配模式。
- 引入模式后要配套测试(单测/契约测试),否则抽象只会增加复杂度。
其他
高并发处理
高并发优化的核心目标是:让请求尽量不进应用层、进了应用层也尽量不打数据库、打了数据库也尽量可控。因此可以从以下六个层面进行优化:
入口层(静态资源、CDN、网关/反向代理、应用运行时)
- 动静分离:JS/CSS/图片/视频走独立域名(如
static.example.com),动态请求走应用域名,减少不必要的 Cookie 传输。 - 对象存储 + CDN:静态资源放 OSS/COS/七牛等对象存储,借助 CDN 边缘缓存降低源站带宽和连接压力。
- 资源传输优化:压缩与合并静态资源,开启 Gzip/Brotli,设置
Cache-Control与Expires,减少重复请求。 - 网关参数调优:按 CPU 核心和连接规模设置并发参数(如 worker、连接数、超时、缓冲区);启用静态缓存与基础防刷策略。
- 应用运行时容量匹配:合理设置进程数/线程池/协程并发上限,避免“过大导致资源打满”或“过小导致请求排队”。
- 启用运行时优化:开启 JIT/字节码缓存/预热机制(按语言特性选择),减少冷启动与重复编译开销。
- 动静分离:JS/CSS/图片/视频走独立域名(如
应用层(代码与执行路径)
- 控制内存占用:避免一次性加载大结果集,优先分页、游标、分块处理;及时释放大对象。
- 减少无效计算:把热点计算结果复用在局部变量中,避免重复调用耗时函数。
- 优先高效写法:优先使用标准库与流式处理能力,降低深层循环与不必要函数调用。
- 关闭生产调试能力:禁用高开销调试/分析组件,仅在排障窗口按需开启。
缓存层(本地缓存 + 分布式缓存)
- 本地热点:使用进程内缓存存配置和高频小数据,降低网络往返。
- 分布式缓存:优先 Redis(结构更丰富、生态更完整),并设置合理 TTL。
- 防止三类缓存问题:击穿、穿透、雪崩分别使用互斥锁、布隆过滤器、随机过期时间等策略。
- 缓存策略细节可参考:缓存击穿、缓存穿透与缓存雪崩。
数据层(关系型数据库与存储拆分)
- 读写分离:主库写、从库读,通过中间件或代码路由分摊读压力。
- 索引治理:围绕慢查询做索引设计,避免函数包裹索引列、
LIKE '%xxx'等失效场景。 - 分库分表:单表数据量到千万级后,按业务键做水平拆分,或按字段做垂直拆分。
- 连接复用:使用连接池减少建连成本,稳定高峰期 RT。
- 定期维护:优化表碎片并更新统计信息,避免执行计划劣化。
架构层(负载均衡、集群、异步化)
- 负载均衡:中小规模可用 Nginx(轮询/IP 哈希/权重),更高规模可选 LVS 或云负载均衡。
- 服务集群:多实例部署,消除单点故障并提升整体吞吐。
- 异步削峰:将发邮件、日志、报表等非实时任务投递到 RabbitMQ/Kafka/Redis 队列。
- I/O 密集场景可采用事件驱动或异步模型(如语言原生 async/await、协程、NIO)提升单机并发能力。
稳定性层(限流、降级、监控告警)
- 接口限流:对秒杀、登录等热点接口实施限流(
limit_req、令牌桶、计数器),保护核心服务。 - 服务降级:高峰期关闭非核心能力(如推荐、评论),优先保障交易与核心链路。
- 全链路监控:至少覆盖资源指标(CPU/内存/磁盘/网络)、服务指标(网关/应用/数据库/缓存)和业务指标(QPS、RT、错误率)。
- 告警闭环:设置明确阈值(如 CPU > 80%、错误率 > 1%)并接入短信/邮件/企业微信,确保可及时响应。
- 接口限流:对秒杀、登录等热点接口实施限流(
跨域
跨域(CORS)本质是浏览器的同源策略限制。后端需要明确声明:哪些来源、方法、请求头可以访问当前接口。
- 先区分两类请求
- 简单请求:
GET/HEAD/POST且请求头受限,通常不会先发预检请求。 - 非简单请求:如
PUT/DELETE、携带Authorization、自定义请求头,会先发OPTIONS预检。
- 简单请求:
- 服务端关键响应头
Access-Control-Allow-Origin:允许访问的来源,生产环境应使用白名单,不建议直接*。Access-Control-Allow-Methods:允许的方法,如GET,POST,PUT,DELETE,OPTIONS。Access-Control-Allow-Headers:允许的请求头,如Content-Type, Authorization。Access-Control-Allow-Credentials:是否允许携带 Cookie;若为true,Allow-Origin不能为*。Access-Control-Max-Age:预检缓存时间,减少重复预检。
- 工程实践建议
- 白名单分环境管理:开发、测试、生产使用独立域名与配置。
- 最小授权原则:只放开必要的方法和请求头。
- 网关统一处理:优先在 API 网关/反向代理层处理 CORS,避免各服务重复配置。
- 预检快速返回:
OPTIONS直接返回200或204,不要进入重业务逻辑。
- 常见问题
- 前端带
credentials,后端却返回Access-Control-Allow-Origin: *。 - 未放行
Authorization导致 Token 请求失败。 - 业务请求有 CORS 头,但
OPTIONS响应缺少 CORS 头。 - 网关与应用重复写 CORS 头,浏览器判定为无效响应。
- 前端带
- 排障顺序
- 先看浏览器 Network:确认是否发了
OPTIONS,及响应码、响应头是否完整。 - 再看网关/服务日志:确认是否被鉴权、路由或中间件提前拦截。
- 最后核对来源三元组:协议、域名、端口是否与预期一致。
- 先看浏览器 Network:确认是否发了