美团2026届后端一二面(附详细参考答案)

2026 届秋招陆续开奖公司的越来越多了,大家比较期待的美团也开了!

根据网上已经爆出的薪资来看,下面是美团后端和其他部分岗位今年已经开奖的薪资情况:

  • 后端:23k * 15.5,北京,白菜,
  • 后端:(25~26)k * 15.5,北京,小 SP
  • 后端:28k * 15.5,北京,大 SP,
  • 后端:(30~32)k * 15.5,北京,SSP
  • 前端:23k * 15.5,北京,白菜
  • 测开:23K*15.5,北京,白菜
  • 大模型(北斗计划):48*15.5,北京

和去年开的差不多,区别不大!后端白菜年包也有接近 35.5w+ ,还是不错的。美团的公积金缴纳比例各地不同,北京可达 12%,上海为 7%,成都为 8%。

美团对新人的培养还是不错的,有很多培训的课程,公司里也有很多技术大佬。你平时如果看美团技术团队的文章的话,应该对美团的技术水平是比较认可的。团队氛围的话,这个要看具体的项目组,大部分应该都是比较和谐的。

下面是星球里一位在美团实习并转正的球友对美团的一些感受:

刚进入美团实习时,我对美团的完善的基研体系感到震惊(相较于学校而言)。大部分时候,我们可以直接使用现成的解决方案,而不需要重复造轮子。此外,美团还有完善的内部文档系统,大家都写得很好(这点让我有点卷)。在实习的三个月里,我也经历了一些困难和压力,但是我意识到打工就是这样,不管去哪里都是一样的。在实习期间,我更多地学习了一些思维方式,坚信只要我不会的东西,我就可以学会。每天都取得一点进步,保持前进。

今年秋招也有不少球友进了美团。

美团的面试难度还是相对偏高一点的,技术一面和二面可能加起来能拷打你两个多小时。

下面给大家分享一篇美团今年秋招的后端面经。我精选了一二面中的一些比较典型的面试问题进行解答, 大家可以感受一下美团的面试难度如何。

拷打项目

面试对着项目提问架构设计以及一些功能的开发思路。

这里记录一些比较有代表性的项目拷打问题,涉及太项目细节的部分就省略了。

你为什么选择 JWT 做身份验证?

相比于 Session 认证的方式来说,使用 JWT 进行身份认证主要有下面 4 个优势:

  1. 无状态 :JWT 自身包含了身份验证所需要的所有信息,因此,我们的服务器不需要存储 Session 信息。这显然增加了系统的可用性和伸缩性,大大减轻了服务端的压力。不过,也正是由于 JWT 的无状态,也导致了它最大的缺点:不可控!
  2. 有效避免了 CSRF 攻击:使用 JWT 进行身份验证不需要依赖 Cookie ,因此可以避免 CSRF 攻击。
  3. 适合移动端应用 :使用 Session 进行身份认证的话,需要保存一份信息在服务器端,而且这种方式会依赖到 Cookie(需要 Cookie 保存 SessionId),所以不适合移动端。但是,使用 JWT 进行身份认证就不会存在这种问题,因为只要 JWT 可以被客户端存储就能够使用,而且 JWT 还可以跨语言使用。
  4. 单点登录友好:使用 Session 进行身份认证的话,实现单点登录,需要我们把用户的 Session 信息保存在一台电脑上,并且还会遇到常见的 Cookie 跨域的问题。但是,使用 JWT 进行认证的话, JWT 被保存在客户端,不会存在这些问题。

但 JWT 并不是银弹,依然存在很多问题需要解决,例如:

  1. 注销登录等场景下 JWT 还有效:这个问题不存在于 Session 认证方式中,因为在 Session 认证方式中,遇到这种情况的话服务端删除对应的 Session 记录即可。但是,使用 JWT 认证的方式就不好解决了。我们也说过了,JWT 一旦派发出去,如果后端不增加其他逻辑的话,它在失效之前都是有效的。
  2. 续签问题 :JWT 通常有一个有效期(exp 字段),当令牌过期时,用户需要重新登录或获取一个新的令牌,这就是所谓的续签(refresh)问题。
  3. JWT 体积太大:JWT 结构复杂(Header、Payload 和 Signature),包含了更多额外的信息,还需要进行 Base64Url 编码,这会使得 JWT 体积较大,增加了网络传输的开销。

实际项目中,不用 JWT 直接使用普通的 Token(随机生成的 ID,不包含具体的信息) 结合 Redis 来做身份认证也是可以的。传统的 Token 通常只是一个唯一标识符,对应的信息(例如用户 ID、Token 过期时间、权限信息)存储在服务端,通常会通过 Redis 保存。传统 Token 体积更小,也更容易解决 JWT 存在的一些问题。

关于 JWT 的详细介绍以及常见问题的解决可以阅读我写的这两篇文章:

敏感词脱敏如何实现的?

后端返回数据给前端的时候,一般需要对敏感词脱敏,类似于下面这样:

脱敏的规则有很多种,例如:

  • 替换(常用):将敏感数据中的特定字符或字符序列替换为其他字符。例如,将信用卡号中的中间几位数字替换为星号(*)或其他字符。
  • 删除:将敏感数据中的部分内容随机删除。例如,将电话号码的随机 3 位数字进行删除。
  • 重排:将原始数据中的某些字符或字段的顺序打乱。例如,将身份证号码的随机位交错互换。
  • 加噪:在数据中注入一些误差或者噪音,达到对数据脱敏的效果。例如,在敏感数据中添加一些随机生成的字符。
  • ......

这里以最常用的替换为例进行介绍,这也是我的项目用到的方法。

我是利用 Hutool 提供的 DesensitizedUtil脱敏工具类配合 Jackson 通过注解的方式完成数据脱敏的。

如果不想引入 Hutool 的话,也可以自己实现一个脱敏工具类,实现逻辑非常简单。

详细介绍可以看笔者写的这篇文章:你的项目敏感词脱敏是如何实现的?

讲一下 MySQL 同步 ES 的实现

使用 Canal 可以做到业务代码完全解耦,API 完全解耦,零代码实现准实时同步, Canal 通过解析 MySQL 的 binlog 日志文件进行数据同步。

Canal 的原理本质上是 模拟了 MySQL 的主从复制协议

  1. 伪装成从库 (Slave): Canal Server 会向 MySQL Master 发送和标准 MySQL Slave 完全一样的握手包,将自己伪装成一个从库。
  2. 订阅 Binlog: MySQL Master 验证通过后,就会把它当成一个真正的从库。从 Canal 指定的位点(Position 或 GTID)开始,持续地、流式地把二进制日志(Binlog)推送给 Canal。Binlog 记录了数据库中所有的数据变更操作(INSERT, UPDATE, DELETE)。
  3. 解析与投递: Canal 接收到这些原始的二进制日志流后,会在内部进行解析,将它们转换成结构化的、易于消费的数据格式(比如 JSON)。最后,再将这些结构化的变更事件投递出去,通常是投递到像 Kafka 或 RocketMQ 这样的消息队列中,由下游的消费程序(比如我们的同步服务)订阅并写入到 Elasticsearch。

不过,Canal 仅仅支持增量同步。实际项目中,可以借助全量导入工具实现全量同步,例如 Datax ,后续再通过 Canal 实现增量同步,相对比较麻烦。

为了进一步提高系统性能和可维护性,我们通常会引入消息队列 (MQ) 作为中间层,解耦 Canal 和 Elasticsearch,

引入 MQ 的优势:

  • 异步处理: Canal 将数据变更消息发送到 MQ 后,无需等待 ES 处理完成即可返回,提高 Canal 的吞吐量。
  • 流量控制: MQ 作为缓冲区,可以削峰填谷,避免 Elasticsearch 瞬时写入压力过大。
  • 数据处理: 可以在消费 MQ 消息的过程中进行数据清洗、转换等操作,例如数据格式化、字段映射等。
  • 系统解耦: Canal 和 Elasticsearch 之间不再直接依赖,提高系统的可扩展性和可维护性。

引入 MQ 后,MySQL 数据同步到 Elasticsearch 的流程如下:

  1. 监听解析: Canal 读取 binlog,解析变更事件。
  2. 发送消息: Canal 将事件封装成消息发送到 MQ。
  3. 消费消息: 消费端监听 MQ,获取变更消息。
  4. 同步数据: 消费端处理消息,更新 Elasticsearch 索引。

Canal 从 1.1.1 版本开始,默认支持将接收到的 binlog 数据直接投递到 MQ,简化了集成流程。 你只需要配置 Canal 连接 MQ 的相关信息,即可实现 Canal 与 MQ 的联动。

项目中用了哪些多线程的知识

在我们的订单系统中,用户下单成功后,系统需要执行一系列非核心的后续操作,比如:发送下单成功短信、为用户增加积分、通知物流系统备货。这些操作都比较耗时,如果让用户在下单接口里同步等待它们全部完成,会导致接口响应时间变得很长,体验很差。

我的解决方案是, 将这些非核心操作全部抽取成独立的 Service 方法,并用 @Async 注解标记,将它们异步化。这样,主下单流程可以迅速完成并向用户返回成功,而这些耗时操作则被提交到后台线程池中慢慢执行,极大地优化了主流程的性能和用户体验。

@Async 注解由 Spring 框架提供,被该注解标注的类或方法会在 异步线程 中执行。这意味着当方法被调用时,调用者将不会等待该方法执行完成,而是可以继续执行后续的代码。

@Async 注解的使用非常简单,需要两个步骤:

  1. 在启动类上添加注解 @EnableAsync ,开启异步任务。
  2. 在需要异步执行的方法或类上添加注解 @Async
java 复制代码
@SpringBootApplication
// 开启异步任务
@EnableAsync
public class YourApplication {

    public static void main(String[] args) {
        SpringApplication.run(YourApplication.class, args);
    }
}

// 异步服务类
@Service
public class MyService {

    // 推荐使用自定义线程池,这里只是演示基本用法
    @Async
    public CompletableFuture<String> doSomethingAsync() {

        // 这里会有一些业务耗时操作
        // ...
        // 使用 CompletableFuture 可以更方便地处理异步任务的结果,避免阻塞主线程
        return CompletableFuture.completedFuture("Async Task Completed");
    }

}

使用 @Async 注解实现异步有什么需要注意的吗?执行出现异常怎么办?

如果没有显式地配置线程池,在 @Async 底层会先在 BeanFactory 中尝试获取线程池,如果获取不到,则会创建一个 SimpleAsyncTaskExecutor 实现。SimpleAsyncTaskExecutor 本质上不算是一个真正的线程池,因为它对于每个请求都会启动一个新线程而不重用现有线程,这会带来一些潜在的问题,例如资源消耗过大。

一定要显式配置一个线程池,推荐ThreadPoolTaskExecutor。并且,还可以根据任务的性质和需求,为不同的异步方法指定不同的线程池。

java 复制代码
@Configuration
@EnableAsync
public class AsyncConfig {

    @Bean(name = "executor1")
    public Executor executor1() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(3);
        executor.setMaxPoolSize(5);
        executor.setQueueCapacity(50);
        executor.setThreadNamePrefix("AsyncExecutor1-");
        executor.initialize();
        return executor;
    }

    @Bean(name = "executor2")
    public Executor executor2() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(4);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("AsyncExecutor2-");
        executor.initialize();
        return executor;
    }
}

异步方法中抛出的异常默认不会被调用者捕获。为了管理这些异常,建议使用CompletableFuture的异常处理功能,或者配置一个全局的AsyncUncaughtExceptionHandler来处理没有正确捕获的异常。

java 复制代码
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer{

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new CustomAsyncExceptionHandler();
    }

}

// 自定义异常处理器
class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {

    @Override
    public void handleUncaughtException(Throwable ex, Method method, Object... params) {
        // 日志记录或其他处理逻辑
    }
}

JavaGuide 网站对这块内容有详细介绍,感兴趣可以看看:Async 注解原理分析

怎么使用 Arthas 排查问题的?

可以参考笔者分享的这篇文章:自从学会 Arthas,日常开发效率直接起飞!

项目中 Jmeter 压测是怎么做的

做 JMeter 压测,主要遵循四步流程:

  1. 规划:会先定好明确的性能目标,比如 QPS 要达到多少,响应时间要低于多少。
  2. 准备脚本:用 JMeter 写好请求脚本,并且对用户 ID、商品 ID 这些变量进行参数化,让请求更真实。
  3. 执行和监控 :在用 JMeter 加压的同时,我们会重点监控服务器的 CPU、内存、GC 等指标。因为真正的瓶颈信息在服务器端,而不是在 JMeter 的报告里。
  4. 分析和调优 : 压测结束后,我们会根据监控数据和报告来分析瓶颈。比如,如果是 CPU 高了,我们就用 Arthas 去看热点代码;如果是数据库慢了,就去优化 SQL。优化完再压一轮,直到达到目标为止。

MySQL

MySQL 存储引擎知道哪些,有什么区别?

MySQL 支持多种存储引擎,你可以通过 SHOW ENGINES 命令来查看 MySQL 支持的所有存储引擎。

从上图我们可以查看出, MySQL 当前默认的存储引擎是 InnoDB。并且,所有的存储引擎中只有 InnoDB 是事务性存储引擎,也就是说只有 InnoDB 支持事务。

我这里使用的 MySQL 版本是 8.x,不同的 MySQL 版本之间可能会有差别。

MySQL 5.5.5 之前,MyISAM 是 MySQL 的默认存储引擎。5.5.5 版本之后,InnoDB 是 MySQL 的默认存储引擎。

你可以通过 SELECT VERSION() 命令查看你的 MySQL 版本。

bash 复制代码
mysql> SELECT VERSION();
+-----------+
| VERSION() |
+-----------+
| 8.0.27    |
+-----------+
1 row in set (0.00 sec)

你也可以通过 SHOW VARIABLES LIKE '%storage_engine%' 命令直接查看 MySQL 当前默认的存储引擎。

bash 复制代码
mysql> SHOW VARIABLES  LIKE '%storage_engine%';
+---------------------------------+-----------+
| Variable_name                   | Value     |
+---------------------------------+-----------+
| default_storage_engine          | InnoDB    |
| default_tmp_storage_engine      | InnoDB    |
| disabled_storage_engines        |           |
| internal_tmp_mem_storage_engine | TempTable |
+---------------------------------+-----------+
4 rows in set (0.00 sec)

关于 MyISAM 和 InnoDB 的对比内容较多,可以参考笔者写的这篇文章:MySQL 常见面试题总结(MySQL 基础、存储引擎、事务、索引、锁、性能优化等)。

更多 MySQL 高频知识点和面试题总结,可以阅读笔者写的这几篇文章:

SELECT * 会导致索引失效吗?

SELECT * 不会直接导致索引失效(如果不走索引大概率是因为 where 查询范围过大导致的),但它可能会带来一些其他的性能问题比如造成网络传输和数据处理的浪费、无法使用索引覆盖。

索引失效的原因有哪些?

  1. 创建了组合索引,但查询条件未遵守最左匹配原则;
  2. 在索引列上进行计算、函数、类型转换等操作;
  3. 以 % 开头的 LIKE 查询比如 LIKE '%abc';;
  4. 查询条件中使用 OR,且 OR 的前后条件中有一个列没有索引,涉及的索引都不会被使用到;
  5. IN 的取值范围较大时会导致索引失效,走全表扫描(NOT IN 和 IN 的失效场景相同);
  6. 发生隐式转换;

MySQL 的默认隔离级别是什么?可以解决幻读问题吗?

简化版参考答案:MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)。标准的 SQL 隔离级别定义里,REPEATABLE-READ(可重复读)是不可以防止幻读的,但 InnoDB 实现的 REPEATABLE-READ 隔离级别其实是可以解决幻读问题发生的。

详细参考答案:MySQL 的默认隔离级别是什么?可以解决幻读问题吗?

Redis

为什么要用 Redis?

项目中引入 Redis 具体做了哪些事情?

Redis 除了可以用来缓存高频访问的数据之外,还以用来实现:

  • 分布式锁 :通过 Redis 来做分布式锁是一种比较常见的方式。通常情况下,我们都是基于 Redisson 来实现分布式锁。关于 Redis 实现分布式锁的详细介绍,可以看我写的这篇文章:如何基于 Redis 实现分布式锁?
  • 限流 :一般是通过 Redis + Lua 脚本的方式来实现限流。如果不想自己写 Lua 脚本的话,也可以直接利用 Redisson 中的 RRateLimiter 来实现分布式限流,其底层实现就是基于 Lua 代码+令牌桶算法。
  • 消息队列:Redis 自带的 List 数据结构可以作为一个简单的队列使用。Redis 5.0 中增加的 Stream 类型的数据结构更加适合用来做消息队列。它比较类似于 Kafka,有主题和消费组的概念,支持消息持久化以及 ACK 机制。
  • 延时队列:Redisson 内置了延时队列(基于 Sorted Set 实现的)。
  • 分布式 Session :利用 String 或者 Hash 数据类型保存 Session 数据,所有的服务器都可以访问。
  • 复杂业务场景:通过 Redis 以及 Redis 扩展(比如 Redisson)提供的数据结构,我们可以很方便地完成很多复杂的业务场景比如通过 Bitmap 统计活跃用户、通过 Sorted Set 维护排行榜。

面试中,根据你项目的实际情况去回答即可!

更多 Redis 高频面试题总结,可以阅读笔者写的这两篇文章:

使用 HyperLogLog 统计页面 UV 怎么做的?

使用 HyperLogLog 统计页面 UV 主要需要用到下面这两个命令:

  • PFADD key element1 element2 ...:添加一个或多个元素到 HyperLogLog 中。
  • PFCOUNT key1 key2:获取一个或者多个 HyperLogLog 的唯一计数。

1、将访问指定页面的每个用户 ID 添加到 HyperLogLog 中。

bash 复制代码
PFADD PAGE_1:UV USER1 USER2 ...... USERn

2、统计指定页面的 UV。

bash 复制代码
PFCOUNT PAGE_1:UV

如果缓存的数据量太大,内存不够用怎么办?

  1. 垂直扩展(增加硬件配置): 最直接的方式是升级现有 Redis 服务器的内存容量,选择配置更高、内存更大的服务器来部署 Redis 实例。 不过,这会受到硬件成本和单机内存容量上限的限制。
  2. 水平扩展(Redis 切片集群): Redis 切片集群就是部署多台 Redis 主节点(master),这些节点之间平等,并没有主从之说,同时对外提供读/写服务。缓存的数据库相对均匀地分布在这些 Redis 实例上,客户端的请求通过路由规则转发到目标 master 上。Redis 切片集群对于横向扩展非常友好,只需要增加 Redis 节点到集群中即可。

Redis 切片集群如何实现?

Redis 3.0 官方推出了分片集群解决方案 Redis Cluster 。经过多个版本的持续完善,Redis Cluster 成为 Redis 切片集群的首选方案,满足绝大部分高并发业务场景需求。

如果项目使用的是 Redis 3.0 之前的版本,可以使用 Twemproxy、Codis 这类开源分片集群方案。Twemproxy、Codis 就相当于是 Proxy 层,负责维护路由规则,实现负载均衡。

操作系统

进程线程区别

进程是操作系统分配资源的基本单位,具备独立的地址空间;线程是 CPU 调度的基本单位,复用所属进程的资源。二者核心差异在于隔离性与共享性:进程彼此隔离,线程间共享内存。引入线程的目的,是在单一应用内部实现低开销、高效率的并发,降低资源占用与通信成本,充分释放多核潜能。

详细介绍:进程和线程有什么区别?有了进程为什么还需要线程?

进程的调度算法有哪些?

进程调度算法的核心目标是决定就绪队列中的哪个进程应该获得 CPU 资源,其设计目标通常是在吞吐量、周转时间、响应时间公平性之间做权衡。

我习惯将这些算法分为两大类:非抢占式抢占式

第一类:非抢占式调度 (Non-Preemptive)

这种方式下,一旦 CPU 分配给一个进程,它就会一直运行下去,直到任务完成或主动放弃(比如等待 I/O)。

  1. 先到先服务调度算法(FCFS,First Come, First Served) : 这是最简单的,就像排队,谁先来谁先用。优点是公平、实现简单。但缺点很明显,如果一个很长的任务先到了,后面无数个短任务都得等着,这会导致平均等待时间很长,我们称之为"护航效应"。
  2. 短作业优先的调度算法(SJF,Shortest Job First) : 从就绪队列中选出一个估计运行时间最短的进程为之分配资源。理论上,它的平均等待时间是最短的,吞吐量很高。但缺点是,它需要预测运行时间,这很难做到,而且可能会导致长作业"饿死",永远得不到执行。

第二类:抢占式调度 (Preemptive)

操作系统可以强制剥夺当前进程的 CPU 使用权,分配给其他更重要的进程。现代操作系统基本都采用这种方式。

  • 时间片轮转调度算法(RR,Round-Robin) : 这是最经典、最公平的抢占式算法。它给每个进程分配一个固定的时间片,用完了就把它放到队尾,切换到下一个进程。它非常适合分时系统,保证了每个进程都能得到响应,但时间片的设置很关键:太长了退化成 FCFS,太短了则会导致过于频繁的上下文切换,增加系统开销。
  • 优先级调度算法(Priority):每个进程都有一个优先级,进程调度器总是选择优先级最高的进程,具有相同优先级的进程以 FCFS 方式执行。这很灵活,可以根据内存要求,时间要求或任何其他资源要求来确定优先级,但同样可能导致低优先级进程"饿死"。

前面介绍的几种进程调度的算法都有一定的局限性,如:短进程优先的调度算法,仅照顾了短进程而忽略了长进程 。那有没有一种结合了上面这些进程调度算法优点的呢?

多级反馈队列调度算法(MFQ,Multi-level Feedback Queue) 是现实世界中最常用的一种算法,比如早期的 UNIX。它非常聪明,结合了 RR 和优先级调度。它设置了多个不同优先级的队列,每个队列使用 RR 调度,时间片大小也不同。新进程先进入最高优先级队列;如果在一个时间片内没执行完,就会被降级到下一个队列。这样既照顾了短作业(在高优先级队列中快速完成),也保证了长作业不会饿死(最终会在低优先级队列中得到执行),是一种非常均衡的方案。

死锁,死锁条件

死锁(Deadlock)描述的是这样一种情况:多个进程/线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于进程/线程被无限期地阻塞,因此程序不可能正常终止。

一个最经典的例子就是**"交叉持锁"**。想象有两个线程和两个锁:

  • 线程 1 先拿到了锁 A,然后尝试去获取锁 B。
  • 几乎同时,线程 2 拿到了锁 B,然后尝试去获取锁 A。

这时,线程 1 等着线程 2 释放锁 B,而线程 2 等着线程 1 释放锁 A,双方都持有对方需要的资源,并等待对方释放,就形成了一个"死结"。

死锁的发生并不是偶然的,它需要同时满足四个必要条件

  1. 互斥:资源必须处于非共享模式,即一次只有一个进程可以使用。如果另一进程申请该资源,那么必须等待直到该资源被释放为止。
  2. 占有并等待:一个进程至少应该占有一个资源,并等待另一资源,而该资源被其他进程所占有。
  3. 非抢占:资源不能被抢占。只能在持有资源的进程完成任务后,该资源才会被释放。
  4. 循环等待:有一组等待进程 {P0, P1,..., Pn}, P0 等待的资源被 P1 占有,P1 等待的资源被 P2 占有,......,Pn-1 等待的资源被 Pn 占有,Pn 等待的资源被 P0 占有。

注意 ⚠️ :这四个条件是产生死锁的 必要条件 ,也就是说只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。

中断和轮询的区别

对比维度 轮询(Polling) 中断(Interrupt)
核心逻辑 CPU 主动循环查询设备状态 设备主动发信号通知 CPU
CPU 资源占用 高(忙等,无效循环浪费算力) 低(仅响应时占用,平时不干扰)
实时性 低(依赖查询间隔,有延迟) 高(即时响应,延迟极低)
适用场景 设备数据频率固定、简单场景(如单片机读取低速传感器) 设备数据随机、实时性要求高的场景(如键盘、磁盘 IO、网络通信)

更多操作系统高频知识点和面试题总结,可以阅读笔者写的这几篇文章:

网络

DNS 解析的过程

整个过程的步骤比较多,我单独写了一篇文章详细介绍:DNS 域名系统详解(应用层)

用户输入网址到显示对应页面的全过程

先来看一张图(来源于《图解 HTTP》):

上图有一个错误需要注意:是 OSPF 不是 OPSF。 OSPF(Open Shortest Path First,ospf)开放最短路径优先协议, 是由 Internet 工程任务组开发的路由选择协议

总体来说分为以下几个步骤:

  1. 在浏览器中输入指定网页的 URL。
  2. 浏览器通过 DNS 协议,获取域名对应的 IP 地址。
  3. 浏览器根据 IP 地址和端口号,向目标服务器发起一个 TCP 连接请求。
  4. 浏览器在 TCP 连接上,向服务器发送一个 HTTP 请求报文,请求获取网页的内容。
  5. 服务器收到 HTTP 请求报文后,处理请求,并返回 HTTP 响应报文给浏览器。
  6. 浏览器收到 HTTP 响应报文后,解析响应体中的 HTML 代码,渲染网页的结构和样式,同时根据 HTML 中的其他资源的 URL(如图片、CSS、JS 等),再次发起 HTTP 请求,获取这些资源的内容,直到网页完全加载显示。
  7. 浏览器在不需要和服务器通信时,可以主动关闭 TCP 连接,或者等待服务器的关闭请求

详细介绍:访问网页的全过程(知识串联)

相关推荐
aiopencode17 分钟前
无需源码的 iOS 加固方案 面向外包项目与存量应用的多层安全体系
后端
打工人你好19 分钟前
如何设计更安全的 VIP 权限体系
java·jvm·安全
语落心生21 分钟前
Apache Geaflow推理框架Geaflow-infer 解析系列(六)共享内存架构
后端
语落心生25 分钟前
Apache Geaflow推理框架Geaflow-infer 解析系列(七)数据读写流程
后端
L.EscaRC27 分钟前
Spring IOC核心原理与运用
java·spring·ioc
语落心生27 分钟前
Apache Geaflow推理框架Geaflow-infer 解析系列(五)环境上下文管理
后端
程序员爱钓鱼29 分钟前
用 Python 批量生成炫酷扫光 GIF 动效
后端·python·trae
摇滚侠40 分钟前
2025最新 SpringCloud 教程,Nacos-总结,笔记19
java·笔记·spring cloud
aiopencode42 分钟前
iOS 应用上架的工程实践复盘,从构建交付到审核通过的全流程拆解
后端