浏览器输入网址后的完整流程

news/2026/1/26 19:48:36/文章来源:https://www.cnblogs.com/guoyafenghome/p/19535375

浏览器输入网址后的完整流程(大白话版)


一、URL解析 - 看看你要去哪

首先浏览器会分析你输入的网址,比如 https://www.taobao.com/product/detail?id=123

它会拆分成:

  • 协议:https(说明要加密传输)
  • 域名:www.taobao.com
  • 路径:/product/detail
  • 参数:id=123

就像你要去一个地方,得先知道是坐飞机还是坐火车(协议),去哪个城市(域名),具体哪条街(路径)。


二、DNS解析 - 把域名翻译成IP地址

为什么需要这步?
因为计算机只认识IP地址(比如 192.168.1.1),不认识域名。就像你说"去王府井",但导航需要具体的经纬度。

查找顺序(从近到远):

  1. 浏览器缓存:先看看浏览器记不记得这个域名对应的IP

    比如你刚访问过淘宝,浏览器会记住:www.taobao.com → 47.96.123.45
    
  2. 操作系统缓存:浏览器不记得,问问操作系统

  3. 本地hosts文件:看看有没有手动配置过

    /etc/hosts 或 C:\Windows\System32\drivers\etc\hosts
    127.0.0.1 localhost
    
  4. 路由器缓存:问问路由器记不记得

  5. DNS服务器查询:以上都没有,就要去DNS服务器问了

    • 本地DNS服务器(一般是运营商提供的)
    • 根DNS服务器 → .com域的DNS → taobao.com的DNS
    • 最终拿到IP地址:47.96.123.45

三、建立TCP连接 - 三次握手

拿到IP地址后,要和服务器建立连接。这就是著名的三次握手

为什么要三次握手?
打个比方,你要和朋友约饭:

你:  "明天中午有空吗?"           (第一次握手 - SYN)
朋友:"有空,你也有空吗?"          (第二次握手 - SYN+ACK)
你:  "有空,那就这么定了!"        (第三次握手 - ACK)

三次才能确认双方都能收发消息。

技术角度:

客户端 → 服务器:SYN(我想和你建立连接)
服务器 → 客户端:SYN+ACK(好的,我同意,你收到了吗?)
客户端 → 服务器:ACK(收到了,开始吧)

四、TLS/SSL握手 - HTTPS加密(如果是HTTPS)

如果是HTTPS,还要多一步加密握手。这步比较复杂,但目的很简单:协商一个只有你和服务器知道的密码

通俗理解:
就像你和朋友要说悄悄话,得先确认一个暗号。

具体步骤:

第一步:打招呼,交换信息

客户端说:"我支持这些加密方式:AES、RSA..."
服务器说:"好,我选AES,这是我的证书(身份证)"

第二步:验证身份

客户端检查服务器的证书:
- 是不是正规CA机构颁发的?(就像检查身份证是不是公安局发的)
- 有没有过期?
- 是不是这个网站的证书?

第三步:生成密钥

双方各自生成一个随机数
然后一起算出一个对称密钥(共享密码)
后面所有通信都用这个密钥加密

为什么这么复杂?
为了防止中间人攻击。就像你和朋友说悄悄话,得先确认对方不是冒充的。


五、发送HTTP请求 - 告诉服务器你要什么

连接建立好了,可以发请求了!

请求长这样:

GET /api/user/info?userId=123 HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Chrome浏览器)
Cookie: sessionId=abc123xyz  (登录凭证)
Accept: application/json
Connection: keep-alive

通俗理解:

  • GET:我要查询数据
  • POST:我要提交数据
  • Host:告诉服务器是访问哪个网站(一台服务器可能部署多个网站)
  • Cookie:身份凭证,证明你是谁(已登录)

六、服务器处理请求 - 后端的活(重点)

这是我们Java后端最熟悉的部分!

6.1 请求到达服务器

用户请求 ↓
DNS解析 ↓
【负载均衡器】(Nginx/LVS)↓
根据策略分发到某台应用服务器↓
【防火墙/WAF】安全检查↓
到达应用服务器

负载均衡的作用:
就像银行有很多窗口,负载均衡器负责把客户分配到不同窗口,避免某个窗口排队太长。

6.2 Web容器接收(Tomcat/Jetty)

Tomcat接收到HTTP请求
创建HttpServletRequest对象
交给应用处理

6.3 Spring MVC处理流程(核心)

这是我们写代码的地方!

// ========== 第1步:DispatcherServlet(前端控制器)==========
// 所有请求的入口,统一接收// ========== 第2步:HandlerMapping(找到对应的Controller)==========
// 根据URL找到处理的方法
// GET /api/user/info → UserController.getUserInfo()// ========== 第3步:拦截器前置处理 ==========
@Component
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {// 检查是否登录String token = request.getHeader("token");if (StringUtils.isEmpty(token)) {response.setStatus(401);return false; // 拦截,不继续执行}return true; // 放行}
}// ========== 第4步:执行Controller ==========
@RestController
@RequestMapping("/api/user")
public class UserController {@Autowiredprivate UserService userService;@GetMapping("/info")public Result getUserInfo(@RequestParam Long userId) {System.out.println("接收到请求,查询用户ID:" + userId);// 调用Service层UserInfo userInfo = userService.getUserInfoById(userId);return Result.success(userInfo);}
}// ========== 第5步:Service业务处理 ==========
@Service
public class UserServiceImpl implements UserService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate RedisTemplate redisTemplate;@Overridepublic UserInfo getUserInfoById(Long userId) {// 1. 先查缓存(Redis)String cacheKey = "user:info:" + userId;UserInfo userInfo = (UserInfo) redisTemplate.opsForValue().get(cacheKey);if (userInfo != null) {System.out.println("缓存命中,直接返回");return userInfo;}// 2. 缓存没有,查数据库System.out.println("缓存未命中,查询数据库");userInfo = userMapper.selectById(userId);// 3. 放入缓存if (userInfo != null) {redisTemplate.opsForValue().set(cacheKey, userInfo, 30, TimeUnit.MINUTES);}return userInfo;}
}// ========== 第6步:DAO层查询数据库 ==========
@Mapper
public interface UserMapper extends BaseMapper<UserInfo> {// MyBatis会自动生成SQL:// SELECT * FROM user_info WHERE id = #{userId}
}// ========== 第7步:拦截器后置处理 ==========
// 记录日志、统计耗时等// ========== 第8步:统一异常处理 ==========
@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(Exception.class)public Result handleException(Exception e) {log.error("系统异常", e);return Result.error("系统异常,请稍后重试");}
}// ========== 第9步:响应数据序列化(JSON) ==========
// Spring Boot默认用Jackson把对象转成JSON
// UserInfo对象 → {"id": 123, "name": "张三"}

6.4 涉及的中间件交互

Redis缓存:

// 为什么要用缓存?
// 数据库查询慢(几十毫秒),Redis查询快(1-2毫秒)
// 减轻数据库压力// 缓存策略
1. 先查缓存,有就直接返回
2. 没有再查数据库,然后放入缓存
3. 设置过期时间,避免数据过期

数据库连接池(Druid/HikariCP):

// 为什么要连接池?
// 每次建立数据库连接很慢(几百毫秒)
// 连接池提前创建好连接,用的时候直接拿// 配置
spring:datasource:hikari:maximum-pool-size: 20    # 最多20个连接minimum-idle: 5          # 最少保持5个空闲连接connection-timeout: 3000 # 获取连接超时时间

消息队列(RabbitMQ/Kafka):

// 为什么要用MQ?
// 1. 异步处理:用户下单后,发送短信通知可以异步做
// 2. 削峰填谷:秒杀时,订单先进队列,慢慢处理@Service
public class OrderService {@Autowiredprivate RabbitTemplate rabbitTemplate;public void createOrder(Order order) {// 保存订单orderMapper.insert(order);// 发送消息到MQ,异步发送短信rabbitTemplate.convertAndSend("order.exchange", "order.created", order);}
}

6.5 微服务架构下的调用

如果是微服务,可能还要调用其他服务:

@Service
public class OrderService {@Autowiredprivate UserServiceClient userServiceClient; // Feign客户端@Autowiredprivate ProductServiceClient productServiceClient;public OrderInfo createOrder(Long userId, Long productId) {// 1. 调用用户服务,检查用户信息UserInfo user = userServiceClient.getUserById(userId);if (user == null) {throw new BusinessException("用户不存在");}// 2. 调用商品服务,检查库存ProductInfo product = productServiceClient.getProductById(productId);if (product.getStock() <= 0) {throw new BusinessException("库存不足");}// 3. 扣减库存(RPC调用)productServiceClient.decreaseStock(productId, 1);// 4. 创建订单Order order = new Order();order.setUserId(userId);order.setProductId(productId);orderMapper.insert(order);return order;}
}

七、返回HTTP响应 - 把结果发回去

服务器处理完后,要把结果返回给浏览器:

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 156
Set-Cookie: sessionId=xyz789; HttpOnly; Secure
Cache-Control: max-age=3600
Access-Control-Allow-Origin: *{"code": 200,"message": "success","data": {"userId": 123,"username": "张三","avatar": "https://cdn.example.com/avatar/123.jpg"}
}

响应头的含义:

  • 200 OK:成功(还有404未找到、500服务器错误等)
  • Content-Type:返回的是JSON格式数据
  • Set-Cookie:设置Cookie(下次请求会自动带上)
  • Cache-Control:缓存控制,3600秒内可以用浏览器缓存

八、浏览器接收响应 - 拿到结果

浏览器接收到响应后:

  1. 解析响应头:看看状态码、Content-Type等
  2. 处理Cookie:保存Cookie,下次自动带上
  3. 解析响应体:把JSON数据解析成JavaScript对象
// 前端JavaScript代码
fetch('/api/user/info?userId=123').then(response => response.json()).then(data => {console.log('用户信息:', data);// 然后更新页面显示document.getElementById('username').textContent = data.data.username;});

九、关闭TCP连接 - 四次挥手

请求完成后,可能会关闭连接(如果不用keep-alive的话):

客户端 → 服务器:FIN(我说完了)
服务器 → 客户端:ACK(知道了)
服务器 → 客户端:FIN(我也说完了)
客户端 → 服务器:ACK(知道了,再见)

为什么是四次?
因为TCP是全双工的,双方都要确认关闭。就像打电话,双方都要说"我挂了"。

HTTP Keep-Alive:

为了性能优化,现在大多保持连接不关闭
一个TCP连接可以发多个HTTP请求
Connection: keep-alive

十、性能优化点(面试加分项)

1. DNS优化

- DNS缓存:减少DNS查询次数
- DNS预解析:<link rel="dns-prefetch" href="//cdn.example.com">
- 使用CDN:让用户访问离他最近的服务器

2. 连接优化

- HTTP/2:一个连接多路复用,并行请求
- Keep-Alive:连接复用
- 减少重定向:每次重定向都要额外请求

3. 后端优化(我们能做的)

缓存优化:

// 多级缓存
1. 本地缓存(Caffeine):最快,但不能共享
2. 分布式缓存(Redis):共享,稍慢
3. 数据库:最慢@Cacheable(value = "userInfo", key = "#userId")
public UserInfo getUserInfoById(Long userId) {return userMapper.selectById(userId);
}

数据库优化:

// 1. 索引优化
CREATE INDEX idx_user_id ON orders(user_id);// 2. 避免N+1查询问题
// 错误示例:循环查询
for (Long userId : userIds) {UserInfo user = userInfoApi.getUserInfoById(userId); // 查N次
}// 正确示例:批量查询
List<UserInfo> users = userInfoApi.getUserInfoListByIds(userIds); // 查1次
Map<Long, UserInfo> userMap = users.stream().collect(Collectors.toMap(UserInfo::getId, Function.identity()));// 3. 读写分离:查询走从库,写入走主库
// 4. 分库分表:数据量大了,拆分表

异步处理:

@Async
public CompletableFuture<UserInfo> getUserInfoAsync(Long userId) {UserInfo userInfo = userMapper.selectById(userId);return CompletableFuture.completedFuture(userInfo);
}// 并行查询多个接口
CompletableFuture<UserInfo> userFuture = getUserInfoAsync(userId);
CompletableFuture<OrderList> orderFuture = getOrderListAsync(userId);CompletableFuture.allOf(userFuture, orderFuture).join();

限流降级(高并发场景):

// 使用Sentinel限流
@SentinelResource(value = "getUserInfo", blockHandler = "handleBlock")
public UserInfo getUserInfo(Long userId) {return userMapper.selectById(userId);
}public UserInfo handleBlock(Long userId, BlockException e) {// 限流后返回默认值或提示return UserInfo.getDefault();
}

4. 响应优化

- Gzip压缩:减少传输大小
- 静态资源CDN:JS、CSS、图片等放CDN
- HTTP缓存:设置Cache-Control,减少请求

十一、完整时间线(实际耗时)

DNS解析:      20-120ms
TCP握手:      30-50ms(1个RTT)
TLS握手:      60-100ms(2个RTT,TLS 1.2)
HTTP请求:     10-30ms
服务器处理:    50-200ms(取决于业务复杂度)- 缓存命中:   1-5ms- 数据库查询: 10-50ms- RPC调用:    10-100ms
HTTP响应:     10-30ms
-------------------
总计:         180-530ms

优化后(理想情况):

DNS缓存命中:   0ms
HTTP/2复用:   0ms(连接已建立)
Redis缓存命中: 1-2ms
总计:         10-30ms

十二、常见面试追问

Q1:为什么TCP是三次握手,不是两次?

两次握手的问题:
如果第一次的SYN包在网络中延迟了,客户端以为丢了,又发了一次
服务器两次都响应,建立了两个连接,浪费资源三次握手可以防止这种情况

Q2:HTTPS一定安全吗?

不一定,可能有这些风险:
1. 证书伪造(中间人攻击)
2. SSL降级攻击
3. 证书过期或不受信任
4. 协议漏洞(如早期的心脏滴血漏洞)所以要:
- 使用正规CA颁发的证书
- 使用最新的TLS版本
- 配置强加密套件

Q3:如何优化接口响应时间?

1. 加缓存(Redis):毫秒级响应
2. 异步处理:不需要立即返回的操作异步做
3. 数据库优化:索引、批量查询
4. 减少RPC调用:能合并的合并
5. 使用CDN:静态资源加速
6. 负载均衡:分散压力

Q4:如何保证接口的幂等性?

// 场景:用户重复点击提交按钮,如何避免重复下单?@Service
public class OrderService {@Autowiredprivate RedisTemplate redisTemplate;public OrderInfo createOrder(String idempotentKey, OrderDTO orderDTO) {// 使用Redis的SETNX实现幂等Boolean success = redisTemplate.opsForValue().setIfAbsent("order:idempotent:" + idempotentKey, "1", 5, TimeUnit.MINUTES);if (!success) {throw new BusinessException("请勿重复提交");}// 创建订单Order order = new Order();// ... 订单逻辑return order;}
}

Q5:如何应对高并发?

1. 缓存:减少数据库压力
2. 限流:保护系统不被打垮
3. 降级:非核心功能暂时关闭
4. 熔断:下游服务挂了,快速失败
5. 异步:MQ削峰填谷
6. 扩容:加机器(弹性伸缩)
7. 数据库:读写分离、分库分表
8. CDN:静态资源加速

总结

完整流程:

  1. URL解析
  2. DNS解析(域名→IP)
  3. TCP三次握手
  4. TLS握手(HTTPS)
  5. 发送HTTP请求
  6. 服务器处理(负载均衡 → Web容器 → Spring MVC → Service → DAO → 数据库/缓存)
  7. 返回HTTP响应
  8. TCP四次挥手(或保持连接)

关键点:

  • 理解整体流程
  • 知道每个环节的作用和可能的优化点
  • 能结合实际项目经验讲解
  • 注重性能优化和高并发处理

面试建议:

  • 先讲主流程,面试官感兴趣会深入问
  • 结合自己项目经验,说说实际遇到的问题和解决方案
  • 提到性能优化点可以加分
  • 不知道的不要硬编,诚实说不清楚,但可以说说自己的理解

附录:常用命令

# 查看DNS解析
nslookup www.baidu.com
dig www.baidu.com# 查看路由
traceroute www.baidu.com# 抓包看HTTP请求
tcpdump -i any -X port 80# 查看TCP连接
netstat -an | grep ESTABLISHED# 测试接口响应时间
curl -w "@curl-format.txt" -o /dev/null -s "http://example.com/api"

curl-format.txt:

time_namelookup:  %{time_namelookup}\n
time_connect:     %{time_connect}\n
time_starttransfer: %{time_starttransfer}\n
time_total:       %{time_total}\n

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1220544.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

宏智树 AI:解码教育类毕业论文生成逻辑,从 “机械拼凑” 到 “学术表达”

作为深耕教育论文写作科普的博主&#xff0c;每年都要见证无数同学在毕业论文写作中陷入困境&#xff1a;选题跟风导致研究价值不足&#xff0c;文献综述写成流水账缺乏深度&#xff0c;实证分析因操作不规范数据失效&#xff0c;甚至生成初稿后满是机械感的 AI 痕迹。 教育类…

JS—对象的深入

目录 前言&#xff1a; 创建对象方式二 读取对象 枚举变量属性 区分变量和属性&#xff0c;函数和方法 this初印象 创建对象方式三 创建对象方式四 注意&#xff1a;三个特点 构造函数&#xff08;new关键字&#xff09;的执行流程 构造函数的弊端 对象的实例化 i…

mindie部署qwen3-8b

参考: https://www.hiascend.com/document/detail/zh/mindie/230/quickstart/mindie_quickstart_0004.html https://www.hiascend.com/document/detail/zh/mindie/230/mindiellm/llmdev/mindie_service0285.html mkdir /root/tmp modelscope download --model Qwen/Qwen3-8B -…

基于深度学习与计算机视觉的高精度表格识别技术,精准识别复杂表格结构,自动输出结构化数据

在传统财务工作中&#xff0c;面对成百上千份格式各异的银行对账单、资产负债表和税务申报表&#xff0c;人工录入不仅效率低下&#xff0c;还极易引入错误。如何从非结构化或半结构化的纸质/电子文档中快速、准确地提取表格数据&#xff0c;成为企业财务数字化转型的关键瓶颈。…

宏智树AI数据分析:把原始数据变成论文硬核实证,小白也能玩转

作为深耕论文写作科普的博主&#xff0c;后台最扎心的求助莫过于&#xff1a;“问卷数据堆了几百条&#xff0c;却连SPSS的界面都搞不懂”“实验数据算出来了&#xff0c;不知道怎么解读成学术结论”“用Excel画的图被导师批‘不专业’&#xff0c;重新做又无从下手”。实证类论…

React Native App 图表绘制完整实现指南 - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

Device

The word "device"​ (/dɪˈvaɪs/) is a noun that encompasses both concrete tools and abstract strategies. At its core, it refers to something created or adapted for a particular purpose. Thi…

Insight

Mastering the Word: "Insight" Insight​ (/ˈɪn.saɪt/) is a noun that refers to a deep, clear, and often sudden understanding of a complex problem or situation.​ It is the ability to see in…

必看!2026年修补料供应商TOP5推荐榜单,解决砂浆需求问题

在2026年,选择合适的修补料砂浆供应商对施工项目至关重要。本篇文章将为您呈现市场上表现突出的五大供应商,包括各自的特点和优势。这些供应商不仅提供高质量的产品,还能满足不同施工需求,是您值得信赖的合作伙伴。…

Flutter for OpenHarmony 引力弹球游戏开发全解析:从零构建一个交互式物理小游戏

Flutter for OpenHarmony 引力弹球游戏开发全解析&#xff1a;从零构建一个交互式物理小游戏 在移动应用开发中&#xff0c;游戏类应用始终是展示框架能力与开发者创意的重要载体。Flutter 作为 Google 推出的跨平台 UI 框架&#xff0c;凭借其高性能渲染引擎、丰富的动画系统和…

学长亲荐!10款AI论文软件测评,本科生毕业论文必备

学长亲荐&#xff01;10款AI论文软件测评&#xff0c;本科生毕业论文必备 2026年AI论文工具测评&#xff1a;为什么你需要一份精准的推荐榜单 随着人工智能技术在学术领域的深入应用&#xff0c;越来越多的本科生开始借助AI论文软件提升写作效率、优化内容质量。然而&#xff…

2026年泵送剂外加剂公司推荐:选对早强剂外加剂源头厂家、供应商和销售厂家

在2026年的泵送剂外加剂市场,选择合适的早强剂外加剂源头厂家至关重要。随着建筑行业对施工效率和混凝土质量要求的提升,早强剂的需求持续增长。市场上涌现出多家供应商,然而并非所有厂家都能提供稳定且高质量的产品…

老年人能力评估系统开发Day3

今天代码还没写完,明天继续

Linux系统负载过高问题排查

当Linux系统的load average(平均负载)过高时,通常意味着系统在某段时间内处于较高的运行压力下。load average反映的是处于可运行状态(Running或Uninterruptible Sleep)的进程平均数量,它不仅与CPU使用有关,还与…

专业服务深耕:电商智能客服与AI客服机器人重构日用品行业服务价值

一、行业核心矛盾&#xff1a;专业适配缺失与服务效能失衡洗发水等日用品为高频消费品类&#xff0c;用户诉求聚焦产品适配、使用答疑、售后解决&#xff0c;且消费触点分散&#xff0c;服务体系陷入双重困境。专业端&#xff0c;人工客服专业储备不足&#xff0c;新员工培训1个…

Web3区块链软件开发全栈解决方案:达普韦伯(Dappweb)2026年实战经验分享

2026年&#xff0c;Web3赛道已从“概念炒作”转向“生产力落地”。香港作为全球RWA与虚拟资产枢纽&#xff0c;SFC监管框架全面收紧&#xff0c;稳定币条例、储备审计、链上合规要求越来越高。与此同时&#xff0c;内地团队出海面临的最大挑战仍是&#xff1a;如何用全栈技术栈…

达普韦伯Dappweb助力传统企业入Web3:区块链公链、交易所、DApp全栈开发详解

2026年&#xff0c;Web3已从“概念炒作”彻底转向“生产力交付”阶段。 传统企业不再问“Web3是什么”&#xff0c;而是直接问&#xff1a;我们怎么低成本、安全、合规地切入&#xff1f; 答案越来越清晰&#xff1a;找对伙伴&#xff0c;从底层基础设施到上层应用&#xff0c;…

Hive实战:精准拆分中英文混合字符串(含重音/空格场景)

在数据处理场景中&#xff0c;经常会遇到中英文混合的字符串&#xff0c;比如品牌名&#xff08;Cline思琳、Sergio Rossi塞乔罗西&#xff09;、商品名&#xff08;iPhone苹果手机&#xff09;等。需要将英文部分和中文部分拆分成独立字段&#xff0c;用于后续的品牌分析、分类…

ES6 核心语法精讲

一、变量声明javascript// let 块级作用域变量 let count 0 count 1 // ✅ 可重新赋值 if (true) {let count 2 // ✅ 新的块级变量 }// const 常量&#xff08;引用不可变&#xff09; const PI 3.14159 // PI 3.14 // ❌ 报错const user { name: John } user.name …

数据分析从“无效报表”到“决策利器”的核心技巧

在大数据时代&#xff0c;几乎所有行业都在强调“用数据说话”&#xff0c;但多数人做的数据分析&#xff0c;却陷入了“报表堆如山&#xff0c;决策用不上”的困境——熬夜做的可视化图表&#xff0c;业务部门只扫一眼&#xff1b;算出来的精准指标&#xff0c;无法落地到实际…