HTTP/HTTPS

一、计算机网络基础

1.1 网络分层模型

计算机网络分层模型是为了实现不同系统之间的互联互通,简化网络的设计、实现和维护。主要有两个著名的分层模型:OSI七层模型TCP/IP协议栈


1.1.1 OSI七层模型

OSI(Open Systems Interconnection)模型是一个理论参考模型,定义了网络通信的七个层次,每一层都有特定的功能和协议。

层次 功能概述 常见协议/设备
物理层 定义硬件设备接口,传输原始的二进制数据(比特流)。 光纤、网线、交换机
数据链路层 提供可靠的点到点数据传输(帧),负责纠错和数据帧控制。 以太网(Ethernet)、PPP、MAC地址
网络层 负责数据在不同网络之间的传输(分组、路由)。 IP(IPv4/IPv6)、ICMP、ARP
传输层 提供端到端的可靠传输,控制数据流量和传输质量。 TCP、UDP
会话层 管理会话连接(建立、维护、终止)。 PPTP、SIP
表示层 数据的格式化、加密解密和压缩解压。 SSL/TLS、JPEG、MPEG
应用层 面向用户的应用服务,提供具体的网络功能。 HTTP、FTP、SMTP

封装与解封装过程(在OSI模型中的体现)

  • 封装过程 :数据从应用层向下传递,每一层都会添加对应的头部信息(Header)。
    1. 应用层生成应用数据。
    2. 表示层和会话层处理格式、加密及连接管理。
    3. 传输层添加传输头部(如TCP头部)。
    4. 网络层添加IP头部。
    5. 数据链路层添加帧头和帧尾。
    6. 物理层将帧转换为比特流并发送。
  • 解封装过程:比特流到达接收端,物理层接收数据后逐层剥去头部,直至还原出应用数据。

1.1.2 TCP/IP协议栈

TCP/IP是一个更简化、更实际的四层模型,与OSI模型存在一定的对应关系。它被广泛用于互联网中。

层次 功能描述 对应OSI层 常见协议/设备
应用层 提供网络应用服务,直接与用户交互。 应用层、表示层、会话层 HTTP/HTTPS、FTP、SMTP
传输层 负责端到端的通信,保证数据传输的可靠性。 传输层 TCP、UDP
网络层 负责数据的寻址和路由。 网络层 IP、ICMP、ARP
网络接口层 处理数据链路层和物理层的细节,完成数据的实际传输。 数据链路层、物理层 Ethernet、Wi-Fi

数据封装与解封装(TCP/IP模型中的体现)

  • 封装过程

    1. 应用层:生成应用数据并封装为消息。
    2. 传输层:添加传输层头部,形成段(TCP段或UDP段)。
    3. 网络层:添加IP头部,形成数据包。
    4. 网络接口层:添加帧头和帧尾,形成数据帧。
  • 解封装过程: 接收端从物理层开始逐层剥离头部,最终在应用层还原数据。


1.1.3 OSI模型与TCP/IP模型的对比

对比点 OSI模型 TCP/IP模型
层次数量 七层 四层
起源 理论模型 实际应用
复杂度 更详细,适合教学和理解 更简化,适合实践
应用场景 理论研究、标准制定 互联网、实际网络通信

1.2 传输层协议详细讲解

传输层协议负责实现主机之间的端到端通信,保证数据能够从发送方正确到达接收方。传输层有两个核心协议:TCP(传输控制协议)UDP(用户数据报协议)


1.2.1 TCP协议

TCP(Transmission Control Protocol)是一种面向连接、可靠的传输协议,主要特点包括:

  • 面向连接:通信前必须建立连接(三次握手),断开时需释放连接(四次挥手)。
  • 可靠传输:通过确认应答、超时重传等机制保证数据准确无误地传递。
  • 流量控制和拥塞控制:通过调整发送速率防止网络拥堵或接收端处理不过来。

1 三次握手详细分析

三次握手是建立TCP连接的过程,确保双方准备就绪且连接可靠。

三次握手过程:

  • 第一次握手(SYN)

    • 客户端发送一个SYN(Synchronize Sequence Number)包,请求建立连接,并发送初始序列号(Seq=x)。
    • 进入 SYN-SENT 状态。
  • 第二次握手(SYN-ACK)

    • 服务端接收到SYN包后,向客户端发送SYN和ACK(Acknowledge)包,确认收到客户端的SYN,同时发送自己的初始序列号(Seq=y)。
    • 进入 SYN-RECEIVED 状态。
  • 第三次握手(ACK)

    • 客户端收到服务端的SYN-ACK包后,发送一个确认ACK包,告知服务端SYN包已接收。
    • 客户端进入 ESTABLISHED 状态,连接建立。
    • 服务端收到ACK后也进入 ESTABLISHED 状态。

三次握手示意图:

复制代码

为什么需要三次握手?

  • 防止旧连接的数据干扰:避免客户端重传的旧SYN包误导致服务端开启连接。
  • 双方确认状态:确保客户端和服务端都已准备好通信。

2 四次挥手详细分析

四次挥手是释放TCP连接的过程。

四次挥手过程:

  • 第一次挥手(FIN)

    • 客户端发送FIN(Finish)包,表示不再发送数据,请求关闭连接。
    • 客户端进入 FIN-WAIT-1 状态。
  • 第二次挥手(ACK)

    • 服务端收到FIN后,发送ACK包确认,表示同意关闭连接。
    • 服务端进入 CLOSE-WAIT 状态,客户端进入 FIN-WAIT-2 状态。
  • 第三次挥手(FIN)

    • 服务端处理完剩余数据后,向客户端发送FIN包,表示可以关闭连接。
    • 服务端进入 LAST-ACK 状态。
  • 第四次挥手(ACK)

    • 客户端收到FIN后,发送ACK包确认,进入 TIME-WAIT 状态,等待一段时间后关闭连接。
    • 服务端收到ACK后进入 CLOSED 状态。

四次挥手示意图:

复制代码

为什么需要四次挥手?

  • 半关闭机制:双方通信独立,发送和接收可以分开关闭。
  • 确保剩余数据传输完成:避免服务端数据未发完连接就被关闭。

3 数据可靠性

TCP通过以下机制保证数据传输的可靠性:

  • 确认应答(ACK)

    • 接收方收到数据后发送ACK,通知发送方数据已到达。
  • 超时重传

    • 如果发送方在一定时间内未收到ACK,会认为数据丢失并重传。
    • 超时时间根据网络条件动态调整。
  • 滑动窗口

    • 控制发送的数据量,确保接收方有足够的缓冲空间。
    • 发送窗口移动时,可以动态调整传输速率,提高传输效率。

滑动窗口的基本概念

滑动窗口是一种 控制数据发送范围的窗口机制,可以简单理解为一个"允许发送的范围"。发送方可以连续发送窗口范围内的数据,而无需等待每一个数据包的确认。

  • 发送窗口:发送方维护的窗口,用于控制允许发送但尚未被确认的数据范围。
  • 接收窗口:接收方维护的窗口,表示当前接收缓存的大小,限制发送方的最大数据发送量。
  • 窗口大小 :窗口的大小由接收方决定,它会在 TCP 报文的头部通过 Window Size 字段通知发送方。

滑动窗口的工作原理

1. 窗口范围与状态

发送窗口是一个动态滑动的范围,分为三部分:

  1. 已发送并确认的数据:这些数据已经被接收方收到并确认,不再需要保留。
  2. 已发送但未确认的数据:发送方需要等待接收方的 ACK 确认。
  3. 可发送但尚未发送的数据:发送方可以立即发送的数据。

2. 滑动过程

  • 每当接收方确认一个数据包(发送 ACK),发送窗口就会向前滑动,腾出新的空间供发送方发送更多的数据。
  • 如果接收方的接收能力(窗口大小)减小,发送窗口的范围也会相应减小。

4 拥塞控制与流量控制

  • 流量控制

    • 目的是防止接收端处理不过来。
    • 使用滑动窗口机制动态调整发送数据量。
  • 拥塞控制

    • 目的是防止网络拥堵。
    • 主要算法:
      • 慢启动:初始阶段发送数据量较小,逐步增加。
      • 拥塞避免:到达阈值后缓慢增长。
      • 快速重传与快速恢复:检测到丢包后,快速降低发送速率并恢复。

1.2.2 UDP协议

UDP(User Datagram Protocol)是一种无连接、不可靠的传输协议,适用于需要快速传输的场景。

UDP的特点

  • 无连接:无需建立连接,数据直接发送到目标。
  • 不可靠:不保证数据一定能到达,不提供重传机制。
  • 面向报文:每个UDP数据报都是独立的,大小在64KB以内。
  • 快速:由于无连接和不保证可靠性,传输效率高。

UDP使用场景

  • DNS查询:快速查询域名的IP地址,对可靠性要求低。
  • 实时视频流:如在线视频、直播,对时延要求高,允许少量数据丢失。
  • 在线游戏:实时性优先,可靠性次要。

1.2.3 TCP与UDP对比

特性 TCP UDP
是否面向连接 面向连接(需三次握手、四次挥手) 无连接
传输可靠性 可靠(确认应答、超时重传、滑动窗口) 不可靠(无确认应答,可能丢包、乱序)
传输效率 较低(有连接管理和数据校验的开销) 高(无额外开销)
流量控制与拥塞控制 有(滑动窗口、拥塞控制)
适用场景 文件传输、电子邮件、网页浏览 实时视频、语音通话、DNS查询

1.3 网络层协议

1.3.1 IP协议

IP协议是网络层的核心,用来给每台设备分配一个"地址",并通过这些地址实现设备之间的通信。


IPv4

IPv4 是我们最熟悉的 IP 地址版本。你可以把 IPv4 想象成"家庭住址"。

IPv4的格式

IPv4 地址是一个 32 位的数字,用"点"分隔成 4 段,每段可以是 0 到 255 之间的数字。

  • 例子192.168.1.1
  • 每台设备都需要有一个这样的地址,才能在网络上通信。

地址的两部分

IPv4 地址分为两部分:

  • 网络部分:就像一个城市名称,表示设备在哪个网络。
  • 主机部分:就像一条街道的门牌号,表示网络里的具体设备。
  • 例子192.168.1.1,如果网络部分是 192.168.1,主机部分就是最后的 1

IPv4的分类

IPv4 地址可以分为几类,就像城市规模大小不一样:

  • A类地址:超大的网络,适合几百万台设备。

    • 地址范围:1.0.0.0126.255.255.255
    • 例子10.0.0.1
  • B类地址:中型网络,适合几万台设备。

    • 地址范围:128.0.0.0191.255.255.255
    • 例子172.16.0.1
  • C类地址:小型网络,最多支持 256 台设备。

    • 地址范围:192.0.0.0223.255.255.255
    • 例子192.168.1.1
  • D类地址:专用于组播,表示一群设备一起接收消息。


IPv4的局限

IPv4 地址总共有 43 亿个,但今天全球有数十亿设备连上互联网,地址早就不够用了。为了解决这个问题,我们需要用到 IPv6。


IPv6

IPv6 是一种新版本的 IP 地址,使用 128 位来表示,比 IPv4 的 32 位多得多。你可以把它看成是"全球地址升级版",确保每个设备都有独立的地址。

IPv6的格式

IPv6 的地址非常长,分为 8 组,用冒号 : 分隔。每组是 16 位,表示为 4 个十六进制数。

  • 例子2001:0db8:85a3:0000:0000:8a2e:0370:7334

为了方便,IPv6 允许省略某些部分:

  • 前面的零可以省略。
    例子2001:db8:85a3:0:0:8a2e:370:7334
  • 连续的全零可以用 :: 代替一次。
    例子2001:db8:85a3::8a2e:370:7334

IPv6的优势

  1. 地址空间大到几乎用不完。每粒沙子都能分到几十个地址。
  2. 内置更强的安全性,支持数据加密和认证。
  3. 更高效:IPv6 消除了 IPv4 的广播机制,更加适合现代网络。

1.3.2 子网划分与CIDR

网络有大有小,有时候需要把一个大的网络划分成多个小网络,或者把几个小网络合并起来,这就需要用到 子网划分CIDR


  • IP地址本身不能单独区分网络部分和主机部分

    例如:192.168.1.1 是一个 IP 地址,但光看这个地址,你无法知道它的网络范围是 192.168.1.0 还是 192.168.0.0

  • 子网掩码的作用是帮助划分网络部分和主机部分

    • 它像一个标尺,告诉网络设备:
      • 哪些位属于"网络部分"(网络地址)。
      • 哪些位属于"主机部分"(主机地址)。

举例:如果一个 IP 地址是 192.168.1.1,你需要知道它属于哪个子网:

  • 如果子网掩码是 255.255.255.0,网络范围是 192.168.1.0/24(前 24 位是网络部分)。
  • 如果子网掩码是 255.255.0.0,网络范围是 192.168.0.0/16(前 16 位是网络部分)。

CIDR

CIDR(无分类域间路由)是用一种更灵活的方式来管理网络地址。

CIDR格式

CIDR 用一个斜杠 / 后面跟数字表示网络的大小。

  • 例子192.168.1.0/24
    • /24 表示前 24 位是网络部分,剩下的 8 位是主机部分。

CIDR的好处

  • 更灵活:不再受传统 A/B/C 类地址限制,可以根据需要分配地址。
  • 节省资源:避免浪费地址。

1.3.3 路由协议基础

路由的作用是帮忙"导航"。就像你用地图软件,路由器的工作就是决定"数据包"应该走哪条路。


静态路由

静态路由就是手动指定一条固定的路径,就像给快递员一个纸质地图。

优缺点

  • 优点:简单可靠,适合小型网络。
  • 缺点:如果路被堵(网络变化),快递员不知道该怎么办。

例子

假设你有两条网络:

  • 192.168.1.0/24
  • 10.0.0.0/8

你可以告诉路由器:"所有去 10.0.0.0 的数据,都走 192.168.1.1 这条路"。


动态路由

动态路由更像是导航软件,能根据实时路况自动调整路线。它通过路由协议完成。

常见动态路由协议

  • RIP(路由信息协议)
    • 像快递员数路口,走最少路口的路(跳数最少)。适合小网络。
  • OSPF(开放最短路径优先)
    • 就像导航软件计算最短时间的路线。适合大型网络。
  • BGP(边界网关协议)
    • 专门为互联网设计,负责管理不同网络之间的路由。

1.3.4 ARP协议

ARP(地址解析协议)是一个帮忙"查找门牌号"的工具。


ARP的作用

在局域网里,数据需要通过 MAC 地址(设备的物理地址)发送,但我们通常只知道对方的 IP 地址。ARP 的作用是把 IP 地址"翻译"成 MAC 地址。


ARP的工作过程

假设你要寄信到 192.168.1.2:

  1. 你问:"谁是 192.168.1.2?告诉我你的门牌号(MAC 地址)。"
    • 这个请求会广播给局域网的所有设备。
  2. 192.168.1.2 回答:"我是 192.168.1.2,我的 MAC 地址是 AA:BB:CC:DD:EE:FF。"
  3. 你记住这个地址,下一次寄信直接用,不用再问了。

1.4 应用层协议

应用层协议负责为用户提供直接的网络服务,如网页浏览、电子邮件、文件传输等。以下详细讲解 DNS协议HTTP/HTTPS电子邮件协议(SMTP、POP3、IMAP)


1.4.1 DNS协议

DNS(Domain Name System)是"域名解析系统",其作用是将 域名(如 www.google.com 转换为 IP地址(如 142.250.185.206),方便人类记忆。

域名与IP地址

  • 域名 :供人类使用的地址,如 www.google.com
  • IP地址 :供计算机通信使用的地址,如 142.250.185.206
  • 问题:因为人类记忆域名更方便,但计算机只能识别 IP 地址,因此需要一种"翻译服务"来将域名转换为 IP 地址,这就是 DNS 的作用。

DNS的查询过程

  • 客户端向本地 DNS 服务器发起请求

    • 用户的设备(如浏览器)向运营商提供的本地 DNS 服务器发送查询请求,要求解析 www.google.com。本地 DNS 服务器称为 递归解析器,负责处理查询。
  • 本地 DNS 服务器检查缓存

    • 本地 DNS 服务器会先检查自己的缓存中是否已有 www.google.com 对应的 IP 地址:
      • 如果有,直接返回结果给客户端。
      • 如果没有,向上级 DNS 服务器发起逐级查询。
  • 逐级查询域名解析

    本地 DNS 服务器开始查询:

    • 查询根服务器
      本地 DNS 服务器向根 DNS 服务器查询 www.google.com。根服务器返回 .com 顶级域名服务器(TLD 服务器)的地址。
    • 查询顶级域名服务器(TLD)
      本地 DNS 服务器向 .com TLD 服务器查询,TLD 服务器返回 google.com 的权威 DNS 服务器地址。
    • 查询权威 DNS 服务器
      本地 DNS 服务器向 google.com 的权威 DNS 服务器查询,获取 www.google.com 的最终 IP 地址。
  • 本地 DNS 服务器返回结果给客户端

    • 本地 DNS 服务器将获取到的 IP 地址缓存起来(用于处理未来的相同查询),然后将结果返回给客户端。

1.4.1.3 常见 DNS 记录类型

  • A记录

    • 将域名解析为 IPv4 地址。
    • 例子www.example.com -> 192.168.1.1
  • AAAA记录

    • 将域名解析为 IPv6 地址。
    • 例子www.example.com -> 2400:cb00:2048:1::c629:d7a2
  • CNAME记录

    • 将一个域名指向另一个域名(别名记录)。
    • 例子cdn.example.com -> www.example.com
  • MX记录

    • 指定域名的邮件服务器地址。
    • 例子example.com -> mail.example.com

1.4.2 HTTP与HTTPS协议

1.4.2.1 HTTP协议

HTTP(HyperText Transfer Protocol)是应用层协议,用于传输网页内容。

  • 特点

    • 无状态:每次请求独立,服务器不记住用户。
    • 明文传输:数据不加密,容易被窃取。
    • 使用 TCP 传输,默认端口 80。
  • 工作过程

    1. 客户端(如浏览器)向服务器发送 HTTP 请求(如 GET、POST 请求)。
    2. 服务器返回 HTTP 响应(如网页内容)。
  • 应用场景:访问网页、接口通信等。


1.4.2.2 HTTPS协议

HTTPS 是 HTTP 的加密版,使用 SSL/TLS 协议对数据进行加密,增强安全性。

  • 区别

    • 数据加密:HTTPS 使用 SSL/TLS 对数据加密,防止数据被窃听。
    • 身份认证:HTTPS 通过数字证书验证服务器身份,防止伪装。
    • 端口不同:HTTPS 默认使用 443 端口。
  • 工作过程

    1. 客户端向服务器发起 HTTPS 请求。
    2. 服务器返回数字证书,客户端验证证书。
    3. 建立加密连接,数据加密传输。
  • 应用场景:银行网站、电商平台等需要保护用户隐私的场景。


1.4.3 电子邮件协议

电子邮件系统有三种常用协议:SMTP、POP3 和 IMAP,它们负责 发送接收 邮件。


1.4.3.1 SMTP协议

SMTP(Simple Mail Transfer Protocol)是用来 发送邮件 的协议。

  • 特点

    • 基于 TCP 协议,默认端口 25 或 587(加密)。
    • 只能发送邮件,不能接收邮件。
  • 工作过程

    1. 客户端将邮件内容交给 SMTP 服务器。
    2. SMTP 服务器查找收件人的邮件服务器(使用 MX 记录)。
    3. SMTP 服务器将邮件传递到收件人的邮件服务器。

1.4.3.2 POP3协议

POP3(Post Office Protocol 3)是用来 接收邮件 的协议。

  • 特点

    • 邮件会从服务器下载到本地,并在服务器中删除。
    • 基于 TCP 协议,默认端口 110 或 995(加密)。
  • 优点

    • 用户可以离线访问邮件。
    • 适合只需在一个设备上管理邮件的场景。
  • 缺点

    • 多设备之间无法同步邮件状态。

1.4.3.3 IMAP协议

IMAP(Internet Message Access Protocol)是另一种 接收邮件 的协议。

  • 特点

    • 邮件保存在服务器上,客户端访问邮件的副本。
    • 基于 TCP 协议,默认端口 143 或 993(加密)。
  • 优点

    • 支持多设备同步邮件状态(如已读、未读)。
    • 邮件不会被删除,适合多人协作管理。
  • 缺点

    • 需要网络连接才能查看邮件内容。

总结

协议 作用 特点 常用端口
DNS 域名解析 将域名转换为 IP 地址 53
HTTP 网页传输 无状态、明文传输 80
HTTPS 安全网页传输 加密通信、身份认证 443
SMTP 发送邮件 用于传递邮件到邮件服务器 25/587
POP3 接收邮件(下载) 邮件从服务器下载到本地,不支持多设备同步 110/995
IMAP 接收邮件(同步) 邮件保存在服务器,支持多设备同步 143/993

1.5 网络相关技术

1.5.1 NAT与端口映射

什么是 NAT?

NAT 的全称是 网络地址转换。它就像一个"地址翻译器",让家里或公司里的多台设备通过一个公共地址访问互联网。

问题背景:为什么需要 NAT?

  • IP 地址不够用:互联网用的是 IPv4 地址,但可用的 IPv4 地址非常有限,而全球有几十亿设备需要联网。
  • 私有地址和公共地址的区别
    • 私有地址 :家里或公司内部的设备,比如 192.168.1.1,只能在局域网内使用。
    • 公共地址 :可以在互联网中直接使用,比如 203.0.113.1

NAT 的作用是将内网(私有地址)转换成外网(公共地址),这样内网中的多台设备可以共享一个公共地址访问互联网。


NAT 是如何工作的?

假设你家里有三台设备(电脑、手机、平板),IP 地址分别是:

  • 电脑:192.168.1.2
  • 手机:192.168.1.3
  • 平板:192.168.1.4

你的路由器有一个公网 IP 地址,比如 203.0.113.1。当这些设备通过路由器访问互联网时,路由器会进行 NAT 翻译:

  • 出站流量(内网到外网)

    • 当你用手机(192.168.1.3)访问 www.google.com,路由器会把手机的私有 IP 地址换成 203.0.113.1
    • 同时,路由器会记录下 手机的请求来源和端口号,以便稍后把返回的数据送回正确的设备。
  • 入站流量(外网到内网)

    • 当 Google 的服务器返回数据时,数据先到达路由器的 203.0.113.1
    • 路由器通过记录找到最初发出请求的手机(192.168.1.3),并把数据送到它。

什么是端口映射?

端口映射是 NAT 的一种扩展功能,让外部设备可以主动访问内网设备。

例子:你想在外网访问家里的监控摄像头

  • 你的摄像头 IP 是 192.168.1.100,端口号是 8080
  • 你的路由器有公网 IP 203.0.113.1
  • 设置端口映射规则:
    • 当外网设备访问 203.0.113.1:8080 时,路由器会把请求转发给 192.168.1.100:8080

这样,你就能从外网访问家里的摄像头了。


1.5.2 防火墙与网络安全

什么是防火墙?

防火墙是一种保护网络的工具,可以阻止不安全的流量进入你的网络。

防火墙的作用

  1. 屏蔽恶意攻击:比如黑客想入侵你的电脑,防火墙会阻止它。
  2. 过滤流量:只允许合法的数据通过,不安全的数据被丢弃。
  3. 保护隐私:防止内部网络信息泄露。

防火墙的种类

  • 硬件防火墙:像一个设备,用于保护整个网络,比如路由器上的防火墙功能。
  • 软件防火墙:安装在电脑或手机上,比如 Windows 自带的防火墙。

防火墙是如何工作的?

防火墙就像"门卫",每个数据包(互联网中的信息)都必须经过它的检查:

  1. 如果数据符合规则,就允许通过。
  2. 如果数据不符合规则,就拒绝通过。

例子:拦截恶意流量

  • 规则:只允许端口 80(网页流量)通过,拒绝其他端口。
  • 如果有人试图通过端口 22(SSH)连接到你的网络,防火墙会直接拒绝。

1.5.3 CDN 与内容分发

什么是 CDN?

CDN(内容分发网络)是一种技术,用来加速网站内容的访问速度。

问题背景:为什么需要 CDN?

  • 网站服务器可能离用户很远,比如服务器在美国,而用户在中国,这会导致访问速度慢。
  • 如果很多人同时访问一个网站,服务器可能会过载,导致无法响应。

CDN 是如何工作的?

  1. 分布式节点:CDN 在全球部署了许多服务器节点,就像"缓存站点"。
  2. 就近访问:当用户访问网站时,CDN 会引导用户连接到离自己最近的节点,而不是连接到原始服务器。

例子:访问一个大流量网站

  • 你访问 www.example.com,实际连接到的是离你最近的 CDN 节点。
  • CDN 节点上缓存了网站的内容(如图片、视频),你可以快速加载这些内容,而不用等待源服务器的响应。

1.5.4 负载均衡

什么是负载均衡?

负载均衡是一种技术,用来把用户的请求分配到多台服务器上,防止某一台服务器过载。

问题背景:为什么需要负载均衡?

  • 如果只有一台服务器,可能无法处理大量用户请求,导致崩溃。
  • 多台服务器可以提高系统的可靠性和性能,但需要一种方法来均匀分配流量。

负载均衡是如何工作的?

假设有 3 台服务器(A、B、C),你运行了一个负载均衡器,它会接收所有用户请求,然后根据某种规则分配请求:

常见的分配策略

  • 轮询(Round Robin)

    • 请求依次分配到 A、B、C,然后循环。
    • 优点:简单,适合服务器性能差不多的场景。
  • 最小连接数(Least Connections)

    • 请求分配给当前负载最轻(连接数最少)的服务器。
    • 优点:适合服务器性能不均的场景。
  • IP 哈希(IP Hash)

    • 根据用户的 IP 地址计算哈希值,固定分配到某台服务器。
    • 优点:用户每次请求都分配到同一台服务器,适合需要"会话保持"的场景(如购物车功能)。

二、HTTP协议基础

2.1 HTTP协议原理详细讲解

HTTP(HyperText Transfer Protocol)是应用层的核心协议,负责客户端(如浏览器)和服务器之间的数据交换。以下从 HTTP 的 无状态特性报文结构常见方法状态码分类 四个方面详细讲解。


2.1.1 HTTP协议的无状态特性及状态保持

1. 什么是 HTTP 的无状态特性?

无状态:每次 HTTP 请求是独立的,服务器不会记住之前发生的请求。

例子:当你访问某个网站的页面 A,再访问页面 B,服务器并不知道你之前访问过页面 A。

优点:简化了服务器的设计,方便扩展。

缺点:无法直接跟踪用户状态(如登录状态、购物车)。


2. 如何通过 Session 和 Cookie 实现状态保持?

为了弥补 HTTP 无状态的缺陷,使用 Session(会话)Cookie(小数据存储) 来保存用户的状态。

1)Cookie

  • Cookie 是存储在客户端的小数据,由服务器生成并发送给客户端
  • 浏览器在后续的请求中会自动携带 Cookie 发送给服务器,用来标识用户。
  • 例子
    • 服务器发送一个 Cookie:Set-Cookie: session_id=12345; Expires=Wed, 21 Oct 2024 07:28:00 GMT
    • 浏览器后续访问时会携带:Cookie: session_id=12345

2)Session

  • Session 是存储在服务器端的数据结构,用来记录用户状态
  • 浏览器通过 Cookie 保存一个唯一的 Session ID,服务器根据这个 ID 找到用户的会话数据。
  • 例子
    • 用户登录后,服务器为其创建一个 Session,分配 ID 12345
    • 用户的购物车信息保存在服务器的 Session 中。

2.1.2 HTTP报文结构

HTTP 报文是客户端和服务器之间通信的数据格式,分为 请求报文响应报文


1. 请求报文结构

请求报文是客户端发送给服务器的内容,结构如下:

1)起始行

描述请求的基本信息,包括 方法URL协议版本

  • 格式<方法> <请求URL> <协议版本>
  • 例子GET /index.html HTTP/1.1

2)请求头

提供请求的附加信息(元数据)。

常见字段:

  • Host :指定目标主机,如 Host: www.example.com
  • User-Agent:客户端信息,如浏览器版本
  • Accept:客户端可接受的内容类型
  • Content-Type (POST 请求常用):请求体的数据格式,如 application/json

3)请求体

包含实际的数据内容(通常在 POST 或 PUT 请求中)。

例子(POST 提交表单数据):

Content-Type: application/x-www-form-urlencoded

username=alice&password=1234

2. 响应报文结构

响应报文是服务器返回给客户端的内容,结构如下:

1)状态行

描述响应的状态,包括 协议版本状态码原因短语

  • 格式<协议版本> <状态码> <原因短语>
  • 例子HTTP/1.1 200 OK

2)响应头

提供响应的附加信息。

  • 常见字段:
    • Content-Type :返回内容类型,如 text/html
    • Content-Length:内容长度
    • Set-Cookie:服务器设置的 Cookie

3)响应体

返回的实际数据内容(如网页 HTML、JSON 数据)。

例子(返回一个简单网页):

html 复制代码
<html>
  <body>
    <h1>Welcome!</h1>
  </body>
</html>
符号 含义 用途 ASCII 值
SP 空格(Space) 用于分隔起始行或状态行中的字段 32
CR 回车(Carriage Return) 标志行的结束,与 LF 配合使用 13
LF 换行(Line Feed) 与 CR 一起完成行的终止操作 10

2.1.3 常见 HTTP 方法

HTTP 定义了多种方法,用于描述客户端的操作。

方法 功能 说明
GET 请求数据 用于获取资源,不修改服务器数据。
POST 提交数据 用于发送数据给服务器(如表单数据)。
PUT 更新数据 替换服务器上的资源(幂等操作)。
DELETE 删除数据 删除服务器上的资源。
HEAD 请求头信息 类似 GET,但只返回响应头,不返回响应体。
OPTIONS 查询支持的方法 获取服务器支持的 HTTP 方法,用于跨域请求预检等场景。

2.1.4 HTTP状态码分类及应用场景

状态码是服务器在响应报文中返回的数字,用来描述请求的处理结果。


2xx 成功

  • 200 OK :请求成功,服务器返回请求的资源。
    • 例子:用户成功打开网页。
  • 201 Created :资源成功创建(常用于 POST)。
    • 例子:用户成功提交表单,创建了一条记录。

3xx 重定向

  • 301 Moved Permanently :资源已永久移动到新地址。
    • 例子:网站域名变更,将旧域名重定向到新域名。
  • 302 Found :资源临时移动到新地址。
    • 例子:用户访问一个链接,服务器临时引导到另一个页面。

4xx 客户端错误

  • 400 Bad Request :请求无效,可能是语法错误或参数不正确。
    • 例子:用户提交了格式错误的表单数据。
  • 404 Not Found :请求的资源不存在。
    • 例子:用户访问了一个不存在的页面。

5xx 服务器错误

  • 500 Internal Server Error :服务器内部错误,无法完成请求。
    • 例子:服务器代码崩溃导致请求失败。
  • 503 Service Unavailable :服务器暂时过载或维护。
    • 例子:高并发时服务器无法响应请求。

2.2 HTTP性能优化

2.2.1 HTTP长连接与短连接

HTTP通信中,客户端(比如浏览器)和服务器需要通过网络连接来传输数据。这种连接可以分为 短连接长连接


1. 短连接(Connection: close)

短连接是什么?

  • 短连接的特点是:每次请求完成后,连接就关闭了

  • 每当客户端想请求新的数据,就需要重新建立连接,这包括一个耗时的 TCP三次握手 过程。

短连接的流程

  1. 浏览器向服务器发起一个请求,比如加载网页的 HTML 文件。

  2. 服务器处理请求并返回 HTML 文件后,连接关闭。

  3. 如果浏览器还要加载网页上的图片、CSS、JS 文件,则需要重新建立连接。

缺点

  • 每次请求都需要建立和关闭连接,增加了额外的时间消耗。

  • 对服务器压力较大,因为频繁的连接和断开需要耗费资源。

例子

浏览器加载一个网页,网页包含 HTML、5 张图片、CSS 文件。短连接模式下,浏览器需要 建立 7 次连接(HTML + 5 图片 + CSS),每次连接都需要三次握手,浪费时间。


2. 长连接(Connection: keep-alive)

长连接是什么?

  • 长连接的特点是:一个连接可以重复使用,直到客户端或服务器主动关闭。

  • 在一个连接上,可以传输多个请求和响应。

长连接的流程

  1. 浏览器建立一个连接,请求网页的 HTML 文件。

  2. 同一个连接上,继续请求网页中的图片、CSS 文件等资源。

  3. 浏览器加载完成后,连接保持一段时间,等待是否还有新的请求。

优点

  • 避免了频繁建立和关闭连接的开销。

  • 提升了网页加载速度,特别是多资源网页(比如图片多的页面)。

例子

浏览器加载同样的网页时,长连接模式下只需 建立一次连接,所有资源(HTML、图片、CSS)都在这个连接上完成传输。


如何实现?

通过 HTTP 报文中的 Connection 字段实现:

  • 短连接:Connection: close

  • 长连接:Connection: keep-alive

长连接是现代 HTTP 的默认模式(从 HTTP/1.1 开始),因此多数情况下不需要手动设置。


2.2.2 HTTP缓存机制

HTTP缓存是性能优化的关键,它能让浏览器直接从本地缓存中加载资源,而不需要每次都向服务器请求。


1. 强缓存

强缓存是什么?

  • 浏览器在缓存有效期内,直接从本地加载资源,而无需发送请求到服务器。

  • 特点:浏览器直接加载资源,不与服务器通信。

实现方式

Expires(HTTP/1.0):

  • 指定资源的到期时间,格式是一个绝对时间。

例子

java 复制代码
Expires: Wed, 22 Nov 2024 08:00:00 GMT
  • 问题:如果客户端的本地时间不准确,可能导致缓存失效或过期。

Cache-Control(HTTP/1.1):

  • 使用相对时间,表示资源的有效期,弥补了 Expires 的不足。

  • 常见指令

    • max-age=3600:资源缓存 3600 秒(1 小时)。

    • no-cache:每次使用前需要与服务器验证(即协商缓存)。

    • no-store:不缓存资源。

强缓存的优点

  • 浏览器不需要发送请求,直接从本地加载资源,速度非常快。

  • 减少了服务器的负担。


2. 协商缓存

协商缓存是什么?

  • 当强缓存失效时,浏览器会向服务器验证缓存是否有效。

  • 如果缓存有效,服务器返回 304 Not Modified 状态码,客户端继续使用本地缓存。

  • 如果缓存无效,服务器返回新的资源。

实现方式

  • Last-ModifiedIf-Modified-Since

    • Last-Modified:服务器返回资源的最后修改时间。

    • If-Modified-Since :浏览器在请求中携带 Last-Modified 时间,询问服务器资源是否更新。

    • 问题:如果资源内容没变,但文件时间戳更新了,会导致缓存失效。

  • ETagIf-None-Match

    • ETag:服务器返回资源的唯一标识(如哈希值)。

    • If-None-Match:浏览器携带 ETag 值请求服务器验证资源是否更新。

    • 优点:更精确,内容不变时缓存不会失效。


强缓存和协商缓存的关系

  • 浏览器先检查 强缓存 是否有效。

  • 如果强缓存失效,再发起 协商缓存 请求。


2.2.3 使用 Gzip 压缩减少传输数据量


1. 什么是 Gzip 压缩?

  • Gzip 是一种压缩算法,用来减少 HTTP 响应数据的大小。

  • 它将原始数据压缩后发送给客户端,客户端解压后再使用。


2. 如何实现 Gzip 压缩?

  1. 客户端声明支持压缩 : 浏览器通过请求头 Accept-Encoding 告诉服务器支持哪些压缩算法:

    java 复制代码
    Accept-Encoding: gzip, deflate
  2. 服务器压缩内容并返回: 如果服务器支持 Gzip 压缩,会在响应头中添加:

    java 复制代码
    Content-Encoding: gzip
  3. 客户端解压: 浏览器解压收到的内容,显示给用户。


3. Gzip 的优势

  1. 减少传输数据量:特别是对 HTML、CSS、JavaScript 等文本文件效果显著。

  2. 加快加载速度:数据更小,传输时间更短。


2.2.4 CDN 优化传输路径


1. 什么是 CDN?

CDN(内容分发网络)通过分布式的缓存服务器,将内容传输到离用户最近的节点,从而加快加载速度。


2. CDN 的工作原理

  1. 用户请求网站内容时,DNS 会将请求指向离用户最近的 CDN 节点。

  2. CDN 节点返回缓存的内容(如图片、视频、网页)。

  3. 如果 CDN 节点没有内容,则从源服务器获取,并缓存下来供后续请求使用。


3. CDN 的优势

  • 加速访问

    • 用户连接到最近的 CDN 节点,减少网络延迟。
  • 分担服务器压力

    • CDN 节点承担了大部分流量,源服务器的负载大大降低。
  • 提高可靠性

    • 即使某些节点故障,其他节点仍可提供服务。

三、HTTPS协议与安全

3.1 HTTPS基本原理详细讲解

HTTPS(HyperText Transfer Protocol Secure)是基于 HTTP 的安全通信协议,使用加密技术保障数据传输的 保密性完整性真实性。它的核心是加密与认证


3.1.1 非对称加密与对称加密的区别

在 HTTPS 中,加密技术分为两种:对称加密非对称加密。两者各有优缺点,通常配合使用。


1. 对称加密

什么是对称加密?

  • 加密和解密使用同一个密钥
  • 特点
    • 加密和解密速度快,适合大规模数据传输。
    • 但密钥必须在通信双方之间安全传输,一旦密钥泄露,数据就不再安全。

例子

  • 假设你用密码 123456 加密消息 Hello,对方也需要用同样的密码解密:

    复制代码

    Copy code

    加密:Hello + 密钥123456 = 加密数据 解密:加密数据 + 密钥123456 = Hello

优缺点

优点 缺点
加密速度快 密钥的传输与管理不安全
算法简单,资源占用少 无法实现多方安全通信

2. 非对称加密

什么是非对称加密?

  • 加密和解密使用不同的密钥:公钥(Public Key)和私钥(Private Key)
    • 公钥公开,用于加密。
    • 私钥保密,用于解密。

工作原理

  • 数据加密:发送方用对方的 公钥 加密数据。
  • 数据解密:接收方用自己的 私钥 解密数据。

例子

  • 假设服务器生成了一对密钥:

    • 公钥(公开):用于加密。

    • 私钥(秘密):用于解密。

  • 浏览器向服务器发送敏感数据时,使用服务器的公钥加密,只有服务器能用私钥解密。

优缺点

优点 缺点
无需共享密钥,安全性更高 加密解密速度较慢
适合多方通信 算法复杂,占用更多资源

3. HTTPS 中如何结合使用?
  • 非对称加密 用于安全地传输 对称加密的密钥
  • 对称加密 用于高效传输实际的数据。

3.1.2 数字证书与 CA 机构的作用

1. 数字证书是什么?

数字证书是由权威机构签发的一种电子文档,用于证明网站的真实性,防止中间人攻击。

数字证书的内容

  • 网站的域名。
  • 网站的公钥。
  • 证书的有效期。
  • 证书颁发机构(CA)的信息。
  • 颁发机构的数字签名。

2. 为什么需要 CA 机构?

CA(Certificate Authority,证书颁发机构)是数字证书的签发方,用来证明证书的合法性。

  • 问题:如果一个网站告诉你它的公钥,如何确认这个公钥是可信的?
  • 解决:CA 机构通过数字签名证明该公钥确实属于这个网站。

CA 的作用

  • 颁发数字证书。
  • 验证证书的合法性。
  • 确保网站的身份真实可靠。

工作流程

  1. 网站向 CA 提交申请,证明其身份(如域名所有权)。
  2. CA 验证身份后,签发数字证书。
  3. 浏览器在访问网站时,验证证书是否由受信任的 CA 签发。

3.1.3 HTTPS 握手过程

HTTPS 的核心是 SSL/TLS 协议。在浏览器和服务器建立安全连接前,会进行一次 TLS/SSL 握手 。握手过程中完成 加密算法协商身份认证对称密钥的生成


1. HTTPS 握手的主要步骤

假设用户的浏览器(客户端)访问某个 HTTPS 网站(服务器),握手过程如下:

1. 客户端向服务器发送加密能力信息

浏览器发送一个请求,包含:

  • 支持的 加密算法(如 AES、RSA)。

  • 一个随机数(Client Random),用于生成对称密钥。

2. 服务器响应,并发送数字证书

服务器从中选择一个加密算法。

服务器返回:

  • 数字证书,包含网站的公钥和域名信息。

  • 一个随机数(Server Random),用于生成对称密钥。

3. 客户端验证数字证书的合法性

浏览器检查证书是否由受信任的 CA 签发:

  • 验证 CA 的签名。

  • 确认证书的域名和有效期。

如果验证失败,浏览器会警告用户网站不安全。

4. 客户端生成对称密钥并加密发送

  • 浏览器生成一个随机数(对称密钥种子)。

  • 使用服务器的公钥加密该种子,并发送给服务器。

5. 服务器用私钥解密对称密钥种子

  • 服务器用私钥解密,得到对称密钥种子。

  • 通过客户端和服务器双方的随机数以及种子,生成最终的对称密钥。

6. 使用对称加密开始传输数据

  • 握手完成后,浏览器和服务器使用协商好的 对称密钥 加密后续的通信内容(如网页内容、用户数据)。

2. HTTPS 握手过程的安全性
  • 公钥加密确保密钥传输安全:即使中间人截获数据,也无法解密对称密钥种子。
  • 对称加密提高传输效率:数据传输中只使用对称加密,速度更快。
  • 证书认证防止中间人攻击:CA 签名确保公钥的可信性。

3.2 HTTPS常见问题详细讲解

HTTPS 在提供安全通信的同时,也面临一些常见问题,比如 中间人攻击性能优化混合内容问题。以下从这三方面详细展开讲解,并提供解决思路。


3.2.1 中间人攻击与证书校验

1. 什么是中间人攻击?

中间人攻击(Man-In-The-Middle Attack,MITM)是指攻击者插入客户端和服务器之间,拦截和篡改双方的通信内容。

中间人攻击的工作原理

  • 攻击者伪装成服务器:

    • 用户访问 HTTPS 网站时,中间人提供伪造的证书,使客户端误以为自己连接的是目标服务器。
  • 攻击者截获通信内容:

    • 攻击者通过伪造证书解密用户数据后,再将篡改或重新加密的数据转发给真正的服务器。

中间人攻击的危害

  • 窃取敏感信息(如密码、信用卡号)。

  • 篡改通信内容,导致数据不可信。


2. 如何防止中间人攻击?

HTTPS 防止中间人攻击的关键在于 证书校验密钥加密

2.1 证书校验

浏览器通过以下方式验证数字证书:

  • 证书链验证:检查证书是否由受信任的 CA 签发。

  • 域名匹配:确保证书中的域名与访问的域名一致。

  • 有效期检查:验证证书是否过期。

如果证书不合法,浏览器会警告用户,显示"不安全的连接"。

2.2 密钥加密

  • 在握手过程中,客户端使用服务器的公钥加密敏感信息,只有持有私钥的服务器才能解密。

  • 即使中间人截获加密数据,也无法解密。


3. 中间人攻击的防御措施

  • 强制使用 HTTPS:通过 HTTP Strict Transport Security(HSTS)防止用户访问 HTTP 版本。

  • 正确配置证书

    • 选择权威 CA 签发的证书。

    • 定期更新证书,确保在有效期内。

  • 启用证书透明度(Certificate Transparency)

    • 防止恶意 CA 签发伪造证书。
  • DNSSEC(域名系统安全扩展)

    • 防止 DNS 劫持引导用户到错误的服务器。

3.2.2 HTTPS性能优化:减少握手延迟

1. HTTPS 握手的延迟来源

HTTPS 握手相比 HTTP 增加了额外的通信步骤,主要延迟包括:

  • TCP 握手:浏览器和服务器建立连接,耗时 1 RTT(Round-Trip Time,单程往返时间)。

  • TLS/SSL 握手:完成加密算法协商、证书校验和密钥交换,耗时 1~2 RTT。


2. HTTPS 性能优化方法

2.1 启用 TLS 1.3

  • 优势:TLS 1.3 简化了握手流程,将耗时从 2 RTT 减少到 1 RTT。

  • 如何实现:升级服务器和客户端支持 TLS 1.3。


2.2 会话复用

  • 什么是会话复用?

    • 如果客户端与服务器已经建立过 TLS 会话,后续连接可以复用会话信息,而无需重新握手。
  • 实现方法

    • Session ID:服务器保存会话信息,客户端通过 ID 复用。

    • Session Ticket:会话信息保存在客户端,由服务器解密使用。


2.3 启用 HTTP/2

  • HTTP/2 的优势

    • 支持多路复用(Multiplexing),允许一个连接同时传输多个请求和响应。

    • 减少了连接数量,提升传输效率。

  • 实现方法:确保服务器支持 HTTP/2。


2.4 使用 CDN

  • 优化点

    1. 缓存内容:减少服务器负载。

    2. 就近节点:降低网络延迟。

  • 实现方法:将网站内容托管到 CDN 服务上。


2.5 缓存证书链

  • 原理:浏览器缓存服务器的证书链,减少重复验证的开销。

  • 实现方法:启用 OCSP Stapling,服务器主动提供证书状态,无需客户端每次查询。


3.2.3 配置 HTTPS 时混合内容(Mixed Content)问题

1. 什么是混合内容?

混合内容是指在 HTTPS 页面中加载了不安全的 HTTP 资源(如图片、CSS、JS 文件)。这些不安全的资源会破坏 HTTPS 的安全性。

混合内容的分类
  • 主动混合内容 :影响页面结构或行为的资源(如 JS 文件)。
    • 危害:攻击者可能篡改 JS 文件,执行恶意代码。
  • 被动混合内容 :不影响页面结构的资源(如图片)。
    • 危害:攻击者可能篡改图片内容,但不会直接危害页面功能。

2. 混合内容的危害

  • 降低页面安全性:攻击者可以通过 HTTP 资源注入恶意代码,绕过 HTTPS 加密。
  • 用户信任受损:浏览器会警告用户页面不安全,影响用户体验。

3. 如何解决混合内容问题?

3.1 确保所有资源使用 HTTPS
  • 解决方式
    • 将资源链接从 http:// 改为 https://
    • 如果资源不支持 HTTPS,替换为其他安全的资源。

3.2 使用 Content Security Policy(CSP)
  • 作用:通过 CSP 指定页面允许加载的资源源。

  • 配置示例 : 在 HTTP 响应头中添加:

    java 复制代码
    Content-Security-Policy: upgrade-insecure-requests;
  • 作用:自动将 HTTP 请求升级为 HTTPS。


3.3 配置自动重定向
  • 解决方式
    • 在服务器端配置规则,将所有 HTTP 请求重定向到 HTTPS。

    • 示例(Nginx 配置)

      java 复制代码
      server {
          listen 80;
          server_name example.com;
          return 301 https://$host$request_uri;
      }

四、Java开发中的网络通信

4.1 基础网络通信详细讲解

网络通信是通过 Socket (套接字)建立连接并传输数据的,主要分为两种协议:TCP(可靠、面向连接)UDP(无连接、快速)


4.1.1 使用 Socket 和 ServerSocket 实现 TCP 通信

1. TCP协议简介

  • TCP(Transmission Control Protocol) 是面向连接的协议,保证数据传输的可靠性。

  • 通信双方在传输数据前需要建立连接,数据传输后需要释放连接。

  • 适用于需要数据完整性和可靠性的场景,例如文件传输、Web 服务。


2. TCP 通信的基本原理

  • 服务器端

    1. 创建 ServerSocket,绑定指定端口。

    2. 监听客户端连接请求。

    3. 客户端连接后,通过 Socket 对象进行数据交互。

  • 客户端

    1. 创建 Socket,指定服务器地址和端口。

    2. 连接服务器后,发送或接收数据。


4.1.2 使用 DatagramSocket 实现 UDP 通信

1. UDP协议简介

  • UDP(User Datagram Protocol) 是无连接的协议,数据以独立的数据包形式发送。

  • 传输速度快,但不保证数据可靠性。

  • 适用于实时通信场景,如视频直播、在线游戏。


2. UDP 通信的基本原理

  • 服务器端

    1. 创建 DatagramSocket,绑定指定端口。

    2. 等待接收客户端发送的 数据包(DatagramPacket)

  • 客户端

    1. 创建 DatagramSocket

    2. 发送数据包到指定的服务器地址和端口。


4.1.3 Java NIO(非阻塞 I/O 模型)(处理 TCP 通信)

Java NIO 是对传统阻塞 I/O 的改进,采用 非阻塞模型,提高了多客户端处理的效率。

  • 非阻塞 I/O:一个线程处理多个客户端连接,避免线程阻塞。
  • 高效并发:适合高并发场景。

NIO 的核心组件

Selector

  • Selector 是 NIO 的核心:通过一个线程管理多个通道(Channel)。

  • 允许同时处理多个连接,提高了服务器的并发能力。

Channel

  • Channel 类似于传统的 Socket,但支持非阻塞模式。

  • 常见的通道类型:

    • SocketChannel:处理 TCP 客户端连接。

    • ServerSocketChannel:处理 TCP 服务端连接。

Buffer

  • Buffer 是数据的容器,用于读取或写入数据。
  • 代替传统 I/O 的流(Stream)模型,支持更灵活的数据管理。

4.1.4.总结

通信方式 特点 适用场景
Socket+ServerSocket 简单、可靠,适合小规模 TCP 通信。 文件传输、聊天应用。
DatagramSocket 快速,无连接,但数据不可靠。 视频直播、在线游戏。
Java NIO 高性能、非阻塞,支持多客户端并发通信。 大规模并发服务器,如 Web 服务。

4.2 HTTP协议的Java实现

4.2.1 使用 HttpURLConnection 发送 HTTP 请求

简介

  • HttpURLConnection 是 Java 标准库提供的类,用于发送 HTTP 请求和处理响应。

  • 它支持常见的 HTTP 方法(如 GET、POST、PUT、DELETE)。

实际应用

  • 适用于非 Spring 项目或轻量级任务,例如独立工具类程序中发送 HTTP 请求。

  • 在现代 Spring 应用中,推荐使用更高级的工具如 RestTemplateWebClient

使用步骤

  1. 创建 URL 对象,并调用 openConnection() 获取连接。

  2. 设置请求方法(如 GETPOST)和请求头。

  3. 如果是 POST 或 PUT 请求,写入请求体数据。

  4. 读取服务器的响应。

注意HttpURLConnection 需要手动管理流和连接,代码较为冗长,且不支持现代特性如异步编程或响应式流。


4.2.2 使用 RestTemplate 和 WebClient(Spring)

1. SpringMVC 与 HTTP 客户端的区别

SpringMVC 的作用

  • SpringMVC 是服务端的"入口",负责接收外部客户端(如浏览器或其他服务)的 HTTP 请求,并返回响应。

  • 例如,当用户访问 /users 接口时,SpringMVC 通过控制层(@RestController)接收请求并处理业务逻辑。

HTTP 客户端的作用

  • RestTemplateWebClient 是用于主动发送 HTTP 请求的工具。

  • 它们是服务端作为"客户端"调用其他服务的 API,例如:

    • 调用支付网关的接口。

    • 从天气 API 获取实时天气数据。

    • 微服务间通信。


2. 使用 RestTemplate(同步阻塞模式)

简介

  • RestTemplate 是 Spring 提供的同步 HTTP 客户端工具。

  • 它采用阻塞模式:调用线程会等待请求完成后,才继续执行后续任务。

实际场景

  • 微服务之间的调用,例如从订单服务获取用户信息。

  • 调用第三方 API,例如物流服务的接口。


使用步骤

1.配置 RestTemplate

2.使用以下常用方法发送请求:

  • getForObject():发送 GET 请求并返回响应体。

  • postForEntity():发送 POST 请求并返回响应。

  • exchange():支持自定义请求头、方法等内容。


代码示例:实际使用 RestTemplate

配置 RestTemplate 在 Spring Boot 中,将 RestTemplate 注册为一个 Bean:

java 复制代码
@Configuration
public class RestTemplateConfig {
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

发送 GET 请求 在控制层调用远程 API:

java 复制代码
@RestController
@RequestMapping("/api")
public class ApiController {

    private final RestTemplate restTemplate;

    public ApiController(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @GetMapping("/get-data")
    public String getData() {
        String url = "https://jsonplaceholder.typicode.com/posts/1";
        return restTemplate.getForObject(url, String.class); // 获取 JSON 响应
    }
}

发送 POST 请求 通过 RestTemplate 提交数据到远程服务:

java 复制代码
@PostMapping("/create-data")
public String createData() {
    String url = "https://jsonplaceholder.typicode.com/posts";
    String requestBody = "{ \"title\": \"foo\", \"body\": \"bar\", \"userId\": 1 }";
    return restTemplate.postForObject(url, requestBody, String.class); // 提交数据并返回结果
}

3. 使用 WebClient(异步非阻塞模式)

简介

  • WebClient 是 Spring WebFlux 提供的响应式 HTTP 客户端工具。

  • 它采用异步非阻塞模式,线程不会等待请求完成,可以并行处理多个任务。

实际场景

  • 高并发场景,例如实时流数据处理。

  • 微服务间通信,需要组合多个 API 调用结果。


使用步骤

1.配置 WebClient

2.使用以下常用方法发送请求:

  • get():发送 GET 请求。

  • post():发送 POST 请求。

  • retrieve():触发请求并获取响应。


代码示例:实际使用 WebClient

配置 WebClient 在 Spring Boot 中,通过 WebClient.Builder 配置:

java 复制代码
@Configuration
public class WebClientConfig {
    @Bean
    public WebClient webClient() {
        return WebClient.builder().build();
    }
}

发送 GET 请求 通过 WebClient 以异步方式获取数据:

java 复制代码
@RestController
@RequestMapping("/api")
public class ApiController {

    private final WebClient webClient;

    public ApiController(WebClient webClient) {
        this.webClient = webClient;
    }

    @GetMapping("/get-data")
    public Mono<String> getData() {
        String url = "https://jsonplaceholder.typicode.com/posts/1";
        return webClient.get()
                        .uri(url)
                        .retrieve()
                        .bodyToMono(String.class); // 返回 Mono 响应
    }
}

发送 POST 请求 通过 WebClient 提交数据并异步处理结果:

java 复制代码
@PostMapping("/create-data")
public Mono<String> createData() {
    String url = "https://jsonplaceholder.typicode.com/posts";
    String requestBody = "{ \"title\": \"foo\", \"body\": \"bar\", \"userId\": 1 }";
    return webClient.post()
                    .uri(url)
                    .bodyValue(requestBody)
                    .retrieve()
                    .bodyToMono(String.class); // 返回 Mono 响应
}

4. RestTemplate 与 WebClient 的区别
特性 RestTemplate WebClient
模式 同步阻塞模式 异步非阻塞模式
线程利用 线程会等待请求完成,资源利用率较低 线程立即返回,可高效处理更多任务
适用场景 简单、传统的 API 调用场景 高并发、高性能场景,或实时数据处理
响应数据 返回直接的数据对象(如 String 返回响应式对象(如 MonoFlux
复杂性 简单易用,逻辑直观 需要理解响应式编程,复杂性较高
Spring 推荐 推荐逐步淘汰,用 WebClient 替代 推荐用于现代化微服务开发

4.2.3 配置 Spring Boot 处理跨域(CORS)

1. 什么是跨域(CORS)

  • CORS(跨域资源共享)允许不同来源的客户端访问你的服务。

  • 默认情况下,浏览器限制跨域请求,需要通过 CORS 配置解决。


2. 配置方法

全局配置

通过 addCorsMappings() 配置跨域规则:

java 复制代码
@Configuration
public class CorsConfig {
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**")
                        .allowedOrigins("http://localhost:3000")
                        .allowedMethods("GET", "POST", "PUT", "DELETE")
                        .allowedHeaders("*")
                        .allowCredentials(true);
            }
        };
    }
}

单个接口配置

通过 @CrossOrigin 注解解决特定接口的跨域问题:

java 复制代码
@RestController
public class SingleCorsController {
    @CrossOrigin(origins = "http://localhost:3000")
    @GetMapping("/cors-enabled")
    public String handleCors() {
        return "CORS enabled for this endpoint!";
    }
}

4.3 HTTPS 在 Java 中的实现

HTTPS 是基于 HTTP 协议加上加密层(SSL/TLS)的通信方式,它能够确保数据传输的安全性,防止信息被窃听或篡改。在实际开发中,为了让 Spring Boot 应用支持 HTTPS,我们需要准备证书并进行相关配置。同时,在生产环境中,通常会使用 Nginx 作为反向代理处理 HTTPS 请求。


4.3.1. 配置 Spring Boot 使用 HTTPS

1 为什么要配置 HTTPS?

默认情况下,Spring Boot 使用 HTTP 协议监听请求。HTTP 是明文传输的,容易被中间人攻击,特别是在涉及登录、支付等敏感数据时,使用 HTTPS 是非常必要的。

2 Spring Boot 支持 HTTPS 的关键

  • HTTPS 需要 SSL/TLS 加密。

  • SSL/TLS 依赖证书,Spring Boot 需要通过一个密钥库文件(如 keystore.jks)加载这些证书。

3 配置步骤

  1. 准备证书

    Spring Boot 使用 Java KeyStore(JKS)文件存储密钥和证书。您可以生成一个自签名证书用于开发和测试,也可以使用 CA 签发的证书用于生产环境。

  2. 配置 Spring Boot 的 application.properties

    application.properties 文件中,设置 HTTPS 的相关参数:

    java 复制代码
    server.port=8443                        # 设置 HTTPS 服务端口
    server.ssl.key-store=classpath:keystore.jks  # 密钥库文件路径
    server.ssl.key-store-password=your-password  # 密钥库密码
    server.ssl.key-store-type=JKS              # 密钥库类型
    server.ssl.key-alias=your-alias            # 密钥别名
  3. 启动应用

    启动后,通过 https://localhost:8443 访问应用,Spring Boot 会监听 HTTPS 请求。


4.3.2. 加载自签名证书与 CA 证书

1 什么是证书?

证书是用来证明服务器身份的文件。浏览器在访问 HTTPS 网站时,会验证这个证书是否可信。

  • 自签名证书:由自己生成,适合测试环境,但不被浏览器信任。

  • CA 签发证书:由权威证书机构颁发,适合生产环境,浏览器会自动信任。


2 如何生成自签名证书?

我们可以使用 Java 自带的 keytool 工具生成自签名证书。

打开终端,运行以下命令:

java 复制代码
keytool -genkeypair -alias mycert -keyalg RSA -keysize 2048 -keystore keystore.jks -validity 365
  • mycert 是证书的别名。

  • keystore.jks 是生成的密钥库文件名。

  • 365 表示证书有效期为 365 天。

按提示输入信息,例如姓名、组织、地区等。

生成的 keystore.jks 文件放置在项目的 src/main/resources 目录下。


3 如何使用 CA 签发的证书?

  1. 生成证书请求文件(CSR)

    向 CA 提交 CSR 文件以获取签名的证书:

    java 复制代码
    keytool -certreq -alias mycert -file certreq.csr -keystore keystore.jks
  2. 提交到 CA

    将生成的 certreq.csr 文件提交到证书机构(如 Let's Encrypt),等待证书签发。

  3. 导入 CA 签名的证书

    当您获得签名的证书文件(如 server.crt)后,使用以下命令将其导入密钥库:

    java 复制代码
    keytool -import -trustcacerts -alias mycert -file server.crt -keystore keystore.jks

4.3.3. 使用 Java KeyStore 工具生成证书

什么是 Java KeyStore?

Java KeyStore(JKS)是一个文件格式,用于存储加密密钥和证书。它是 Spring Boot 默认支持的密钥库格式。

生成密钥库

创建密钥库并生成密钥对

java 复制代码
keytool -genkeypair -alias your-alias -keyalg RSA -keysize 2048 -keystore keystore.jks -validity 365

查看密钥库内容

java 复制代码
keytool -list -v -keystore keystore.jks

这些命令可以生成密钥库文件和自签名证书,供开发环境使用。


4.3.4. 配置 Nginx 作为反向代理,启用 HTTPS

1 为什么使用 Nginx?

在生产环境中,Spring Boot 的 HTTPS 通常只作为开发和测试用途。在实际部署中:

  • Nginx 作为反向代理接收 HTTPS 请求,并将流量转发到 Spring Boot 应用。

  • Nginx 提供更好的性能、负载均衡支持,并简化证书管理。


2 配置 Nginx 支持 HTTPS

Step 1: 安装 Nginx

java 复制代码
sudo apt update
sudo apt install nginx

Step 2: 准备证书

  1. 获取 SSL 证书(自签名或 CA 签发的证书)。

  2. 将证书文件(如 server.crtserver.key)放置到 /etc/nginx/ssl/ 目录下。

Step 3: 配置 Nginx

编辑 Nginx 配置文件(通常是 /etc/nginx/sites-available/default),示例如下:

java 复制代码
server {
    listen 443 ssl;
    server_name yourdomain.com;

    ssl_certificate /etc/nginx/ssl/server.crt;         # 证书路径
    ssl_certificate_key /etc/nginx/ssl/server.key;     # 私钥路径

    location / {
        proxy_pass http://localhost:8080;             # 转发到 Spring Boot 应用
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

server {
    listen 80;
    server_name yourdomain.com;

    return 301 https://$host$request_uri;             # 将 HTTP 强制重定向到 HTTPS
}

Step 4: 测试并重启 Nginx

测试配置是否正确:

java 复制代码
sudo nginx -t

重启 Nginx 服务:

java 复制代码
sudo systemctl reload nginx

Step 5:验证 HTTPS

  1. 在浏览器中访问 https://yourdomain.com

  2. 确认页面能够正确加载,且浏览器地址栏显示安全锁图标。


五、Java Web开发中的实际应用

5.1 使用 Servlet 处理 HTTP 请求

  • HttpServletRequest:用于解析客户端发送的请求参数,处理 GET 和 POST 数据。
  • HttpServletResponse:用于设置返回的响应头和响应体。
  • Cookie 和 Session:提供会话管理功能,Cookie 存储在客户端,Session 存储在服务器端,分别适用于不同的应用场景。

5.1.1 HttpServletRequest 解析 GET/POST 参数

  • HttpServletRequest 是 Servlet 提供的接口,用于封装 HTTP 请求的信息。它包含请求的头部信息、请求参数、请求体以及客户端的信息。
  • 通过 HttpServletRequest,我们可以访问客户端发送的参数、Cookie、表单数据等。

怎样获取GET 请求的参数

  • GET 请求 是最常见的请求方式,参数通过 URL 的查询字符串传递。
  • HttpServletRequest 提供了以下方法解析这些参数:
    • getParameter(String name):获取指定名称的参数值。
    • getParameterMap():获取所有参数的键值对,返回 Map<String, String[]>
    • getQueryString():获取原始查询字符串(name=John&age=25)。

代码示例

java 复制代码
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 获取单个参数
        String name = request.getParameter("name");
        String age = request.getParameter("age");

        // 获取完整的查询字符串
        String queryString = request.getQueryString();

        // 返回解析结果
        response.getWriter().write("Query String: " + queryString + "\n");
        response.getWriter().write("Name: " + name + ", Age: " + age);
    }
}

代码解释

  • request.getParameter("name") 获取指定的参数值,例如 John
  • request.getParameterMap() 返回所有参数的键值对。
  • 使用 response.getWriter().write() 输出响应内容。

访问 URL/hello?name=John&age=25&city=NewYork
返回结果

java 复制代码
Query String: name=John&age=25
Name: John, Age: 25

怎样获取POST 请求的参数

  • POST 请求 的参数通常位于请求体中,例如表单数据或 JSON 数据。
  • HttpServletRequest 提供以下方法解析:
    • getParameter(String name):用于解析表单参数。
    • getInputStream()getReader():用于解析原始请求体数据。

代码示例:解析表单参数

java 复制代码
@WebServlet("/submit")
public class SubmitServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 获取表单参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        // 返回解析结果
        response.getWriter().write("Username: " + username + ", Password: " + password);
    }
}

代码解释

  • request.getParameter("username") 获取表单中的参数值。
  • response.setContentType("text/plain") 设置响应类型为纯文本。

访问表单

html 复制代码
<form action="/submit" method="POST">
    <input name="username" value="admin">
    <input name="password" value="1234">
    <button type="submit">Submit</button>
</form>

返回结果

java 复制代码
Username: admin, Password: 1234

5.1.2 HttpServletResponse 设置响应头与响应体

推荐使用 Spring 提供的 ResponseEntity,可以同时设置响应头和响应体

  • HttpServletResponse 用于封装服务器向客户端返回的数据,包括响应头、响应体以及状态码。
  • 响应头:通知客户端有关响应的信息(例如内容类型、缓存策略)。
  • 响应体:实际返回给客户端的数据,例如 HTML、JSON 或文件。

1.设置响应头
常用方法

  • setHeader(String name, String value):设置单个响应头。
  • addHeader(String name, String value):添加一个多值响应头。
  • setContentType(String type):设置响应内容类型。

代码示例:设置响应头

java 复制代码
@WebServlet("/set-headers")
public class HeaderServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setHeader("Content-Type", "application/json");
        response.setHeader("Cache-Control", "no-cache");
        response.addHeader("Custom-Header", "CustomValue");

        response.getWriter().write("{\"message\":\"Headers have been set!\"}");
    }
}

代码解释

  • setHeader("Content-Type", "application/json") 设置响应的 MIME 类型为 JSON。
  • setHeader("Cache-Control", "no-cache") 禁用缓存。

2.设置响应体
常用方法

  • getWriter():用于写入文本数据(如 HTML、JSON)。
  • getOutputStream():用于写入二进制数据(如文件或图片)。

代码示例:设置响应体

java 复制代码
@WebServlet("/response-body")
public class BodyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("text/html");
        response.getWriter().write("<h1>This is the response body</h1>");
    }
}

代码解释

  • response.setContentType("text/html") 设置响应类型为 HTML。
  • response.getWriter().write() 输出 HTML 内容到客户端。

使用 Cookie

  • 概念:Cookie 是存储在客户端的键值对,用于保存用户的状态信息。
  • 方法
    • HttpServletResponse.addCookie(Cookie cookie):设置 Cookie。
    • HttpServletRequest.getCookies():获取客户端发送的所有 Cookie。

代码示例

java 复制代码
@WebServlet("/cookie")
public class CookieServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        Cookie cookie = new Cookie("username", "JohnDoe");
        cookie.setMaxAge(3600); // 有效期 1 小时
        response.addCookie(cookie);
        response.getWriter().write("Cookie has been set!");
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie c : cookies) {
                response.getWriter().write("Cookie: " + c.getName() + " = " + c.getValue() + "\n");
            }
        }
    }
}

使用 Session

  • 概念:Session 是存储在服务器端的会话数据,使用唯一的 Session ID 标识客户端。
  • 方法
    • HttpServletRequest.getSession():获取当前会话。
    • setAttribute()getAttribute():设置和获取会话属性。

代码示例

java 复制代码
@WebServlet("/session")
public class SessionServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        HttpSession session = request.getSession();
        session.setAttribute("user", "JohnDoe");
        response.getWriter().write("Session has been set!");
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
        HttpSession session = request.getSession();
        String user = (String) session.getAttribute("user");
        response.getWriter().write("Session user: " + user);
    }
}

5.2 RESTful Web服务

RESTful Web 服务是一种设计风格,基于 HTTP 协议,将资源作为核心,通过标准的 HTTP 方法(如 GET、POST、PUT、DELETE 等)对资源进行操作。在 Spring Boot 中,RESTful Web 服务的实现主要依赖于注解和自动配置。

  • @RestController 与 @RequestMapping

    • @RestController 用于创建 RESTful 控制器。
    • @RequestMapping 结合 @GetMapping 等用于映射路径和方法。
  • @PathVariable 和 @RequestParam

    • @PathVariable 从路径中提取动态参数。
    • @RequestParam 从查询字符串中提取参数。
  • 返回 JSON 数据

    • Spring Boot 自动将对象序列化为 JSON。
    • 通过自定义响应格式,可以封装统一的返回结果结构,增强系统的可维护性和一致性。

5.2.1 Spring Boot 中的 @RestController 与 @RequestMapping

@RestController

  • 是 Spring MVC 提供的注解,用于标识该类为 REST 控制器。
  • 它相当于 @Controller@ResponseBody 的组合,表示所有方法的返回值都会直接作为 HTTP 响应体。

示例

java 复制代码
@RestController
public class HelloController {

    @GetMapping("/hello")
    public String sayHello() {
        return "Hello, RESTful Web Service!";
    }
}

解释

  • @RestController 声明了一个 RESTful 控制器。
  • @GetMapping 是对 HTTP GET 请求的映射,返回的字符串直接作为响应体。

@RequestMapping

  • @RequestMapping 是 Spring 提供的注解,用于映射 HTTP 请求到指定的控制器方法。
  • 支持指定路径、HTTP 方法、请求参数等。
  • 它可以作用在类和方法上:
    • 类级别:为整个控制器定义一个基础路径。
    • 方法级别:为每个具体的操作定义路径。

示例

java 复制代码
@RestController
@RequestMapping("/api")
public class ApiController {

    @GetMapping("/users")
    public String getUsers() {
        return "Returning all users";
    }

    @PostMapping("/users")
    public String createUser() {
        return "User created";
    }
}

解释

  • 类级别的 @RequestMapping("/api") 为该控制器的所有方法设置了基础路径。
  • @GetMapping@PostMapping 分别映射 GET 和 POST 请求。

5.2.2 使用 @PathVariable 和 @RequestParam 处理动态参数

@PathVariable

  • 用于从 URL 路径中提取动态参数。
  • 常用于操作特定资源,例如获取指定用户的信息。

示例

java 复制代码
@RestController
@RequestMapping("/api")
public class UserController {

    @GetMapping("/users/{id}")
    public String getUserById(@PathVariable("id") String userId) {
        return "User ID: " + userId;
    }
}

解释

  • {id} 是路径中的动态参数。
  • @PathVariable("id") 将路径中的 id 值绑定到方法参数 userId

访问 URL/api/users/123
返回结果User ID: 123


@RequestParam

  • 用于从请求参数中提取值,例如 URL 查询参数。
  • 常用于过滤、分页等操作。

示例

java 复制代码
@RestController
@RequestMapping("/api")
public class ProductController {

    @GetMapping("/products")
    public String getProducts(@RequestParam("category") String category,
                              @RequestParam(value = "page", defaultValue = "1") int page) {
        return "Category: " + category + ", Page: " + page;
    }
}

解释

  • @RequestParam("category") 绑定请求参数 category 到方法参数。
  • @RequestParam(value = "page", defaultValue = "1") 设置了分页的默认值。

访问 URL/api/products?category=electronics&page=2
返回结果Category: electronics, Page: 2


5.2.3 返回 JSON 数据与自定义响应格式

返回 JSON 数据

  • Spring Boot 默认使用 Jackson 库将对象序列化为 JSON。
  • 在 RESTful Web 服务中,通常通过返回对象而不是直接返回字符串。
  • Spring Boot 自动将返回的对象序列化为 JSON。

示例

java 复制代码
@RestController
@RequestMapping("/api")
public class UserController {

    @GetMapping("/users/{id}")
    public User getUserById(@PathVariable("id") int userId) {
        return new User(userId, "John Doe", "john.doe@example.com");
    }
}

class User {
    private int id;
    private String name;
    private String email;

    // 构造函数、getter 和 setter
    public User(int id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }
    public int getId() { return id; }
    public String getName() { return name; }
    public String getEmail() { return email; }
}

解释

  • User 是一个简单的 Java 对象(POJO)。
  • Spring Boot 自动将返回的 User 对象序列化为 JSON。

访问 URL/api/users/1
返回结果

java 复制代码
{
    "id": 1,
    "name": "John Doe",
    "email": "john.doe@example.com"
}

自定义响应格式

  • 可以使用自定义对象封装响应数据,例如统一返回结果的格式。

示例

java 复制代码
@RestController
@RequestMapping("/api")
public class ProductController {

    @GetMapping("/products/{id}")
    public ApiResponse getProductById(@PathVariable("id") int productId) {
        Product product = new Product(productId, "Smartphone", 699.99);
        return new ApiResponse(200, "Success", product);
    }
}

class Product {
    private int id;
    private String name;
    private double price;

    public Product(int id, String name, double price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }
    // Getter 和 Setter
}

class ApiResponse {
    private int status;
    private String message;
    private Object data;

    public ApiResponse(int status, String message, Object data) {
        this.status = status;
        this.message = message;
        this.data = data;
    }
    // Getter 和 Setter
}

访问 URL/api/products/101
返回结果

java 复制代码
{
    "status": 200,
    "message": "Success",
    "data": {
        "id": 101,
        "name": "Smartphone",
        "price": 699.99
    }
}

解释

  • ApiResponse 是一个通用响应对象,封装了状态码、消息和实际数据。
  • Product 是返回的具体业务数据。

5.3 拦截器与过滤器

拦截器(Interceptor)和过滤器(Filter)是 Web 应用中的两个重要组件,它们都可以在 HTTP 请求到达控制器之前或返回响应之前执行特定的逻辑。两者的功能有所重叠,但应用场景和实现方式不同。


5.3.1 自定义过滤器处理 HTTP 请求

1.过滤器的概念

  • 过滤器(Filter) 是 Servlet 提供的组件,位于请求的入口,可以对所有请求进行预处理或对响应进行后处理。
  • 常用于日志记录、请求参数校验、编码设置等场景。

2.过滤器的特点

  • 可以拦截所有进入 Servlet 容器的 HTTP 请求和响应。
  • 在过滤器链中按顺序执行。

3.自定义过滤器的实现步骤

  • 实现 javax.servlet.Filter 接口。
  • 重写 doFilter 方法。
  • 注册过滤器到 Spring Boot 应用中。

5.3.2 使用拦截器记录请求日志或验证用户权限

拦截器的概念

  • 拦截器(Interceptor) 是 Spring 提供的机制,可以拦截 HTTP 请求和响应,通常用于实现跨模块的业务逻辑。
  • 应用场景包括:
    • 验证用户权限。
    • 记录请求和响应日志。
    • 修改请求或响应内容。

拦截器的特点

  • 基于 Spring 的 HandlerInterceptor 接口,紧密结合 Spring MVC。
  • 作用于特定的控制器或路径。

拦截器的实现步骤

  • 实现 HandlerInterceptor 接口。
  • 配置拦截器并指定拦截路径。

代码示例:记录请求日志的拦截器

java 复制代码
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class LoggingInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("PreHandle: Request URI: " + request.getRequestURI());
        return true; // 返回 true 继续执行后续处理,返回 false 阻止请求
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("PostHandle: Request URI: " + request.getRequestURI());
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("AfterCompletion: Request URI: " + request.getRequestURI());
    }
}

代码解释

  • preHandle:在请求到达控制器之前执行,适合处理权限验证或记录日志。
  • postHandle:在控制器方法执行后,渲染视图之前执行,可以修改返回的数据。
  • afterCompletion:在请求完成(视图渲染完成)后执行,适合资源清理或异常处理。

配置拦截器 使用 Spring 的 WebMvcConfigurer 配置拦截器:

java 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {

    private final LoggingInterceptor loggingInterceptor;

    public WebConfig(LoggingInterceptor loggingInterceptor) {
        this.loggingInterceptor = loggingInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loggingInterceptor)
                .addPathPatterns("/**") // 拦截所有路径
                .excludePathPatterns("/auth/**"); // 排除特定路径
    }
}

5.3.3.拦截器与过滤器的对比

特点 过滤器(Filter) 拦截器(Interceptor)
依赖 Servlet 规范,独立于 Spring MVC Spring MVC 框架的一部分
作用范围 所有 HTTP 请求,包括静态资源 仅作用于动态请求(经过 Spring MVC 的控制器)
执行顺序 在拦截器之前执行 在过滤器之后执行,控制器处理之前或之后执行
典型用途 编码设置、日志记录、通用请求过滤 权限验证、业务逻辑处理、数据处理

六、HTTP协议的调试与优化

6.1 调试工具

在 Web 开发和调试中,合适的工具能够高效地帮助开发者查看请求、分析响应,以及调试问题。以下介绍三种常用工具:浏览器开发者工具、Postman 和 Fiddler/Charles。


6.1.1 使用浏览器开发者工具查看 HTTP 请求与响应

概述

现代浏览器(如 Chrome、Firefox、Edge)内置开发者工具,可以捕获和分析 HTTP 请求及响应,帮助开发者优化页面性能、排查问题。

主要功能

  • 网络监控:实时显示所有 HTTP 请求,包含请求方法、状态码、响应时间等。
  • 请求和响应详情:查看每个请求的头信息、请求参数及响应数据。
  • 性能分析:通过瀑布图等方式分析资源加载顺序及时间。

操作步骤(以 Chrome 为例)

1.打开开发者工具

F12Ctrl + Shift + I(Windows)/Cmd + Option + I(Mac)。

或者右键页面 → 选择"检查"。

2.切换到"网络"面板

点击顶部的"网络"(Network)选项卡。

3.捕获网络请求

刷新页面,网络面板会显示所有网络请求。

4.查看请求详情

点击某个请求,在右侧查看具体信息,包括:

  • Headers:请求头和响应头信息。
  • Payload:POST 请求的提交数据。
  • Preview:响应内容的预览。
  • Timing:资源加载的时间分布。

6.1.2 使用 Postman 发送调试 HTTP 请求

概述

Postman 是功能强大的 API 调试和测试工具,支持发送各种 HTTP 请求(如 GET、POST、PUT、DELETE),便于测试后端接口。

主要功能

  • 发送请求:支持设置请求方法、URL、参数和请求头。
  • 查看响应:直观展示状态码、响应头及返回数据。
  • 自动化测试:通过测试脚本自动验证 API 的正确性。

操作步骤

1.安装 Postman

Postman 官网 下载适合您操作系统的版本。

2.创建请求

  • 打开 Postman,点击"New"→"HTTP Request"。
  • 输入 URL,例如 https://jsonplaceholder.typicode.com/posts/1
  • 设置请求方法(如 GET)。

3.发送请求

点击"Send"按钮,Postman 会发送请求并显示响应结果。

4.查看响应

响应内容包括状态码、响应头、响应体(JSON、HTML 等)。


6.1.3 使用 Fiddler 或 Charles 抓包分析

概述

Fiddler 和 Charles 是专业的抓包工具,可以拦截计算机的所有 HTTP/HTTPS 流量,适合调试网络问题和复杂的请求分析。

主要功能

  • 抓包:拦截记录所有进出计算机的 HTTP/HTTPS 流量。
  • 修改请求:可动态修改请求或响应数据,测试不同的场景。
  • SSL 解密:解密 HTTPS 流量,查看加密的数据内容。

操作步骤(以 Fiddler 为例)

1.安装 Fiddler

从 Fiddler 官网 下载并安装。

2.启动抓包

打开 Fiddler,默认捕获所有网络流量。

3.设置 HTTPS 解密

  • 点击菜单栏的"Tools" → "Options" → "HTTPS"。
  • 勾选"Decrypt HTTPS traffic",启用 HTTPS 解密。

4.查看请求和响应

  • 在左侧会话列表中选择一个请求。
  • 查看右侧的详细信息,包括请求头、响应头和响应体。

5.修改请求

右键点击某个请求,选择"Edit Request",修改后重新发送。

注意事项

  • 启用 HTTPS 解密可能会引发安全警告,仅在受信环境中使用。
  • 持续抓包可能影响性能,建议调试完成后停止抓包。

6.2 性能优化

Web 性能优化的核心是提升页面加载速度、减少服务器压力和提高用户体验。以下内容将详细讲解如何通过减少请求数、使用浏览器缓存和配置 CDN 加速资源加载来优化性能。


6.2.1 减少请求数:合并 CSS 和 JS 文件

为什么减少请求数?

  • 每个 HTTP 请求都会有一定的开销,包括 DNS 解析、TCP 连接建立等。

  • 请求数过多会导致浏览器对同一域名的并发连接数限制(通常是 6 个),从而拖慢资源加载。

  • 合并文件可以减少 HTTP 请求数,从而加快页面加载。

实现方式

合并 CSS 文件

  • 将多个 CSS 文件合并成一个文件,减少 HTTP 请求。

合并前

html 复制代码
<link rel="stylesheet" href="/styles/reset.css">
<link rel="stylesheet" href="/styles/layout.css">
<link rel="stylesheet" href="/styles/theme.css">

合并后

html 复制代码
<link rel="stylesheet" href="/styles/all.css">
  • 使用工具:可以手动合并,也可以使用 Webpack 或 Gulp 等构建工具自动处理。

合并 JS 文件

  • 将多个 JS 文件合并成一个文件。

合并前

html 复制代码
<script src="/scripts/jquery.js"></script>
<script src="/scripts/main.js"></script>
<script src="/scripts/analytics.js"></script>

合并后

html 复制代码
<script src="/scripts/all.js"></script>

在 Spring Boot 项目中的实现

  • 静态资源管理: 将合并后的 CSS 和 JS 文件放置在 src/main/resources/static 下,Spring Boot 默认会加载此目录的静态资源。

  • 配置示例:

html 复制代码
<link rel="stylesheet" href="/static/styles/all.css">
<script src="/static/scripts/all.js"></script>

额外优化:启用 HTTP/2

  • HTTP/2 协议支持多路复用,可以同时加载多个资源,部分缓解请求数的问题。

  • 配置 HTTPS 后,Spring Boot 支持 HTTP/2:

html 复制代码
server.http2.enabled=true

6.2.2 使用浏览器缓存优化页面加载速度

为什么使用浏览器缓存?

  • 缓存允许浏览器直接从本地加载资源,而不是每次都请求服务器。

  • 可以显著减少 HTTP 请求数,缩短页面加载时间,提高用户体验。

实现方式

1.配置强缓存

强缓存 通过 Cache-ControlExpires 头控制。浏览器在缓存有效期内直接使用本地缓存,不向服务器发送请求。

在 Spring Boot 中配置强缓存:

  • 使用 WebMvcConfigurer 设置静态资源缓存:

    java 复制代码
    @Configuration
    public class CacheConfig implements WebMvcConfigurer {
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("/static/**")
                    .addResourceLocations("classpath:/static/")
                    .setCachePeriod(31536000); // 缓存时间:1 年
        }
    }

2.配置协商缓存

协商缓存 通过 ETagLast-Modified 头控制。浏览器会向服务器发送请求并带上这些头部,服务器通过判断资源是否更新来返回新资源或 304 状态码。

在 Spring Boot 中配置协商缓存:

  • 静态资源的协商缓存可以通过 application.properties 配置:

    java 复制代码
    spring.resources.cache.cachecontrol.max-age=31536000
    spring.resources.cache.cachecontrol.must-revalidate=true

结合使用强缓存与协商缓存

  • 设置强缓存优先减少请求。

  • 当强缓存失效后,使用协商缓存判断资源是否更新。


6.2.3 配置 CDN 加速资源加载

什么是 CDN?

  • 内容分发网络(CDN)是分布式的服务器网络,用于将静态资源缓存到多个节点中,让用户可以从最近的节点加载资源。

  • 通过减少网络延迟和服务器压力,提升资源加载速度。

CDN 的优势

  • 加速加载:用户请求会路由到地理位置最近的节点,减少延迟。

  • 分布式可靠性:节点之间可以互为备份,提升资源的可用性。

  • 减轻服务器压力:通过将资源分发到 CDN 节点,减少主服务器的请求压力。

在 Spring Boot 项目中使用 CDN

将静态资源托管到 CDN

  • 上传资源到 CDN:可以手动将 CSS、JS 文件上传到 CDN,或者通过 CI/CD 工具自动上传。

  • 更新资源路径:在 HTML 中替换资源 URL

    html 复制代码
    <link rel="stylesheet" href="https://cdn.example.com/styles/all.css">
    <script src="https://cdn.example.com/scripts/all.js"></script>

使用 CDN 加速动态资源

  • 动态内容(如 API 数据)通常不能直接缓存,但可以通过 CDN 的动态加速功能提升响应速度。

  • 配置 CDN 服务提供商(如 Cloudflare、阿里云 CDN)的反向代理功能,将动态请求加速到主服务器。

示例:配置阿里云 CDN

登录阿里云 CDN 控制台

创建加速域名

  • 绑定您的主服务器域名(如 example.com)。

  • 配置加速路径(如 /static/**)。

同步资源到 CDN

  • 上传静态资源到主服务器,CDN 会自动同步和缓存。

更新资源路径: 在 HTML 中替换资源 URL:

七、HTTP/2与HTTP/3

HTTP/2 和 HTTP/3 是现代互联网中的两种关键通信协议,分别在性能和效率上相较 HTTP/1.1 进行了重大改进。

  • HTTP/2

    • 核心特性:多路复用、Header 压缩、服务器推送。
    • 适用场景:目前主流的 Web 应用,解决了 HTTP/1.1 的主要性能瓶颈。
    • 局限:依然受到 TCP 的队头阻塞问题影响。
  • HTTP/3

    • 核心特性:基于 QUIC 协议,消除队头阻塞,连接建立更快。
    • 适用场景:需要低延迟、高并发、或运行在弱网环境下的应用(如实时视频流、游戏服务器)。
    • 局限:部署和兼容性逐步完善中,但已成为未来趋势。

7.1 HTTP/2

HTTP/2 是 HTTP 协议的第二个正式版本,于 2015 年发布。它主要解决了 HTTP/1.1 的性能问题,提升了数据传输效率。

7.1.1 核心特性

1.多路复用
  • **定义:**在单个 TCP 连接内同时处理多个请求和响应,无需为每个请求单独建立连接。
  • 传统问题:
    • 在 HTTP/1.1 中,每个请求需要单独的 TCP 连接。
    • 如果一个请求卡住(如等待服务器响应),后续请求也会受阻(队头阻塞)。
  • HTTP/2 的改进:
    • 将每个请求分成帧(Frame)并赋予唯一的流标识符(Stream ID)。
    • 帧交错传输后,客户端和服务器根据标识符重新组装数据。
  • 效果:
    • 同一连接可并行处理多个请求和响应。
    • 减少延迟,提高资源加载速度。
2.Header 压缩
  • **定义:**通过 HPACK 算法压缩 HTTP 头部信息,减少数据冗余。
  • 传统问题:
    • HTTP/1.1 中,每次请求都需要传输完整的头部(如 HostUser-Agent 等)。
    • 多个请求的头部信息常常重复,导致浪费带宽。
  • HTTP/2 的改进:
    • 使用头部表(Header Table)维护已发送的头部信息。
    • 对于重复的头部,只需发送索引号,而非完整字段。
  • 效果:
    • 大幅减少传输的数据量,尤其在大量小资源请求时显著提升效率。
3.服务器推送(Server Push)
  • **定义:**服务器可以在客户端未请求某些资源时主动推送资源。
  • 优势:
    • 提前发送可能需要的资源(如 CSS 文件、JS 文件),减少页面加载延迟。
  • 限制:
    • 需合理控制推送资源,避免带宽浪费。

7.1.2 优缺点

优点

  • 支持多路复用,解决了 HTTP/1.1 的队头阻塞问题。
  • Header 压缩减少了传输冗余,提高带宽利用率。
  • 服务器推送功能能主动加速页面资源加载。

缺点

  • 依赖 TCP 协议,丢包时需要重传整个连接的数据。
  • 复杂性增加,部分实现需要调整网络设备配置。

7.2 HTTP/3

HTTP/3 是基于 QUIC 协议的下一代 HTTP 协议,旨在解决 HTTP/2 的不足,特别是 TCP 队头阻塞问题。

7.2.1 核心特性

1.基于 QUIC 协议
  • **定义:**QUIC 是 Google 开发的传输协议,基于 UDP 实现,结合了 TCP 的可靠性和 UDP 的灵活性。
  • 特性:
    • 内置加密功能,传输默认安全。
    • 消除了 TCP 的队头阻塞问题。
2.流级别的多路复用
  • HTTP/2 的局限:
    • 多路复用虽然解决了应用层的队头阻塞,但 TCP 连接的丢包依然会阻塞所有流。
  • HTTP/3 的改进:
    • QUIC 实现了流级别的多路复用,丢包只会影响某个流,不影响其他流的传输。
  • 效果:
    • 在弱网环境中,传输效率显著提升。
3.快速连接建立
  • 传统问题:
    • TCP 需要三次握手,而 TLS 加密又引入了额外的握手延迟。
  • HTTP/3 的改进:
    • QUIC 将连接建立和 TLS 握手合并为一次操作。
    • 支持 0-RTT 重连(无需等待,即可发送数据)。
  • 效果:
    • 减少连接建立时间,显著降低延迟。

7.2.2 优缺点

优点

  • 基于 UDP,无队头阻塞问题。
  • 流级别的多路复用和快速连接建立显著降低延迟。
  • 更适合高并发和弱网环境。

缺点

  • 基于 UDP,可能受到防火墙限制。
  • 兼容性尚在逐步完善,部分设备和浏览器需要升级。

7.3 HTTP/2 与 HTTP/3 对比

特性 HTTP/2 HTTP/3
底层协议 TCP UDP + QUIC
多路复用 应用层支持,依赖 TCP,可能受队头阻塞影响 流级别支持,无队头阻塞问题
连接建立 TCP 三次握手 + TLS QUIC 一次握手,支持 0-RTT
头部压缩 HPACK QPACK
丢包处理 丢包需重传整个连接 丢包仅影响单个流
适用场景 适用于大多数现代 Web 应用 更适合高并发、低延迟和移动网络的场景
  • HTTP/2 已被广泛支持,适合立即部署的场景。
  • HTTP/3 是未来方向,适合在对性能要求极高的场景中优先尝试。
相关推荐
AI原吾3 小时前
探索Python的HTTP之旅:揭秘Requests库的神秘面纱
开发语言·python·http·requests
闲人-闲人3 小时前
HTTP Accept用法介绍
网络·网络协议·http
xiaoxiongip6663 小时前
HTTP工作原理
网络·网络协议·http·https·ip
_可乐无糖3 小时前
如何还原 HTTP 请求日志中的 URL 编码参数?详解 %40 到 @
网络·python·https
嘉琪0015 小时前
websocket是什么?
网络·websocket·网络协议
高耳机High-Earphone6 小时前
不只是请求和响应:使用Fiddler抓包URL和Method全指南(中)
网络协议·测试工具·http·fiddler·method·url·get/post
earthzhang20218 小时前
《深入浅出HTTPS》读书笔记(9):对称加密算法
开发语言·前端·网络协议·https·1024程序员节
蝌蚪代理9 小时前
node.js.抓取代理ip(提供参考)
网络协议·tcp/ip·node.js