写给后端程序员的HTTP缓存原理介绍

本文介绍HTTP缓存机制,包括ETag、If-None-Match及Cache-Control的作用与配置方法,帮助理解并优化Web服务性能。

通过Internet获取资源既缓慢,成本又高。为此,Http协议里包含了控制缓存的部分,以使Http客户端可以缓存和重用以前获取的资源,从而优化性能,提升体验。虽然Http中关于缓存控制的部分,随着协议演进,有一些变化。但我觉着,作为后端程序员,在开发Web服务时,只需要关注请求头If-None-Match、响应头ETag、响应头Cache-Control就足够了。因为这三个Http头就可以满足你的需求,并且,当今绝大多数的浏览器,都支持这三个Http头。我们所要做的就是,确保每个服务器响应都提供正确的 HTTP 头指令,以指导浏览器何时可以缓存响应以及可以缓存多久。

  缓存在哪儿?

  上图中有三个角色,浏览器、Web代理和服务器,如图所示Http缓存存在于浏览器和Web代理中。当然在服务器内部,也存在着各种缓存,但这已经不是本文要讨论的Http缓存了。所谓的Http缓存控制,就是一种约定,通过设置不同的响应头Cache-Control来控制浏览器和Web代理对缓存的使用策略,通过设置请求头If-None-Match和响应头ETag,来对缓存的有效性进行验证。

  响应头ETag

  ETag全称Entity Tag,用来标识一个资源。在具体的实现中,ETag可以是资源的hash值,也可以是一个内部维护的版本号。但不管怎样,ETag应该能反映出资源内容的变化,这是Http缓存可以正常工作的基础。

  如上例中所展示的,服务器在返回响应时,通常会在Http头中包含一些关于响应的元数据信息,其中,ETag就是其中一个,本例中返回了值为x1323ddx的ETag。当资源/file的内容发生变化时,服务器应当返回不同的ETag。

  请求头If-None-Match

  对于同一个资源,比如上一例中的/file,在进行了一次请求之后,浏览器就已经有了/file的一个版本的内容,和这个版本的ETag,当下次用户再需要这个资源,浏览器再次向服务器请求的时候,可以利用请求头If-None-Match来告诉服务器自己已经有个ETag为x1323ddx的/file,这样,如果服务器上的/file没有变化,也就是说服务器上的/file的ETag也是x1323ddx的话,服务器就不会再返回/file的内容,而是返回一个304的响应,告诉浏览器该资源没有变化,缓存有效。

  如上例中所示,在使用了If-None-Match之后,服务器只需要很小的响应就可以达到相同的结果,从而优化了性能。

  响应头Cache-Control

  每个资源都可以通过Http头Cache-Control来定义自己的缓存策略,Cache-Control控制谁在什么条件下可以缓存响应以及可以缓存多久。最快的请求是不必与服务器进行通信的请求:通过响应的本地副本,我们可以避免所有的网络延迟以及数据传输的数据成本。为此,HTTP规范允许服务器返回一系列不同的Cache-Control 指令,控制浏览器或者其他中继缓存如何缓存某个响应以及缓存多长时间。

Cache-Control 头在 HTTP/1.1 规范中定义,取代了之前用来定义响应缓存策略的头(例如 Expires)。当前的所有浏览器都支持 Cache-Control,因此,使用它就够了。

  以下我来介绍可以再Cache-Control中设置的常用指令。

  max-age

  该指令指定从当前请求开始,允许获取的响应被重用的最长时间(单位为秒。例如:Cache-Control:max-age=60表示响应可以再缓存和重用 60 秒。需要注意的是,在max-age指定的时间之内,浏览器不会向服务器发送任何请求,包括验证缓存是否有效的请求,也就是说,如果在这段时间之内,服务器上的资源发生了变化,那么浏览器将不能得到通知,而使用老版本的资源。所以在设置缓存时间的长度时,需要慎重。

  public和private

  如果设置了public,表示该响应可以再浏览器或者任何中继的Web代理中缓存,public是默认值,即Cache-Control:max-age=60等同于Cache-Control:public, max-age=60。

  在服务器设置了private比如Cache-Control:private, max-age=60的情况下,表示只有用户的浏览器可以缓存private响应,不允许任何中继Web代理对其进行缓存 - 例如,用户浏览器可以缓存包含用户私人信息的 HTML 网页,但是 CDN 不能缓存。

  no-cache

  如果服务器在响应中设置了no-cache即Cache-Control:no-cache,那么浏览器在使用缓存的资源之前,必须先与服务器确认返回的响应是否被更改,如果资源未被更改,可以避免下载。这个验证之前的响应是否被修改,就是通过上面介绍的请求头If-None-match和响应头ETag来实现的。

需要注意的是,no-cache这个名字有一点误导。设置了no-cache之后,并不是说浏览器就不再缓存数据,只是浏览器在使用缓存数据时,需要先确认一下数据是否还跟服务器保持一致。如果设置了no-cache,而ETag的实现没有反应出资源的变化,那就会导致浏览器的缓存数据一直得不到更新的情况。

  no-store

  如果服务器在响应中设置了no-store即Cache-Control:no-store,那么浏览器和任何中继的Web代理,都不会存储这次相应的数据。当下次请求该资源时,浏览器只能重新请求服务器,重新从服务器读取资源。

  怎样决定一个资源的Cache-Control策略呢?

  下面这个流程图,可以帮到你。

<think>我们正在准备后端开发面试,需要整理常见面试问题及其参考答案。根据引用[1]和引用[2],面试官会考察分布式系统、并发、数据结构与算法、数据库、网络、系统设计等方面的问题。同时,引用[3]提到了可变值与不可变值的选择,引用[4]提到了线程的等待和唤醒机制,这些都是并发编程中的重要知识点。 下面我们将分类列出后端开发面试的常见问题,并提供简要的参考答案。注意,由于篇幅限制,参考答案不会过于详细,但会覆盖核心要点。 ### 一、数据结构与算法 1. **快速排序的原理是什么?** 快速排序是一种分治算法。它选择一个元素作为基准(pivot),将数组分为两部分:小于基准的元素和大于等于基准的元素,然后递归地对这两部分进行排序。 时间复杂度:平均$O(n \log n)$,最坏$O(n^2)$(当数组已经有序时)。优化方法包括随机选择基准或使用三数取中法。 2. **如何实现二叉树的遍历?** - 前序遍历:根节点 -> 左子树 -> 右子树 - 中序遍历:左子树 -> 根节点 -> 右子树 - 后序遍历:左子树 -> 右子树 -> 根节点 可以使用递归或迭代(栈)实现。 ### 二、数据库 1. **数据库事务的ACID特性是什么?** - 原子性(Atomicity):事务中的所有操作要么全部成功,要么全部失败回滚。 - 一致性(Consistency):事务执行前后,数据库状态必须保持一致(满足所有约束)。 - 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务。 - 持久性(Durability):一旦事务提交,其结果就是永久性的。 2. **什么是SQL注入?如何防止?** SQL注入是攻击者通过输入恶意SQL语句来操纵数据库。防止方法: - 使用预编译语句(Prepared Statements)和参数化查询。 - 对用户输入进行过滤和转义。 - 最小化数据库权限。 ### 三、网络 1. **TCP和UDP的区别?** - TCP是面向连接的、可靠的、基于字节流的传输层协议,提供超时重传、流量控制、拥塞控制等机制,保证数据顺序和正确性。 - UDP是无连接的、不可靠的、基于数据报的协议,不保证顺序和正确性,但开销小、速度快。 2. **HTTP状态码有哪些?** - 1xx:信息响应 - 2xx:成功(如200 OK) - 3xx:重定向(如301 永久重定向) - 4xx:客户端错误(如404 未找到) - 5xx:服务器错误(如500 内部服务器错误) ### 四、并发编程 1. **线程和进程的区别?** - 进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位。 - 一个进程可以包含多个线程,线程共享进程的内存空间,而进程之间内存独立。 - 线程切换开销小于进程。 2. **wait()和sleep()的区别?** - `wait()`是Object类的方法,调用时会释放锁,需要被`notify()`或`notifyAll()`唤醒。 - `sleep()`是Thread类的方法,调用时不释放锁,时间到后自动恢复运行[^4]。 ### 五、分布式系统 1. **分布式系统中常见的问题和挑战是什么?如何解决?** - 问题:网络延迟、分区、节点故障、数据一致性等。 - 解决方案: - 使用超时和重试处理网络问题。 - 通过副本机制(Replication)提高可用性。 - 使用分布式一致性算法(如Raft、Paxos)保证数据一致性。 - 采用分布式事务协议(如两阶段提交)[^1]。 ### 六、系统设计 1. **如何设计一个短链接服务?** - 将长链接映射为短链接(使用哈希或自增ID)。 - 使用分布式键值存储(如Redis)缓存映射关系。 - 使用数据库持久化存储。 - 考虑高并发和扩展性。 ### 七、编程语言(以Python为例) 1. **Python中可变对象和不可变对象有什么区别?** - 不可变对象:创建后不能修改(如int, tuple, str)。修改操作会创建新对象。 - 可变对象:创建后可以修改(如list, dict, set)。 - 使用不可变对象可以提高并发安全性和可预测性[^3]。 2. **Python的GIL是什么?** GIL(全局解释器锁)是CPython解释器中的互斥锁,它确保同一时刻只有一个线程执行Python字节码。这导致多线程程序在CPU密集型任务中无法充分利用多核。 以上是后端开发面试中常见的问题及参考答案。建议根据自身知识短板进行深入学习,并多做实践。 §§相关问题§§ 1. 如何优化数据库查询性能? 2. 解释CAP定理及其在分布式系统中的应用。 3. 什么是RESTful API?设计原则是什么? 4. 解释HTTP/2的新特性。 5. 如何设计一个高并发的秒杀系统?</think>### 后端开发面试常见问题及参考答案 #### 一、数据结构与算法 1. **快速排序的原理与实现** 快速排序采用分治策略: - 选择基准元素(pivot) - 将数组分为小于基准和大于基准的两部分 - 递归排序子数组 时间复杂度:平均 $O(n \log n)$,最坏 $O(n^2)$ 优化:随机选择基准或三数取中法避免最坏情况 ```python def quicksort(arr): if len(arr) <= 1: return arr pivot = arr[len(arr)//2] left = [x for x in arr if x < pivot] middle = [x for x in arr if x == pivot] right = [x for x in arr if x > pivot] return quicksort(left) + middle + quicksort(right) ``` 2. **二叉树遍历的实现** - 前序遍历:根 → 左 → 右 - 中序遍历:左 → 根 → 右 - 后序遍历:左 → 右 → 根 递归实现示例(中序): ```python def inorder(root): return inorder(root.left) + [root.val] + inorder(root.right) if root else [] ``` --- #### 二、数据库 1. **事务的ACID特性** - **原子性**(Atomicity):事务全部成功或全部回滚 - **一致性**(Consistency):事务前后数据满足完整性约束 - **隔离性**(Isolation):并发事务互不干扰 - **持久性**(Durability):提交后数据永久存储 实现方式:WAL(Write-Ahead Logging)和锁机制[^1] 2. **SQL索引优化** - 索引类型:B+树(范围查询)、哈希(等值查询) - 最左前缀原则:联合索引 $(a,b,c)$ 仅支持 $a$、$a,b$、$a,b,c$ 查询 - 避免索引失效:不使用函数转换字段、避免 `OR` 条件 --- #### 三、并发编程 1. **线程同步机制** - `synchronized`(Java)/ `Lock`(Python):保证代码块互斥访问 - `wait()` 与 `notify()`:线程等待与唤醒(需在同步块中使用)[^4] - 信号量(Semaphore):控制并发线程数 2. **可变性与不可变性** - **不可变对象**(如String):线程安全,无需锁 - **可变对象**(如HashMap):需同步控制 优先使用不可变对象提高并发安全性[^3] --- #### 四、分布式系统 1. **CAP定理** - **一致性**(Consistency):所有节点数据一致 - **可用性**(Availability):每个请求都能响应 - **分区容错性**(Partition tolerance):网络分区时系统仍可用 分布式系统必须满足 **P**,在 **C** 和 **A** 间权衡(如CP的ZooKeeper vs AP的Cassandra)[^1] 2. **分布式事务解决方案** - **两阶段提交(2PC)**:协调者统一决策,存在阻塞风险 - **TCC模式**(Try-Confirm-Cancel):业务层补偿机制 - **消息队列**:最终一致性(如RabbitMQ事务消息) --- #### 五、网络协议 1. **TCP vs UDP** | 特性 | TCP | UDP | |------------|----------------|--------------| | 连接 | 面向连接 | 无连接 | | 可靠性 | 可靠(重传机制) | 不可靠 | | 速度 | 慢 | 快 | | 应用场景 | HTTP/MySQL | 视频流/DNS | 2. **HTTP状态码** - `200 OK`:请求成功 - `404 Not Found`:资源不存在 - `503 Service Unavailable`:服务不可用 - `401 Unauthorized`:未认证 --- #### 六、系统设计 1. **短链接系统设计** - 哈希算法:将长URL映射为62进制短码(如 `aBc1d`) - 存储:Redis缓存键值对,MySQL持久化 - 容错:布隆过滤器防恶意请求 2. **高并发限流策略** - **令牌桶算法**:恒定速率生成令牌,突发流量可处理 - **漏桶算法**:固定速率处理请求 - 工具:Redis + Lua脚本实现分布式限流 --- ### 面试准备建议 1. **基础知识**:重点复习算法、数据库、操作系统 2. **项目经验**:用STAR法则(情境-任务-行动-结果)描述项目 3. **系统设计**:从需求分析 → 数据模型 → 接口设计逐步展开 4. **行为问题**:准备“团队冲突处理”“技术决策依据”等案例
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值