版本修订
文档版本号 | 修订日期 | 修订人 | 修订内容 |
---|---|---|---|
v0.1.0 | 2020-03-22 | 厉华 | 创建 增加 什么是Web服务器 增加 预备知识 增加 架构设计 |
v0.2.0 | 2020-03-23 | 厉华 | 增加 模块设计 |
v0.3.0 | 2020-03-29 | 厉华 | 增加 跨平台Windows实现 增加 HTTPS实现 增加 minihetao 增加 辅助工具 |
v0.4.0 | 2020-03-29 | 厉华 | 跟随hetao v0.1.0.0调整内容 |
Web服务器一般指网站服务器,是部署在网络中的一种计算机程序,对外采用HTTP/HTTPS协议作为对外接口规范,向浏览器等客户端提供文档查询、放置更新文档、上传下载文件等服务。现代Web服务器还具备代理转发、负载均衡、应用逻辑等附加功能。
Web服务的工作流程分为五个步骤:连接、请求、本地处理、响应、断开。首先浏览器通过DNS获得Web服务器IP和PORT地址信息,与之建立TCP连接,然后发送HTTP请求,Web服务器接收和解析请求,找到本地静态网页文档或动态生成网页,响应回浏览器,双方断开TCP连接。
现代Web服务器还承担代理转发负载均衡功能
本文以我在2017年完全自研的Web服务器开源产品hetao
作为示例,讲解如何设计一个Web服务器,尽量不涉及到编程语言和具体代码实现。
HTTP协议的设计在1989年的欧洲核子研究组织所发起,经历了最初的v0.9版,以及较为完善的v1.0版,在1999年公布的RFC2616定义了现今广泛使用的v1.1版,在2015年互联网工程任务组提交发布了v2.0版。
HTTP(网络七层)是建立在TCP(网络四层)上的一种通讯协议,它在TCP连接续存期内定义了通讯数据交换格式。
HTTP请求格式如下:
(请求方法)(空格)(URI)(空格)(请求协议和版本)\r\n
(请求头1名字)(空格;可选):(空格;可选)(请求头1值)\r\n
...
(请求头n名字)(空格;可选):(空格;可选)(请求头n值)\r\n
\r\n
(请求体;可选)
HTTP请求示例如下:
GET / HTTP/1.1
Host: www.163.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
或
POST /user HTTP/1.1
Host: www.test.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-length: 31
username=calvin&password=123456
浏览器向Web服务器提交HTTP请求时,请求URL会被拆分为几段,作为HTTP请求数据来源,比如:
http://www.test.com/users
会被拆成
GET /users HTTP/1.1
Host: www.test.com
...
HTTP请求头选项列表如下:
HTTP请求头选项名字 | 值示例 | 值类型 | 说明 |
---|---|---|---|
Host | www.163.com | 字符串 | 浏览器希望访问Web服务器的域名,用于多虚拟主机环境 |
User-Agent | Mozilla/5.0 (Windows NT... | 字符串 | 浏览器方的软件环境 |
Accept | text/html,application/... | 字符串 | 浏览器可以接受的文本类型和优先级,供服务端参考 |
Accept-Language | zh-CN,zh;q=0.8,zh-TW;... | 字符串 | 浏览器可以接受的文本语言代码,供服务端参考 |
Accept-Encoding | gzip, deflate, br | 字符串 | 浏览器可以接受的数据编码(压缩)算法 |
Connection | keep-alive | 枚举字符串 | 本次请求响应结束后是否需要保持连接用于下一次请求响应 |
Content-length | 31 | 整型 | HTTP体的长度;如果出现则表示有请求体 |
(其它自定义请求头选项名) | ... | ... | ... |
HTTP响应格式如下:
(响应协议和版本)(空格)(状态码)(空格)(状态码描述)\r\n
(响应头1选项名字)(空格;可选):(空格;可选)(响应头1选项值)\r\n
...
(响应头n选项名字)(空格;可选):(空格;可选)(响应头n选项值)\r\n
\r\n
(响应体;可选)
HTTP响应示例如下:
HTTP/1.1 200 OK
Date: Sun, 15 Mar 2020 07:39:12 GMT
Content-type: text/html; charset=GBK
Expires: Sun, 15 Mar 2020 07:40:29 GMT
Server: nginx
或
HTTP/1.1 200 OK
Date: Sun, 15 Mar 2020 07:39:12 GMT
Content-type: text/html; charset=GBK
Expires: Sun, 15 Mar 2020 07:40:29 GMT
Server: nginx
Content-length: 319
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb18030" />
<title>Welcome</title>
</head>
<body>
Hello HETAO
</body>
</html>
HTTP响应头选项列表如下:
HTTP响应头选项名字 | 值示例 | 值类型 | 说明 |
---|---|---|---|
Server | nginx | 字符串 | Web服务器软件名 |
Content-type | text/html; charset=GBK | 字符串 | 文本类型和编码 |
Expires | Sun, 15 Mar 2020 07:40:29 GMT | 网页过期时间 | |
Content-encoding | gzip | 响应体数据的编码(压缩)算法 | |
Content-length | 31 | 整型 | HTTP体的长度;如果出现则表示有响应体 |
所谓HTTPS只不过是在TCP三步握手完成后,HTTP分组开始前,做SSL握手,协商双方加解密算法和密钥。SSL握手分组较复杂,下图是根据hetao代码分析出来的分组图:
hetao
的层次结构分为七层,从下到上分别是公共基础层、进程管理层、事件管理层、事件处理层、安全控制层、应用入口层、应用API层。
公共基础层提供基础的数据结构算法库、工程技术库,以及本项目公共复用代码如宏、函数等。
进程管理层负责软件入口、进程/线程结构,以及进程/线程管理和控制。
由于hetao
基于多路复用事件模型,事件管理层负责事件总线的创建、维护和销毁,以及各事件类的分派。
事件处理层负责网络IO事件、文件系统IO事件和内部事件的具体处理。
安全控制层负责TCP安全控制、HTTP会话安全控制、HTTP报文安全限制等。
应用入口层和API层是为SOCGI模块新增,用于hetao
搭载应用逻辑,实现应用动态库管理、应用开发部署规范、实例生命周期、应用编程上下文访问管理,以及SOCGI API、RESTful API编程接口库。
hetao
配置架构图表达了配置文件的内容结构布局和内部数据结构层次。
hetao
进程结构为“管理进程+多个工作进程”,可配置工作进程数量
,一般的,如果做纯静态资源服务器配置为服务器CPU核数,如果做应用服务器配置为CPU核数*2。
CPU亲和性
用于把某个进程绑定在某个CPU核上,尽量减少CPU切换上下文开销,提高性能。
Accept锁
用在多个工作进程在接受TCP新连接时的防止旧版本操作系统惊群现象,并设计了进程间调配TCP连接数均衡算法,一般情况下可不用开启。
日志文件
分两类:全局调试日志和网站日志。全局调试日志文件名默认为error.log
,记录着内部处理和数据流转的动作细节和状态信息,根据日志等级过滤输出,为内部调试和生产报错提供全局跟踪。网站日志根据不同网站配置为不同的日志文件,默认名为access.log
,建议名为access_(网站域名).log
,记录着网站的所有HTTP访问记录,为数据分析和攻击防御提供专向信息。
安全限制
用于TCP连接、HTTP会话、HTTP报文的安全控制,防御攻击。
TCP选项
用于TCP功能选项,比如改善延迟。
HTTP选项
用于HTTP功能选项,比如启用HTTP报文压缩、超时。
错误页面
用于HTTP出现可控错误时导向页面文件名配置。每个HTTP错误码对应一个错误页面文件名。
媒体类型描述表
配置了页面文件名扩展名与媒体类型描述之间的关系,用于组织HTTP响应头选项Content-type
。每个文件扩展名对应一个媒体类型描述。
hetao
允许创建多个侦听地址
,每个侦听地址
上运行多个网站域
(又称虚拟主机
),每个网站域
有独立的域名
、本地文档根目录
、索引文件列表
、访问日志文件名
,还有一组域名重定向
配置、重写URI
配置、代理转发
配置、SOCGI
配置。代理转发
即转发HTTP请求到下游服务器(一般为应用服务器),通过比较URI文件扩展名与代理转发资源文件扩展名
来确定HTTP请求是否需要代理转发,然后使用负载均衡算法
在一组下游服务器地址
中挑选一个进行转发。SOCGI
用于hetao
把HTTP请求转交给指定的应用动态库来处理,通过比较URI文件扩展名与SOCGI资源文件扩展名
确定当前HTTP请求是否需要交给应用动态库处理,如果配置成空则达成RESTful效果,socgi应用动态库文件名
设置应用动态库文件名。
hetao
允许网站域
下面再细分URI子配置
下挂各自的域名重定向
、重写URI
、代理转发
和SOCGI
配置,比如不同URI对应不同的代理转发下游服务器集群或应用动态库文件。
hetao
进程架构由一个监视进程和多个工作进程组组成,监视进程负责监视工作进程,一旦工作进程崩溃则重新创建一个新的工作进程。每个工作进程由一个定时器线程和一个工作线程组成。
定时器线程负责执行一些定时任务,比如定期获取时间信息并刷新时间缓存。
工作线程负责创建事件总线并处理侦听TCP连接事件、收发上下游HTTP通讯数据事件和文件系统文件变动事件,当接收完整HTTP请求后处理从文件系统读取资源文件或触发代理转发或装载应用动态库处理。
当启用SOCGI功能时,完整HTTP请求会被交付给应用动态库处理,动态库调用hetao SOCGI API
获取HTTP请求信息,做业务处理,然后调用hetao SOCGI API
组织HTTP响应信息,最后hetao
把HTTP响应发回给HTTP请求方。
hetao
在SOCGI接口中还提供了hetao RESTful API
,允许应用快速创建RESTful应用。
Web服务器的核心是HTTP解析器,其功能和性能决定Web服务器质量。
hetao采用我之前自研的HTTP解析器开源项目fasterhttp作为其HTTP解析核心。fasterhttp的目标是提供一个流式、支持完整HTTP协议v1.x、极高性能的HTTP解析器,更重要的是它与使用者之间功能边界清晰,专注做好组装和解析HTTP协议报文,不负责通讯管理、通讯模型和进程/线程框架等,方便嵌入到各式各样的场景中使用,且性能十分突出,可能是世界上最快的通用HTTP解析器。目前该解析器已用在了很多重要地方,比如某城商行核心系统新一代中间件。有关fasterhttp具体参见gitee或github。
fasterhttp使用十分简单:
创建HTTP会话时调用函数CreateHttpEnv
创建HTTP环境。
由于hetao采用非堵塞多路复用通讯模型,HTTP请求阶段,服务端反复调用函数ReceiveHttpRequestNonblock
读socket数据并流式解析掉,如果HTTP协议报文还未收完整则函数返回FASTERHTTP_INFO_NEED_MORE_HTTP_BUFFER
,如果收完整则函数返回0
,返回其它值则表示出现错误。
当接收完整HTTP请求后,应用可调用GetHttpHeaderPtr_METHOD
、GetHttpHeaderPtr_URI
、QueryHttpHeaderPtr
、GetHttpBodyPtr
等函数获得请求信息。
应用处理完自己的业务逻辑后,调用FormatHttpResponseStartLine
、GetHttpResponseBuffer
、Strcpy*HttpBuffer
、Strcat*HttpBuffer
、Memcat*HttpBuffer
等函数构造HTTP响应报文,然后进入HTTP响应阶段。
HTTP响应阶段,服务端当出现可写事件时调用函数SendHttpResponseNonblock
写数据到socket,如果一次写不完则返回FASTERHTTP_INFO_TCP_SEND_WOULDBLOCK
,如果写完则返回0
,返回其它值则表示出现错误。
HTTP请求响应结束后,调用函数ResetHttpEnv
重置HTTP环境,或者调用函数DestroyHttpEnv
销毁环境。
当启用代理转发时,HTTP请求阶段,hetao
代理方作为客户端反复调用函数SendHttpRequestNonblock
转发HTTP请求到下游服务器,直到发送完整HTTP请求,切换为HTTP响应阶段。
HTTP响应阶段,hetao
代理方作为客户端反复调用函数ReceiveHttpResponseNonblock
接收HTTP响应直至收完,然后开启hetao
代理方响应HTTP上游请求方通讯过程。
hetao
监视进程初始化时会创建一个内部环境结构HetaoEnv
,该结构包含配置文件信息,如媒体类型描述表、侦听地址,还包含运行中所需的所有信息。创建工作进程时为每个工作进程继承一份,在内部各个层次各个模块中传递该结构,简化代码设计。
内部环境结构具体包含信息如下:
监视进程初始化内部环境HetaoEnv
时,分配一块大内存用以存放进程信息结构数组,每个工作进程写自己所属结构单元,监视进程可以随时通过结构数组查询工作进程状态,如进程PID
,每个工作进程也能通过结构数组访问其它工作进程状态,如目前HTTP会话数量
。
监视进程初始化内部环境HetaoEnv
时,装载配置文件中的媒体类型描述表
构建媒体类型哈希表
,响应每一个HTTP请求时,根据请求资源文件扩展名查询媒体类型哈希表,组织HTTP响应头选项Content-type
。
因为媒体类型在装载配置时就已经确定数量,后面使用中只有查询,且要求快速查询,所以采用哈希表。
监视进程初始化内部环境HetaoEnv
时,预分配哈希空间用以存放IP限制信息链表数组,哈希单元数量为2^16个,如果发生哈希冲撞则哈希节点上追加链表节点。
因为维护IP限制信息要求快速,所以采用哈希表,又因为IP数量较多,全放在哈希表中占用内存很大,故采用哈希表+链表的二级数据结构。
hetao
处理每个静态资源的HTTP请求时,会把文件数据在内存中缓存一份,下次再有相同资源的请求到来时,直接从内存获取,以降低HTTP处理时间。
每缓存一个静态资源文件,都会在静态资源内容缓存链表中追加一个节点,节点内含路径文件名
、文件数据
、gzip压缩的文件数据
和deflate压缩的文件数据
,同时挂接到文件系统监视树和文件名树上,注册文件系统inotify事件,后续一旦发生文件变动,文件系统会主动通知hetao
,hetao
从监视树定位文件做清理。
因为静态资源文件缓存数量不定,所以采用链表,又为了加快后续相同静态资源文件查询效率,加了文件名树,又因为使用了文件系统inotify,所以加了文件系统监视树,最后由于Windows操作系统上用WIN32 API ReadDirectoryChangesW
和完成端口实现文件系统监视,复用了文件名树,也不能用文件系统监视树代替链表存放数据。
监视进程初始化内部环境HetaoEnv
时,把所有侦听地址装载为链表,每个侦听地址信息结构对应配置文件信息结构。
因为域名重定向信息数量在装载配置时就已经确定,所以采用哈希表存放,以提高查询性能。
因为原URI配置支持正则表达式,URI重写在处理每一个HTTP请求时只能遍历所有配置,所以采用链表存放。
因为代理转发负载均衡算法支持轮询和最少连接数,下游服务器信息存放就采用链表,再创建一棵最少连接服务器树用作同步排序。
配置文件中的配置项直接装载到SOCGI子环境中,同时定义了动态库打开句柄
和动态库入口函数指针
用于缓存打开信息,一次打开重复使用,如果处于更新目标需要重打开,目前只能通过重启或优雅重启hetao
来实现,后面会考虑引入文件系统事件监视自动重新打开。
website下的redirect、rewrite、forward、socgi是针对该website所有URI,hetao还支持细分URI的redirect、rewrite、forward、socgi。比如可以针对某个URI启用重写URI,针对某个URI设置不同的代理转发下游服务器集群网址,针对某个URI装载不用的应用动态库处理HTTP请求等。
每个TCP连接到来时都会分配一个HTTP会话结构,TCP断开时回收该结构,为了提高性能,采用预分配HTTP会话块,一个块是一个HTTP会话结构数组,当一个块中所有HTTP会话结构都被分配完时,申请一个新块,即批量申请一批HTTP会话结构,当一个块中还有未用的HTTP会话结构,则直接分配走,当一个块中所有HTTP会话结构都为未用,释放该会话块。
每个HTTP会话块中还创建一个已使用和未使用HTTP会话链表与之对应,用于关联同一个块中的单元。
全局创建一颗已使用HTTP会话活跃超时时间戳树和累计超时时间戳树,同步排序,用于快速查询即将超时的HTTP会话。活跃超时指连续没有数据收发的超时,累计超时指每次HTTP请求和响应总时间超时。
HTTP会话结构中包含网络地址信息、HTTP报文环境、SSL握手环境,还有代理转发相关信息。
命令管道会话结构是一个空结构,用于配合事件总线注册需要提供会话结构。
hetao
启动后,装载配置文件,初始化内部环境,转换为守护进程,监视进程创建工作进程组(还有命令管道、事件总线等)并监视其异常,一旦崩溃则重启之。
每个工作进程创建一个定时器线程和一个工作线程。
定时器线程负责每秒获取当前时间戳并格式化时间字符串,提供给日志等模块缓存使用,避免大量获取当前时间操作和格式化字符串操作。
工作线程把命令管道事件和文件系统事件订阅注册到事件总线,进入事件循环,每次等待、拉取和处理一批事件。每批事件处理前检查如果定时器线程刷新过时间戳,清理一批活跃超时和累计超时的现存HTTP会话。
从事件总线中等待、取出一批事件,依次处理每个事件。
事件主要包括网络IO事件、文件系统订阅事件和命令管道事件。
单线程的多路复用的事件处理都采用非堵塞、快速处理为原则,每个事件处理不能耗费太长时间,否则会卡住线程导致暂停对外提供服务,但单线程绑定CPU核能减少CPU切换时间片、IO操作也往往大大慢于本地计算,最终能提高程序处理并发度和吞吐。
初始化时,侦听会话订阅可读事件注册进事件总线中,TCP新连接被接受后也会订阅HTTP会话的可读事件注册进事件总线中。
所以判断事件分支处理:
OnAcceptingSocket
,接受TCP新连接,向HTTP会话管理模块申请一个HTTP会话,订阅可读事件进事件总线,如果启用了HTTPS,设置SSL开始握手标志并初次调用函数OnAcceptingSslSocket
。OnAcceptingSslSocket
继续接受客户端SSL握手;如果设置了转发SSL握手标志但还未SSL握手服务端完成,调用通讯模块函数OnConnectingSslForward
继续发起SSL握手服务端;如果设置了代理转发标志,调用通讯模块函数OnReceivingSocket
继续接收转发服务端响应数据;其它情况,调用通讯模块函数OnReceivingSocket
继续接收客户端请求数据。OnAcceptingSslSocket
继续接受客户端SSL握手;如果设置了SSL转发标志但还未SSL握手服务端完成,调用通讯模块函数OnAcceptingSslSocket
继续接受客户端SSL握手;如果设置了代理转发标志,当转发状态为连接中,调用通讯模块函数OnConnectingForward
继续转发连接服务端,当转发状态为连接完成,调用通讯模块函数OnSendingForward
继续转发请求数据到服务端;其它情况,调用通讯模块函数OnSendingSocket
继续发送应数据到客户端。SetHttpSessionUnused
回收HTTP会话。OnConnectingForward
测试转发连接状态并做相应处置;其它情况,调用HTTP会话管理模块函数SetHttpSessionUnused
回收HTTP会话。SetHttpSessionUnused
回收HTTP会话。HTTP静态资源请求到来时,hetao
会读取文件并响应回去,同时把静态资源文件缓存在内存中,并订阅该文件的文件系统变动事件。后续一旦发生该文件的编辑、改名、删除等变化,文件系统会主动通知hetao
清理其缓存。
初始化时,命令管道订阅可读事件注册进事件总线。当监视进程想要通知工作进程做一些事情时,会通过命令管道发送命令字节,工作线程做相应处理。
当网络IO事件接收到完整HTTP报文后,进入处理HTTP请求,处理分八个环节:
Host
在配置中查找对应的虚拟主机(网站域)。静态资源文件缓存是为了提高服务端HTTP性能而引入的一种机制,第一次访问静态资源文件时同时把文件数据缓存在内存中,下次直接读取内存即可,由于存在内存和文件系统一致性问题,故引入文件系统事件订阅来让文件系统监视和主动通知服务端清理缓存。
静态资源文件缓存管理分三个阶段。
创建缓存阶段:
hetao
。hetao
向文件缓存查询,没有找到缓存的文件数据。hetao
向文件系统读取文件数据。hetao
把文件数据写入缓存。hetao
向文件系统订阅注册该文件变动事件。hetao
返回文件数据响应给浏览器。使用缓存阶段:
hetao
。hetao
向文件缓存查询,找到缓存的文件数据。hetao
返回文件数据响应给浏览器。清理缓存阶段:
hetao
。hetao
清理缓存中该文件数据。在HTTP请求处理过程中,用户希望用自己的逻辑替代Web服务器的逻辑,hetao
支持搭载应用动态库,在HTTP请求处理的某个环节,如果应用动态库中存在相应函数,则执行该函数,替代hetao
实现细节。这些函数分为两类:入口函数和HTTP信息API函数,入口函数又分生命周期管理函数、和HTTP处理环节入口函数。
InitHttpApplication
用于Web服务器启动时装载应用动态库后调用,一般做创建业务环境。
CleanHttpApplication
用于Web服务器停止时卸载应用动态库前调用,一般做销毁业务环境。
RedirectHttpDomain
用户自定义域名重定向逻辑。
RewriteHttpUri
用户自定义重写URI逻辑。
BeforeProcessHttpResource
HTTP请求处理前用户自定义逻辑,比如无HTTP体报错信息的HTTP请求头选项通证权限检查逻辑。
ProcessHttpResource
用户HTTP自定义请求处理逻辑,比如报错信息方HTTP体的HTTP请求头选项通证权限检查逻辑。
SelectForwardServer
用户自定义选择下游服务器。(待实现)
CallHttpApplication
调用用户应用动态库逻辑(业务处理逻辑)。
GetHttpResource
用户自定义组织HTTP资源文件数据逻辑。
AfterProcessHttpResource
HTTP请求处理后用户自定义逻辑。
SOCGISetUserData
和SOCGIGetUserData
用于向HTTP上下文环境中设置和取出用户自定义数据,在动态库实例多个函数中共享信息时使用。
SOCGIGetConfigPathfilename
用于把Web服务器配置中的动态库附带配置文件名传递给应用。
SOCGIGetHttpHeaderPtr_METHOD
、SOCGIGetHttpHeaderPtr_URI
、SOCGIGetHttpHeaderPtr_VERSION
、SOCGIQueryHttpHeaderPtr
、SOCGIGetHttpBodyPtr
等用于让应用获取HTTP请求信息。
SOCGIFormatHttpResponse
用于让应用组织HTTP响应信息。
hetao
在SOCGI层上又实现了REST应用控制器。
hetao
REST应用控制器用当前HTTP请求到预配置的REST配置表中查询符合HTTP方法、URI条件的路由条目,调用对应的入口函数实现业务处理逻辑。业务开发方只要直接编写业务逻辑代码,通过配置挂接入口即可,分拣路由由REST应用控制器负责。
RESTCreateRestServiceControler
用于根据路由配置表构造一个RESTful控制器。
RESTDispatchRestServiceControler
用于根据当前HTTP请求和路由配置表分拣到对应的业务逻辑入口。
RESTDestroyRestServiceControler
用于销毁RESTful控制器。
HttpApplicationContext
用于在业务逻辑中获取HTTP上下文环境。
RESTGetHttpMethodPtr
、RESTGetHttpUriPtr
、RESTGetHttpUriPathsCount
、RESTGetHttpUriPathPtr
、RESTGetHttpUriQueriesCount
、RESTGetHttpUriQueryKeyPtr
、RESTGetHttpUriQueryValuePtr
、RESTGetHttpRequestBodyPtr
等用于RESTful代码中让应用获取HTTP请求信息。
RESTFormatHttpResponse
用于RESTful代码中让应用组织HTTP响应信息。
如果启用配置项tcp_options.nodelay
(0为禁用,1为启用;默认启用),一旦有数据从hetao
提交到操作系统就会立即发送网络,能大幅降低TCP通讯延迟,如果禁用,将等待一会儿凑后面的数据合并延迟发送,能增加系统吞吐量。
如果启用配置项tcp_options.linger
(-1为禁用,大于等于0为启用;默认禁用),当发送缓冲区还有数据待发送时,禁用会等待发送完再关闭TCP,如果启用则会等待一段时间。
如果启用配置项http_options.compress_on
(0为禁用,1为启用;默认启用),按浏览器建议做压缩,否则无视浏览器建议统统不压缩。
hetao
作为服务端接收发送两个字节之间不能停顿http_options.timeout
(默认为30)秒,否则超时断开。
hetao
作为服务端处理整个HTTP请求和响应总耗时不能大于http_options.elapse
(默认为60)秒,否则超时断开。
hetao
作为客户端做代理转发时一旦连不上下游服务器,就禁用该服务器http_options.forward_disable
(默认为60)秒。
在低版本操作系统中,多个进程/线程在事件总线上同时等待侦听事件会造成惊群现象,hetao
为了避免这个问题设计了侦听轮转机制:每次只有一个工作进程处于接受下一批TCP新连接状态,处理完后挑选一个目前HTTP会话最少的工作进程担此重任。这样还能使各个工作进程处理的HTTP会话数更均衡。
hetao
对缓存的静态资源文件大小存在限制,当文件大小小于limits.max_file_cache
(默认1MB)才予以缓存,防止大文件占用大内存。
hetao
对累计最多HTTP会话存在限制,当保持的HTTP会话数量到达limits.max_http_session_count
时会拒绝TCP新连接。
如果启用配置项limits.max_connections_per_ip
(-1为禁用;默认禁用),将限制每个IP最多建立HTTP会话数量。
如果启用配置项limits.max_headers_count
(0为禁用;默认128),每个HTTP请求报文的头选项数量不能超过该配置值,防御请求者恶意构造的HTTP报文。
如果启用配置项limits.max_headers_length
(0为禁用;默认4KB),每个HTTP请求报文的头大小不能超过该配置值,防御请求者恶意构造的HTTP报文。
如果启用配置项limits.max_header_content_length
(0为禁用;默认4MB),每个HTTP请求报文的体大小不能超过该配置值,防御请求者恶意构造的HTTP报文。
目前hetao
支持两种负载均衡算法:轮询算法和最少连接数算法。
要开启负载均衡,在配置文件listen.website.forward.forward_type
配置要代理转发下游服务器的HTTP资源文件扩展名。
listen.website.forward.forward_rule
配置轮询算法"R",在listen.website.forward.forward_server
配置下游服务器地址数组,当代理转发触发时,hetao
将依次轮询挑选下游服务器,第一次挑第一个,第二次挑第二个,挑完最后一个后重新挑第一个。
listen.website.forward.forward_rule
配置轮询算法"L",在listen.website.forward.forward_server
配置下游服务器地址数组,当代理转发触发时,hetao
将挑选一个当前保持转发连接最少的一个下游服务器。
热重载可以实现hetao
使用新版主程序或修改后的配置文件工作而不用对外停止服务。
其技术原理为发送信号SIGUSR2给老版监视进程,老版监视进程复制成一个新进程,重新装载配置文件,新进程的代码映像来自于新版主程序文件,如果有相同侦听端口也会被继承下来,然后新版监视进程照旧创建工作进程组,这时新来两版监视器和工作进程同时工作,确认新版工作正常后发送信号SIGTERM给老版监视进程,让其和老版工作进程结束。期间始终对外提供服务,没有无服务时间窗口。
hetao
的官方Windows工程构建IDE是VS2008,解决方案文件src/vc2008/vc2008.sln
负责构建主程序文件hetao
、所有依赖库和辅助工具,解决方案文件test/vc2008/vc2008.sln
负责构建所有测试用应用动态库。
由于Windows和Linux底层提供的功能和接口差异,hetao
的代码实现上采用条件编译方式兼顾两种操作系统的接口调用,以下为主要功能的接口差异:
功能 | Linux实现 | Windows实现 |
---|---|---|
注册为服务 | (无) | OpenSCManager,CreateService, ChangeServiceConfig2 |
创建进程 | fork | hetao()-> CreateProcess("--child")-> hetao |
结束合并进程 | waitpid | OpenProcess, WaitForSingleObject |
创建线程 | pthread_create | CreateThread |
事件总线 | epoll_create,epoll_ctl | CreateIoCompletionPort, WSAIoctl |
通讯收发 | socket,recv,send,close | WSASocket, WSARecv, WSASend, closesocket |
文件系统事件接口 | inotify | ReadDirectoryChangesW |
共享内存 | shmget | CreateFileMapping, MapViewOfFile |
装载动态库 | dlopen,dlsym,dlclose | LoadLibrary, GetProcAddress, FreeLibrary |
hetao
Linux版和Windows版都使用OpenSSL来实现HTTPS的SSL握手。
当hetao
作为服务端接受客户端SSL握手时,调用函数SSL_new
创建SSL环境,接着调用函数SSL_set_fd
关联TCP套接字和SSL环境,然后调用函数SSL_set_accept_state
设置我是服务端,进入基于事件循环的迭代非堵塞握手处理流程。
基于事件循环的迭代非堵塞握手处理流程中任何方向的握手分组都调用函数SSL_do_handshake
来处理,然后调用函数SSL_get_error
询问下一步该发送还是接收,注册进事件总线,迭代等待处理事件,直到SSL握手最终完成。对方交换证书分组时调用函数SSL_get_peer_certificate
获取。
当hetao
作为客户端做HTTP请求转发时,调用函数SSL_new
创建SSL环境,接着调用函数SSL_set_fd
关联TCP套接字和SSL环境,然后调用函数SSL_set_connect_state
设置我是客户端,进入基于事件循环的迭代非堵塞握手处理流程。
hetao
提供了一个简易版软件,无需配置文件的运行起一个静态资源Web服务器minihetao
,便于知道网站根目录后快速用浏览器访问网站。
Linux版minihetao
运行命令行语法为:
USAGE : minihetao wwwroot
Windows版minihetao
有一个对话框窗口
点击选定一个网站根目录wwwroot
后,点击按钮"Running"即可快速创建一个Web服务端,点击按钮"Stop"停止,按钮"Hide"缩小窗口到系统托盘,按钮"Exit"退出。
除了通过窗口创建Web服务端外,还有另一种更快速的创建方法。点击按钮"Registe folder popup-menu"注册monihetao
到右键菜单中,然后在“我的电脑”里任意目录点击右键弹出快捷菜单,选择“minihetao”,就自动打开minihetao
窗口,自动填充wwwroot,自动点击按钮“Running”,用户要做的只有打开浏览器访问网址“http://localhost/”,是不是很方便。
一款优秀的软件肯定需要完备的辅助工具,hetao
自带了两个辅助工具用于配置文件格式检查和应用动态库符号完整性检查。
hetaocheckconf
和Nginx
怪异的需要额外学习成本的配置文件语法格式不同,hetao
配置文件采用标准JSON格式,但为了在启动或热重载前发现配置错误,hetao
提供了辅助工具hetaocheckconf
来校验配置文件语法合法性,其运行命令行语法为:
USAGE : hetaocheckconf hetao.conf
hetaocheckso
hetao
支持装载应用动态库用于HTTP请求时的业务逻辑处理,为了避免在运行时爆出动态库符号不完整问题,工具hetaocheckso
可以在开发期检查动态库,毕竟构建时发现问题总好于测试业务时。
在开发期构建完应用动态库后立即调用工具hetaocheckso
尝试挂接应用动态库,Linux上可以嵌入到makefile中,Windows可以配置到IDE中。
hetaocheckso
运行命令行语法为:
USAGE : hetaocheckso *.socgi [-r]
*.socgi
就是动态库文件名,无论绝对路径还是相对路径,反正站在当前目录里能让hetaocheckso
打开应用动态库就行。
-r
是可选参数,当存在时,如果检查应用动态库不通过(比如缺少符号),立即删除该动态库。这个参数是为了与构建工具配合,做到构建物提交出去前必须经过符号完整性校验。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。