一 浏览器输入URL后会发生什么?
1. DNS解析
DNS(Domain Name System)解析是访问网址的第一步。当用户在浏览器中输入一个网址(如www.baidu.com)时,计算机需要将这个域名转换为IP地址才能找到目标服务器。这个转换的过程就是DNS解析。
具体过程:
- 浏览器首先会检查本地缓存(如浏览器缓存、系统缓存)中是否有该域名的IP地址记录。
- 如果没有找到,浏览器会向本地DNS服务器发起查询请求。
- 本地DNS服务器会在其缓存中查找,如果仍未找到,则会向根域名服务器发起查询,逐级向下查找,直到找到目标域名对应的IP地址。
- 本地DNS服务器将查询到的IP地址返回给浏览器。
2.建立TCP连接
TCP(Transmission Control Protocol)是一种可靠的传输协议,通过三次握手建立连接,确保数据的可靠传输。
具体过程:
- 第一次握手:客户端(如浏览器)发送一个SYN(同步序列编号)包到服务器,并进入SYN_SENT状态,等待服务器确认。
- 第二次握手:服务器收到SYN包后,会发送一个SYN+ACK(同步序列编号+确认)包给客户端,同时自己也进入SYN_RCVD状态。
- 第三次握手:客户端收到服务器的SYN+ACK包后,会发送一个ACK(确认)包给服务器,此包发送完毕,客户端和服务器均进入ESTABLISHED(连接已建立)状态,完成三次握手。
3.发起HTTP请求
一旦TCP连接建立,浏览器就会通过HTTP(HyperText Transfer Protocol)协议向服务器发送请求。
请求内容:
- 请求行包含了请求方法(如GET、POST)、URL和HTTP协议版本;
- 请求头部由关键字/值对组成,通知服务器有关客户端请求的信息;
- 空行用于分隔请求头部和请求包体;
- 请求包体(对于GET请求通常为空)包含要提交的数据。
4.服务器处理请求
服务器接收到客户端的HTTP请求后,会根据请求的内容进行相应的处理。这可能包括查询数据库、执行服务器端代码、读取文件等。服务器的处理过程取决于请求的性质和服务器端的配置。
5.服务器响应
服务器处理完请求后,会将结果封装在HTTP响应报文中,并发送回浏览器。HTTP响应报文包含状态行、响应头部、空行和响应包体四个部分。状态行由协议版本、状态码及其描述组成;响应头部用于描述服务器的基本信息和数据的描述;响应包体包含实际的数据,如HTML内容、图片数据等。
6.浏览器渲染
浏览器接收到服务器的响应后,会开始解析HTML文档,并应用CSS样式、执行JavaScript代码等,最终将页面渲染出来呈现给用户。
7.关闭TCP连接
在数据传输完毕后,浏览器和服务器会进行TCP连接的关闭。这个过程通常涉及四次挥手,以确保双方都能正确地释放资源。
具体过程:
- 客户端发送一个FIN(结束)包给服务器,并进入FIN_WAIT_1状态。
- 服务器收到FIN包后,发送一个ACK包给客户端,并进入CLOSE_WAIT状态。
- 服务器也发送一个FIN包给客户端,并进入LAST_ACK状态。
- 客户端收到服务器的FIN包后,发送一个ACK包给服务器,并进入TIME_WAIT状态。等待一段时间后(通常是2MSL,即两倍的最大报文段寿命),客户端进入CLOSED状态,TCP连接关闭完成。
二 描述一下三次握手?丢失时重发为什么倍增重发等待时间?
第一次握手:客户端请求建立连接,向服务端发送一个同步报文(SYN=1),同时选择一个随机数 seq = x 作为初始序列号,并进入SYN_SENT状态,等待服务器确认。
第二次握手:服务端收到连接请求报文后,如果同意建立连接,则向客户端发送同步确认报文(SYN=1,ACK=1),确认号为 ack = x + 1,同时选择一个随机数 seq = y 作为初始序列号,此时服务器进入SYN_RECV状态。
第三次握手:客户端收到服务端的确认后,向服务端发送一个确认报文(ACK=1),确认号为 ack = y + 1,序列号为 seq = x + 1,客户端和服务器进入ESTABLISHED状态,完成三次握手。
在 TCP 数据传输中,若某个数据包丢失,接收方不会收到该数据包的 ACK 确认,发送方会超时并触发重传机制。为了提高网络的稳定性和防止网络拥塞,TCP 使用指数退避算法来控制重传超时时间(RTO)的增长。
倍增重发等待时间可以避免网络拥塞,当网络发生拥塞时,数据包可能丢失。简单的重传机制(例如固定等待时间)可能会导致更多的数据包丢失,加剧网络拥塞。倍增重发等待时间可以减少这种情况发生的频率,因为每次重传的等待时间都会加倍,从而减少对网络的压力。此外,网络不稳定或拥塞的情况下,短时间内频繁重传可能无法成功,因此逐步增加等待时间可以给网络更多时间恢复正常,增加重传成功的几率。
三 tcp传输哪里会受到攻击?如何进行改进?
1.TCP SYN Flood 攻击
攻击者向目标服务器发送大量的 SYN 请求,但不完成三次握手过程。这会导致目标服务器保持大量半开连接,消耗系统资源,最终可能导致服务拒绝(DoS)或影响正常用户连接。
使用 SYN Cookies 技术来避免保存半开连接的状态,只有在完成三次握手后才分配资源;增加服务器的连接队列大小,以便能够处理更多的半开连接;使用防火墙和入侵检测系统(IDS)来检测和过滤异常的 SYN 请求。
2.TCP RST 攻击
攻击者向目标发送伪造的 TCP RST(重置)包,以强制中断现有的 TCP 连接。这可以导致正常的连接被意外中断。
确保 TCP RST 包的源 IP 地址和端口号匹配连接的实际状态;通过使用加密协议(如 TLS)保护传输层的连接,可以防止攻击者直接干扰数据流。
3.TCP Session Hijacking(会话劫持)
攻击者窃取或伪造 TCP 会话中的数据包,以劫持合法的会话。攻击者可以伪装成合法用户,访问受保护的资源。
使用安全的加密协议(如 TLS、SSL)加密 TCP 数据流,防止数据包被窃取或篡改;加强认证机制,确保会话的合法性,例如通过使用多因素认证(MFA)。
4.TCP IP Spoofing(IP 欺骗)
攻击者伪造 IP 地址来发送恶意数据包,以伪装成另一个合法用户或主机。
配置反向路径验证(RPF)来检测和阻止 IP 欺骗流量;实施网络层的安全策略,如源 IP 验证和流量过滤,防止伪造的 IP 地址流量。
5.TCP ACK Flood 攻击
攻击者向目标发送大量的 TCP ACK 包,占用带宽和处理能力,导致服务性能下降或拒绝服务。
使用流量监控工具来检测异常的 ACK 流量,并设置流量限制;使用防火墙和流量分析工具来过滤和阻止恶意的 ACK 包。
四 Redis用在那些场景下?你是怎么设计这个数据结构的?
Redis的使用场景如:用于缓存数据库查询结果、网页内容、用户会话等,减少对后端数据库的访问,提高应用程序的响应速度;储用户会话信息;实时数据分析和处理,例如实时统计、实时推荐等;实现消息队列;游戏排行榜、网页点击量计数器等。
Redis常用的数据结构包括:
1.字符串(String):存储简单的键值对数据,支持存储文本、数字等。
2.列表(List):存储有序的数据集合,支持从两端插入和删除元素。
3.集合(Set):存储唯一的无序元素集合,支持集合运算(并集、交集、差集)。
4.有序集合(Sorted Set):存储带有分数的有序集合,支持按分数排序。
5.哈希(Hash):存储键值对集合,每个键对应一个字段和其值。
五 如何保证MySQL和Redis的缓存一致性?
1. 缓存延时双删策略
首先更新数据库中的数据。删除与该数据相关的缓存,防止读取到旧数据。等待一小段时间后(如 500ms 到 1s),再次删除缓存,确保在这段时间内,若有其他读请求更新了缓存,也能再次清理掉它。
2. 读写操作加分布式锁
在写 MySQL 数据库前,先获取一把分布式锁。锁定成功后,进行数据库写操作。在释放锁前,删除 Redis 中的缓存数据。在读请求到来时,如果发现缓存中没有数据,在从数据库读取数据前也加锁,确保写请求和读请求不会出现冲突。
3. 异步消息队列解决方案
先更新 MySQL 数据库,再将数据库更新的操作发送到消息队列中。消息队列的消费者监听数据库的更新,接收到消息后,更新或删除 Redis 缓存。
4. 先删缓存再更新数据库
先删除 Redis 中缓存的数据,随后更新 MySQL 中的数据库数据。
六 分布式锁加锁和解锁的过程?
(一)加锁
1.基于数据库的分布式锁
请求加锁:节点执行 INSERT
操作,将锁信息写入数据库表。
检查结果:如果 INSERT
操作成功,表示获取锁成功;否则,可能存在锁已被其他节点持有,需要等待或重试。
超时机制:为了防止锁持有者崩溃导致死锁,通常会设置锁的超时时间。
2.基于 Redis 的分布式锁
请求加锁:使用 SET
命令并设置 NX
(仅当键不存在时设置)和 EX
(设置键的过期时间)选项。
检查结果:如果返回 OK
,则锁成功;否则,可能需要重试或采取其他措施。
超时机制:设置合适的超时时间,防止锁长期未释放。
3.基于 ZooKeeper 的分布式锁
请求加锁:节点创建一个持久有序节点(例如 /locks/lock-
)以获取锁。ZooKeeper 会根据节点的顺序来控制锁的持有者。
检查结果:节点需要监视前一个节点的删除事件。如果前一个节点被删除,则表示当前节点可以获得锁。
超时机制:ZooKeeper 的会话机制本身支持节点的自动删除,可以处理持有者崩溃的问题。
(二)解锁
1.基于数据库的分布式锁
请求解锁:节点执行 DELETE
操作,将锁信息从数据库表中删除,释放锁。
检查结果:解锁操作完成后,其他节点可以尝试获取锁。
2.基于 Redis 的分布式锁
请求解锁:使用 DEL
命令删除锁键。为了防止误删,通常会在解锁时先检查锁的持有者。
检查结果:确认锁键已被删除,其他节点可以尝试获取锁。
3.基于 ZooKeeper 的分布式锁
请求解锁:删除持久有序节点,释放锁。ZooKeeper 会自动处理前一个节点的删除事件,从而让其他节点能够尝试获取锁。
检查结果:节点被删除后,ZooKeeper 会通知等待的节点,让它们尝试获取锁。
七 如何避免消息的重复消费?
(一)消息去重机制
1.消息唯一标识:为每条消息分配一个唯一的标识符(如 UUID),消费者在处理消息时可以根据这个标识符来检查是否已经处理过该消息。
实现方法:
- 消息生产者在发送消息时生成唯一标识符。
- 消费者在处理消息之前检查该标识符是否已经存在于持久化存储(如数据库、Redis 等)中。
- 如果存在,则跳过处理;如果不存在,则处理并将标识符记录下来。
2. 消息去重数据库:在消息处理系统中使用数据库或缓存来记录已经处理的消息 ID。通过查询这个数据库或缓存,消费者可以判断消息是否已经被处理过。
实现方法:
- 在消费消息时,检查消息 ID 是否存在于去重数据库中。
- 如果存在,则不再处理;如果不存在,则进行处理并将消息 ID 插入去重数据库。
(二)消息确认机制
1. 确认机制(Acknowledgment):消费者在成功处理消息后向消息队列发送确认(acknowledgment)消息,以通知消息队列该消息已被处理。
实现方法:
- 使用消息确认机制(
basic.ack
)来告知 RabbitMQ 消息已经被正确处理。- 设置消费者的提交偏移量(offset)策略,确保消息被处理后才提交偏移量。
2. 事务机制:使用事务机制确保消息处理的原子性。如果处理失败,则回滚事务,确保消息不会被重复处理。
实现方法:
- 在支持事务的消息队列中使用事务(如 RabbitMQ 的事务模式)。
- 确保事务的提交和回滚操作能够保证消息的正确性和一致性。
(三)幂等性设计
1. 幂等操作:设计消费者的处理逻辑,使得多次处理同一条消息的结果是相同的,即使消息被重复消费也不会对系统产生不同的效果。
实现方法:
- 在数据库操作中使用唯一约束或合并操作来避免重复数据(如
INSERT IGNORE
或ON DUPLICATE KEY UPDATE
)。- 设计业务逻辑时确保重复操作不会产生不同的结果(如订单支付逻辑)。
(四)消息队列配置
1. 消息队列的重复消费控制:一些消息队列提供了内建的机制来处理重复消息问题,例如设置消息的过期时间和重复投递策略。
实现方法:
- 设置
message TTL
(存活时间)和dead-letter exchange
(死信交换机)来处理消息过期和重试。- 配置消费者的
auto-offset-reset
策略,确保正确处理消息的偏移量。
2. 高级消息队列功能:利用消息队列的高级功能(如事务、消息分组)来避免重复消费。
实现方法:
- 使用事务和幂等生产者来保证消息的顺序和避免重复消息的产生。