Web服务器
1.Web Server(网页服务器)。
-
通过HTTP协议与客户端(浏览器)进行通信,接收、存储、处理来自客户端的HTTP请求,并对其请求作出HTTP相应,返回给客户端其请求的内容(文件、网页等)或返回一个error信息。
-
用户在浏览器中键入“域名”或“IP地址:端口号”,浏览器将域名解析成IP地址向对应的Web服务器发送一个HTTP请求。底层是通过TCP协议的三次握手与目标Web服务器建立连接,然后HTTP协议生成针对目标Web服务器的HTTP请求报文,通过TCP、IP等协议发送到目标Web服务器上。
2.HTTP协议。
-
请求/响应模型, 定义 Web 客户端如何从 Web 服务器请求 Web 页面,以及服务器如何把 Web 页面传送给客户端。
-
客户端向服务器发送一个请求报文,请求报文包含请求的方法、URL、协议版本、请求头部和请求数据。
-
服务器以一个状态行作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。
3. HTTP 请求/响应的步骤:
-
客户端连接到 Web 服务器
-
一个HTTP客户端,通常是浏览器,与 Web 服务器的 HTTP 端口(默认为 80 )建立一个 TCP 套接字连接。
-
发送HTTP请求
-
通过 TCP 套接字,客户端向 Web 服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据 4 部分组成。
-
服务器接受请求并返回HTTP响应
-
Web 服务器解析请求,定位请求资源。服务器将资源复本写到 TCP 套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据 4 部分组成。
-
释放TCP连接
-
若 connection 模式为 close,则服务器主动关闭 TCP连接,客户端被动关闭连接,释放 TCP 连接;
-
若connection 模式为 keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求。
-
客户端浏览器解析HTML内容
-
客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。
-
然后解析每一个响应头,响应头告知以下为若干字节的 HTML 文档和文档的字符集。
-
客户端浏览器读取响应数据 HTML,根据HTML 的语法对其进行格式化,并在浏览器窗口中显示。
例如:在浏览器地址栏键入URL,按下回车之后会经历以下流程:
1. 浏览器向 DNS 服务器请求解析该 URL 中的域名所对应的 IP 地址;
2. 解析出 IP 地址后,根据该 IP 地址和默认端口 80,和服务器建立 TCP 连接;
3. 浏览器发出读取文件( URL 中域名后面部分对应的文件)的 HTTP 请求,该请求报文作为 TCP 三
次握手的第三个报文的数据发送给服务器;
4. 服务器对浏览器请求作出响应,并把对应的 HTML 文本发送给浏览器;
5. 释放 TCP 连接;
6. 浏览器将该 HTML 文本并显示内容。
4.HTTP请求报文格式
5.HTTP相应报文格式
6.服务器编程的基本框架
-
IO处理单元
-
处理客户的连接,读写网络数据
-
等待并接受新的客户连接,接收客户数据,将服务器响应数据返回客户端
-
逻辑单元(多个,并发处理多个客户任务)
-
业务进程或线程
-
分析并处理客户数据,然后将结果传递给IO处理单元或者直接发送给客户端(取决于事件处理模式)
-
网络存储单元
-
数据库、文件或缓存
-
请求队列
-
各单元之间的通信方式的抽象
-
IO处理单元接收到客户请求时,需要以某种方式通知逻辑单元来处理该请求;同时多个逻辑单元访问 一个存储单元时需要采用某种机制来协调处理竞态条件
7.两种高效的事件处理模式:服务器通常需要处理三类事件:IO事件、信号以及定时时间。
-
Reactor模式
-
同步IO模型实现
-
要求主线程(IO处理单元)只负责监听文件描述符上是否有事件发生,有的话立即通知工作线程(逻辑单元),将socket可读可写事件放入请求队列,交给工作线程处理。
-
由工作线程完成读写数据、接收新的连接,以及处理客户请求的任务。
-
Proactor模式
-
异步IO模型实现
-
将所有IO操作都交给主线程和内核处理(进行读写),工作线程仅仅负责业务逻辑。
8.使用同步IO模拟Proactor模式
-
主线程执行数据读写操作,读写完成之后,主线程向工作线程通知这一“完成事件”。(从工作线程的角度来看,它们直接获得了数据读写的结果,接下来只要对读写结果进行逻辑处理)
-
过程:
-
主线程往 epoll 内核事件表中注册 socket 上的读就绪事件。
-
主线程调用 epoll_wait 等待 socket 上有数据可读。
-
当 socket 上有数据可读时,epoll_wait 通知主线程。主线程从 socket 循环读取数据,直到没有更多数据可读,然后将读取到的数据封装成一个请求对象并插入请求队列。
-
睡眠在请求队列上的某个工作线程被唤醒,它获得请求对象并处理客户请求,然后往 epoll 内核事件表中注册 socket 上的写就绪事件。
-
主线程调用 epoll_wait 等待 socket 可写。
-
当 socket 可写时,epoll_wait 通知主线程。主线程往 socket 上写入服务器处理客户请求的结果。
9.线程池
-
由服务器预先创建的一组子线程,线程池中所有子线程都运行相同的代码,当有新任务到来时,主线程通过某种方式选择线程池中的某个子线程为之服务。
-
选择子线程的算法:
-
随机算法
-
Round Robin(轮流算法)
-
共享工作队列
-
主线程和所有子线程通过共享工作队列同步,子线程都睡眠在该共享工作队列上,有新任务来时,主线程将任务添加到工作队列,唤醒正在等待任务的子线程,有一个子线程获得新任务的“接管权”,它从工作队列中取得任务并执行之,其他子线程继续睡眠。
-
线程池中的线程数量
-
CPU密集型任务
-
与CPU处理器数量一样
-
IO密集型任务
-
多于CPU处理器数量,IO处理较慢,多于cores数的线程将为CPU争取更多的任务,避免在线程处理IO的过程造成CPU空闲导致资源浪费。
-
特点:
-
空间换时间,浪费服务器的硬件资源,换取运行效率。
-
池是一组资源的集合,这组资源在服务器启动之初就被完全创建好并初始化,这称为静态资源。
-
当服务器进入正式运行阶段,开始处理客户请求的时候,如果它需要相关的资源,可以直接从池中获取,无需动态分配。
-
当服务器处理完一个客户连接后,可以把相关的资源放回池中,无需执行系统调用释放资源。