Java IO包 Java NIO包Java IO知识图谱
1 OSI七层网络模型
上下层之间遵循的约定叫做"接口",同层之间遵循的约定叫做"协议".
OSI七层参考模型&TCP/IP四层参考模型 ISO七层网络参考模型 七层协议- 物理层
- 定义物理设备标准,模数转换与数模转换,这一层数据叫做比特。
- 数据链路层
- MAC地址封装与解封,这一层数据叫做帧,数据通过交换机传输。
- 网络层
- IP地址封装与解封,这一层数据叫做数据包,这一层设备是路由器。
- 传输层
- 定义了传输协议和端口号,这一层数据叫做段,TCP、UDP
- 会话层
- 数据传输通道
- 表示层
- 对接收数据进行解释、加解密、解压缩等
- 应用层
- 终端应用
2 TCP/IP四层模型
TCP/IP四层模型- 网络接口层/数据链路层
- 网络层
- IP
- 传输层f
- TCP、UDP
- 应用层
- HTTP、DNS、FTP、TELNET、SMTP
3 HTTP报文结构&TCP数据包
HTTP报文结构
- 报文首部
- 请求行
- 响应行
- 首部字段
- 请求首部字段
- 响应首部字段
- 通用首部字段
- 实体首部字段
- 其它
- 报文主体
TCP数据包TCP数据包结构
- 源端口号( 16 位):它(连同源主机 IP 地址)标识源主机的一个应用进程。
- 目的端口号( 16 位):它(连同目的主机 IP 地址)标识目的主机的一个应用进程。这两个值加上 IP 报头中的源主机 IP 地址和目的主机 IP 地址唯一确定一个 TCP 连接。
- 顺序号seq( 32 位):用来标识从 TCP 源端向 TCP 目的端发送的数据字节流,它表示在这个报文段中的第一个数据字节的顺序号。如果将字节流看作在两个应用程序间的单向流动,则 TCP 用顺序号对每个字节进行计数。序号是 32bit 的无符号数,序号到达 2 的32次方 - 1 后又从 0 开始。当建立一个新的连接时, SYN 标志变 1 ,顺序号字段包含由这个主机选择的该连接的初始顺序号 ISN ( Initial Sequence Number )。
- 确认号ack( 32 位):包含发送确认的一端所期望收到的下一个顺序号。因此,确认序号应当是上次已成功收到数据字节顺序号加 1 。只有 ACK 标志为 1 时确认序号字段才有效。 TCP 为应用层提供全双工服务,这意味数据能在两个方向上独立地进行传输。因此,连接的每一端必须保持每个方向上的传输数据顺序号。
- TCP 报头长度( 4 位):给出报头中 32bit 字的数目,它实际上指明数据从哪里开始。需要这个值是因为任选字段的长度是可变的。这个字段占 4bit ,因此 TCP 最多有 60 字节的首部。然而,没有任选字段,正常的长度是 20 字节。
- 保留位( 6 位):保留给将来使用,目前必须置为 0 。
- 控制位( control flags , 6 位):在 TCP 报头中有 6 个标志比特,它们中的多个可同时被设置为 1 。依次为:
URG :为 1 表示紧急指针有效,为 0 则忽略紧急指针值。
ACK :为 1 表示确认号有效,为 0 表示报文中不包含确认信息,忽略确认号字段。
PSH :为 1 表示是带有 PUSH 标志的数据,指示接收方应该尽快将这个报文段交给应用层而不用等待缓冲区装满。
RST :用于复位由于主机崩溃或其他原因而出现错误的连接。它还可以用于拒绝非法的报文段和拒绝连接请求。一般情况下,如果收到一个 RST 为 1 的报文,那么一定发生了某些问题。
SYN :同步序号,为 1 表示连接请求,用于建立连接和使顺序号同步( synchronize )。
FIN :用于释放连接,为 1 表示发送方已经没有数据发送了,即关闭本方数据流。 - 窗口大小( 16 位):数据字节数,表示从确认号开始,本报文的源方可以接收的字节数,即源方接收窗口大小。窗口大小是一个 16bit 字段,因而窗口大小最大为 65535字节。
- 校验和( 16 位):此校验和是对整个的 TCP 报文段,包括 TCP 头部和 TCP 数据,以 16 位字进行计算所得。这是一个强制性的字段,一定是由发送端计算和存储,并由接收端进行验证。
- 紧急指针( 16 位):只有当 URG 标志置 1 时紧急指针才有效。TCP 的紧急方式是发送端向另一端发送紧急数据的一种方式。
13/04/2018 Page 162 of 283 - 选项:最常见的可选字段是最长报文大小,又称为 MSS(Maximum Segment Size) 。每个连接方通常都在通信的第一个报文段(为建立连接而设置 SYN 标志的那个段)中指明这个选项,它指明本端所能接收的最大长度的报文段。选项长度不一定是 32 位字的整数倍,所以要加填充位,使得报头长度成为整字数。
- 数据: TCP 报文段中的数据部分是可选的。在一个连接建立和一个连接终止时,双方交换的报文段仅有 TCP 首部。如果一方没有数据要发送,也使用没有任何数据的首部来确认收到的数据。在处理超时的许多情况中,也会发送不带任何数据的报文段。
4 TCP三次握手~建立连接
TCP三次握手建立连接- 客户端:SYN=1,seq=x
- 服务端:SYN=1,ACK=1,ack=x+1,seq=y
- 客户端:ACK=1,ack=y+1,seq=x+1
5 TCP四次挥手~关闭连接
TCP四次挥手关闭连接TCP连接是全双工,数据可在两个方向上同时传递。单方向的关闭叫做半关闭。
- 客户端:FIN=1,seq=x,客户端进入FIN-WAIT
- 服务端:ACK=1,ack=x+1,seq=y,服务端进入CLOSE-WAIT
- 服务端:FIN=1,ACK=1,ack=y+1,seq=z,服务端进入LAST-ACK
- 客户端:ACK=1,ack=z+1,seq=x+1,客户端进入TIME-WAIT,服务端进入CLOSED(被动关闭)
- 客户端:TIME-WAIT,等待计时器设置的2MSL(Maximum Segment Lifetime,报文最大生存时间)后,客户端进入CLOSED(主动关闭)
6 客户端第三次握手发送ACK后为什么要等待2MSL才关闭?
- RFC 793 [Postel 1981c] 指出MSL为2分钟。但在Berkeley的实现上使用的值为30s,实现中的常用值也有1分钟,或2分钟。
- 防止服务端没有收到最后一次ACK数据包的情况下,可以重发第三次握手的FIN包;
- 清空链路中当前客户端的全部报文,防止对后续产生影响。
- 可以通过设置SO_REUSEADDR选项达到不必等待2MSL时间结束再使用此端口。
7 HTTP/TCP长短链接
持久连接
- http是无状态的应用层协议,TCP是传输层的协议,说http的长短链接其实指的是TCP的长短链接。
- HTTP1.0不支持长连接,HTTP1.1加入了keep-alive支持长连接(Connection:keep-alive),现在的http协议都是1.1,都默认支持长连接。设置Connection: Close可以断开长连接。
- 一个TCP连接是否为长连接,是通过设置HTTP的Connection Header来决定的,而且是需要两边都设置才有效。
- header头超时设置Keep-Alive:timeout=20,代表20秒超时。
- 持久连接使得请求以管线化(pipelining)方式发送成为可能,不用等待响应可以直接发送下一个请求。
长连接优劣
- 长连接优点:
- 可以省去较多的TCP建立和关闭的操作,减少浪费,节约时间。
- 长连接缺点:
- 服务器端有很多TCP连接时,会降低服务器的性能。
- 需要管理所有的TCP连接,检测是否是无用的连接(长时间没有读写事件),并进行关闭等操作。
- 长连接多适用于读写操作频繁,点对点的通讯,而且连接数不能太多情况。例如:数据库的连接用长连接, 如果用短连接频繁的通信会造成socket错误,而且频繁的socket 创建也是对资源的浪费。
短连接优劣
- 短连接优点:
- 对于服务器来说管理较为简单,存在的连接都是有用的连接,不需要额外的控制手段。
- 短连接缺点:
- 但如果客户请求频繁,将在TCP的建立和关闭操作上浪费较多时间和带宽。
- 短连接适用场景:
- 短连接用于并发量大,且每个用户无需频繁向服务器发数据包。 而像WEB网站的http服务一般都用短链接。因为长连接对于服务端来说会耗费一定的资源像,WEB网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源。
8 http请求响应流程
- URL地址解析(DNS域名解析)
- 封装http数据包
- 封装TCP包并建立传输连接
- 客户端发送请求命令
- 服务端处理请求
- 客户端接收服务端响应
- 渲染页面
- 服务端关闭连接
9 http协议方法和状态码
HTTP1.1使用的方法
- GET
- POST
- PUT
- DELETE
- HEAD
- OPTIONS
- TRACE
- CONNECT
HTTP状态码
- 1**:请求正在处理
- 2**:请求正常处理完毕
- 200:OK
- 204:No content
- 206:范围请求
- 3**:重定向
- 301
- 302
- 303
- 304
- 307
- 4**:客户端错误
- 400:Bad Request
- 401:Unauthorized
- 403:Forbidden
- 404:not found
- 5**:服务器错误
- 500:Internal Server Error
- 502:Bad Gateway
- 503:Service Unavailable
- 504:Gateway timeout
10 HTTPS请求过程
HTTPS=HTTP + SSL
https请求过程- 建立连接获取证书
- 证书验证
- 数据加密与传输
11 CDN原理
- DNS智能解析
- DNS解析找到最近的CDN节点,将请求转发到此节点,如果有数据直接返回
- 缓存技术
- 最近CDN节点无数据,缓存服务器就会去源服务器查找内容,找到了就返回客户端,同时将数据缓存到本地,为后续访问提供方便。
- 分发服务系统
- 负载均衡系统
- 管理系统
12 URI(统一资源标识符) & URL(统一资源定位符) & URN
URI&URL&URN- URL是URI的子集,定义web资源访问方式,不包括资源名
- URN是URI的子集,用特定命名空间的名称标识资源,不包括访问方式
- 比如说URI是http://bitpoetry.io/posts/hello.html#intro
- URL就是http://bitpoetry.io/posts/hello.html
- URN就是bitpoetry.io/posts/hello.html#intro
13 WebSocket底层原理及实现?
- websocket是第七层即应用层上的一个应用层协议,基于HTML5技术。
- 握手阶段:依赖http协议进行一次握手
- 数据传输阶段:双工的TCP连接
- 分片传输
- 边生成数据边传递消息
- 真正意义的长连接
14 websocket单机最大可以支撑多少连接?
- IE:6
- Google chrome:256
- Firefox:200
- Mac safari:1273
- Tomcat的websocket最大连接数:200
15 一个TCP连接可以对应几个http请求?几个http请求可以一起发送吗?
- 一个TCP连接对应多个http请求
- 几个http请求在http1.1不能一起发送
- RFC 2616认为无法根据多个结果判断请求是哪个发的
- HTTP pipelining想解决此问题,但是浏览器默认也是关闭的,因为有些代理服务器不能很好处理pipelining,而且维护请求顺序需要花费很多开销。
- 多http请求在http2.0可以一起发送
- HTTP2.0提供了Multiplexing多路传输特性
16 浏览器对同一host最多建立多少TCP连接?
- 受浏览器限制,Chrome允许对同一host最多建立6个TCP连接。
17 单机最大TCP连接数
- 做服务器端:
- 监听在某些端口,外部来的长连接数的上限取决于它的CPU,内存资源;
- 做客户端:
- 端口号在TCP协议字段里,一共两个字节,二进制的16位,意味着有2^16= 65536个端口号可用,但0-1023系统通常保留为知名服务端口,所以最多有64512个端口做为端口池资源。
18 计算机网络发展的七个阶段?
计算机发展阶段19 各种网络体系结构及其协议?
网络体系结构及其协议20 数据传输方式分类?
- 面向有连接型与面向无连接型
- 有连接型:发送方要确认接收方打开了连接才能发送数据
- 无连接型:发送方无需确认接收方是否存在,可以随时发送数据
- 电路交换与分组交换
- 电路交换:历史久远,比如电话网
- 分组交换/蓄积交换:TCP/IP
- 根据接收端数量分类
- 单播:1对1
- 广播:1对N,比如电视播放
- 多播:1对N,限定接收端是谁,比如电视会议
- 任播:选一台作为接收端
21 MAC寻址(地址转发表)与IP寻址(路由控制表)?
MAC寻址与IP寻址22 网络构成要素及搭建网络的设备?
网络构成要素 搭建网络的主要设备23 TCP/IP的发展历史
TCP/IP发展历史24 协议标准化流程
协议标准化流程25 TCP/IP协议族组成?
TCP/IP协议族26 TCP与UDP异同?
- TCP(Transmission Control Protocol)是面向有连接的可靠传输层协议
- 建立连接要3次握手、断开连接要4次挥手;
- 不适用于视频会议等多媒体领域场景;
- 可以控制丢包重发、乱序控制等;
- 重发超时一般设置为0.5秒的整数倍,一般设置为6秒左右;
- TCP提供了滑动窗口机制并行处理提高了网络通信的效率;
- TCP可以通过窗口大小进行流控制;
- TCP提高网络利用率的手段:
- 使用Nagle算法:发送端剩余发送数据较少,采用延迟发送处理机制
- 延迟确认应答
- 捎带应答
- UDP(User Datagram Protocol)是利用IP提供面向无连接的非可靠传输层协议
- UDP协议主要作用是将网络数据流量压缩成数据包的形式;
- UDP最好在局域网内使用,大大减少丢包概率;
- 可以确保发送消息的大小,不关注对方是否收到了数据;
- UDP简单高效,适用于包总量数据较少、多播、广播以及视频等即时多媒体领域场景,聊天用的ICQ和QQ也是使用UDP协议;
- UDP不提供复杂的控制机制,细节的处理会交给上层应用去完成。
27 网络拓扑
- 总线型
- 环型
- 星型
- 网状型
28 数据链路层
常用数据链路29 公共网络分类
- 模拟电话线路
- ADSL
- 移动通信服务
- FTTH
- 有线电视
- 专线
- VPN
- 公共无线LAN
- X. 25
- 帧中继
- ISDN
30 IP地址
- IP寻址
- 路由控制
- IP分包与组包
31 IP为什么要设计为面向无连接?
- 简单化,面向有连接比面向无连接复杂
- 高速化,管理连接繁琐费时,IP要提高速度,需要连接时委托上层TCP处理。
32 IPv4与IPv6
- IPv4
- 32位正整数,分4组,以.分割,最多允许2^32(约43亿)台机器联网
- 网络标识 + 主机标识
- 分配IP地址要去掉全部为0(IP地址无法获知)和全部为1(广播地址)的情况
- IP地址分为4类:
- A类:0开头,0.0.0.0~127.0.0.0,后24位为主机标识,主机地址上限为16777214
- B类:10开头,128.0.0.1~191.255.0.0,后16位为主机标识,主机地址上限为65534
- C类:110开头,192.168.0.0~239.255.255.0,后8位为主机标识,主机地址上限为254(2^8-2)
- D类:1110开头,224.0.0.0~239.255.255.255,没有主机标识,常用于多播
- 广播地址:主机地址都是1
- 本地广播:本地网络内的广播
- 直接广播:不同网络之间的广播
- IPv6
- IPv6是为了解决IPv4地址耗尽的问题,IPv4是32位,IPv6是128位,最多支持2^128
33 IPv4首部&IPv6首部
IPv4首部 IPv6首部34 DNS作用
- 域名解析,支持IPv4和IPv6
35 ARP原理(Address Resolution Protocol)
- ARP通过IP地址查找到MAC地址,只支持IPv4,IPv6可以用ICMPv6.
- IP地址和MAC地址缺一不可
36 RARP(Reverse Address Resolution Protocol)
- RARP,反地址解析协议,通过MAC地址定位IP地址。
37 ICMP原理
- 验证网络设置是否正确、IP包是否成功送达等,便于网络问题诊断。
38 DHCP原理
- DHCP,Dynamic Host Configuration Protocol,自动配置管理IP地址,即插即用。
39 NAT原理
40 提升HTTP传输速率的方法?
- 压缩传输的内容编码
- gzip(GNU zip)
- compress(UNIX系统的标准压缩)
- deflate(zlib)
- identity(不进行编码)
- 分块传输编码
41 内容协商机制?
内容协商机制指的是客户端和服务器端就响应的资源内容进行交涉,然后提供给客户端最合适的资源。
- 服务器驱动协商
- 客户端驱动协商
- 透明协商
42 端到端首部&逐跳首部
- 逐跳首部(Hop-by-hop Header):只对单次转发有效,需提供Connection首部字段。
- Connection
- Keep-Alive
- Proxy-Authenticate
- Proxy-Authorization
- Trailer
- TE
- Transfer-Encoding
- Upgrade
- 端到端首部(End-to-end Header):必须保存在由缓存生成的响应中,必须被转发。
43 HTTP缺陷&HTTPS优势
HTTP缺陷
- 明文传输
- 缺乏身份验证
- 报文完整性问题,可能被篡改
https好处
- 加密
- 对称加密
- AES,Advanced Encryption Standard
- DES,Data Encryption Standard
- 非对称加密
- RSA
- DH(Diffie-Hellman)
- 椭圆曲线
- 对称加密
- 身份认证
- 证书
- 完整性保护
- MD5
- SHA
44 HTTP身份认证手段
- BASIC基本认证
- 安全等级最低
- DIGEST摘要认证
- 安全等级次低
- SSL客户端认证
- 安全等级高,证书需要费用
- 基于表单认证
- Cookie
- Session
- token
45 HTTP增强协议
- SPDY
- Google开发的基于TCP协议的应用层协议,目的是优化http协议的性能,核心思想是减少TCP连接数。
- WebSocket
- 全双工通信标准,长连接
- websocket协议由IETF定位标准,websocket API由W3C定为标准
- WebDAV
- 基于万维网的分布式文件系统
- HTTP2.0
- 二进制传输
- 多路复用(Multiplexing):单一连接发起多重的请求/响应
- 头部压缩
- 请求/响应管线化pipeline
- 对请求划分优先级
- WebSocket
- TLS义务化
- 协商
- 流量控制
- 服务端push技术(Server Push)
- 更安全,对TLS进行升级,网络访问用HTTPS
- 改善HTTP1.x的缺点
- HTTP1.0是短连接,一次只允许在一个TCP连接上发起一个请求,HTTP1.1的pipeline不成熟,只能部分处理请求并发,浏览器也默认不支持。
- 单向请求,只能由client发起请求。
- 请求与响应报文首部冗余量大
- 文本传输,数据未压缩,导致数据传输量大
46 构建web内容的技术
- 展示
- HTML/HTML5
- CSS/CSS3
- JavaScript
- 数据
- XML
- JSON
47 web攻击技术
- 主动攻击
- SQL注入
- OS命令注入
- 被动攻击
- 跨站脚本攻击(XSS,Cross-Site Scripting)
- 跨站点请求伪造(CSRF,Cross-Site Request Forgeries)
- HTTP首部注入攻击:向首部添加内容
- HTTP响应截断攻击:向主体添加内容
- 邮件首部注入攻击
- 目录遍历攻击
- 远程文件包含漏洞
- 强制浏览
- 不正确的错误消息处理
- 开放重定向
- 会话劫持
- 会话固定攻击
- DNS劫持 / 域名劫持:把域名解析到错误的IP地址或者恶意让用户访问指定IP地址
- 其他安全漏洞
- 密码破解
- 穷举法 / 暴力破解法
- 字典攻击
- 点击劫持 / 界面伪装
- DoS攻击 /拒绝服务攻击
- DDoS攻击,多台机器发起的DoS攻击
- 后门程序
- 密码破解
HTTPS攻击
HTTPS攻击多存在于中间人攻击的环境中,主要是针对HTTPS使用的压缩算法和加密算法等进行攻击。
- 0x01 CRIME(Compression Ratio Info-leak Made Easy)
- 0x02 TIME(Timing Info-leak Made Easy)
- 0X03 BEAST(Browser Exploit Against SSL/TLS)
- 0x04 POODLE
中间人攻击防御手段:
- 针对安全性要求比较高的 app,可采取客户端预埋证书的方式锁死证书,只有当客户端证书和服务端的证书完全一致的情况下才允许通信,如一些银行类的app,但这种方式面临一个问题,证书过期的问题,因证书有一定的有效期,当预埋证书过期了,只有通过强制更新或者要求用户下载证书来解决。
- 针对安全性要求一般的app,可采用通过校验域名,证书有效性、证书关键信息及证书链的方式。
48 应用层协议
远程登录
- Telnet
- SSH
文件传输
- FTP
- SFTP
电子邮件
- SMTP
- POP
- IMAP
网络管理
- SNMP,Simple Network Management Protocol
- MIB,Management Information Base
- RMON,Remote Monitoring
多媒体通信实现技术
- H. 323
- SIP
- RTP,Real-Time Protocol
- 数字压缩技术
49 路由协议
路由控制类型
- 静态路由:路由映射关系固定,通常手工处理。
- 动态路由:自动失效转移
路由协议
路由控制范围跟路由协议有关,通常使用两种路由协议。
- IGP,Interior Gateway Protocol,内部网关协议
- RIP,Routing Information Protocol,路由信息协议
- RIP2
- OSPF,Open Shortest Path First,开放式最短路径优先
- EGP,Exterior Gateway Protocol,外部网关协议
- BGP,Border Gateway Protocol,边界网关协议
路由算法
- 距离向量算法(Distance-Vector)
- 链路状态算法(Link-State)
常用路由协议主要路由协议
50 SSO单点登录原理及实现方案?
单点登录SSO(Single Sign On),用户在多系统环境只需要登录一次,就可以得到其关联系统的信任不用重新登录。SSO就是要解决两个问题:
用户凭证信息的存储和验证。
基于Cookie作为凭证媒介
Cookie存储在客户端,客户端登录后服务端返回加密的cookie,用户携带此cookie访问其他子系统,子系统服务端校验cookie,通过则允许登录。
- cookie不安全
- 通过加密解决
- 不能跨域
- 通过JSONP解决跨域
基于Session
用redis存储session实现sso
SSO-分布式session基于Token(JWT,Jason Web Token)
JWT方案服务端不存储session,服务端是无状态的,便于扩展。JWT的数据结构主要分为三个部分,Header和Payload等JSON 对象也要使用 Base64URL 算法转成字符串。
- Header
Header 部分是一个 JSON 对象,描述 JWT 的元数据。 - Payload
Payload部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,除了官方字段,你还可以在这个部分定义私有字段。- iss (issuer):签发人
- exp (expiration time):过期时间
- sub (subject):主题
- aud (audience):受众
- nbf (Not Before):生效时间
- iat (Issued At):签发时间
- jti (JWT ID):编号
- Signature
Signature部分是对前两部分的签名,防止数据篡改。
JWT的特点:
(1)JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。
(2)JWT 不加密的情况下,不能将秘密数据写入 JWT。
(3)JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。
(4)JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。
(5)JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。
(6)为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。
JWT适用场景:
一次性的身份认证、api的鉴权等,这些场景能充分发挥jwt无状态以及分布式验证的优势。
JWT不适用的场景:
不要试图用jwt去代替session。这种模式下其实传统的session+cookie机制工作的更好,jwt因为其无状态和分布式,事实上只要在有效期内,是无法作废的,用户的签退更多是一个客户端的签退,服务端token仍然有效,你只要使用这个token,仍然可以登陆系统。另外一个问题是续签问题,使用token,无疑令续签变得十分麻烦,当然你也可以通过redis去记录token状态,并在用户访问后更新这个状态,但这就是硬生生把jwt的无状态搞成有状态了,而这些在传统的session+cookie机制中都是不需要去考虑的。
CAS
CAS是开源的单点登录系统,主要分为CAS client和CAS server,CAS只是说明了原理,具体实现要自己考虑。
SSO-CAS CAS序列图基于 SAML
SAML(Security Assertion Markup Language ,安全断言标记语言)的出现大大简化了 SSO ,并被 OASIS 批准为 SSO 的执行标准 。开源组织 OpenSAML 实现了 SAML 规范。
基于网关
基于OAuth2.0
51 cookie、session、token区别?
51.1 cookie
cookie生成规则
- cookie是服务端生成的,在客户端以key-value形式保存用户信息的
- 浏览器F12》Storage》Cookies可以看到cookie的组成:
- Name,相当于key
- Value,当前用户信息
- Domain,域名
- Path,路径
- Expires/Max-Age,过期时间
- Size,大小
cookie使用规则
- 服务器端响应头中设置set-cookie
- 客户端保存cookie信息,并在发送请求时候在请求头中带上cookies
cookie有效期
- 如果设置了cookie的有效期,则在有效期内有效,即使浏览器关闭了,cookie也存在;
- 如果没有设置cookie的有效期,则浏览器关闭cookie失效。
cookie优缺点
- 优点
- 可以保存用户信息和状态,配合无状态的http完成用户身份验证
- 缺点
- 浏览器保存的cookie数量和大小有限制
- cookie信息通过header或者URL明文传输容易泄露或被人劫持
- cookie在客户端保存,客户端可以关闭cookie
- 不支持跨域访问
51.2 session
session生成规则
- session在服务端生成,以hash表的key-value形式保存在服务端的内存中
- 一个用户一个session,一个session对应一个sessionid,key就是sessionid,value就是session中存储的用户信息。
session使用规则
- 客户端请求服务端分配sessionid,sessionid随响应头set-cookie保存在客户端的cookie中;
- 客户端发请求时候带上cookies,服务端从cookies中取出sessionid,根据sessionid找到对应的用户信息。
session有效期
- session默认有效期是30分钟,如果30分钟内session没有被访问过,则session失效。
session优缺点
- 优点
- 保存在服务端中,比保存在客户端要安全
- 缺点
- 保存在服务端内存中,当访问用户很多时候内存占用很大,性能会收到影响。
- 可扩展性差,增加了新节点没有用户的session信息
- CORS(跨域资源共享):当我们需要让数据跨多台移动设备上使用时,跨域资源的共享会是一个让人头疼的问题。在使用Ajax抓取另一个域的资源,就可以会出现禁止请求的情况。
- CSRF(跨站请求伪造):用户在访问银行网站时,他们很容易受到跨站请求伪造的攻击,并且能够被利用其访问其他的网站
51.3 token
token生成规则
token一般是登录时候由服务端生成,组成规则如下:
- UUID,用户唯一身份标识
- time,时间戳
- sign,签名,一般是UUID+time+salt(根据hash算法生成的字符串)
- 常用固定参数,可选
token使用规则
- 用户通过用户名和密码发送请求后,服务端生成token,随http响应保存在客户端的cookies或local storage中;
- 用户请求时候带上token,服务端获取token用于验证用户身份。
token有效期
- 根据token中的时间戳跟当前时间对比计算,判断过期与否,默认有效期是7天。
token优缺点
- 优点
- 支持跨域访问
- 可以在多个服务间共享,不占用服务端内存,用CPU计算时间替代session存储空间,服务端可以无状态扩展
- 支持移动设备
- 安全
- 缺点
- 需要额外的时间开销,CPU需要每次校验传过来的token是否有效。
52 OAuth2.0原理
52.1 OAuth2.0 & JWT & SSO & Shiro区别
- SSO,单点登录,OAuth2.0可以用来实现SSO。
- OAuth2.0是授权的协议、框架、标准,跟OAuth1.0不兼容,核心思想是颁发令牌(token),使得第三方应用使用该token在特定时间、特定范围内访问指定资源。
- JWT,Json Web Token,一个开放的用户认证标准,通过Header、Payload、Signature生成token。
- Shiro,基于java语言的安全框架,也可以实现授权。
52.2 OAuth2.0四种角色
- 资源所有者(resources owner):拥有被访问资源的用户
- 客户端/第三方应用(client):第三方应用,获取资源服务器提供的资源
- 授权服务器(authorization server):认证服务器,提供授权许可code、令牌token等
- 资源服务器(resource server):资源服务器,拥有被访问资源的服务器,需要通过token来确定是否有权限访问。
52.3 OAuth2.0四种授权模式
授权码模式(authorization code)
功能最完整、流程最严密、最安全的授权模式,code保证了token的安全性,即使code被拦截,由于没有app_secret,也是无法通过code获得token的。
授权码模式-
应用场景:
各大应用内的qq,微信,微博登录等。比如某应用内的qq登录,过程如下:
a.用户点击qq登录,会先跳转到qq登录页面,这时请求已经跳转到qq服务器了,然后用户输入账号或者扫码登录,这时所有请求都在qq服务器完成。
b.用户正确登录后,qq服务器返回用户的code给第三方应用,然后第三方应用再使用code去授权服务器请求获取token。(这一步用户不可见)
c.第三方应用获取到token后,再使用token获取用户的qq名称,头像等信息。 -
优缺点:
- 优点:用户可以控制自身的一些权限是否给第三方,第三方只能获取到用户临时产生的一个访问的code,安全性。
- 缺点:认证过程繁琐。
隐式授权码模式/简单模式(implicit)
和授权码模式类似,只不过少了获取code的步骤,是直接获取令牌token的,适用于公开的浏览器单页应用,令牌直接从授权服务器返回,不支持刷新令牌,且没有code安全保证,令牌容易因为被拦截窃听而泄露。
不支持refresh token。
密码模式(resource owner password credentials)
这种模式是最不推荐的,因为client可能存了用户密码。这种模式主要用来做遗留项目升级为oauth2的适配方案,当然如果client是自家的应用,也是可以的。支持refresh token。
客户端凭证模式(client credentials)
这种模式直接根据client的id和密钥即可获取token,无需用户参与。这种模式比较合适消费api的后端服务,比如拉取一组用户信息等,不支持refresh token,主要是没有必要。
53 IO模型(AIO & NIO & BIO(同步阻塞) & 多路复用IO)
- 阻塞IO模型(BIO(Blocking IO),同步阻塞IO,即传统的IO)
用户线程发出IO请求后,如果数据没有就绪就会一直阻塞等待,用户线程交出CPU,待数据就绪后,内核返回结果给用户线程,用户线程才会解除block状态。 - 非阻塞IO模型(NIO(Non-blocking IO),同步非阻塞IO,不同于Java的NIO(New IO))
用户线程会一直占用CPU轮询内核数据是否准备就绪 - 多路复用IO模型(多路复用IO(IO Multiplexing),异步阻塞IO,Reactor设计模式)
- 通过一个线程管理多个socket,只有当socket真正有读写事件发生时才会占用资源进行实际的读写操作,适合连接数比较多的情况。
- 多路复用IO模型是通过轮询的方式检测是否有事件到达,一旦事件响应体很大,轮询事件就会很长,导致后续事件迟迟得不到处理,影响新的事件轮询。
- 信号驱动IO模型
用户线程发起一个IO请求,会给对应的socket注册一个信号函数然后继续执行,待内核数据准备好后会发送一个信号给用户线程,用户线程收到信号后,便在信号函数中调用IO读写操作完成真实的IO请求操作。 - 异步IO模型(AIO(Asynchronous IO),NIO2.0,JDK7开始支持,异步非阻塞IO,Proactor设计模式)
异步IO是最理想的IO模型,跟信号驱动IO不同的是,当用户线程收到内核返回的信号时表示IO操作已完成,不用再去显示调用IO操作。
54 Java传统IO
54.1 Java IO包
Java IO包54.2 Java IO分类
IO-操作方式分类 IO-操作对象分类54.3 Java IO & NIO
- NIO三大核心部分:Channel通道、Buffer缓冲区、Selector选择器。
- IO是面向流的,NIO是面向缓冲区的。
54.4 Java NIO比传统IO的优势
- 传统IO是BIO(Blocking IO),Java NIO是异步非阻塞IO
- Java NIO有缓冲机制:buffer和channel机制
- NIO有零拷贝(零拷贝的前提是数据不需要加工)
- NIO提供MappedByteBuffer,将文件直接映射到虚拟内存,提高IO吞吐能力
- NIO的IO多路复用模型、事件驱动模型、非阻塞模型
55 Java NIO
55.1 Java NIO包
Java NIO包55.2 Channel
- NIO的Channel类似于IO的Stream,Stream是单向的,Channel是双向的。
- NIO的Channel有4种实现:
- FileChannel:文件IO
- DatagramChannel:UDP
- SocketChannel:TCP Client
- ServerSocketChannel:TCP Server
55.3 Buffer
- Buffer,缓冲区,实际上是一个容器,是一个连续数组,Channel提供从网络或文件读取数据的通道,但是读写数据都必须经过Buffer。
- 在NIO中,Buffer是顶层父类,是抽象类,常用的Buffer子类有:
- ByteBuffer
- ShortBuffer
- IntBuffer
- LongBuffer
- FloatBuffer
- DoubleBuffer
- CharBuffer
- 客户端发送数据,服务端接受数据的过程:
55.4 Selector
- Selector检测多个Channel是否有事件发生,如果有事件发生,就获取事件针对每个事件做相应的处理。
- 只用一个单线程就可以管理多个Channel,不用维护多个线程,避免多线程上下文切换带来的的开销。
56 Netty原理
- 基于Java NIO实现的异步事件驱动NIO框架。
- Netty的所有IO操作都是异步非阻塞的。
- 通过Future-Listener机制,用户可以方便的主动获取或者通过通知机制获得IO操作结果。
57 Netty为什么是高性能的?
- IO多路复用
- 事件驱动
- 异步非阻塞NIO,优于同步阻塞IO
- 高效传输
- 零拷贝技术:transferTo
- 堆外直接内存
- ByteBuffer采用堆外直接内存进行Socket读写,避免字节缓冲区二次拷贝;
- 多个小的Buffer可以合并成大的Buffer批量传输
- 内存池
- 直接内存分配和回收比较麻烦,为了重用缓冲区,Netty提供了基于内存池的缓冲区重用机制
- 高效的Reactor线程模型
- Reactor单线程模型
- 一个NIO线程处理所有IO操作
- Reactor多线程模型
- 一组NIO线程处理所有IO操作
- Acceptor线程监听服务端,接收客户端的TCP连接请求
- NIO线程池负责网络IO读写操作
- 主从Reactor多线程模型
- 接收请求的是Acceptor线程池
- Reactor单线程模型
- 无锁设计
- Netty的IO线程采用串行无锁设计,避免多线程竞争导致性能下降
- 高性能序列化框架protobuf
- SO_RCVBUF和SO_SNDBUF:通常建议值为128K或者256K。
- 小包封大包,防止网络阻塞
- 软中断Hash值和CPU绑定
58 Netty为什么不支持AIO而用NIO?
- Linux系统AIO的实现底层仍然是epoll,与NIO相比,性能没有明显优势;
- Windows系统AIO底层实现良好,但是Netty不看重Windows市场。
59 Netty服务端通信流程
Netty服务端通信流程60 Netty客户端通信流程
Netty客户端通信流程61 高效序列化机制
- Google protobuf > Google thrift > kryo / FST / Apache Avro / fastjson > Google Gson / Jackson > JDK
61.1 Google protobuf
protobuf特点-
Protocol Buffer的序列化 & 反序列化简单 & 速度快的原因是:
- 编码 / 解码 方式简单(只需要简单的数学运算 = 位移等等)
- 采用 Protocol Buffer 自身的框架代码 和 编译器 共同完成
-
Protocol Buffer的数据压缩效果好(即序列化后的数据量体积小)的原因:
- 采用了独特的编码方式,如Varint、Zigzag编码方式等等
- 采用T - L - V 的数据存储方式:减少了分隔符的使用 & 数据存储得紧凑
-
Google protobuf优点
- 二进制消息,性能好/效率高(空间和时间效率都很不错)
- proto文件生成目标代码,简单易用
- 序列化反序列化直接对应程序中的数据类,不需要解析后在进行映射(XML,JSON都是这种方式)
- 支持向前兼容(新加字段采用默认值)和向后兼容(忽略新加字段),简化升级
- 支持多种语言(可以把proto文件看做IDL文件)
- Netty等一些框架集成
-
Google protobuf缺点
- 官方只支持C++,JAVA和Python语言绑定
- 二进制可读性差(貌似提供了Text_Fromat功能)
- 二进制不具有自描述特性
- 默认不具备动态特性(可以通过动态定义生成消息类型或者动态编译支持)
- 只涉及序列化和反序列化技术,不涉及RPC功能(类似XML或者JSON的解析器)
61.2 Facebook thrift
thrift特点-
Facebook thrift优点
- 支持非常多的语言绑定
- 性能高效
- thrift文件生成目标代码,简单易用
- 消息定义文件支持注释
- 数据结构与传输表现的分离,支持多种消息格式
- 包含完整的客户端/服务端堆栈,可快速实现RPC
- 支持同步和异步通信
- 应用在Facebook开源日志收集系统、淘宝实时数据传输平台、Evernote开放接口、Quora、HBase等
-
Facebook thrift缺点
- 和protobuf一样不支持动态特性
61.3 Apache Avro
-
Apache Avro优点
- 二进制消息,性能好/效率高
- 使用JSON描述模式
- 模式和数据统一存储,消息自描述,不需要生成stub代码(支持生成IDL)
- RPC调用在握手阶段交换模式定义
- 包含完整的客户端/服务端堆栈,可快速实现RPC
- 支持同步和异步通信
- 支持动态消息
- 模式定义允许定义数据的排序(序列化时会遵循这个顺序)
- 提供了基于Jetty内核的服务基于Netty的服务
- Hadoop和Redis默认的序列化框架
-
Apache Avro缺点
- 只支持Avro自己的序列化格式
- 语言绑定不如Thrift丰富
61.4 kryo
- 速度快,序列化后体积小
- 跨语言支持较复杂
- dubbox默认的就是kryo+FST
61.5 FST
fst是完全兼容JDK序列化协议的系列化框架,序列化速度大概是JDK的4-10倍,大小是JDK大小的1/3左右。
61.6 hessian
- 默认支持跨语言
- 较慢
61.7 json
开源的Jackson
- 相比json-lib框架,Jackson所依赖的jar包较少,简单易用并且性能也要相对高些。
- Jackson社区相对比较活跃,更新速度也比较快。
- Jackson对于复杂类型的json转换bean会出现问题,一些集合Map,List的转换出现问题。
- Jackson对于复杂类型的bean转换Json,转换的json格式不是标准的Json格式。
阿里巴巴Fastjson
- Fastjson是一个Java语言编写的高性能的JSON处理器,由阿里巴巴公司开发。
- 无依赖,不需要例外额外的jar,能够直接跑在JDK上。
- FastJson在复杂类型的Bean转换Json上会出现一些问题,可能会出现引用的类型,导致Json转换出错,需要制定引用。
- FastJson采用独创的算法,将parse的速度提升到极致,超过所有json库。
Google的Gson
- Gson是目前功能最全的Json解析神器,Gson当初是为因应Google公司内部需求而由Google自行研发而来,但自从在2008年五月公开发布第一版后已被许多公司或用户应用。
- Gson的应用主要为toJson与fromJson两个转换函数,无依赖,不需要例外额外的jar,能够直接跑在JDK上。
- 使用这种对象转换之前需先创建好对象的类型以及其成员才能成功的将JSON字符串成功转换成相对应的对象。
- 类里面只要有get和set方法,Gson完全可以将复杂类型的json到bean或bean到json的转换,是JSON解析的神器。
- Gson在功能上面无可挑剔,但是性能上面比FastJson有所差距。
61.8 JDK序列化
- 无法跨语言。这应该是java序列化最致命的问题了。由于java序列化是java内部私有的协议,其他语言不支持,导致别的语言无法反序列化,这严重阻碍了它的应用。
- 序列后的码流太大。java序列化的大小是二进制编码的5倍多!
- 序列化性能太低。java序列化的性能只有二进制编码的6.17倍,可见java序列化性能实在太差了。
62 Netty Channel原理
62.1 Channel类图
Channel类图62.2 Netty传输方式/协议
- NIO,Non-blocking IO,io.netty.channel.socket.nio,基于java.nio.channels的工具包,使用选择器作为基础的方法,在高连接数时使用;
- OIO,Old blocking IO,io.netty.channel.socket.oio,基于java.net的工具包,使用阻塞流,适用于低连接数、需要低延时、阻塞时使用;
- Local,io.netty.channel.local,用来在虚拟机之间本地通信,在同一个JVM内通信时使用;
- Embedded,io.netty.channel.embedded,嵌入传输,它允许在没有真正网络的运输中使用ChannelHandler,可以非常有用的来测试ChannelHandler的实现。测试ChannelHandler时使用。
63 Netty Buffer原理
63.1 Buffer API
ByteBuf的作用是在Netty中通过Channel传输数据,Netty的ByteBuf相当于JDK的ByteBuffer,只是比JDK做了很多优化。Netty的Buffer API主要有两个接口:
- ByteBuf:字节数据容器
- ByteBuf分为读和写两部分,所有数据操作都要维护数据索引,根据数据索引可以顺序读或者跳读。
- ByteBuf类型字节数组,读和写的开始索引都为0,读写索引位置相同时候再读取会抛出异常IndexOutOfBoundsException
- ByteBuf默认最大容量限制是Integer.MAX_VALUE
- ByteBufHolder:辅助接口,提供的方法很少,主要用于释放资源等辅助操作
- ByteBufAllocator:类似线程池,负责分配ByteBuf实例
- Unpooled:创建缓冲区的工具类
- ByteBufUtil:静态工具类
63.2 Buffer分类
- Heap Buffer(堆缓冲区)
- 最常用的缓冲区,数据存储在JVM的堆空间,访问非堆缓冲区的数组会导致UnsupportedOperationException
- 提供了直接访问数组的方法,通过ByteBuf.array()来获取Byte[]数据。
- Direct Buffer(直接缓冲区)
- 直接缓冲区在堆外直接分配内存,缺点就是内存的分配和释放比堆缓冲区复杂,Netty使用内存池来解决。
- 直接缓冲区不支持数组访问数据
- Composite Buffer(复合缓冲区)
- JDK的ByteBuffer没有复合缓冲区的功能
64 Netty ChannelHandler
64.1 ChannelHandler类图
ChannelHandler类图64.2 ChannelPipeline
ChannelPipeline是ChannelHandler实例的列表,用于处理或截获通道的接收和发送数据。
64.3 ChannelHandlerContext
ChannelHandler的上下文
64.4 Channel生命周期状态模型
- channelUnregistered
- channelRegistered
- channelActive
- channelInactive
65 Netty编解码器codec
65.1 编码器Encoder
- MessageToByteEncoder,消息对象到字节对象
- MessageToMessageEncoder,消息对象到消息对象
65.2 解码器Decoder
- ByteToMessageDecoder,解码字节到消息
- ReplayingDecoder,解码消息到字节
- MessageToMessageDecoder,解码消息到消息
65.3 编解码器codec
- byte-to-byte
- ByteToMessageCodec
- MessageToMessageCodec
65.4 HTTP编解码器
- HttpRequestEncoder,将HttpRequest或HttpContent编码成ByteBuf
- HttpRequestDecoder,将ByteBuf解码成HttpRequest和HttpContent
- HttpResponseEncoder,将HttpResponse或HttpContent编码成ByteBuf
- HttpResponseDecoder,将ByteBuf解码成HttpResponse和HttpContent
66 Netty对HTTP的优化
66.1 HTTP
- 使用HTTP时候通过数据压缩可以减少传输流量,但是压缩数据会增加CPU负载,Netty支持两种数据压缩方式:gzip和deflate
66.2 HTTPS
- 网络传输的重要数据要加密,Netty提供了SSLHandler可以实现加密。
66.3 WebSocket
- WebSocket是全双工传输数据,不需要请求-响应模式,早期的WebSocket只能传输文本数据,现在也能传输二进制数据。
- Netty中的WebSocket类型有:
- BinaryWebSocketFrame,包含二进制数据
- TextWebSocketFrame,包含文本数据
- ContinuationWebSocketFrame,包含二进制数据或文本数据,BinaryWebSocketFrame和
- TextWebSocketFrame的结合体
- CloseWebSocketFrame,WebSocketFrame代表一个关闭请求,包含关闭状态码和短语
- PingWebSocketFrame,WebSocketFrame要求PongWebSocketFrame发送数据
- PongWebSocketFrame,WebSocketFrame要求PingWebSocketFrame响应
66.4 SPDY
- Google开发的基于TCP的应用层协议,对HTTP协议的增加,采用多路复用、请求优先级、HTTP报文头压缩、强制使用SSL加密等手段提高网络速度。
66.5 Netty处理空闲连接和超时
- IdleStateHandler,当一个通道没有进行读写或运行了一段时间后出发IdleStateEvent
- ReadTimeoutHandler,在指定时间内没有接收到任何数据将抛出ReadTimeoutException
- WriteTimeoutHandler,在指定时间内有写入数据将抛出WriteTimeoutException
66.6 Netty序列化机制
- JDK序列化
CompatibleObjectEncoder
CompactObjectInputStream
CompactObjectOutputStream
ObjectEncoder
ObjectDecoder
ObjectEncoderOutputStream
ObjectDecoderInputStream - JBOSS编组序列化:速度是JDK序列化的3倍,体积更小
CompatibleMarshallingEncoder
CompatibleMarshallingDecoder
MarshallingEncoder
MarshallingDecoder - protobuf序列化
ProtobufDecoder
ProtobufEncoder
ProtobufVarint32FrameDecoder
ProtobufVarint32LengthFieldPrepender
67 TCP粘包拆包问题
67.1 TCP为什么发生粘包拆包问题?为什么UDP不会发生粘包拆包问题?
- 粘包拆包是网络底层的问题,常发生在数据链路层、网络层、传输层。
- TCP是基于字节流的,没有边界,且TCP首部没有表示数据长度的字段
- UDP是基于报文的,有消息保护边界,且UDP首部有16bit标识报文长度,应用层能区分开不同报文,不会发生粘包拆包问题,粘包拆包只发生在TCP协议中。
- 粘包发生的原因
- 应用程序写入数据小于套接字缓冲区大小,网卡将应用多次写入的数据发送到网络上,这将会发生粘包。
- 接收方法不及时读取套接字缓冲区数据,这将发生粘包。
- 拆包发生的原因
- 应用程序写入的数据大于套接字缓冲区大小,这将会发生拆包。
- 进行MSS(最大报文长度)大小的TCP分段,当TCP报文长度-TCP头部长度>MSS的时候将发生拆包。
67.2 TCP粘包拆包解决方案
- 实际长度:发送端给每个数据包添加包首部,首部中应该至少包含数据包的长度,这样接收端在接收到数据后,通过读取包首部的长度字段,便知道每一个数据包的实际长度了。
- 固定长度:发送端将每个数据包封装为固定长度(不够的可以通过补0填充),这样接收端每次从接收缓冲区中读取固定长度的数据就自然而然的把每个数据包拆分开来。
- 分隔符:可以在数据包之间设置边界,如添加特殊符号,这样,接收端通过这个边界就可以将不同的数据包拆分开。
67.3 Netty怎么解决粘包拆包?
- 分隔符协议
- DelimiterBasedFrameDecoder,解码器,接收ByteBuf由一个或多个分隔符拆分,如NUL或换行符
- LineBasedFrameDecoder,解码器,接收ByteBuf以分割线结束,如"\n"和"\r\n"
- 长度协议
- FixedLengthFrameDecoder
- LengthFieldBasedFrameDecoder
68 Netty引导
68.1 引导的类图
引导的类图68.2 引导分类
- 引导客户端和无连接协议:Bootstrap
Bootstrap用来连接远程主机,有1个EventLoopGroup - 引导服务端:ServerBootstrap
ServerBootstrap用来绑定本地端口,有2个EventLoopGroup
69 RESTful
69.1 RESTful定义
- REST:表述性状态转移,Representational State Transfer,是一种互联网软件的架构原则
- RESTful:一个架构如果符合REST原则,它就是RESTful架构
69.2 RESTful特点
- 资源(Resources):每个资源对应一个URI,通过URI访问资源。
- 表现层(Representation):把资源呈现出来的的形式,叫做它的表现层,如XML、json等
- 状态转换(State Transfer):HTTP协议无状态,HTTP操作方式:
- GET:获取资源
- POST:新建资源(也可用于更新资源)
- PUT:更新资源
- DELETE:删除资源
69.3 RESTful架构
- 浏览器使用GET/POST/PUT/DELETE等请求方式对指定的URI资源进行增删改查操作
- 前后端分离
69.4 GET与POST请求区别
- 传输方式:get通过浏览器地址栏传送,post通过报文传送
- 数据大小:get参数有大小限制,post没有
- 安全性:get安全性差于post
- 幂等性:get、put、delete都是幂等操作,post不是幂等操作
- 对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。也不尽然,Firefox对post请求就只发一次包。
70 Netty源码
Bootstrap or ServerBootstrap
EventLoop
EventLoopGroup
ChannelPipeline
Channel
Future or ChannelFuture
ChannelInitializer
ChannelHandler
71 TCP拥塞控制算法
71.1 TCP四大拥塞控制策略
- 基于丢包的拥塞控制:将丢包视为出现拥塞,采取缓慢探测的方式,逐渐增大拥塞窗口,当出现丢包时,将拥塞窗口减小,如 Reno、Cubic 等。
- 基于时延的拥塞控制:将时延增加视为出现拥塞,延时增加时增大拥塞窗口,延时减小时减小拥塞窗口,如 Vegas、FastTCP 等。
- 基于链路容量的拥塞控制:实时测量网络带宽和时延,认为网络上报文总量大于带宽时延乘积时出现了拥塞,如 BBR。
- 基于学习的拥塞控制:没有特定的拥塞信号,而是借助评价函数,基于训练数据,使用机器学习的方法形成一个控制策略,如 Remy。
71.2 Reno
拥塞控制状态机拥塞控制状态机(Congestion Control State Machine)
-
Open状态
Open状态是拥塞控制状态机的默认状态。这种状态下,当ACK到达时,发送方根据拥塞窗口cwnd(Congestion Window)是小于还是大于慢启动阈值ssthresh(slow start threshold),来按照慢启动或者拥塞避免算法来调整拥塞窗口。 -
Disorder状态
当发送方检测到DACK(重复确认)或者SACK(选择性确认)时,状态机将转变为Disorder状态。在此状态下,发送方遵循飞行(in-flight)包守恒原则,即一个新包只有在一个老包离开网络后才发送,也就是发送方收到老包的ACK后,才会再发送一个新包。 -
CWR状态
发送方接收到一个显示拥塞通知时,并不会立刻减少拥塞窗口cwnd,而是每收到两个ACK就减少一个段,直到窗口的大小减半为止。当cwnd正在减小并且网络中有没有重传包时,这个状态就叫CWR(Congestion Window Reduced,拥塞窗口减少)状态。CWR状态可以转变成Recovery或者Loss状态。 -
Recovery状态
当发送方接收到足够(推荐为三个)的DACK(重复确认)后,进入该状态。在该状态下,拥塞窗口cnwd每收到两个ACK就减少一个段(segment),直到cwnd等于慢启动阈值ssthresh,也就是刚进入Recover状态时cwnd的一半大小。 发送方保持 Recovery 状态直到所有进入 Recovery状态时正在发送的数据段都成功地被确认,然后发送方恢复成Open状态,重传超时有可能中断 Recovery 状态,进入Loss状态。 -
Loss状态
当一个RTO(重传超时时间)到期后,发送方进入Loss状态。所有正在发送的数据标记为丢失,拥塞窗口cwnd设置为一个段(segment),发送方再次以慢启动算法增大拥塞窗口cwnd。
Loss 和 Recovery 状态的区别是:Loss状态下,拥塞窗口在发送方设置为一个段后增大,而 Recovery 状态下,拥塞窗口只能被减小。Loss 状态不能被其他的状态中断,因此,发送方只有在所有 Loss 开始时正在传输的数据都得到成功确认后,才能退到 Open 状态。
四大拥塞控制算法
Reno将拥塞控制的过程分为四个阶段:慢启动、拥塞避免、快重传和快恢复,是现有的众多拥塞控制算法的基础。
Reno四大算法1. 慢热启动算法 – Slow Start
所谓慢启动,也就是TCP连接刚建立,一点一点地提速,试探一下网络的承受能力,以免直接扰乱了网络通道的秩序。
- 连接建好的开始先初始化拥塞窗口cwnd大小为1,表明可以传一个MSS大小的数据。
- 每当收到一个ACK,cwnd大小加一,呈线性上升。
- 每当过了一个往返延迟时间RTT(Round-Trip Time),cwnd大小直接翻倍,乘以2,呈指数上升。
- 还有一个ssthresh(slow start threshold),是一个上限,当cwnd >= ssthresh时,就会进入“拥塞避免算法”(后面会说这个算法)。
2. 拥塞避免算法 – Congestion Avoidance
如同前边说的,当拥塞窗口大小cwnd大于等于慢启动阈值ssthresh后,就进入拥塞避免算法。算法如下:
- 收到一个ACK,则cwnd = cwnd + 1 / cwnd。
- 每当过了一个往返延迟时间RTT,cwnd大小加一。
过了慢启动阈值后,拥塞避免算法可以避免窗口增长过快导致窗口拥塞,而是缓慢的增加调整到网络的最佳值。
3. 拥塞状态时的算法
一般来说,TCP拥塞控制默认认为网络丢包是由于网络拥塞导致的,所以一般的TCP拥塞控制算法以丢包为网络进入拥塞状态的信号。对于丢包有两种判定方式,一种是超时重传RTO[Retransmission Timeout]超时,另一个是收到三个重复确认ACK。
超时重传是TCP协议保证数据可靠性的一个重要机制,其原理是在发送一个数据以后就开启一个计时器,在一定时间内如果没有得到发送数据报的ACK报文,那么就重新发送数据,直到发送成功为止。
但是如果发送端接收到3个以上的重复ACK,TCP就意识到数据发生丢失,需要重传。这个机制不需要等到重传定时器超时,所以叫做快速重传,而快速重传后没有使用慢启动算法,而是拥塞避免算法,所以这又叫做快速恢复算法。
超时重传RTO[Retransmission Timeout]超时,TCP会重传数据包。TCP认为这种情况比较糟糕,反应也比较强烈:
- 由于发生丢包,将慢启动阈值ssthresh设置为当前cwnd的一半,即ssthresh = cwnd / 2.
- cwnd重置为1
- 进入慢启动过程
最为早期的TCP Tahoe算法就使用上述处理办法,但是由于一丢包就一切重来,导致cwnd重置为1,十分不利于网络数据的稳定传递。
所以,TCP Reno算法进行了优化。当收到三个重复确认ACK时,TCP开启快速重传Fast Retransmit算法,而不用等到RTO超时再进行重传:
- cwnd大小缩小为当前的一半
- ssthresh设置为缩小后的cwnd大小
- 然后进入快速恢复算法Fast Recovery。
4. 快速恢复算法 – Fast Recovery
TCP Tahoe是早期的算法,所以没有快速恢复算法,而Reno算法有。在进入快速恢复之前,cwnd和ssthresh已经被更改为原有cwnd的一半。快速恢复算法的逻辑如下:
- cwnd = cwnd + 3 * MSS,加3 * MSS的原因是因为收到3个重复的ACK。
- 重传DACKs指定的数据包。
- 如果再收到DACKs,那么cwnd大小增加一。
- 如果收到新的ACK,表明重传的包成功了,那么退出快速恢复算法。将cwnd设置为ssthresh,然后进入拥塞避免算法。
如图所示,第五个包发生了丢失,所以导致接收方接收到三次重复ACK,也就是ACK5。所以将ssthresh设置为当时cwnd的一半,也就是6/2 = 3,cwnd设置为3 + 3 = 6。然后重传第五个包。当收到新的ACK时,也就是ACK11,则退出快速恢复阶段,将cwnd重新设置为当前的ssthresh,也就是3,然后进入拥塞避免算法阶段。
Reno拥塞控制过程
慢启动阶段,在没有出现丢包时每收到一个 ACK 就将拥塞窗口大小加一(单位是 MSS,最大单个报文段长度),每轮次发送窗口增加一倍,呈指数增长,若出现丢包,则将拥塞窗口减半,进入拥塞避免阶段;当窗口达到慢启动阈值或出现丢包时,进入拥塞避免阶段,窗口每轮次加一,呈线性增长;当收到对一个报文的三个重复的 ACK 时,认为这个报文的下一个报文丢失了,进入快重传阶段,立即重传丢失的报文,而不是等待超时重传;快重传完成后进入快恢复阶段,将慢启动阈值修改为当前拥塞窗口值的一半,同时拥塞窗口值等于慢启动阈值,然后进入拥塞避免阶段,重复上诉过程。
Reno 算法将收到 ACK 这一信号作为拥塞窗口增长的依据,在早期低带宽、低时延的网络中能够很好的发挥作用,但是随着网络带宽和延时的增加,Reno 的缺点就渐渐体现出来了,发送端从发送报文到收到 ACK 经历一个 RTT,在高带宽延时(High Bandwidth-Delay Product,BDP)网络中,RTT 很大,导致拥塞窗口增长很慢,传输速度需要经过很长时间才能达到最大带宽,导致带宽利用率降低。
Reno适用场景
适用于低延时、低带宽的网络。
71.3 Cubic
Cubic拥塞控制过程
Cubic是Linux 内核 2.6 之后的默认 TCP 拥塞控制算法,使用一个立方函数(cubic function)作为拥塞窗口的增长函数,其中,C 是调节因子,t 是从上一次缩小拥塞窗口经过的时间,Wmax 是上一次发生拥塞时的窗口大小,β是乘法减小因子。从函数中可以看出拥塞窗口的增长不再与 RTT 有关,而仅仅取决上次发生拥塞时的最大窗口和距离上次发生拥塞的时间间隔值。
Cubic 拥塞窗口增长曲线如下,凸曲线部分为稳定增长阶段,凹曲线部分为最大带宽探测阶段。如图 2 所示,在刚开始时,拥塞窗口增长很快,在接近 Wmax 口时,增长速度变的平缓,避免流量突增而导致丢包;在 Wmax 附近,拥塞窗口不再增加;之后开始缓慢地探测网络最大吞吐量,保证稳定性(在 Wmax 附近容易出现拥塞),在远离 Wmax 后,增大窗口增长的速度,保证了带宽的利用率。
当出现丢包时,将拥塞窗口进行乘法减小,再继续开始上述增长过程。此方式可以使得拥塞窗口一直维持在 Wmax 附近,从而保证了带宽的利用率。
Cubic 算法的优点在于只要没有出现丢包,就不会主动降低自己的发送速度,可以最大程度的利用网络剩余带宽,提高吞吐量,在高带宽、低丢包率的网络中可以发挥较好的性能。
但是,Cubic 同之前的拥塞控制算法一样,无法区分拥塞丢包和传输错误丢包,只要发现丢包,就会减小拥塞窗口,降低发送速率,而事实上传输错误丢包时网络不一定发生了拥塞,但是传输错误丢包的概率很低,所以对 Cubic 算法的性能影响不是很大。
Cubic 算法的另一个不足之处是过于激进,在没有出现丢包时会不停地增加拥塞窗口的大小,向网络注入流量,将网络设备的缓冲区填满,出现 Bufferbloat(缓冲区膨胀)。由于缓冲区长期趋于饱和状态,新进入网络的的数据包会在缓冲区里排队,增加无谓的排队时延,缓冲区越大,时延就越高。另外 Cubic 算法在高带宽利用率的同时依然在增加拥塞窗口,间接增加了丢包率,造成网络抖动加剧。
Cubic适用场景
适用于高带宽、低丢包率网络,能够有效利用带宽。
71.4 Vegas
Vegas拥塞控制过程
Vegas将时延 RTT 的增加作为网络出现拥塞的信号,RTT 增加,拥塞窗口减小,RTT 减小,拥塞窗口增加。具体来说,Vegas 通过比较实际吞吐量和期望吞吐量来调节拥塞窗口的大小,
期望吞吐量:Expected = cwnd / BaseRTT,
实际吞吐量:Actual = cwnd / RTT,
diff = (Expected-Actual) * BaseRTT,
BaseRTT 是所有观测来回响应时间的最小值,一般是建立连接后所发的第一个数据包的 RTT,cwnd 是目前的拥塞窗口的大小。Vegas 定义了两个阈值 a,b,当 diff > b 时,拥塞窗口减小,当 a <= diff <=b 时,拥塞窗口不变,当 diff < a 时,拥塞窗口增加。
Vegas 算法采用 RTT 的改变来判断网络的可用带宽,能精确地测量网络的可用带宽,效率比较好。但是,网络中 Vegas 与其它算法共存的情况下,基于丢包的拥塞控制算法会尝试填满网络中的缓冲区,导致 Vegas 计算的 RTT 增大,进而降低拥塞窗口,使得传输速度越来越慢,因此 Vegas 未能在 Internet 上普遍采用。
Vegas适用场景
适用于网络中只存在 Vegas 一种拥塞控制算法,竞争公平的情况。
71.5 BBR
BBR拥塞控制过程
BBR是谷歌在 2016 年提出的一种新的拥塞控制算法,已经在 Youtube 服务器和谷歌跨数据中心广域网上部署,据 Youtube 官方数据称,部署 BBR 后,在全球范围内访问 Youtube 的延迟降低了 53%,在时延较高的发展中国家,延迟降低了 80%。目前 BBR 已经集成到 Linux 4.9 以上版本的内核中。
BBR 算法不将出现丢包或时延增加作为拥塞的信号,而是认为当网络上的数据包总量大于瓶颈链路带宽和时延的乘积时才出现了拥塞,所以 BBR 也称为基于拥塞的拥塞控制算法(Congestion-Based Congestion Control)。BBR 算法周期性地探测网络的容量,交替测量一段时间内的带宽极大值和时延极小值,将其乘积作为作为拥塞窗口大小(交替测量的原因是极大带宽和极小时延不可能同时得到,带宽极大时网络被填满造成排队,时延必然极大,时延极小时需要数据包不被排队直接转发,带宽必然极小),使得拥塞窗口始的值始终与网络的容量保持一致。
由于 BBR 的拥塞窗口是精确测量出来的,不会无限的增加拥塞窗口,也就不会将网络设备的缓冲区填满,避免了出现 Bufferbloat 问题,使得时延大大降低。如图 4 所示,网络缓冲区被填满时时延为 250ms,Cubic 算法会继续增加拥塞窗口,使得时延持续增加到 500ms 并出现丢包,整个过程 Cubic 一直处于高时延状态,而 BBR 由于不会填满网络缓冲区,时延一直处于较低状态。
由于 BBR 算法不将丢包作为拥塞信号,所以在丢包率较高的网络中,BBR 依然有极高的吞吐量,如图 5 所示,在 1% 丢包率的网络环境下,Cubic 的吞吐量已经降低 90% 以上,而 BBR 的吞吐量几乎没有受到影响,当丢包率大于 15% 时,BBR 的吞吐量才大幅下降。
BBR 算法是反馈驱动的,有自主调节机制,不受 TCP 拥塞控制状态机的控制,通过测量网络容量来调整拥塞窗口,发送速率由自己掌控,而传统的拥塞控制算法只负责计算拥塞窗口,而不管发送速率(pacing rate),怎么发由 TCP 自己决定,这样会在瓶颈带宽附近因发送速率的激增导致数据包排队或出现丢包。
经过测试,在高延时、高丢包率的环境下,BBR 相对于 Cubic 算法在传输速度上有较大的提升,BBR 算法的不足之处在于设备队列缓存较大时,BBR 可能会竞争不过 Cubic 等比较激进算法,原因是 BBR 不主动去占据队列缓存,如果 Cubic 的流量长期占据队列缓存,会使得 BBR 在多个周期内测量的极小 RTT 增大,进而使 BBR 的带宽减小。
BBR适用场景
适用于高带宽、高时延、有一定丢包率的长肥网络,可以有效降低传输时延,并保证较高的吞吐量。
71.6 Remy
Remy拥塞控制过程
Remy也称为计算机生成的拥塞控制算法(computer-generated congestion-control algorithm),采用机器学习的方式生成拥塞控制算法模型。通过输入各种参数模型(如瓶颈链路速率、时延、瓶颈链路上的发送者数量等),使用一个目标函数定量判断算法的优劣程度,在生成算法的过程中,针对不同的网络状态采用不同的方式调整拥塞窗口,反复修改调节方式,直到目标函数最优,最终会生成一个网络状态到调节方式的映射表,在真实的网络中,根据特定的网络环境从映射表直接选取拥塞窗口的调节方式。
Remy 试图屏蔽底层网络环境的差异,采用一个通用的拥塞控制算法模型来处理不同的网络环境。这种方式比较依赖输入的训练集(历史网络模型),如果训练集能够全面覆盖所有可能出现的网络环境及拥塞调节算法,Remy 算法在应用到真实的网络环境中时能够表现的很好,但是如果真实网络与训练网络差异较大,Remy 算法的性能会比较差。
Remy适用场景
网络环境为复杂的异构网络,希望计算机能够针对不同网络场景自动选择合适的拥塞控制方式,要求现有的网络模型能够覆盖所有可能出现情况。
72 C10K问题及解决方案
C10K,支持10000个客户端同时连接。
- 每个进程/线程处理一个连接
- 资源占用过多,可扩展性差
- 每个进程/线程同时处理多个连接(IO多路复用)
- select-》poll-》epoll
网友评论