一: OSI 模型 Open System Interconnect 开放系统互连参考模型, 是由 ISO(国际标准化组织)定义的, 它是个灵活的, 稳健的和可互操作的模型, OSI 模型的目的是为了规范不同系统的互联标准, 使两个不同的系统能够较容易的通信, 而不需要改变底层的硬件或软件的逻辑, OSI 模型分为七层, OSI 把网络按照层次分为七层, 由下到上分别为物理层, 数据链路层, 网络层, 传输层, 会话层, 表示层, 应用层.
1.1: 第七层: 应用层的功能:
为应用软件提供接口, 使应用程序能够使用网络服务. 常见的应用层协议:
http(80),ftp(20/21),smtp(25),pop3(110),telnet(23),dns(53)等
1.2: 第六层: 表示层的功能:
数据的编码和解码, 数据的加密和解密, 数据和压缩和解压缩, 常见的标准有 JPEG/ASCII 等
1.3: 第五层: 会话层的功能:
建立, 管理和终止表示层实体之间的会话连接, 在设各或节点之间提供会话控制, 它在系统之间协调通信过程, 并提供 3 种不同的方式来组织它们之间的通信: 单工, 半双工和全双工
1.4: 第四层: 传输层的功能:
负责建立端到端的连接, 保证报文在端到端之间的传输. 提供可靠 TCP 及不可靠 UDP 的传输机制, 服务点编址, 分段与重组, 连接控制, 流量控制, 差错控制.
1.5: 第三层: 网络层的功能:
定义逻辑地址, 逻辑寻址, 将数据分组从源传输到目的, 路径选择, 路由发现, 维护路由表, 功能是隔离广播域; 隔离广播, 路由选择; 维护路由表, 寻址及转发, 流量管理并连接广域网
1.6: 第二层: 数据链路层的功能:
组帧, 物理编址, 将数据帧从链路上的一个节点传递到另一个节点, 流量控制, 差错控制, 接入控制
1.7: 第一层: 物理层的功能:
在介质上传递比特流, 定义接口和媒体的物理特性, 定义比特的表示, 数据传输速率, 信号的传输模式(单工, 半双工, 全双工), 定义网络物理拓扑(网状, 星型, 环型, 总线型等)
二: TCP 协议简介:
TCP, 全称 Transfer Control Protocol, 中文名为传输控制协议, 它工作在 OSI 的传输层, 提供面向连接的可靠传输服务, TCP 的工作主要是建立连接, 然后从应用层程序中接收数据并进行传输. TCP 采用虚电路连接方式进行工作, 在发送数据前它需要在发送方和接收方建立一个连接, 数据在发送出去后, 发送方会等待接收方给出一个确认性的应答, 否则发送方将认为此数据丢失, 并重新发送此数据.
三: TCP 三次握手:
在建立连接的时候, 所谓的客户端与服务端是相对应的, 即要看是谁主动连接的谁, 如果 A 主动连接 B 那么 A 就是客户端而 B 是服务端, 如果返过来 B 主动连接 A, 那么 B 就是客户端而 A 就成了服务端.
3.1: 连接过程:
第一次握手: 客户端发送 SYN 标志位为 1 的请求到服务端, 并随机生成一个 seq 序列号 x, 其中 seq 是随机产生的数据包的序列号.
第二次握手: 服务器收到客户端请求并返回 SYN=1,ACK=1,seq=y,ack=x+1, 其中 ACK=1 表示是响应报文, seq=y 是服务器随机产生的数据包序列号, ack=x+1 是确认客户端序列号有效并返回给客户端确认.
第三次握手: 客户端收到服务器的确认 ack=x+1 有效的验证信息, 即在自己发送的序列号基础之上加了 1 表示服务器收到并返回, 表示第二次连接有效, 然后客户端恢回复 ACK=1,seq=x+1,ack=y+1, 这是讲服务器发来 + 1 后的序列号当做自己的 seq 序列号, 确认号 ack 使用服务器的随机号 y 再加 1 即 ack=y+1, 这样客户端就完成了第三次的验证在讲数据包发给服务器, 服务器收到后验证确认号是在自己的 seq 之上加了 1, 表示没有问题就开始传输数据.
注:
ACK :TCP 协议规定, 只有 ACK=1 时有效, 也规定连接建立后所有发送的报文的 ACK 必须为 1
Seq: 序号, 4 字节, 范围为 0^32-1^32, 共 4284967296, 达到时重新开始计算
在第三次的时候 SYN 等于 0, 因为 SYN(SYNchronization) 只 i 在连接建立时用来同步序号, 当 SYN=1 而 ACK=0 时, 表明这是一个连接请求报文, 对方若同意建立连接, 则应在响应报文中使 SYN=1 和 ACK=1. 因此, SYN 置 1 就表示这是一个连接请求或连接接受报文, 链路建立成功之后就将标志位置为 0.
- SYN(synchronous 建立联机) ACK(acknowledgement 确认)
- PSH(push 传送) FIN(finish 结束)
- RST(reset 重置) URG(urgent 紧急)
- Sequence number(顺序号码) Acknowledge number(确认号码)
四: TCP 的四次断开:
TCP 断开要四次是因为 TCP 传输数全双工的, 即数据是在同一时间内两条数据链路双向互相传输的, 因此每个方向都要单独关闭一次, 断开需要客户端到服务端断开一次, 而服务端到客户端也需要断开一次, 这样的断开才是完整的断开,
第一次断开: 客户方发给服务器一个 FIN 为 1 的请求, FIN 为 1 表示是一个断开连接的请求, 即表示数据传输完毕请求断开, 并发送 seq 序列号和 Ack 确认号.
第二次断开: 服务器收到客户端请求并返回 ACK 标志位为 1,Ack 为 Seq+1 等于 201, 并将对方的 Ack 作为自己的 Seq 序列号的确认数据包, biao 接收到请求同意断开.
第三次断开: 服务器发送 ACK=1,FIN=1,Seq 等于客户端第一次请求断开的 Ack 确认号 + 1, 即 Seq 等于 501 的断开请求给客户端.
第四次断开: 客户端发送 ACK=1,Ack 在上一步 Seq 上 + 1 等于 502, 并使用在第二次断开中服务器发送的 Ack 确号 201 作为本次的序列号发给服务器表示同意断开, 服务器收到后验证序列号是第二次的, 验证 Ack 是第三次 + 1 的, 确认没有问题后同意断开, 然后将端口置为 TIME_WAIT 状态, 等待 2 MSL 时间后置为关闭状态, 被动方收到主动方的报文确认 Ack 确认号没有问题后将端口置为 CLOSED, 至此端口关闭.
- SYN(synchronous 建立联机) ACK(acknowledgement 确认)
- PSH(push 传送) FIN(finish 结束)
- RST(reset 重置) URG(urgent 紧急)
- Sequence number(顺序号码) Acknowledge number(确认号码)
四次断开的图形示意如下:
五: TCP 端口的十一种连接状态:
TCP 端口一共有十一种状态, CLOSE_WAIT 表示是程序 y 关闭连接, 而 TIME_WAIT 只占用一个 socket 连接, 到时间之后会释放, 因此大量的 CLOSE_WAIT 是比大量的 TIME_WAIT 影响更大, 另外还有 FIN_WAIT1 和 FIN_WAIT2, 如果有 FIN_WAIT2 也表示服务有问题, 以下是每个端口状态的含义:
5.1:CLOSED: 端口默认是关闭状态.
5.2:LISTEN: 服务器程序开始监听一个端口, 就是 LISTEN 状态.
5.3:SYN_RCVD: 三次握手的第二次握手后的端口状态, 是收到了客户端发送的 SYN_SENT 数据包之后的状态, 这个状态很短暂, 正常在服务器上是很少看到的, 除非服务器故意不发送最后一次握手数据包, 服务器返回给客户端 SYN 确认之后就会将在自己的端口置为 SYN_RCVD.
5.4:SYN_SENT:SYN_SENT 状态表示客户端已发送 SYN=1 的请求连接报文, 发送之后客户端就会将自己的端口状态置为 SYN_SENT.
5.5:ESTABLISHED: 表示已经连接成功, 客户端收到服务器的确认报文会回复服务器, 然后就将端口置为 ESTABLISHED, 服务器第三次收到客户端的 Ack 确认就会将端口置为 ESTABLISHED 并开始传输数据.
5.6:FIN_WAIT_1: 出现在主动关闭方, FIN_WAIT_1 状态实际上是当 SOCKET 在 ESTABLISHED 状态时, 当任意一方想主动关闭连接, 向对方发送了 FIN=1 的断开连接请求报文, 此时该 SOCKET 即 进入到 FIN_WAIT_1 状态. 而当对方回应 ACK 报文后, 则进入到 FIN_WAIT_2 状态, 当然在实际的正常情况下, 无论对方何种情况下, 都应该马 上回应 ACK 报文, 所以 FIN_WAIT_1 状态一般是比较难见到的, 而 FIN_WAIT_2 状态还有时常常可以用 netstat 看到.
5.7:FIN_WAIT_2: 出现在主动关闭方, 当被动方回应 FIN_WAIT_1 的 ACK 报文后, 则进入到 FIN_WAIT_2 状态
5.8:TIME_WAIT: 出现在主动关闭方, 表示收到了对方的 FIN 请求关闭报文, 并发送出了 ACK 报文, 就等 2MSL 后即可回到 CLOSED 可用状态了. 如果 FIN_WAIT_1 状态下, 收到了对方同时带 FIN 标志和 ACK 标志的报文时, 可以直接进入到 TIME_WAIT 状态, 而无须经过 FIN_WAIT_2 状态.
5.9:CLOSING: 这种状态比较特殊, 实际情况中应该是很少见, 属于一种比较罕见的例外状态. 正常情况下, 当你发送 FIN 报文后, 按理来说是应该先收到 (或同时收到) 对方的 ACK 报文, 再收到对方的 FIN 报文. 但是 CLOSING 状态表示你发送 FIN 报文后, 并没有收到对方的 ACK 报文, 反而却也收到了对方的 FIN 报文. 什 么情况下会出现此种情况呢? 其实细想一下, 也不难得出结论: 那就是如果双方几乎在同时 close 一个 SOCKET 的话, 那么就出现了双方同时发送 FIN 报 文的情况, 也即会出现 CLOSING 状态, 表示双方都正在关闭 SOCKET 连接.
5.10:CLOSE_WAIT: 表示在等待关闭端口, 这种状态存在于被动关闭的一方.
5.11:LAST_ACK: 是被动关闭方在主动关闭一方在发送 FIN 报文后, 最后等待对方的 ACK 报文, 当再次收到 ACK 报文后, 也即可以进入到 CLOSED 可用状态了.
5.12: 区分主动断开和被动端口方的端口状态:
主动端口方: SYN_SENT,FIN_WAIT1,FIN_WAIT2,CLOSING,TIME_WAIT .
被动断开方: LISTEN,SYN_RCVD,CLOSE_WAIT,LAST_ACK .
都具有的: CLOSED ,ESTABLISHED .
5.13: 关于优化:
socket 就是一个 TCP 连接, 包括源地址, 源端口, 目标地址, 目标端口和协议(TCP|UDP),0 端口是保留不能使用的, 因此服务器的最大端口使用数量为 63353 个, 最大 65536 个端口是因为 TCP 报文头部有个端口长度为 2^16 次方等于 65536, 查看当前打开的端口范围# cat /proc/sys.NET/ipv4/ip_local_port_range, 单个 IP 地址能接受的最大并发为六万多, 1 万个 TIME_WAIT 大约使用 1MB 的内存 CPU 占用更小, 因此资源使用很小可以忽略不计, 但是会占用一个 socket, 可以通过在负载上配置多个公网 IP 地址以提高高并发的问题,
- [[email protected] ~]# cat /proc/sys.NET/ipv4/tcp_tw_recycle
- 0 #用于快速回收处于 TIME_WAIT 状态的 socket 以便重新分, 在负载服务器不能打开, 会导致通过 nat 上网的后续用户无法打开网页, 因为后面的访问用户时间戳小于前面的用户, 会导致数据包被负载服务器丢弃, 可以在内网使用, 但是通常建议关闭.
- [[email protected] ~]# cat /proc/sys.NET/ipv4/tcp_tw_reuse
- 0 #kernel 会复用处于 TIME_WAIT 状态的 socket, 即允许将 TIME_WAIT 状态得 socket 用于直接新的 TCP 连接, 负载服务器建议打开
- [[email protected] ~]# cat /proc/sys.NET/ipv4/tcp_timestamps
- 1 #记录数据包的时间戳, 判断是新的数据包还是旧的, 如果是旧的就丢弃, 配合上面两个选项的时候一定要打开才生效.
六: Apache 的工作模式:
Apache 2.X 支持插入式并行处理模块, 称为多路处理模块(Multi-Processing Modules,MPM), 在 Linux 系统, 有 3 个不同类型的版本可供选择, 具体如下:
6.1:Prefork MPM: 预派生模式, 有一个主控制进程, 然后生成多个子进程, 使用 select 模型, 最大并发 1024, 每个子进程有一个独立的线程响应用户请求, 相对比较占用内存, 但是比较稳定, 可以设置最大和最小进程数, 是最古老的一种模式, 也是最稳定的模式, 适用于访问量不是很大的场景.
优点: 稳定
缺点: 慢, 占用资源, 不适用于高并发场景
配置文件原内容:
- #prefork MPM
- #StartServers: number of server processes to start
- #MinSpareServers: minimum number of server processes which are kept spare
- #MaxSpareServers: maximum number of server processes which are kept spare
- #MaxRequestWorkers: maximum number of server processes allowed to start
- #MaxConnectionsPerChild: maximum number of connections a server process serves
- #before terminating
- <IfModule mpm_prefork_module>
- StartServers 5 #定义 apache 服务在启动时启动的子进程数量
- MinSpareServers 5 #定义最小空闲进程数, 空闲进程就是没有处理用户请求的进程数
- MaxSpareServers 10 #定义最大空闲进程数
- MaxRequestWorkers 250 #定义在 prefork 模式下的最大并发连接数, 表示了 apache 的最大并发处理能力, 超过的连接请求将被排队等候处理.
- MaxConnectionsPerChild 0 #进程生命周期内, 处理的最大请求数目. 达到该数目后, 进程将死掉. 如果设置为 0, 表示没有限制. 该参数的意义在于, 避免了可能存在的内存泄露带来的系统问题.
- </IfModule>
如果确定合适的 MaxRequestWorkers 呢?
首先, 通过 top 命令查看 apache 进程占用的资源, 主要看 %CPU 和 %MEM 这两个指标, 例如, 每个进程的 CPU 占用率不超过 1%, 每个进程的内存占用率不超过 2%, 考虑内存限制, 比较合适的 apache 进程数量为 50 个, 然后, 逐步测试最大值. 通过观测得来的 CPU 和内存的指标有一定的误差, 一般可以适当调节这个数值, 例如调到 1.5 或者 2 倍, 再通过峰值场景下的机器是否卡顿来判断是继续上调还是下调.
6.2:woker MPM: 是一种多进程和多线程混合的模型, 有一个控制进程, 启动多个子进程, 每个子进程里面包含固定的线程, 使用线程程来处理请求, 当线程不够使用的时候会再启动一个新的子进程, 然后在进程里面再启动线程处理请求, 由于其使用了线程处理请求, 因此可以承受更高的并发.
优点: 相比 prefork 占用的内存较少, 可以同时处理更多的请求
缺点: 使用 keep-alive 的长连接方式, 某个线程会一直被占据, 即使没有传输数据, 也需要一直等待到超时才会被释放. 如果过多的线程, 被这样占据, 也会导致在高并发场景下的无服务线程可用.(该问题在 prefork 模式下, 同样会发生)
配置文件原内容详解:
- #worker MPM
- #StartServers: initial number of server processes to start
- #MinSpareThreads: minimum number of worker threads which are kept spare
- #MaxSpareThreads: maximum number of worker threads which are kept spare
- #ThreadsPerChild: constant number of worker threads in each server process
- #MaxRequestWorkers: maximum number of worker threads
- #MaxConnectionsPerChild: maximum number of connections a server process serves
- #before terminating
- <IfModule mpm_worker_module>
- StartServers 3 # #定义 apache 服务在启动时启动的子进程数量, 默认是 3 个
- MinSpareThreads 75 # 整个控制进程保持最小数的空闲线程数
- MaxSpareThreads 250 # 整个控制进程保持最大数的空闲线程数
- #ThreadLimit 64 # 每个子进程可以启动的线程数量上限值, 默认没有设置
- ThreadsPerChild 25 # 每个子进程启动的线程默认数量, 开启启动两个子进程每个子进程 25 个 线程, 就是 apache 启动后开启 25 个线程.
- MaxRequestWorkers 400 # 所有子进程加起来的线程数量最大值, 数量等于最大启动的进程数 * ThreadsPerChild(每个进程的线程数)
- MaxConnectionsPerChild 0 # 每个子进程被请求多少次服务后被 kill 掉重新生成一个新的子进程, 为了解决内存回收方面的问题, 0 为不设置
- </IfModule>
6.3:event MPM:Apache 中最新的模式, 属于事件驱动模型(epoll), 每个进程响应多个请求, 在现在版本里的已经是稳定可用的模式. 它和 worker 模式很像, 最大的区别在于, 它解决了 keep-alive 场景下, 长期被占用的线程的资源浪费问题(某些线程因为被 keep-alive, 空挂在哪里等待, 中间几乎没有请求过来, 甚至等到超时).event MPM 中, 会有一个专门的线程来管理这些 keep-alive 类型的线程, 当有真实请求过来的时候, 将请求传递给服务线程, 执行完毕后, 又允许它释放. 这样增强了高并发场景下的请求处理能力.
event 只在有数据发送的时候才开始建立连接, 连接请求才会触发工作线程, 即使用了 TCP 的一个选项, 叫做延迟接受连接 TCP_DEFER_ACCEPT, 加了这个选项后, 若客户端只进行 TCP 连接, 不发送请求, 则不会触发 Accept 操作, 也就不会触发工作线程去干活, 进行了简单的防 ***(TCP 连接), 可以使用 Telnet 进行测试验证:
主机 192.168.10.130 为客户端机器, 192.168.10.131 为 apache 服务器机器使用 event 模式:
在 192.168.10.130 上 telnet 192.168.10.131 80, 然后在 192.168.10.130 客户端机器上使用 netstat 查看, 发现连接已经建立, 处于 ESTABLISHED 状态, 然后再到 apache 服务器 192.168.10.131 使用 netstat 查看, 发现是处于 SYN_RECV 状态.
优点: 单线程响应多请求, 占据更少的内存, 高并发下表现更优秀, 会有一个专门的线程来管理 keep-alive 类型的线程, 当有真实请求过来的时候, 将请求传递给服务线程, 执行完毕后, 又允许它释放
缺点: 没有线程安全控制
配置文件内容:
- #event MPM
- #StartServers: initial number of server processes to start
- #MinSpareThreads: minimum number of worker threads which are kept spare
- #MaxSpareThreads: maximum number of worker threads which are kept spare
- #ThreadsPerChild: constant number of worker threads in each server process
- #MaxRequestWorkers: maximum number of worker threads
- #MaxConnectionsPerChild: maximum number of connections a server process serves
- #before terminating
- <IfModule mpm_event_module>
- StartServers 3 #apache 服务启动的子进程数, 默认 3 个
- MinSpareThreads 75 #控制进程保持最小的空闲线程数
- MaxSpareThreads 250 #控制进程保持的最大空闲线程数
- ThreadsPerChild 25 #每个子进程启动的线程数
- MaxRequestWorkers 400 #并发最大请求数, 也就是所有子进程加起来的线程数量, woker 模式下的 400 就是并发 400, 但是由于是异步处理请求的, 因此这里的 400 比 woker 模型下的并发处理速度要快很多, 因为 event 省略了工作线程的会话保持.
MaxConnectionsPerChild 0 #每个子进程请求多少次以后被 kill 掉重新生成一个新的子进程.
</IfModule>
来源: http://www.bubuko.com/infodetail-3061778.html