72 Star 197 Fork 48

calvinwilliams / hetao

Create your Gitee Account
Explore and code with more than 5 million developers,Free private repositories !:)
Sign up
Clone or download
Cancel
Notice: Creating folder will generate an empty file .keep, because not support in Git
Loading...
README.md

高性能、功能完整、支持搭载应用的国产原创Web服务器(hetao)

版本修订

文档版本号 修订日期 修订人 修订内容
v1.0.0 2016-08-08 厉华 创建
v1.0.1 2016-08-17 厉华 新增章节 压测
v1.0.2 2016-08-20 厉华 修改章节 虚拟主机;新增章节 内部实现
v1.0.3 2016-08-28 厉华 新增章节 扩大系统限制
重新压测
v1.0.4 2016-09-01 厉华 补充 配置通览和说明
v1.0.5 2016-09-04 厉华 重写 网站配置
v1.0.6 2016-09-06 厉华 重写 配置通览和说明
重写 网站配置
v1.0.7 2016-09-07 厉华 重写 配置通览和说明
v1.0.8 2016-09-08 厉华 重写 配置文件
v1.0.9 2016-09-10 厉华 修改 编译安装;修改 配置文件
新增 配置文件格式检查工具
v1.0.10 2016-09-11 厉华 增加 hetao/0.7.0压测
v1.0.11 2016-09-18 厉华 概述中新增 hetao安全机制
随hetao/0.8.0增加配置项说明
v1.0.12 2016-10-07 厉华 新增 WINDOWS(VS2008编译工程)
新增 选择hetao的理由
v1.0.13 2016-10-13 厉华 新增 配置文件最小化
新增 用minihetao直接启动,无需配置文件
v1.0.14 2016-10-15 厉华 配置文件中的template改成new_uri
v1.0.15 2016-10-17 厉华 新增 WINDOWS二进制包安装;完善 用minihetao直接启动,无需配置文件
v1.0.16 2016-10-21 厉华 新增 配置包含文件
v1.0.17 2016-10-22 厉华 新增 重定向域名功能
v1.0.18 2020-03-15 厉华 新增 扩展开发;新增 配置文件 中的 SOCGI
v1.0.19 2020-04-05 厉华 跟随hetao v0.1.0.0调整内容

1. 前言

2010年,我给我行新核心项目研发了核心后台应用服务平台,采用了定制通讯协议,几年使用下来无论与第三方业务系统(大多数是JAVA体系)对接、还是协议效率等方面都感受不好,趁着今年发起研发新一代核心后台应用服务平台契机,重新审视通讯协议的设计,最终选择了HTTP/1.1。

于是我花时间研发了高性能HTTP解析器fasterhttp,在编写示例时想,既然有了HTTP解析器为何不研发一个静态页面Web服务器呢?于是结合文件系统主动通知机制inotify研发了htmlserver,改善了传统的被动轮询更新的缓存设计,性能比号称世界最快的Nginx还要快好几倍,我备受鼓舞。

htmlserver发布后受到了广大网友的巨大反响,除了攻击名字幼稚、版本号和认为我压测数据作弊的喷子外,还是有不少网友提出了中肯的意见和建议,当然避免不了和Nginx的功能比较,于是,原只是支持静态页面的研发目标又一次“被逼”扩展为还要支持动态页面、反向代理负载均衡。(好深的坑啊)

原名字已不适合,于是我重新创建了一个项目hetao,hetao v0.1.0从htmlserver v1.0.0移过来继续研发。

故事还在继续...

题外话,我原以为阿里这么强大的研发能力完全应该自研Web Server,因为只有自研才符合大公司的定制之道,遗憾的是Tengine竟然是基于Nginx改造的,不管怎么样,hetao是我一个人利用工作之余从底层完全自研(基本功能在3个月内完成) ^_^

2. 概述

hetao是一款国人原创研发的开源的C语言实现的支持高并发、超高性能Web服务器,使用高性能HTTP解析器fasterhttp作为其解析核心,在开启Keep-Alive和gzip压缩(现代浏览器默认开启)时性能比nginx约快3倍。如此高性能得益于轻巧的架构设计和采用Inotify文件变化主动通知缓存机制,把大量静态文件尽可能缓存在内存直接读取,比传统的轮询式检查文件机制避免了大量存储IO。

hetao的设计理念是快速、稳定和完整。没有完全采用apache或nginx纯模块化架构,因为大多数人使用webserver一般都会把所有模块都打上,除了动态内容模块(如mod_php),很少见到有人特意去组装模块,那还不如直接全部编译在一起算了,使用简单,避免了管理员或运维人员面对过多选择带来的学习成本。当你需要本地定制化时,直接改代码吧,因为它就是开源的嘛。hetao只有在动态内容上才设计了模块接口,以适应各种各样的语言架构和开发者。

2.1. hetao功能

  • 支持主流操作系统Linux(基于epoll)、WINDOWS(基于IOCP)
  • 支持HTTP/1.0、HTTP/1.1
  • 支持通讯超时控制
  • 支持多侦听端口
  • 支持多虚拟主机
  • 支持自定义错误页面
  • 支持自定义缺省index文件
  • 支持自适应Keep-Alive
  • 支持自适应gzip、deflate压缩
  • 支持HTTPS
  • 支持反向代理负载均衡(目前支持轮询、最少连接数算法),支持HTTP与HTTPS互转
  • 支持改写URI
  • 支持重定向域名
  • 支持优雅重启/重载配置,重启期间完全不中断对外服务
  • 支持工作进程绑定CPU
  • 支持进程崩溃后自动重启
  • 支持搭载socgi应用
  • 在socgi上搭载RESTful应用

2.2. hetao安全机制

  • HTTP请求报文合法性校验
  • HTTP报文最大请求头限制和最多请求头选项限制
  • 活跃超时控制(防止僵尸连接)和累积超时控制(防止慢速攻击)
  • 每个IP连接数限制
  • 全局最大连接数限制
  • 最大单个文件缓存大小

2.3. 选择hetao的理由

  • hetao在Linux上的综合性能约比Nginx还要快三倍,尤其适合中小型静态文件
  • hetao是众多开源Web服务器中在WINDOWS版本唯一全部采用IOCP模型。Apache的WINDOWS版本是传统的Leader-Follow多进程模型,Nginx则是多线程select模型(玩具?)
  • hetao配置文件采用JSON标准格式,简洁易写,而且支持行注释和块注释。Apache配置格式比较复杂,Nginx配置格式多变怪异且不支持块注释
  • hetao设计精炼,代码结构简洁易读,代码量小,易于改造
  • hetao是中国国产原创,作者可随时联系交流calvinwilliams@163.com,中文资料较多
  • Linux版提供指定目录直接创建Web站点,WINDOWS版提供了右键目录直接创建Web站点,便于测试页面
  • hetao提供了socgi层搭载应用动态库实现应用逻辑,并在socgi基础上实现了一个RESTful应用控制器

3. 安装

3.1. Linux源码包编译安装

3.1.1. 下载源码

http://gitee/calvinwilliams/hetao

https://github.com/calvinwilliams/hetao

git clone或直接下载zip包到本地解开

3.1.2. 配置安装路径

确认安装路径,以下为默认安装到系统目录里,如果要安装到其它目录,请修改src/makefile.Linux中的

...
###### 目标文件、安装目录配置区
NOCLEAN_DIRINST_NOCOVER=        /var/hetao
NOCLEAN_DIRINST2_NOCOVER=       /var/hetao/log
BIN                     =       hetao
BININST                 =       /usr/local/bin
NOCLEAN_OBJ             =       ../bin/hetao.sh
NOCLEAN_OBJINST         =       /usr/local/bin
NOCLEAN_OBJ_NOCOVER     =       ../conf/hetao.conf
NOCLEAN_OBJINST_NOCOVER =       /etc/hetao
NOCLEAN_OBJ2_NOCOVER    =       ../certs/*
NOCLEAN_OBJINST2_NOCOVER=       /etc/hetao/certs
NOCLEAN_OBJ3_NOCOVER    =       ../www/*
NOCLEAN_OBJINST3_NOCOVER=       /var/hetao/www
HDER                    =       hetao_socgi.h hetao_rest.h LOGC.h
HDERINST                =       /usr/include/hetao
LIB                     =       libhetao_util.so libhetao_socgi.so
LIBINST                 =       /usr/lib64
...

3.1.3. 编译安装

在源码根目录执行编译命令,Linux环境构造文件为makefile.Linux

$ sudo make -f makefile.Linux clean install
make[1]: 进入目录“/home/calvin/src/hetao/src”
rm -f IDL_hetao_conf.dsc.o
rm -f Util.o
rm -f Config.o
rm -f Envirment.o
rm -f MonitorProcess.o
rm -f WorkerProcess.o
rm -f WorkerThread.o
rm -f TimerThread.o
rm -f OnAcceptingSocket.o
rm -f OnAcceptingSslSocket.o
rm -f OnReceivingSocket.o
rm -f OnSendingSocket.o
rm -f ProcessHttpRequest.o
rm -f OnConnectingForward.o
rm -f OnConnectingSslForward.o
rm -f OnSendingForward.o
rm -f OnReceivingForward.o
rm -f VirtualHostHash.o
rm -f ListenSession.o
rm -f HttpSession.o
rm -f HtmlCacheSession.o
rm -f HtmlCacheEventHander.o
rm -f HtmlCacheWdTree.o
rm -f HtmlCachePathfilenameTree.o
rm -f HttpSessionTimeoutTree.o
rm -f HttpSessionElapseTree.o
rm -f LeastConnectionCountTree.o
rm -f MimeTypeHash.o
rm -f RewriteUri.o
rm -f RedirectDomain.o
rm -f IpLimitsHash.o
rm -f list.o
rm -f LOGC.o
rm -f fasterjson.o
rm -f fasterhttp.o
rm -f IDL_hetao_conf.dsc.o
rm -f Util.o
rm -f Config.o
rm -f HttpApplicationContext.o
rm -f RestServiceContext.o
rm -f RestServiceControler.o
rm -f list.o
rm -f rbtree.o
rm -f LOGC.o
rm -f fasterjson.o
rm -f fasterhttp.o
rm -f hetaocheckconf
rm -f hetaocheckso
rm -f minihetao
rm -f hetao
rm -f libhetao_util.so
rm -f libhetao_socgi.so
rm -f Config.o
rm -f Envirment.o
rm -f fasterhttp.o
rm -f fasterjson.o
rm -f hetaocheckconf.o
rm -f hetaocheckso.o
rm -f hetao.o
rm -f HtmlCacheEventHander.o
rm -f HtmlCachePathfilenameTree.o
rm -f HtmlCacheSession.o
rm -f HtmlCacheWdTree.o
rm -f HttpApplicationContext.o
rm -f HttpSessionElapseTree.o
rm -f HttpSession.o
rm -f HttpSessionTimeoutTree.o
rm -f IDL_hetao_conf.dsc.o
rm -f IpLimitsHash.o
rm -f LeastConnectionCountTree.o
rm -f ListenSession.o
rm -f list.o
rm -f LOGC.o
rm -f MimeTypeHash.o
rm -f minihetao.o
rm -f MonitorProcess.o
rm -f OnAcceptingSocket.o
rm -f OnAcceptingSslSocket.o
rm -f OnConnectingForward.o
rm -f OnConnectingSslForward.o
rm -f OnReceivingForward.o
rm -f OnReceivingSocket.o
rm -f OnSendingForward.o
rm -f OnSendingSocket.o
rm -f ProcessHttpRequest.o
rm -f rbtree.o
rm -f RedirectDomain.o
rm -f RestServiceContext.o
rm -f RestServiceControler.o
rm -f RewriteUri.o
rm -f TimerThread.o
rm -f Util.o
rm -f VirtualHostHash.o
rm -f WorkerProcess.o
rm -f WorkerThread.o
make[1]: 离开目录“/home/calvin/src/hetao/src”
make[1]: 进入目录“/home/calvin/src/hetao/test”
make[2]: 进入目录“/home/calvin/src/hetao/test/test_socgi_hello”
rm -f test_socgi_hello.o
rm -f test_socgi_hello.socgi
make[2]: 离开目录“/home/calvin/src/hetao/test/test_socgi_hello”
make[2]: 进入目录“/home/calvin/src/hetao/test/test_socgi_rest_hello”
rm -f test_socgi_rest_hello.o
rm -f test_socgi_rest_hello.socgi
make[2]: 离开目录“/home/calvin/src/hetao/test/test_socgi_rest_hello”
make[2]: 进入目录“/home/calvin/src/hetao/test/test_socgi_rest_full”
rm -f util.o
rm -f test_socgi_rest_full.o
rm -f GET_.o
rm -f GET_path1_.o
rm -f GET_path1.o
rm -f GET_path1_n_file.o
rm -f GET_path1_path2_.o
rm -f GET_path1_path2.o
rm -f GET_path1_path2_file1__key1_value1.o
rm -f GET_path1_path2_file2__key1_value1__key2_value2.o
rm -f GET_path1_path2_file3__.o
rm -f GET_path1_path2_file4__key1.o
rm -f GET_path1_path2_file5__key1_.o
rm -f GET_path1_path2_file6__key1__.o
rm -f GET_path1_path2_file7__key1___.o
rm -f GET_path1_path2_file.o
rm -f POST_path1_file.o
rm -f PUT_path1_file.o
rm -f DELETE_path1_file.o
rm -f test_socgi_rest_full.socgi
make[2]: 离开目录“/home/calvin/src/hetao/test/test_socgi_rest_full”
make[1]: 离开目录“/home/calvin/src/hetao/test”
make[1]: 进入目录“/home/calvin/src/hetao/src”
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c list.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c rbtree.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c LOGC.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c fasterjson.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c fasterhttp.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -o libhetao_util.so list.o rbtree.o LOGC.o fasterjson.o fasterhttp.o -shared -L. -lpcre -lpthread -lssl -lcrypto -lz -ldl 
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c HttpApplicationContext.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c RestServiceContext.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c RestServiceControler.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -o libhetao_socgi.so HttpApplicationContext.o RestServiceContext.o RestServiceControler.o -shared -L. -lpcre -lpthread -lssl -lcrypto -lz -ldl 
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c hetaocheckconf.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c IDL_hetao_conf.dsc.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c Util.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c Config.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -o hetaocheckconf hetaocheckconf.o list.o LOGC.o fasterjson.o fasterhttp.o IDL_hetao_conf.dsc.o Util.o Config.o libhetao_util.so -L. -lpcre -lpthread -lssl -lcrypto -lz -ldl 
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c hetaocheckso.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -o hetaocheckso hetaocheckso.o list.o LOGC.o fasterjson.o fasterhttp.o IDL_hetao_conf.dsc.o Util.o Config.o libhetao_util.so -L. -lpcre -lpthread -lssl -lcrypto -lz -ldl 
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c minihetao.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c Envirment.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c MonitorProcess.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c WorkerProcess.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c WorkerThread.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c TimerThread.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c OnAcceptingSocket.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c OnAcceptingSslSocket.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c OnReceivingSocket.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c OnSendingSocket.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c ProcessHttpRequest.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c OnConnectingForward.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c OnConnectingSslForward.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c OnSendingForward.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c OnReceivingForward.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c VirtualHostHash.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c ListenSession.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c HttpSession.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c HtmlCacheSession.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c HtmlCacheEventHander.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c HtmlCacheWdTree.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c HtmlCachePathfilenameTree.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c HttpSessionTimeoutTree.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c HttpSessionElapseTree.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c LeastConnectionCountTree.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c MimeTypeHash.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c RewriteUri.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c RedirectDomain.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c IpLimitsHash.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -o minihetao minihetao.o IDL_hetao_conf.dsc.o Util.o Config.o Envirment.o MonitorProcess.o WorkerProcess.o WorkerThread.o TimerThread.o OnAcceptingSocket.o OnAcceptingSslSocket.o OnReceivingSocket.o OnSendingSocket.o ProcessHttpRequest.o OnConnectingForward.o OnConnectingSslForward.o OnSendingForward.o OnReceivingForward.o VirtualHostHash.o ListenSession.o HttpSession.o HtmlCacheSession.o HtmlCacheEventHander.o HtmlCacheWdTree.o HtmlCachePathfilenameTree.o HttpSessionTimeoutTree.o HttpSessionElapseTree.o LeastConnectionCountTree.o MimeTypeHash.o RewriteUri.o RedirectDomain.o IpLimitsHash.o libhetao_util.so -L. -lpcre -lpthread -lssl -lcrypto -lz -ldl 
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -c hetao.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -o hetao hetao.o IDL_hetao_conf.dsc.o Util.o Config.o Envirment.o MonitorProcess.o WorkerProcess.o WorkerThread.o TimerThread.o OnAcceptingSocket.o OnAcceptingSslSocket.o OnReceivingSocket.o OnSendingSocket.o ProcessHttpRequest.o OnConnectingForward.o OnConnectingSslForward.o OnSendingForward.o OnReceivingForward.o VirtualHostHash.o ListenSession.o HttpSession.o HtmlCacheSession.o HtmlCacheEventHander.o HtmlCacheWdTree.o HtmlCachePathfilenameTree.o HttpSessionTimeoutTree.o HttpSessionElapseTree.o LeastConnectionCountTree.o MimeTypeHash.o RewriteUri.o RedirectDomain.o IpLimitsHash.o libhetao_util.so -L. -lpcre -lpthread -lssl -lcrypto -lz -ldl 
rm -f /usr/bin/hetaocheckconf
cp -rf hetaocheckconf /usr/bin/
rm -f /usr/bin/hetaocheckso
cp -rf hetaocheckso /usr/bin/
rm -f /usr/bin/minihetao
cp -rf minihetao /usr/bin/
rm -f /usr/bin/hetao
cp -rf hetao /usr/bin/
mkdir -p /usr/include/hetao
cp -rf hetao_socgi.h /usr/include/hetao/
cp -rf hetao_rest.h /usr/include/hetao/
cp -rf LOGC.h /usr/include/hetao/
mkdir -p /var/hetao
mkdir -p /var/hetao/log
rm -f /usr/bin/../bin/hetao.sh
cp -rf ../bin/hetao.sh /usr/bin/
mkdir -p /etc/hetao
cp -rf ../conf/hetao.conf /etc/hetao/
mkdir -p /etc/hetao/certs
cp -rf ../certs/gencert.sh /etc/hetao/certs/
cp -rf ../certs/server.crt /etc/hetao/certs/
cp -rf ../certs/server.csr /etc/hetao/certs/
cp -rf ../certs/server.key /etc/hetao/certs/
cp -rf ../certs/server.pem /etc/hetao/certs/
mkdir -p /var/hetao/www
cp -rf ../www/error_pages /var/hetao/www/
cp -rf ../www/index.html /var/hetao/www/
make[1]: 离开目录“/home/calvin/src/hetao/src”
make[1]: 进入目录“/home/calvin/src/hetao/test”
make[2]: 进入目录“/home/calvin/src/hetao/test/test_socgi_hello”
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -I/root/include -I/usr/include/hetao  -c test_socgi_hello.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -o test_socgi_hello.socgi test_socgi_hello.o -shared -L. -L/root/lib -L/usr/lib64 -lhetao_socgi 
hetaocheckso ./test_socgi_hello.socgi -r
OK
cp -rf test_socgi_hello.socgi /var/hetao/www/
make[2]: 离开目录“/home/calvin/src/hetao/test/test_socgi_hello”
make[2]: 进入目录“/home/calvin/src/hetao/test/test_socgi_rest_hello”
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -I/root/include -I/usr/include/hetao  -c test_socgi_rest_hello.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -o test_socgi_rest_hello.socgi test_socgi_rest_hello.o -shared -L. -L/root/lib -L/usr/lib64 -lhetao_socgi 
hetaocheckso ./test_socgi_rest_hello.socgi -r
OK
cp -rf test_socgi_rest_hello.socgi /var/hetao/www/
make[2]: 离开目录“/home/calvin/src/hetao/test/test_socgi_rest_hello”
make[2]: 进入目录“/home/calvin/src/hetao/test/test_socgi_rest_full”
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -I/root/include -std=gnu99 -I/usr/include/hetao  -c util.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -I/root/include -std=gnu99 -I/usr/include/hetao  -c test_socgi_rest_full.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -I/root/include -std=gnu99 -I/usr/include/hetao  -c GET_.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -I/root/include -std=gnu99 -I/usr/include/hetao  -c GET_path1_.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -I/root/include -std=gnu99 -I/usr/include/hetao  -c GET_path1.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -I/root/include -std=gnu99 -I/usr/include/hetao  -c GET_path1_n_file.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -I/root/include -std=gnu99 -I/usr/include/hetao  -c GET_path1_path2_.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -I/root/include -std=gnu99 -I/usr/include/hetao  -c GET_path1_path2.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -I/root/include -std=gnu99 -I/usr/include/hetao  -c GET_path1_path2_file1__key1_value1.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -I/root/include -std=gnu99 -I/usr/include/hetao  -c GET_path1_path2_file2__key1_value1__key2_value2.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -I/root/include -std=gnu99 -I/usr/include/hetao  -c GET_path1_path2_file3__.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -I/root/include -std=gnu99 -I/usr/include/hetao  -c GET_path1_path2_file4__key1.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -I/root/include -std=gnu99 -I/usr/include/hetao  -c GET_path1_path2_file5__key1_.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -I/root/include -std=gnu99 -I/usr/include/hetao  -c GET_path1_path2_file6__key1__.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -I/root/include -std=gnu99 -I/usr/include/hetao  -c GET_path1_path2_file7__key1___.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -I/root/include -std=gnu99 -I/usr/include/hetao  -c GET_path1_path2_file.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -I/root/include -std=gnu99 -I/usr/include/hetao  -c POST_path1_file.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -I/root/include -std=gnu99 -I/usr/include/hetao  -c PUT_path1_file.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -I/root/include -std=gnu99 -I/usr/include/hetao  -c DELETE_path1_file.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -o test_socgi_rest_full.socgi util.o test_socgi_rest_full.o GET_.o GET_path1_.o GET_path1.o GET_path1_n_file.o GET_path1_path2_.o GET_path1_path2.o GET_path1_path2_file1__key1_value1.o GET_path1_path2_file2__key1_value1__key2_value2.o GET_path1_path2_file3__.o GET_path1_path2_file4__key1.o GET_path1_path2_file5__key1_.o GET_path1_path2_file6__key1__.o GET_path1_path2_file7__key1___.o GET_path1_path2_file.o POST_path1_file.o PUT_path1_file.o DELETE_path1_file.o -shared -L. -L/root/lib -L/usr/lib64 -lhetao_socgi 
hetaocheckso ./test_socgi_rest_full.socgi -r
OK
cp -rf test_socgi_rest_full.socgi /var/hetao/www/
make[2]: 离开目录“/home/calvin/src/hetao/test/test_socgi_rest_full”
make[1]: 离开目录“/home/calvin/src/hetao/test”

没有报错的话就能编译出可执行文件hetao。也可以加上参数-j 10以加快编译速度。

编译输出最后一段提示本次配置预安装目标,确认后执行后面的安装命令。

执行安装命令:

$ sudo make -f makefile.Linux install
mkdir -p /var/hetao
mkdir -p /var/hetao/log
cp -rf hetao /usr/local/bin/
cp -rf ../bin/hetao.sh /usr/local/bin/
mkdir -p /etc/hetao
cp -rf ../conf/hetao.conf /etc/hetao/
mkdir -p /etc/hetao/certs
cp -rf ../certs/gencert.sh /etc/hetao/certs/
cp -rf ../certs/server.crt /etc/hetao/certs/
cp -rf ../certs/server.csr /etc/hetao/certs/
cp -rf ../certs/server.key /etc/hetao/certs/
cp -rf ../certs/server.pem /etc/hetao/certs/
mkdir -p /var/hetao/www
cp -rf ../www/error_pages /var/hetao/www/
cp -rf ../www/index.html /var/hetao/www/

安装过程做了如下事情:

  • 自动创建日志目录/var/hetao/log
  • 自动复制主执行程序hetao到/usr/local/bin/
  • 自动复制管理脚本hetao.sh到/usr/local/bin/
  • 自动复制缺省配置文件hetao.conf到/etc/hetao/
  • 自动复制示例证书文件到/etc/hetao/certs/
  • 自动复制示例首页文件到/var/hetao/www/
  • 自动复制自定义出错页面文件到/var/hetao/www/error_pages/
  • 自动复制开发头文件到/usr/include/hetao/
  • 自动复制开发库文件到/usr/lib64/

这样就安装好了!

3.1.4. 用缺省配置第一次启动并测试

使用自带脚本以缺省配置启动

$ su - root
# cd /etc/hetao 
# hetao.sh start

如果没有产生输出、/var/hetao/log/error.log没有产生WARN及以上等级日志的话表示启动成功。注意:缺省配置文件中的侦听端口为80。

可以看到进程,hetao进程结构由一个管理进程+n个工作进程组成

$ ps -ef | grep hetao | grep -v grep
root   14122     1  0 23:17 ? 00:00:00 hetao /home/calvin/etc/hetao.conf
root   14123 14122  0 23:17 ? 00:00:00 hetao /home/calvin/etc/hetao.conf

以及侦听端口

$ netstat -an | grep -w 80
tcp    0      0 0.0.0.0:80    0.0.0.0:*       LISTEN

自测一下

$ curl http://localhost/index.html
<!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>

恭喜您,启动成功!

使用自带脚本停止hetao

# hetao.sh stop

3.1.5. 用直接命令代替管理脚本

hetao.sh假设配置文件在/etc/hetao/hetao.conf

你也可以使用原始命令来启动hetao,命令行语法为:

# hetao
hetao v0.1.0.0 build Apr  5 2020 06:51:34
USAGE : hetao hetao.conf

3.1.6. 扩大系统限制

默认系统中单个进程最大可打开描述字只有1024个,肯定不能满足一个正式的Web服务器的需要,那么作为生产环境,一定要扩大系统限制。

一些推荐的系统限制设置放在conf/*,把文件内容追加到系统配置中,需要root权限。

conf/limits.conf.add -> /etc/security/limits.conf

*       soft    nofile  65536
*       hard    nofile  65536
*       soft    nproc   unlimited
*       hard    nproc   unlimited

conf/sysctl.conf.add -> /etc/sysctl.conf

fs.file-max=65536
net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_tw_recycle=1
net.ipv4.tcp_fin_timeout = 30 
net.ipv4.tcp_keepalive_time = 1200 
net.ipv4.ip_local_port_range = 1024 65000 
net.ipv4.tcp_max_tw_buckets = 5000

执行以下命令生效

sysctl -p

3.2. WINDOWS源码包编译安装(VS2008工程)

3.2.1. 创建安装目录结构

双击执行install_directories_before_compile_on_windows.bat将在\Program Files下创建目录hetao,以及子目录binconfwwwlog,并自动复制win\bin\*\Program Files\hetao\bin\目录,自动复制conf/hetao.conf.WINDOWS\Program Files\hetao\conf\hetao.conf,自动复制www/*\Program Files\hetao\www\

setup-win.png

3.2.2. 编译源码

打开VS2008解决方案文件src/vs2008/vc2008.sln,“生成”->"批生成"->“重新生成解决方案”,将自动编译出hetao、hetaocheckconf、hetaocheckso并自动复制到\Program Files\hetao\bin\

vs2008_complie.png

如需测试示例,打开VS2008解决方案文件test/vs2008/vc2008.sln,“生成”->"批生成"->“重新生成解决方案”,将自动编译出socgi和restful示例并自动复制到\Program Files\hetao\www\

3.2.3. 安装为服务

\Program Files\hetao\bin>hetao ../conf/hetao.conf --install-service

install_service.png

3.2.4. 用缺省配置第一次启动并测试

在WINDOWS服务中找到并启动“Hetao Service”,观察日志目录\Program Files\hetao\log

start_service.png

在浏览器里访问“http://localhost/”

web_browser_test.png

3.2.5. 卸载服务

\Program Files\hetao\bin>hetao ../conf/hetao.conf --uninstall-service

3.2.6. 使用minihetao快速测试网页

hetao自带小工具minihetao可以快速指定某目录为网站根路径并启动一个Web服务器。

运行\Program Files\hetao\bin\minihetao

minihetao.png

修改wwwroot路径\Program Files\hetao\www后点击按钮"Running"启动。

minihetao还支持直接右键目录弹出菜单启动,参见章节“用minihetao直接启动,无需配置文件”。

注意:环境变量HETAO_ERROR_LOG_PATHFILENAMEHETAO_ERROR_LOG_LEVELHETAO_ACCESS_LOG_PATHFILENAME作用于hetao装载配置文件前的日志输出,也作用于minihetao全程日志输出,可以作为调试方式。

在Windows上可以设置为:

HETAO_ERROR_LOG_PATHFILENAME=\Program Files\hetao\log\error.log
HETAO_ERROR_LOG_LEVEL=DEBUG
HETAO_ACCESS_LOG_PATHFILENAME=\Program Files\hetao\log\access.log

4. 配置文件

4.1. 配置通览和说明

安装时复制的配置文件为缺省配置,可根据实际情况调整,如侦听端口、server配置等。

$ cat conf/hetao.conf
{
	"worker_processes" : 1 ,
	"cpu_affinity" : 1 ,
	"accept_mutex" : 1 ,
	
	"error_log" : "/var/hetao/log/error.log" ,
	"log_level" : WARN ,
	
	"user" : "nobody" ,
	
	"limits" :
	{
		"max_http_session_count" : 100000 ,
		"max_file_cache" : 1024000 ,
		"max_connections_per_ip" : -1 ,
		"max_headers_count" : 128 ,
		"max_headers_length" : 4096 ,
		"max_header_content_length" : 4194304
	} ,
	
	/* for test
		curl "http://localhost/"
		curl "http://localhost/index.html"
		curl "http://localhost/mydir/"
		curl "http://localhost/mydir/index.html"
		curl "http://localhost/mydir/mydir2/"
		curl "http://localhost/mydir2"
		curl "http://localhost/mydir2/"
		curl "http://localhost/mydir/index2.html"
	*/
	"listen" :
	{
		"ip" : "" ,
		"port" : 80 ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log"
		}
	} ,
	
	/*
	"listen" :
	{
		"ip" : "" ,
		"port" : 443 ,
		"ssl" :
		{
			"certificate_file" : "/etc/hetao/certs/server.pem" ,
			"certificate_key_file" : "/etc/hetao/certs/server.key"
		} ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log"
		}
	} ,
	*/
	
	/*
	"listen" :
	{
		"ip" : "" ,
		"port" : 80 ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log" ,
			"redirect" : { "domain":"www.test.com" , "new_domain":"http://www.test2.com" }
		} ,
		"website" :
		{
			"domain" : "www.test.com" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log"
		} ,
		"website" :
		{
			"domain" : "www.test2.com" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log"
		}
	} ,
	*/
	
	/*
	"listen" :
	{
		"ip" : "" ,
		"port" : 80 ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log" ,
			"rewrite" : { "pattern":"/(.+)/(.+)" , "new_uri":"/(2)/(1)" }
		}
	} ,
	*/
	
	/*
	"listen" :
	{
		"ip" : "" ,
		"port" : 80 ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log" ,
			"forward" :
			{
				"forward_type" : "php" ,
				"forward_rule" : "R" ,
				"forward_server" : { "ip" : "127.0.0.1" , "port" : 8081 } ,
				"forward_server" : { "ip" : "127.0.0.1" , "port" : 8082 } ,
				"forward_server" : { "ip" : "127.0.0.1" , "port" : 8083 }
			}
		}
	} ,
	"listen" :
	{
		"ip" : "" ,
		"port" : 8081 ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log"
		}
	} ,
	"listen" :
	{
		"ip" : "" ,
		"port" : 8082 ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www/mydir" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log"
		}
	} ,
	"listen" :
	{
		"ip" : "" ,
		"port" : 8083 ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www/mydir/mydir2" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log"
		}
	} ,
	*/
	
	/*
	"listen" :
	{
		"ip" : "" ,
		"port" : 80 ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log" ,
			"forward" :
			{
				"forward_type" : "php" ,
				"forward_rule" : "R" ,
				"ssl" :
				{
					"certificate_file" : "/etc/hetao/certs/server.pem" ,
					"certificate_key_file" : "/etc/hetao/certs/server.key"
				} ,
				"forward_server" : { "ip" : "127.0.0.1" , "port" : 8081 } ,
				"forward_server" : { "ip" : "127.0.0.1" , "port" : 8082 } ,
				"forward_server" : { "ip" : "127.0.0.1" , "port" : 8083 }
			}
		}
	} ,
	*/
	
	/*
	"listen" :
	{
		"ip" : "" ,
		"port" : 80 ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log" ,
			"socgi" :
			{
				"socgi_type" : "socgi" ,
				"socgi_config_pathfilename" : "conf/test.conf" ,
				"socgi_bin_pathfilename" : "/var/hetao/www/test_socgi_hello.socgi"
			}
		}
	} ,
	*/
	
	/*
	"listen" :
	{
		"ip" : "" ,
		"port" : 80 ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log" ,
			"socgi" :
			{
				"socgi_type" : "socgi" ,
				"socgi_bin_pathfilename" : "www/test_socgi_demo_check_token.socgi"
			}
		}
	} ,
	*/
	
	/*
	"listen" :
	{
		"ip" : "" ,
		"port" : 80 ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log" ,
			"socgi" :
			{
				"socgi_bin_pathfilename" : "/var/hetao/www/test_socgi_rest_hello.socgi"
			}
		}
	} ,
	*/
	
	/*
	"listen" :
	{
		"ip" : "" ,
		"port" : 80 ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log" ,
			"socgi" :
			{
				"socgi_bin_pathfilename" : "/var/hetao/www/test_socgi_rest_full.socgi"
			}
		}
	} ,
	*/
	
	/* for test
		curl "http://localhost/mydir"
		curl "http://localhost/"
		curl "http://localhost/mydir/index.html/mydir2"
		curl "http://localhost/mydir/mydir2/index.html"
		curl "http://localhost/mydir/mydir2/mydir3/index.html"
	*/
	/*
	"listen" :
	{
		"ip" : "" ,
		"port" : 80 ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log" ,
			"location" :
			{
				"location" : "^/mydir[/]?$" ,
				"redirect" : { "domain":"www.test.com" , "new_domain":"http://www.test2.com" }
			} ,
			"location" :
			{
				"location" : "^/mydir/[^/]+/[^/]+$" ,
				"rewrite" : { "pattern":"/(.+)/(.+)/(.+)" , "new_uri":"/(1)/(3)/(2)" }
			}
		} ,
		"website" :
		{
			"domain" : "www.test.com" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log"
		} ,
		"website" :
		{
			"domain" : "www.test2.com" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log"
		}
	} ,
	*/
	
	/* for test
		curl "http://localhost/mydir"
		curl "http://localhost/mydir/mydir2/index.html"
	*/
	/*
	"listen" :
	{
		"ip" : "" ,
		"port" : 80 ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log" ,
			"location" :
			{
				"location" : "^/mydir[/]?$" ,
				"forward" :
				{
					"forward_type" : "html" ,
					"forward_rule" : "R" ,
					"forward_server" : { "ip" : "127.0.0.1" , "port" : 8081 } ,
					"forward_server" : { "ip" : "127.0.0.1" , "port" : 8082 }
				}
			} ,
			"location" :
			{
				"location" : "^/mydir/[^/]+/[^/]+$" ,
				"forward" :
				{
					"forward_type" : "html" ,
					"forward_rule" : "R" ,
					"forward_server" : { "ip" : "127.0.0.1" , "port" : 8081 } ,
					"forward_server" : { "ip" : "127.0.0.1" , "port" : 8082 } ,
					"forward_server" : { "ip" : "127.0.0.1" , "port" : 8083 }
				}
			}
		}
	} ,
	"listen" :
	{
		"ip" : "" ,
		"port" : 8081 ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log"
		}
	} ,
	"listen" :
	{
		"ip" : "" ,
		"port" : 8082 ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log"
		}
	} ,
	"listen" :
	{
		"ip" : "" ,
		"port" : 8083 ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log"
		}
	} ,
	*/
	
	"tcp_options" :
	{
		"nodelay" : 1 ,
		"linger" : -1
	} ,
	
	"http_options" :
	{
		"compress_on" : 1 ,
		"timeout" : 30 ,
		"elapse" : 60 ,
		"forward_disable" : 60
	} ,
	
	"error_pages" :
	{
		"error_page_400" : "/var/hetao/www/error_pages/error_page_400.html" ,
		"error_page_401" : "/var/hetao/www/error_pages/error_page_401.html" ,
		"error_page_403" : "/var/hetao/www/error_pages/error_page_403.html" ,
		"error_page_404" : "/var/hetao/www/error_pages/error_page_404.html" ,
		"error_page_408" : "/var/hetao/www/error_pages/error_page_408.html" ,
		"error_page_500" : "/var/hetao/www/error_pages/error_page_500.html" ,
		"error_page_503" : "/var/hetao/www/error_pages/error_page_503.html" ,
		"error_page_505" : "/var/hetao/www/error_pages/error_page_505.html"
	} ,
	
	"mime_types" :
	{
		"mime_type" : { "type":"html htm shtml" , "mime":"text/html" , "compress_enable":1 } ,
		"mime_type" : { "type":"css" , "mime":"text/css" , "compress_enable":1 } ,
		"mime_type" : { "type":"xml" , "mime":"text/xml" , "compress_enable":1 } ,
		"mime_type" : { "type":"txt" , "mime":"text/plain" , "compress_enable":1 } ,
		"mime_type" : { "type":"gif" , "mime":"image/gif" } ,
		"mime_type" : { "type":"jpeg jpg" , "mime":"image/jpeg" } ,
		"mime_type" : { "type":"png" , "mime":"image/png" } ,
		"mime_type" : { "type":"tif tiff" , "mime":"image/tiff" } ,
		"mime_type" : { "type":"ico" , "mime":"image/x-ico" } ,
		"mime_type" : { "type":"jng" , "mime":"image/x-jng" } ,
		"mime_type" : { "type":"bmp" , "mime":"image/x-ms-bmp" } ,
		"mime_type" : { "type":"svg svgz" , "mime":"image/svg+xml" , "compress_enable":1 } ,
		"mime_type" : { "type":"jar war ear" , "mime":"application/java-archive" } ,
		"mime_type" : { "type":"json" , "mime":"application/json" , "compress_enable":1 } ,
		"mime_type" : { "type":"doc" , "mime":"application/msword" } ,
		"mime_type" : { "type":"pdf" , "mime":"application/pdf" } ,
		"mime_type" : { "type":"rtf" , "mime":"application/rtf" } ,
		"mime_type" : { "type":"xls" , "mime":"application/vnd.ms-excel" } ,
		"mime_type" : { "type":"ppt" , "mime":"application/vnd.ms-powerpoint" } ,
		"mime_type" : { "type":"7z" , "mime":"application/x-7z-compressed" } ,
		"mime_type" : { "type":"rar" , "mime":"application/x-rar-compressed" } ,
		"mime_type" : { "type":"swf" , "mime":"application/x-shockwave-flash" } ,
		"mime_type" : { "type":"xhtml" , "mime":"application/xhtml+xml" , "compress_enable":1 } ,
		"mime_type" : { "type":"bin exe dll iso img msi msp msm" , "mime":"application/octet-stream" } ,
		"mime_type" : { "type":"zip" , "mime":"application/zip" } ,
		"mime_type" : { "type":"docx" , "mime":"application/vnd.openxmlformats-officedocument.wordprocessingml.document" } ,
		"mime_type" : { "type":"xlsx" , "mime":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" } ,
		"mime_type" : { "type":"pptx" , "mime":"application/vnd.openxmlformats-officedocument.presentationml.presentation" } ,
		"mime_type" : { "type":"mid midi kar" , "mime":"audio/midi" } ,
		"mime_type" : { "type":"mp3" , "mime":"audio/mpeg" } ,
		"mime_type" : { "type":"ogg" , "mime":"audio/ogg" } ,
		"mime_type" : { "type":"m4a" , "mime":"audio/x-m4a" } ,
		"mime_type" : { "type":"ra" , "mime":"audio/x-realaudio" } ,
		"mime_type" : { "type":"3gpp 3gp" , "mime":"video/3gpp" } ,
		"mime_type" : { "type":"ts" , "mime":"video/mp2t" } ,
		"mime_type" : { "type":"mp4" , "mime":"video/mp4" } ,
		"mime_type" : { "type":"mpeg mpg" , "mime":"video/mpeg" } ,
		"mime_type" : { "type":"mov" , "mime":"video/quicktime" } ,
		"mime_type" : { "type":"webm" , "mime":"video/webm" } ,
		"mime_type" : { "type":"flv" , "mime":"video/x-flv" } ,
		"mime_type" : { "type":"m4v" , "mime":"video/x-m4v" } ,
		"mime_type" : { "type":"mng" , "mime":"video/x-mng" } ,
		"mime_type" : { "type":"asx asf" , "mime":"video/x-ms-asf" } ,
		"mime_type" : { "type":"wmv" , "mime":"video/x-ms-wmv" } ,
		"mime_type" : { "type":"avi" , "mime":"video/x-msvideo" }
	}
}
配置项 说明
worker_processes Linux环境里启动的工作进程数量,如果为-1则设置为CPU核数量;缺省值为1
cpu_affinity 如果为1,则子进程绑定在CPU上,如果为0,不绑定;缺省值为1
accept_mutex 如果为1,开启侦听轮转,防止多子进程因epoll惊群而引起的CPU稍稍高耗,每次只有一个工作进程处于接受下一批TCP新连接状态,处理完后挑选一个目前HTTP会话最少的工作进程担此重任;缺省值为0
error_log 详细日志文件名。支持$...$环境变量展开。以下所有目录文件配置项都可以内嵌环境变量;缺省值为空
log_level 详细日志文件内的日志等级,枚举有DEBUG、INFO、WARN、ERROR、FATAL;缺省值为ERROR
user 启动后以该用户身份(可选配置);缺省值为"nobody"
limits 限制设置
-   max_http_session_count 最大HTTP通讯会话并发数量;缺省值为100000
-   max_file_cache 最大缓存文件大小;缺省值为1024000
-   max_connections_per_ip 每个IP最大连接数限制,-1为不限制;缺省值为-1
-   headers_count_hardmax HTTP请求最大头数量;缺省值为128
-   headers_len_hardmax HTTP请求最大头选项大小;缺省值为4KB
-   header_content_length_val_hardmax HTTP请求最大体大小;缺省值为4MB
listen 网络侦听地址
-   ip 本地侦听端口,填空则为0.0.0.0
-   port 本地侦听端口
-   ssl 服务端安全加密规则(可选配置块)
-   -   certificate_file 公钥证书文件名
-   -   certificate_key_file 私钥文件名
-   website [] 网站配置;一个网路侦听地址里可以运行多个网站
-   -   domain 网站域名,用于匹配HTTP请求头选项Host区分虚拟主机。如果填空则统配所有
-   -   wwwroot 网站本地根目录
-   -   index 当浏览器请求的是目录,尝试的入口文件,格式为"/index.html",如果有多个,则格式为"/index.html,/index.htm,..."。注意:入口文件名前有"/"
-   -   access_log 事件日志文件名,一个HTTP请求写一条事件日志
-   -   redirect 域名重定向规则(可选配置块)
-   -   -   domain 当前域名,如"www.test.com"
-   -   -   new_domain 重定向域名,如"http://www.test2.com/"
-   -   rewrite 改写URI规则(可选配置块)
-   -   -   pattern 原URI正则匹配式,如"/(.+)/(.+)"
-   -   -   new_uri 新URI格式,如"/(2)/(1)"
-   -   forward 代理转发规则(可选配置块)
-   -   -   forward_type 代理转发资源文件扩展名,不包含'.'
-   -   -   forward_rule 负载均衡算法,目前支持:R轮询,L最少连接数
-   -   -   ssl 客户端安全加密规则(可选配置块)
-   -   -   certificate_file 公钥证书文件名
-   -   -   forward_server [] 后端应用服务器地址
-   -   -   -   ip 后端侦听端口
-   -   -   -   port 后端侦听端口
-   -   socgi 把HTTP请求交给应用逻辑处理(可配置块)
-   -   -   socgi_type SOCGI资源文件扩展名,不包含'.',如果不配置则等同RESTful效果
-   -   -   socgi_config_pathfilename 应用动态库配置文件名
-   -   -   socgi_bin_pathfilename 应用动态库文件名
-   -   location 再细分URI的子配置
-   -   -   redirect 域名重定向规则(可选配置块)
-   -   -   rewrite 改写URI规则(可选配置块)
-   -   -   forward 代理转发规则(可选配置块)
-   -   -   socgi 把HTTP请求交给应用逻辑处理(可配置块)
tcp_options TCP选项(可选配置块)
-   nodelay 当为1时,启用TCP选项TCP_NODELAY,有助于提高响应速度;当为0时,关闭之;缺省值为1
-   linger 当大于等于0时,启用TCP选项SO_LINGER并设置成其值;当为-1时,不设置之;缺省值为-1
http_options HTTP选项(可选配置块)
-   compress_on 是否响应浏览器端的压缩请求,有助于大幅减少通讯传输流量;缺省值为1
-   timeout HTTP活跃超时时间,单位:秒;缺省值为30
-   elapse HTTP累积超时时间,单位:秒;缺省值为60
-   forward_disable 当反向代理连接后端失败后,暂禁时间,单位:秒;缺省值为60
error_pages 出错页面配置(可选配置块)
-   error_page_??? HTTP响应???时返回的页面文件,目前支持400、401、403、404、408、500、503、505
mime_types 流类型配置集合。主要用于填充HTTP响应头选项Content-Type;缺省值为缺省配置文件中信息
-   mime_type [] 流类型配置
-   -   type 文件扩展名
-   -   mime 流类型描述,填充HTTP响应头选项Content-Type
-   -   compress_enable 是否压缩缓存,1位压缩,不出现或0为不压缩

最后注意:json元素之间有","以及最后一个元素后面没有","。

4.2. 配置文件最小化

上述配置中每一项都可以不写以启用缺省值,仅配置listen,达到配置最小化。安装包中自带了精简化配置文件

$ cat conf/hetao.conf.Linux.minimize
{
        "listen" :
        {
                "ip" : "" ,
                "port" : 80 ,
                "website" :
                {
                        "domain" : "" ,
                        "wwwroot" : "/var/hetao/www" ,
                        "index" : "/index.htm,/index.html" ,
                        "access_log" : "/var/hetao/log/access.log"
                }
        }
}

4.3. 网站配置和示例

网站配置层次关系:

listen(侦听) - website(网站) - forward_server(反向代理转发服务器)

一个hetao运行实例里可以有多个listen,每个listen为一个ip、port对,对应一个TCP服务端侦听。每个listen上可以配置多个网站website,基于域名domain识别虚拟主机。每个website上可以配置成某一文件类型forward_type转发到后方应用服务器forward_server,以及负载均衡算法forward_rule。

domain需要匹配浏览器访问Web服务器请求头选项Host的值(URL中"http://"与"/"之间的部分)以确定服务器使用哪个虚拟主机来响应,如:

http://www.google.com/		domain为"www.google.com"
http://192.168.1.110:8080/	domain为"192.168.1.110:8080"

4.3.1. 简单的网站配置

	"listen" :
	{
		"ip" : "" ,
		"port" : 80 ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log"
		}
	} ,

4.3.2. 带域名的虚拟主机网站配置(两个虚拟主机)

	"listen" :
	{
		"ip" : "" ,
		"port" : 80 ,
		"website" :
		{
			"domain" : "www.test.com" ,
			"wwwroot" : "/var/hetao/www.test.com" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/www_test_com_access.log"
		} ,
		{
			"domain" : "www.test2.com" ,
			"wwwroot" : "/var/hetao/www.test2.com" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/www_test_com_access.log"
		}
	} ,

4.3.3. 需要改写URI的网站配置(/xxx/yyy改写为/yyy/xxx)

	"listen" :
	{
		"ip" : "" ,
		"port" : 80 ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log" ,
			"rewrite" : { "pattern":"/(.+)/(.+)" , "new_uri":"/(2)/(1)" }
		}
	} ,

4.3.4. 需要重定向域名的网站配置(www.google.com改写为www.baidu.com)

	"listen" :
	{
		"ip" : "" ,
		"port" : 80 ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log" ,
			"redirect" : { "domain":"www.google.com" , "new_domain":"http://www.baidu.com" }
		}
	} ,

4.3.5. 简单的HTTPS网站配置

	"listen" :
	{
		"ip" : "" ,
		"port" : 443 ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log" ,
      "ssl" :
      {
        "certificate_file" : "/etc/hetao/certs/server.pem" ,
        "certificate_key_file" : "/etc/hetao/certs/server.key"
      }
		}
	} ,

4.3.6. 反向代理配置,针对文件类型php,轮询算法

	"listen" :
	{
		"ip" : "" ,
		"port" : 80 ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log" ,
			"forward" :
			{
				"forward_type" : "php" ,
				"forward_rule" : "R" ,
				"forward_server" : { "ip" : "192.168.6.111" , "port" : 8081 } ,
				"forward_server" : { "ip" : "192.168.6.111" , "port" : 8082 } ,
				"forward_server" : { "ip" : "192.168.6.111" , "port" : 8083 }
			}
		}
	} ,

4.3.7. 反向代理配置,针对文件类型php,轮询算法,转发时装载证书变成HTTPS

	"listen" :
	{
		"ip" : "" ,
		"port" : 80 ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log" ,
			"forward" :
			{
				"forward_type" : "php" ,
				"forward_rule" : "R" ,
				"ssl" :
				{
					"certificate_file" : "/etc/hetao/certs/server.crt"
				} ,
				"forward_server" : { "ip" : "192.168.6.111" , "port" : 1443 } ,
				"forward_server" : { "ip" : "192.168.6.111" , "port" : 1443 } ,
				"forward_server" : { "ip" : "192.168.6.111" , "port" : 1443 }
			}
		}
	} ,

4.3.8. SOCGI

URI扩展名为“.do”的HTTP请求,调用应用动态库:

	"listen" :
	{
		"ip" : "" ,
		"port" : 80 ,
		"website" :
		{
			"domain" : "localhost" ,
			"wwwroot" : "www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "log/access.log" ,
			"socgi" :
			{
				"socgi_type" : "do" , "socgi_bin_pathfilename" : "www/test_socgi_hello.socgi"
			}
		}
	}

hetao把符合该文件扩展名的请求导向应用。

RESTful风格应用的URI一般没有扩展名:

	"listen" :
	{
		"ip" : "" ,
		"port" : 80 ,
		"website" :
		{
			"domain" : "localhost" ,
			"wwwroot" : "www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "log/access.log" ,
			"socgi" :
			{
				"socgi_type" : "" , "socgi_bin_pathfilename" : "www/test_socgi_rest_full.socgi"
			}
		}
	} 

hetao把请求URI传递给应用中的路由去分拣。

4.3.9. 细分URI的子配置

website下的redirect、rewrite、forward、socgi是针对该website所有URI,hetao还支持细分URI的redirect、rewrite、forward、socgi。

以下配置为针对不同URI的redirect和rewrite处理,如果请求URI是/mydir则重定向到www.test2.com,如果请求URI是/mydir/index.html/mydir2则重写URI为/mydir/mydir2/index.html

	"listen" :
	{
		"ip" : "" ,
		"port" : 80 ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log" ,
			"location" :
			{
				"location" : "^/mydir[/]?$" ,
				"redirect" : { "domain":"www.test.com" , "new_domain":"http://www.test2.com" }
			} ,
			"location" :
			{
				"location" : "^/mydir/[^/]+/[^/]+$" ,
				"rewrite" : { "pattern":"/(.+)/(.+)/(.+)" , "new_uri":"/(1)/(3)/(2)" }
			}
		} ,
		"website" :
		{
			"domain" : "www.test2.com" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log"
		}
	} ,

以下配置,如果请求URI是/mydir则设置代理转发下游服务器集群127.0.0.1:8081,8082,如果请求URI是/mydir/mydir2/index.html则设置代理转发下游服务器集群127.0.0.1:8081,8082,8083。自己同时也扮演下游代理服务器便于测试。

	"listen" :
	{
		"ip" : "" ,
		"port" : 80 ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log" ,
			"location" :
			{
				"location" : "^/mydir[/]?$" ,
				"forward" :
				{
					"forward_type" : "html" ,
					"forward_rule" : "R" ,
					"forward_server" : { "ip" : "127.0.0.1" , "port" : 8081 } ,
					"forward_server" : { "ip" : "127.0.0.1" , "port" : 8082 }
				}
			} ,
			"location" :
			{
				"location" : "^/mydir/[^/]+/[^/]+$" ,
				"forward" :
				{
					"forward_type" : "html" ,
					"forward_rule" : "R" ,
					"forward_server" : { "ip" : "127.0.0.1" , "port" : 8081 } ,
					"forward_server" : { "ip" : "127.0.0.1" , "port" : 8082 } ,
					"forward_server" : { "ip" : "127.0.0.1" , "port" : 8083 }
				}
			}
		}
	} ,
	"listen" :
	{
		"ip" : "" ,
		"port" : 8081 ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log"
		}
	} ,
	"listen" :
	{
		"ip" : "" ,
		"port" : 8082 ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log"
		}
	} ,
	"listen" :
	{
		"ip" : "" ,
		"port" : 8083 ,
		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log"
		}
	} ,

4.4. 配置包含文件

配置文件可以包含其它文件,如把虚拟主机配置单独移到外面的一个文件里,然后在hetao.conf中包含它。在配置文件中任意位置加入

!include filename

或者

!include "filename"

filename是相对于hetao.conf所在路径的相对路径+包含文件名。如

		"website" :
		{
			"domain" : "" ,
			"wwwroot" : "/var/hetao/www" ,
			"index" : "/index.html,/index.htm" ,
			"access_log" : "/var/hetao/log/access.log" ,
			!include hetao_redirect.conf
			...

hetao_redirect.conf

			redirect { "domain":"www.google.com" , "new_domain":"www.baidu.com" } ,

注意:小心json格式中的','

4.5. 配置文件格式检查工具

json配置格式很容易写坏,比如段落之间少了',',又比如括号不匹配,工具hetaocheck用来检查配置文件格式是否正确。

执行参数与hetao一致,以下表示格式正确

$ hetaocheck /etc/hetao/hetao.conf
OK

以下表示格式有误

$ hetaocheck /etc/hetao/hetao.conf
2016-03-27 01:12:10 | ERROR | 14309:3086837744:Config.c:163 | DSCDESERIALIZE_JSON_hetao_conf failed[-134][0] , errno[0]
FAILED[-1]

5. 服务器管理

5.1. 直接用命令管理

启动hetao

$ hetao ~/etc/hetao.conf

查询hetao进程

$ ps -ef | grep hetao | grep -v grep
calvin   14876     1  0 00:10 ?  00:00:00 hetao /home/calvin/etc/hetao.conf
calvin   14877 14876  0 00:10 ?  00:00:00 hetao /home/calvin/etc/hetao.conf

优雅的重启hetao,或者重载配置文件

$ ps -ef | grep hetao | grep -v grep
calvin   14876     1  0 00:10 ?   00:00:00 hetao /home/calvin/etc/hetao.conf
calvin   14877 14876  0 00:10 ?   00:00:00 hetao /home/calvin/etc/hetao.conf
$ kill -USR2 14876
$  ps -ef | grep hetao | grep -v grep
calvin   14876     1  0 00:10 ?   00:00:00 hetao /home/calvin/etc/hetao.conf
calvin   14877 14876  0 00:10 ?   00:00:00 hetao /home/calvin/etc/hetao.conf
calvin   14889     1  0 00:12 ?  00:00:00 hetao /home/calvin/etc/hetao.conf
calvin   14890 14889  0 00:12 ?   00:00:00 hetao /home/calvin/etc/hetao.conf
$ kill 14876
$ ps -ef | grep hetao | grep -v grep
calvin   14889     1  0 00:12 ?  00:00:00 hetao /home/calvin/etc/hetao.conf
calvin   14890 14889  0 00:12 ?   00:00:00 hetao /home/calvin/etc/hetao.conf

向hetao发送重新打开日志文件信号

$ kill -USR1 14889

停止hetao

$ kill 14889

5.2. 用自带脚本管理

启动hetao(默认配置文件路径~/etc/hetao.conf)

$ hetao.do start
hetao start ok
calvin   14703     1  0 00:05 ?  00:00:00 hetao /home/calvin/etc/hetao.conf
calvin   14704 14703  0 00:05 ?   00:00:00 hetao /home/calvin/etc/hetao.conf

查询hetao进程

$ hetao.do status
calvin   14703     1  0 00:05 ?  00:00:00 hetao /home/calvin/etc/hetao.conf
calvin   14704 14703  0 00:05 ?   00:00:00 hetao /home/calvin/etc/hetao.conf

重启hetao

$ hetao.do restart
calvin   14703     1  0 00:05 ?   00:00:00 hetao /home/calvin/etc/hetao.conf
calvin   14704 14703  0 00:05 ?    00:00:00 hetao /home/calvin/etc/hetao.conf
hetao end ok
hetao start ok
calvin   14761     1  0 00:06 ?   00:00:00 hetao /home/calvin/etc/hetao.conf
calvin   14762 14761  0 00:06 ?    00:00:00 hetao /home/calvin/etc/hetao.conf

优雅的重启hetao,或者重载配置文件

$ hetao.do restart_graceful
calvin   14761     1  0 00:06 ?  00:00:00 hetao /home/calvin/etc/hetao.conf
calvin   14762 14761  0 00:06 ?   00:00:00 hetao /home/calvin/etc/hetao.conf
new hetao pid[14796] start ok
old hetao pid[14761] end ok
calvin   14796     1  0 00:06 ?  00:00:00 hetao /home/calvin/etc/hetao.conf
calvin   14797 14796  0 00:06 ?   00:00:00 hetao /home/calvin/etc/hetao.conf

向hetao发送重新打开日志文件信号

$ hetao.do relog
calvin   14796     1  0 00:06 ?  00:00:00 hetao /home/calvin/etc/hetao.conf
calvin   14797 14796  0 00:06 ?   00:00:00 hetao /home/calvin/etc/hetao.conf
send signal to hetao for reopenning log

停止hetao

$ hetao.do stop
calvin   14796     1  0 00:06 ?  00:00:00 hetao /home/calvin/etc/hetao.conf
calvin   14797 14796  0 00:06 ?   00:00:00 hetao /home/calvin/etc/hetao.conf
hetao end ok

5.3. 用minihetao直接启动,无需配置文件

一般用于临时测试。

Linux版本的minihetao读入网站根目录wwwroot直接启动,无需配置文件

$ minihetao /var/hetao/www

WINDOWS版本的minihetao是个带窗口的小程序

minihetao.png

wwwroot右边的路径编辑框以及选择目录按钮 : 设置网站根目录
Running : 启动网站
Stop : 关闭网站
Registe folder popup-menu : 注册目录右键菜单项直接启动网站的注册表配置
Unregiste folder popup-menu : 卸载目录右键菜单项直接启动网站的注册表配置
Hide : 隐藏窗口,缩小到托盘
Exit : 退出minihetao

注意:WINDOWS版本minihetao还可以右键某驱动器或目录直接启动网站。

6. 开发应用

6.1. SOCGI开发规范

在HTTP请求处理过程中,用户希望用自己的逻辑替代Web服务器的逻辑,hetao支持搭载应用动态库,在HTTP请求处理的某个环节,如果应用动态库中存在相应函数,则执行该函数,替代hetao实现细节。这些函数分为两类:入口函数和HTTP信息API函数,入口函数又分生命周期管理函数、和HTTP处理环节入口函数。

6.1.1. 生命周期管理函数

函数名 函数描述 函数说明 返回值影响
InitHttpApplication 动态库实例初始化函数 Web服务器启动装载应用动态库时调用,一般里面放创建业务环境,比如连接数据库 返回HTTP_OK继续hetao启动,如果返回非HTTP_OK则中断hetao启动
CleanHttpApplication 动态库实例清理函数 Web服务器启动装载应用动态库时调用,一般里面放销毁业务环境 固定返回HTTP_OK

6.1.2. HTTP处理环节入口函数(在上图中以黄色标准)

被调用条件:符合HTTP资源文件扩展名,且应用动态库中存在该函数。

函数名 函数描述 函数说明 返回值影响
RedirectHttpDomain 域名重定向函数 一般放用户自定义重定向域名逻辑 返回0继续hetao自身域名重定向逻辑
返回HTTP_OK代替Web服务器自身域名重定向逻辑设置HTTP响应码HTTP_MOVED_PERMANNETLY
返回小于0(致命错误)立即结束进程/线程
返回其它按返回值设置HTTP响应码并立即返回
RewriteHttpUri 重写URI函数 一般放用户自定义重写URI逻辑 返回0继续Web服务器自身重写URI逻辑
返回HTTP_OK代替hetao自身重写URI逻辑
返回小于0(致命错误)立即结束进程/线程
返回其它按返回值设置HTTP响应码并立即返回
BeforeProcessHttpResource HTTP请求处理前函数 一般放无HTTP体报错信息的HTTP请求头通证权限检查逻辑 返回HTTP_OK继续后续流程
返回小于0(致命错误)立即结束进程/线程
返回其它按返回值设置HTTP响应码并立即返回
ProcessHttpResource HTTP请求处理函数 一般放报错信息填充HTTP体的HTTP请求头通证权限检查逻辑 返回0继续Web服务器自身处理逻辑
返回HTTP_OK跳过hetao自身处理逻辑
返回小于0(致命错误)立即结束进程/线程
返回其它按返回值设置HTTP响应码并立即返回
SelectForwardServer
(待实现)
选择下游服务器函数 一般放用户自定义选择逻辑 返回HTTP_OK继续后续流程
返回小于0(致命错误)立即结束进程/线程
返回其它按返回值设置HTTP响应码并立即返回
CallHttpApplication 用户业务处理逻辑函数 作为App服务器,搭载业务逻辑处理HTTP请求 返回HTTP_OK继续后续流程
返回小于0(致命错误)立即结束进程/线程
返回其它按返回值设置HTTP响应码并立即返回
GetHttpResource 组织静态资源文件内容 作为静态资源服务器,自己编写逻辑提供静态资源文件数据 返回HTTP_OK继续后续流程
返回小于0(致命错误)立即结束进程/线程
返回其它按返回值设置HTTP响应码并立即返回
AfterProcessHttpResource HTTP请求处理后函数 一般放修改前面已组织好的HTTP响应报文 返回HTTP_OK继续后续流程
返回小于0(致命错误)立即结束进程/线程
返回其它按返回值设置HTTP响应码并立即返回

HTTP信息访问函数用于在入口函数用户代码中存取HTTP请求和响应信息,详见应用开发参考。

一个简单的代码模板如下:

#include "hetao_socgi.h"

INITHTTPAPPLICATION InitHttpApplication ;
int InitHttpApplication( struct HttpApplicationContext *ctx )
{
	...
	
	return 0;
}

CALLHTTPAPPLICATION CallHttpApplication ;
int CallHttpApplication( struct HttpApplicationContext *ctx )
{
	...
	
	return 0;
}

CLEANHTTPAPPLICATION CleanHttpApplication ;
int CleanHttpApplication( struct HttpApplicationContext *ctx )
{
	...
	
	return 0;
}

hetao的socgi编程接口头文件是hetao_socgi.h,库文件是libhetao_socgi.so、libhetao_util.so, Windows库文件分别是hetao_socgi.[lib,dll]、hetao_util.[lib,dll]。

以下是一个示例,代码在test/test_socgi_hello/test_socgi_hello.c。

#include "hetao_socgi.h"

#include "LOGC.h"

INITHTTPAPPLICATION InitHttpApplication ;
int InitHttpApplication( struct HttpApplicationContext *ctx )
{
	InfoLog( __FILE__ , __LINE__ , "InitHttpApplication" );
	
	return HTTP_OK;
}

CALLHTTPAPPLICATION CallHttpApplication ;
int CallHttpApplication( struct HttpApplicationContext *ctx )
{
	char	http_body[ 1024 ] ;
	int	http_body_len ;
	
	int	nret = 0 ;
	
	InfoLog( __FILE__ , __LINE__ , "InitHttpApplication" );
	
	memset( http_body , 0x00 , sizeof(http_body) );
	http_body_len = SNPRINTF( http_body , sizeof(http_body)-1 , "hello test_socgi_hello.socgi , my config filename is [%s]\n" , SOCGIGetConfigPathfilename(ctx) ) ;
	nret = SOCGIFormatHttpResponse( ctx , http_body , http_body_len , NULL ) ;
	if( nret )
	{
		ErrorLog( __FILE__ , __LINE__ , "SOCGIFormatHttpResponse failed[%d]" , nret );
		return HTTP_INTERNAL_SERVER_ERROR;
	}
	else
	{
		InfoLog( __FILE__ , __LINE__ , "SOCGIFormatHttpResponse ok" );
		return HTTP_OK;
	}
}

CLEANHTTPAPPLICATION CleanHttpApplication ;
int CleanHttpApplication( struct HttpApplicationContext *ctx )
{
	InfoLog( __FILE__ , __LINE__ , "CleanHttpApplication" );
	
	return HTTP_OK;
}

示例中复用了hetao的日志库,应包含头文件LOGC.h

SOCGIFormatHttpResponse为组织HTTP响应报文。hetao_socgi.h里还有其它函数接口给与应用使用,函数SOCGIGetHttpEnv得到HTTP应用上下文中的HTTP对象,应用可以通过hetao的HTTP解析库fasterhttp访问HTTP数据,SOCGISetUserData和SOCGIGetUserData用于设置和获取应用自定义数据,用于三入口之间传递信息。

6.2. SOCGI应用开发参考

6.2.1. 工具宏

6.2.1.1. 简单缓冲区格式化宏

6.2.1.1.1. STRNCMPSTRN
宏定义 #define STRNCMPSTRN(_str1_,_str1_len_,_cmp_,_str2_,_str2_len_) ( (_str1_len_) _cmp_ (_str2_len_) && STRNCMP( (_str1_) , _cmp_ , (_str2_) , (_str2_len_) ) )
宏说明 带长度的比较两个字符数组
输入参数 _str1_ : 字符数组1
_str1_len_ : 字符数组1长度
_cmp_ : 比较符
_str2_ : 字符数组2
_str2_len_ : 字符数组2长度
返回值 0 : 构造成功
大于0 : 字符数组1大
小于0 : 字符数组2大

示例

char	buf1[...] ;
char	buf2[...] ;
...
if( STRNCMPSTRN( buf1 , strlen(buf1) , == , buf2 , strlen(buf2) ) )
{
	...
}
6.2.1.1.2. STRNEQSTR
宏定义 #define STRNEQSTRN(_str1_,_str1_len_,_str2_) STRNCMPSTRN( (_str1_) , (_str1_len_) , == , (_str2_) , (strlen(_str2_)) )
宏说明 带长度的比较两个字符数组,其中第二个字符数组自动计算长度
输入参数 _str1_ : 字符数组1
_str1_len_ : 字符数组1长度
_str2_ : 字符数组2
返回值 0 : 字符串相同
非0 : 字符串不相同

示例

char	buf1[...] ;
char	buf2[...] ;
...
if( STRNEQSTR( buf1 , strlen(buf1) , buf2 ) )
{
	...
}
6.2.1.1.3. STRNEQRSTR
宏定义 #define STRNEQRSTR(_str1_,_str1_len_,_const_str2_) STRNCMPSTRN( (_str1_) , (_str1_len_) , == , (_literal_str2_) , (sizeof(_literal_str2_)-1) )
宏说明 带长度的比较两个字符数组,其中第二个字符数组为字面量
输入参数 _str1_ : 字符数组1
_str1_len_ : 字符数组1长度
_literal_str2_ : 字符数组2
返回值 0 : 字符串相同
非0 : 字符串不相同

示例

char	buf1[...] ;
...
if( STRNCMPRSTR( buf1 , strlen(buf1) , == , "hello" ) )
{
	...
}
6.2.1.1.4. HTTP_RETURN_NEWLINE
宏定义 #define HTTP_RETURN_NEWLINE "\r\n"
宏说明 便于格式化HTTP时加入换行
6.2.1.1.5. HTML_NEWLINE
宏定义 #define HTML_NEWLINE "
"
宏说明 便于格式化HTML时加入小换行
6.2.1.1.6. HTML_RETURN_NEWLINE
宏定义 #define HTML_RETURN_NEWLINE "

"
宏说明 便于格式化HTML时加入大换行
6.2.1.1.7. BUFNPRINTF
宏定义 #define BUFNPRINTF(_buf_base_,_buf_size_,_str_len_,_format_,...) ...
宏说明 格式化字符串追加到缓冲区
输入参数 _buf_base_ : 缓冲区
_buf_size_ : 缓冲区大小
_str_len_: 缓冲区内有效字符串长度
_format_,... : 要追加的格式化串和参数集
返回值 (无)

示例

char	buf[...] ;
int	buf_len ;
int	count ;
...
BUFNPRINTF( buf , sizeof(buf) , buf_len , "count[%d]" , count )

注意:buf_len会自动累加和封顶。

6.2.1.1.8. BUFPRINTF
宏定义 #define BUFPRINTF(_buf_base_,_str_len_,_format_,...) BUFNPRINTF(_buf_base_,sizeof(_buf_base_),_str_len_,_format_,__VA_ARGS__)
宏说明 格式化字符串追加到缓冲区,不用给sizeof(_buf_base_)
输入参数 _buf_base_ : 缓冲区
_str_len_: 缓冲区内有效字符串长度
_format_,... : 要追加的格式化串和参数集
返回值 (无)

示例

char	buf[...] ;
int	buf_len ;
int	count ;
...
BUFPRINTF( buf , buf_len , "count[%d]" , count )

注意:buf_len会自动累加和封顶。

6.2.1.1.9. BUFNSTRCAT
宏定义 #define BUFNSTRCAT(buf_base,buf_size,str_len,cat_str) ...
宏说明 格式化字符串追加到缓冲区
输入参数 _buf_base_ : 缓冲区
_buf_size_ : 缓冲区大小
_str_len_: 缓冲区内有效字符串长度
_cat_str_,... : 要追加的字符串
返回值 (无)

示例

char	buf[...] ;
int	buf_len ;
...
BUFNSTRCAT( buf , sizeof(buf) , buf_len , "ok" )

注意:buf_len会自动累加和封顶。

6.2.1.1.10. BUFSTRCAT
宏定义 #define BUFSTRCAT(_buf_base_,_str_len_,_cat_str_) BUFNSTRCAT(_buf_base_,sizeof(_buf_base_),_str_len_,_cat_str_)
宏说明 格式化字符串追加到缓冲区
输入参数 _buf_base_ : 缓冲区
_str_len_: 缓冲区内有效字符串长度
_cat_str_,... : 要追加的字符串
返回值 (无)

示例

char	buf[...] ;
int	buf_len ;
...
BUFSTRCAT( buf , buf_len , "ok" )

注意:buf_len会自动累加和封顶。

6.2.2. API函数

6.2.2.1. 构造HTTP响应信息类

6.2.2.1.1. SOCGIFormatHttpResponse
函数原型 int SOCGIFormatHttpResponse( struct HttpApplicationContext *ctx , char *http_response_body , int http_response_body_len , char *http_header_format , ... );
函数说明 构造HTTP响应
输入参数 struct HttpApplicationContext *ctx : HTTP环境上下文环境
char *http_response_body : HTTP响应体
int http_response_body_len : HTTP响应体长度
char *http_header_format , ... : HTTP响应头;如果有多行的话,要用"\r\n"分隔
输出参数 (无)
返回值 0 : 构造成功
非0 : 失败,具体失败原因见错误宏

6.2.2.2. HTTP信息类

6.2.2.2.1. SOCGIGetHttpHeaderPtr_METHOD
函数原型 char *SOCGIGetHttpHeaderPtr_METHOD( struct HttpApplicationContext *ctx , int *p_value_len );
函数说明 从HTTP环境上下文环境中得到HTTP请求方法
输入参数 struct HttpApplicationContext *ctx : HTTP环境上下文环境
输出参数 int *p_value_len : HTTP请求方法长度
返回值 HTTP请求方法
6.2.2.2.2. SOCGIGetHttpHeaderPtr_URI
函数原型 char *SOCGIGetHttpHeaderPtr_URI( struct HttpApplicationContext *ctx , int *p_value_len );
函数说明 从HTTP环境上下文环境中得到HTTP的请求URI
输入参数 struct HttpApplicationContext *ctx : HTTP环境上下文环境
输出参数 int *p_value_len : HTTP的请求URI长度
返回值 HTTP的请求URI
6.2.2.2.3. SOCGIGetHttpHeaderPtr_VERSION
函数原型 char *SOCGIGetHttpHeaderPtr_VERSION( struct HttpApplicationContext *ctx , int *p_value_len );
函数说明 从HTTP环境上下文环境中得到HTTP的请求版本号
输入参数 struct HttpApplicationContext *ctx : HTTP环境上下文环境
输出参数 int *p_value_len : HTTP的请求版本号长度
返回值 HTTP的请求版本号
6.2.2.2.4. SOCGIQueryHttpHeaderPtr
函数原型 char *SOCGIQueryHttpHeaderPtr( struct HttpApplicationContext *ctx , char *name , int *p_value_len );
函数说明 从HTTP环境上下文环境中得到HTTP的请求头选项
输入参数 struct HttpApplicationContext *ctx : HTTP环境上下文环境
输出参数 int *p_value_len : HTTP的请求头选项长度
返回值 HTTP的请求头选项
6.2.2.2.5. SOCGITravelHttpHeaderPtr
函数原型 struct HttpHeader *SOCGITravelHttpHeaderPtr( struct HttpApplicationContext *ctx , struct HttpHeader *p_header );
函数说明 从HTTP环境上下文环境中遍历HTTP的请求头选项
输入参数 struct HttpApplicationContext *ctx : HTTP环境上下文环境
struct HttpHeader *p_header : 上一个HTTP请求头选项,第一次传入NULL,后一次传入前一次返回值
输出参数 (无)
返回值 HTTP的请求头选项,直至返回NULL
6.2.2.2.6. SOCGIGetHttpHeaderNamePtr
函数原型 char *SOCGIGetHttpHeaderNamePtr( struct HttpHeader *p_header , int *p_name_len );
函数说明 从HTTP的请求头选项中获取名字
输入参数 struct HttpHeader *p_header : HTTP的请求头选项
输出参数 int *p_name_len : 请求头选项名字长度
返回值 请求头选项名字
6.2.2.2.7. SOCGIGetHttpHeaderValuePtr
函数原型 char *SOCGIGetHttpHeaderValuePtr( struct HttpHeader *p_header , int *p_value_len );
函数说明 从HTTP的请求头选项中获取值
输入参数 struct HttpHeader *p_header : HTTP的请求头选项
输出参数 int *p_name_len : 请求头选项值长度
返回值 请求头选项值
6.2.2.2.8. SOCGIGetHttpBodyPtr
函数原型 char *SOCGIGetHttpBodyPtr( struct HttpApplicationContext *ctx , int *p_body_len );
函数说明 从HTTP环境上下文环境中获取HTTP请求体
输入参数 struct HttpHeader *p_header : HTTP的请求头选项
输出参数 int *p_body_len : HTTP请求体长度
返回值 HTTP请求体

6.2.2.3. 其它类

6.2.2.3.1. SOCGIGetConfigPathfilename
函数原型 char *SOCGIGetConfigPathfilename( struct HttpApplicationContext *ctx );
函数说明 从HTTP环境上下文环境中得到应用配置文件名,其配置路径为"/listen/website/socgi.socgi_config_pathfilename"
输入参数 struct HttpApplicationContext *ctx : HTTP环境上下文环境
输出参数 (无)
返回值 应用配置文件名
6.2.2.3.2. SOCGISetUserData
函数原型 void SOCGISetUserData( struct HttpApplicationContext *ctx , void *user_data );
函数说明 设置用户自定义变量到平台上下文环境中
输入参数 struct HttpApplicationContext *ctx : HTTP环境上下文环境
void *user_data : 用户自定义变量地址
输出参数 (无)
返回值 (无)
6.2.2.3.3. SOCGIGetUserData
函数原型 void *SOCGIGetUserData( struct HttpApplicationContext *ctx );
函数说明 从平台上下文环境中拿出某个用户自定义变量
输入参数 struct HttpApplicationContext *ctx : HTTP环境上下文环境
输出参数 (无)
返回值 viud * : 用户自定义变量地址

6.3. RESTful应用开发规范

hetao在hetao_socgi层上又增加了hetao_rest层,便于直接开发RESTful风格应用。开发接口头文件在hetao_rest.h中,示例test/test_socgi_rest_hello/test_socgi_rest_hello.c演示了简单开发。

#include "hetao_rest.h"

#include "LOGC.h"

funcRestServiceEntry GET_hello ;
int GET_hello( struct RestServiceContext *ctx )
{
	char		http_body[ 1024 ] = "" ;
	int		http_body_len = 0 ;
	
	int		nret = 0 ;
	
	InfoLog( __FILE__ , __LINE__ , "GET_hello" );
	
	BUFSTRCAT( http_body , http_body_len , "hetao_rest : hello world\n" ) ;
	nret = RESTFormatHttpResponse( ctx , http_body , http_body_len , NULL ) ;
	if( nret )
	{
		ErrorLog( __FILE__ , __LINE__ , "RESTFormatHttpResponse failed[%d]" , nret );
		return nret;
	}
	else
	{
		InfoLog( __FILE__ , __LINE__ , "RESTFormatHttpResponse ok" );
		return 0;
	}
}

static struct RestServiceConfig		g_RSC_ARRAY[] = 
	{
		{ HTTP_METHOD_GET , "/hello.do" , GET_hello } ,
		{ "" , "" , NULL }
	} ;

INITHTTPAPPLICATION InitHttpApplication ;
int InitHttpApplication( struct HttpApplicationContext *ctx )
{
	struct RestServiceControler	*ctl = NULL ;
	
	InfoLog( __FILE__ , __LINE__ , "InitHttpApplication" );
	
	ctl = RESTCreateRestServiceControler( g_RSC_ARRAY ) ;
	if( ctl == NULL )
	{
		ErrorLog( __FILE__ , __LINE__ , "RESTCreateRestServiceControler failed" );
		return REST_FATAL_CREATE_RESTSERVICECONTROLER;
	}
	else
	{
		DebugLog( __FILE__ , __LINE__ , "RESTCreateRestServiceControler ok" );
	}
	
	SOCGISetUserData( ctx , ctl );
	
	return 0;
}

CALLHTTPAPPLICATION CallHttpApplication ;
int CallHttpApplication( struct HttpApplicationContext *ctx )
{
	struct RestServiceControler	*ctl = NULL ;
	
	int				nret = 0 ;
	
	InfoLog( __FILE__ , __LINE__ , "CallHttpApplication" );
	
	ctl = SOCGIGetUserData( ctx ) ;
	if( ctl == NULL )
	{
		ErrorLog( __FILE__ , __LINE__ , "SOCGIGetUserData failed" );
		return REST_FATAL_GET_RESTSERVICECONTROLER;
	}
	else
	{
		DebugLog( __FILE__ , __LINE__ , "SOCGIGetUserData ok" );
	}
	
	nret = RESTDispatchRestServiceControler( ctl , SOCGIGetHttpEnv(ctx) ) ;
	if( nret )
	{
		ErrorLog( __FILE__ , __LINE__ , "RESTDispatchRestServiceControler failed[%d]" , nret );
		return nret;
	}
	else
	{
		DebugLog( __FILE__ , __LINE__ , "RESTDispatchRestServiceControler ok" );
	}
	
	return 0;
}

CLEANHTTPAPPLICATION CleanHttpApplication ;
int CleanHttpApplication( struct HttpApplicationContext *ctx )
{
	struct RestServiceControler	*ctl = NULL ;
	
	InfoLog( __FILE__ , __LINE__ , "CleanHttpApplication" );
	
	ctl = SOCGIGetUserData( ctx ) ;
	if( ctl == NULL )
	{
		ErrorLog( __FILE__ , __LINE__ , "SOCGIGetUserData failed" );
		return REST_FATAL_GET_RESTSERVICECONTROLER;
	}
	else
	{
		DebugLog( __FILE__ , __LINE__ , "SOCGIGetUserData ok" );
	}
	
	RESTDestroyRestServiceControler( ctl );
	
	return 0;
}

首先应用开发服务接口GET_hello(在示例中只是简单的组织了HTTP响应报文),然后配置到RESTful服务列表配置g_RSC_ARRAY中,其中第一列为HTTP方法,第二列为URI,可以使用“{}”通配任意数据,三入口函数代码基本不用修改直接拿来用,函数InitHttpApplication根据RESTful服务列表配置创建RESTful服务控制器,设置进HTTP应用上下文中,当符合URI扩展名的HTTP请求到来时hetao会调用函数CallHttpApplication,从HTTP应用上下文中取出RESTful服务控制器,调用函数RESTDispatchRestServiceControler分派到对应的服务函数中,函数CleanHttpApplication用于销毁RESTful服务控制器。

函数CallHttpApplication的参数上下文和服务函数中的参数上下文是两个结构体,前者是socgi规范定义的HTTP应用上下文,后者是REST服务上下文。

在服务函数中可使用hetao_rest.h中的函数访问HTTP信息,函数RESTGetHttpMethodPtr用于查询HTTP请求方法,函数RESTGetHttpUriPtr用于查询HTTP请求URI,函数RESTGetHttpUriPathsCount用于查询URI分解后的目录数量,函数RESTGetHttpUriPathPtr用于查询每一段的目录名,函数RESTGetHttpUriQueriesCount用于ChaunceyURI分解后的参数数量,函数RESTGetHttpUriQueryKeyPtr和RESTGetHttpUriQueryValuePtr用于查询参数键值,函数RESTGetHttpRequestBodyPtr用于获取HTTP请求体,函数RESTFormatHttpResponse用于组织HTTP响应报文。

一个比较复杂的示例在test/test_socgi_rest_full/test_socgi_rest_full.c,其RESTful服务配置表供使用参考

static struct RestServiceConfig		g_RSC_ARRAY[] = 
	{
		{ HTTP_METHOD_GET , "/" , GET_ } ,					/* curl "http://localhost/" */
		{ HTTP_METHOD_GET , "/path1" , GET_path1 } ,				/* curl "http://localhost/path1" */
		{ HTTP_METHOD_GET , "/path1/" , GET_path1_ } ,				/* curl "http://localhost/path1/" */
		{ HTTP_METHOD_GET , "/path1/path2" , GET_path1_path2 } ,		/* curl "http://localhost/path1/path2" */
		{ HTTP_METHOD_GET , "/path1/path2/" , GET_path1_path2_ } ,		/* curl "http://localhost/path1/path2/" */
		{ HTTP_METHOD_GET , "/path1/path2/file" , GET_path1_path2_file } ,	/* curl "http://localhost/path1/path2/file" */
		{ HTTP_METHOD_GET , "/path1/{}/file" , GET_path1_n_file } ,		/* curl "http://localhost/path1/123/file1" */
		{ HTTP_METHOD_GET , "/path1/path2/file1" , GET_path1_path2_file1__key1_value1 } ,		/* curl "http://localhost/path1/path2/file1?key1=value1" */
		{ HTTP_METHOD_GET , "/path1/path2/file2" , GET_path1_path2_file2__key1_value1__key2_value2 } ,	/* curl "http://localhost/path1/path2/file2?key1=value1&key2=value2" */
		{ HTTP_METHOD_GET , "/path1/path2/file3" , GET_path1_path2_file3__ } ,				/* curl "http://localhost/path1/path2/file3?" */
		{ HTTP_METHOD_GET , "/path1/path2/file4" , GET_path1_path2_file4__key1 } ,			/* curl "http://localhost/path1/path2/file4?key1" */
		{ HTTP_METHOD_GET , "/path1/path2/file5" , GET_path1_path2_file5__key1_ } ,			/* curl "http://localhost/path1/path2/file5?key1=" */
		{ HTTP_METHOD_GET , "/path1/path2/file6" , GET_path1_path2_file6__key1__ } ,			/* curl "http://localhost/path1/path2/file6?key1&" */
		{ HTTP_METHOD_GET , "/path1/path2/file7" , GET_path1_path2_file7__key1___ } ,			/* curl "http://localhost/path1/path2/file7?key1=&" */
		{ HTTP_METHOD_POST , "/path1/file" , POST_path1_file } ,		/* curl -X POST -d "this is a POST test for restserver" "http://localhost/path1/file" */
		{ HTTP_METHOD_PUT , "/path1/file" , PUT_path1_file } ,		/* curl -X PUT -d "this is a PUT test for restserver" "http://localhost/path1/file" */
		{ HTTP_METHOD_DELETE , "/path1/file" , DELETE_path1_file } ,	/* curl -X DELETE -d "this is a DELETE test for restserver" "http://localhost/path1/file" */
		{ "" , "" , NULL }
	} ;

6.4. RESTful应用开发参考

6.4.1. 工具宏

6.4.1.1. HTTP方法宏

用于在RESTful控制器配置中配置。

宏定义 #define HTTP_METHOD_GET "GET"
宏定义 #define HTTP_METHOD_POST "POST"
宏定义 #define HTTP_METHOD_HEAD "HEAD"
宏定义 #define HTTP_METHOD_TRACE "TRACE"
宏定义 #define HTTP_METHOD_OPTIONS "OPTIONS"
宏定义 #define HTTP_METHOD_PUT "PUT"
宏定义 #define HTTP_METHOD_DELETE "DELETE"

6.4.2. 函数原型

6.4.2.1. RESTful应用动态库函数原型

6.4.2.1.1. funcRestServiceEntry
函数原型 typedef int funcRestServiceEntry( struct RestServiceContext *ctx );
函数说明 当RESTful控制器分拣时调用
输入参数 struct RestServiceContext *ctx : RESTful环境上下文环境
输出参数 (无)
返回值 0 : 构造成功
非0 : 失败,具体失败原因见错误宏

6.4.3. API函数

6.4.3.1. RESTful服务控制器类

6.4.3.1.1. RESTCreateRestServiceControler
函数原型 struct RestServiceControler *RESTCreateRestServiceControler( struct RestServiceConfig *config_array ); );
函数说明 用代码中配置的RESTful路由表,构造RESTful服务控制器
输入参数 struct RestServiceConfig *config_array : 在代码中配置的RESTful路由表;http_method是HTTP请求头方法,http_uri_paths_match是HTTP请求头URI,目录文件名可用"{}"通配,如"/books/{}",pfuncRestServiceEntry是RESTful服务入口函数
输出参数 (无)
返回值 struct RestServiceControler * : RESTful服务控制器
6.4.3.1.2. RESTDispatchRestServiceControler
函数原型 int RESTDispatchRestServiceControler( struct RestServiceControler *ctl , struct HttpEnv *http );
函数说明 用当前HTTP请求,查询RESTful服务控制器,分派调用RESTful服务入口函数
输入参数 struct RestServiceControler *ctl : RESTful服务控制器
struct HttpEnv *http : HTTP结构,内有当前HTTP请求,调用socgi层函数SOCGIGetHttpEnv取出
输出参数 (无)
返回值 0 : 分派成功
非0 : 失败,具体失败原因见错误宏
6.4.3.1.3. RESTDestroyRestServiceControler
函数原型 void RESTDestroyRestServiceControler( struct RestServiceControler *ctl );
函数说明 销毁RESTful服务控制器
输入参数 struct RestServiceControler *ctl : RESTful服务控制器
输出参数 (无)
返回值 (无)

6.4.3.2. 查询RESTful请求信息类

6.4.3.2.1. RESTGetHttpMethodPtr
函数原型 char *RESTGetHttpMethodPtr( struct RestServiceContext *ctx , int *p_http_method_len );
函数说明 从RESTful环境中得到当前HTTP请求的方法
输入参数 struct RestServiceContext *ctx : RESTful环境上下文环境
输出参数 int *p_method_len : 如果传入,返回时赋值为HTTP请求方法的长度
返回值 char * : HTTP请求方法;没有C字符串结束符,须按长度访问
6.4.3.2.2. RESTGetHttpUriPtr
函数原型 char *RESTGetHttpUriPtr( struct RestServiceContext *ctx , int *p_http_uri_len );
函数说明 从RESTful环境中得到当前HTTP请求的方法
输入参数 struct RestServiceContext *ctx : RESTful环境上下文环境
输出参数 int *p_method_len : 如果传入,返回时赋值为HTTP请求方法的长度
返回值 char * : HTTP请求方法;没有C字符串结束符,须按长度访问
6.4.3.2.3. RESTGetHttpUriPathsCount
函数原型 int RESTGetHttpUriPathsCount( struct RestServiceContext *ctx );
函数说明 从RESTful环境中得到当前HTTP请求URI的分解出来的目录文件数量
输入参数 struct RestServiceContext *ctx : RESTful环境上下文环境
输出参数 (无)
返回值 int : 分解出来的目录文件数量
6.4.3.2.4. RESTGetHttpUriPathPtr
函数原型 char *RESTGetHttpUriPathPtr( struct RestServiceContext *ctx , int index , int *p_path_len );
函数说明 从RESTful环境中得到当前HTTP请求URI的分解出来的某段目录文件名
输入参数 struct RestServiceContext *ctx : RESTful环境上下文环境< br />int index : 分解出来的目录文件名段序号;从1开始
int *p_path_len : 目录文件名长度
输出参数 (无)
返回值 char * : 分解出来的某段目录文件名;没有C字符串结束符,须按长度访问
6.4.3.2.5. RESTGetHttpUriQueriesCount
函数原型 int RESTGetHttpUriQueriesCount( struct RestServiceContext *ctx );
函数说明 从RESTful环境中得到当前HTTP请求URI的分解出来的参数数量
输入参数 struct RestServiceContext *ctx : RESTful环境上下文环境
输出参数 (无)
返回值 int : 分解出来的参数数量
6.4.3.2.6. RESTGetHttpUriQueryKeyPtr
函数原型 char *RESTGetHttpUriQueryKeyPtr( struct RestServiceContext *ctx , int index , int *p_key_len );
函数说明 从RESTful环境中得到当前HTTP请求URI的分解出来的某段参数名
输入参数 struct RestServiceContext *ctx : RESTful环境上下文环境< br />int index : 分解出来的参数段序号;从1开始
int *p_key_len : 参数名长度
输出参数 (无)
返回值 char * : 分解出来的某段参数名;没有C字符串结束符,须按长度访问
6.4.3.2.7. RESTGetHttpUriQueryValuePtr
函数原型 char *RESTGetHttpUriQueryValuePtr( struct RestServiceContext *ctx , int index , int *p_value_len );
函数说明 从RESTful环境中得到当前HTTP请求URI的分解出来的某段参数值
输入参数 struct RestServiceContext *ctx : RESTful环境上下文环境< br />int index : 分解出来的参数段序号;从1开始
int *p_key_len : 参数值长度
输出参数 (无)
返回值 char * : 分解出来的某段参数值;没有C字符串结束符,须按长度访问
6.4.3.2.8. RESTGetHttpRequestBodyPtr
函数原型 char *RESTGetHttpRequestBodyPtr( struct RestServiceContext *ctx , int *p_body_len );
函数说明 从RESTful环境中得到当前HTTP请求体
输入参数 struct RestServiceContext *ctx : RESTful环境上下文环境< br />int *p_body_len : HTTP请求体长度
输出参数 (无)
返回值 char * : HTTP请求体;没有C字符串结束符,须按长度访问

6.4.3.3. 构造RESTful响应信息类

6.4.3.3.1. RESTFormatHttpResponse
函数原型 int RESTFormatHttpResponse( struct RestServiceContext *ctx , char *http_response_body , int http_response_body_len , char *http_header_format , ... );
函数说明 构造HTTP响应
输入参数 struct RestServiceContext *ctx : RESTful环境上下文环境
char *http_response_body : HTTP响应体
int http_response_body_len : HTTP响应体长度
char *http_header_format , ... : HTTP响应头;如果有多行的话,要用"\r\n"分隔
输出参数 (无)
返回值 0 : 构造成功
非0 : 失败,具体失败原因见错误宏

6.4.3.4. 其它类

6.4.3.4.1. RESTGetHttpEnv
函数原型 struct HttpEnv *RESTGetHttpEnv( struct RestServiceContext *ctx );
函数说明 从RESTful环境上下文环境中得到HTTP结构,后续可以使用fasterhttp库操作该结构,编译时包含其头文件,链接时包含其库文件
输入参数 struct RestServiceContext *ctx : HTTP环境上下文环境
输出参数 (无)
返回值 HTTP环境

7. 压测

7.1. 压测环境

7.1.1. 压测平台

压测发起端为台机PC(192.168.6.17),配置如下:

CPU : Intel Core i3-3240 3.40GHz 3.40GHz
内存 : 512MB
WindowsXP里面装了VMWARE 10里面装了RedHat Enterprise Linux Server release 5.4 ( 32BITS )

压测网络为百兆有线

压测服务端为台机PC(192.168.6.111),配置如下:

CPU : AMD E-350 1.60GHz 1.60GHz
内存 : 4GB
RedHat Enterprise Linux Server release 5.4 ( 32BITS )

7.1.2. 压测客户端

压测客户端采用Apache自带工具ab。

因ab只支持HTTP/1.0而不支持HTTP/1.1,会引发Nginx的压缩和Keep-Alive不能同时开启的BUG,故修改了ab.c中填充HTTP请求版本的代码,重新编译成ab2供压测使用。hetao和Apache则不受影响。

httpd-2.2.17/support/ab.c

   1609     /* setup request */
   1610     if (posting <= 0) {
   1611         snprintf_res = apr_snprintf(request, sizeof(_request),
   1612             "%s %s HTTP/1.1\r\n"
   1613             "%s" "%s" "%s"
   1614             "%s" "\r\n",
   1615             (posting == 0) ? "GET" : "HEAD",
   1616             (isproxy) ? fullurl : path,
   1617             keepalive ? "Connection: Keep-Alive\r\n" : "",
   1618             cookie, auth, hdrs);
   1619     }
   1620     else {
   1621         snprintf_res = apr_snprintf(request,  sizeof(_request),
   1622             "%s %s HTTP/1.1\r\n"
   1623             "%s" "%s" "%s"
   1624             "Content-length: %" APR_SIZE_T_FMT "\r\n"
   1625             "Content-type: %s\r\n"
   1626             "%s"
   1627             "\r\n",
   1628             (posting == 1) ? "POST" : "PUT",
   1629             (isproxy) ? fullurl : path,
   1630             keepalive ? "Connection: Keep-Alive\r\n" : "",
   1631             cookie, auth,
   1632             postlen,
   1633             (content_type[0]) ? content_type : "text/plain", hdrs);
   1634     }

7.1.3. 压测服务端

选用以下Web服务器软件做横向压测,版本和配置侦听端口如下:

hetao/0.2.0 && hetao/0.7.0,侦听端口为9527
Nginx/1.9.13,侦听端口为9528
Apache/2.2.14,侦听端口为9529
Tengine/2.1.2,侦听端口为9530

(原计划还有kangle/3.4.8,但是从官网上下载的源代码编译安装始终报错,猜可能是我的Linux编译器gcc版本过低,不支持__sync_原子操作,但rhel5.4也不低啊,算了,不用它了)

...
g++ -I../module/access -I../module/whm -O2 -g -DNDEBUG  -D_REENTRANT -DLINUX -D_LARGE_FILE -D_FILE_OFFSET_BITS=64 -D__USE_FILE_OFFSET64 -L../lib   -o kangle cache.o KConfig.o forwin32.o garbage_c.o HttpCore.o KAccess.o KAcserver.o KAcserverManager.o KBuffer.o KChain.o KConfigBuilder.o KConfigParser.o KContentType.o KDiskCache.o KPortSelector.o KKqueueSelector.o KEpollSelector.o KFastcgiFetchObject.o KFastcgiUtils.o KFetchObject.o KFileMsg.o KFileName.o KHtmlSupport.o KHtmlSupportException.o KHttpKeyValue.o KHttpManage.o KHttpObject.o KHttpObjectHash.o KHttpObjectParserHook.o KHttpProtocolParser.o KHttpProtocolParserHook.o KHttpProxyFetchObject.o KHttpRequest.o KHttpServerParser.o KLang.o KLangParser.o KLogElement.o KReg.o KSelector.o KSelectorManager.o KSequence.o KServerListen.o KSocket.o KSocketFetchObject.o KTable.o KThreadPool.o KTimeMatch.o KUrlValue.o KVirtualHost.o KVirtualHostManage.o KWriteBack.o KWriteBackManager.o KXmlContext.o KXml.o KXmlException.o KXmlSupport.o lib.o log.o main.o malloc_debug.o md5.o work.o utils.o KAccessParser.o KString.o KRewriteMark.o KSingleProgram.o KHttpTransfer.o KDeChunked.o KGzip.o KServer.o KSelectable.o KStream.o KNsVirtualHost.o KContentMark.o KRedirectMark.o KLineFile.o KMultiHostAcl.o test.o KHttpFieldValue.o KSingleAcserver.o KMultiAcserver.o KSockPoolHelper.o KEnvInterface.o KRedirect.o KCgiRedirect.o KCgiFetchObject.o KPipeStream.o KCgi.o KCgiEnv.o KApiRedirect.o KApiEnv.o HttpExt.o KApiFetchObject.o KHttpHeadPull.o KSockFastcgiFetchObject.o KApiFastcgiFetchObject.o KPathRedirect.o KLogManage.o KBaseVirtualHost.o process.o KContentTransfer.o KChunked.o KCacheStream.o KHttpField.o KHttpDigestAuth.o KHttpAuth.o KHttpBasicAuth.o KAuthMark.o KObjectList.o KAjpMessage.o KAjpFetchObject.o KExpressionParseTree.o KSSICommandCondition.o KSSICommandEcho.o KSSICommandInclude.o KSSIContext.o KSSIRedirect.o KSSICommandSet.o KSSIProcess.o KSSICommand.o KSSICommandPrintEnv.o KSSIFetchObject.o KServiceProvider.o KISAPIServiceProvider.o directory.o KSSICommandExec.o KSSICommandConfig.o ssl_utils.o KApiPipeStream.o KPoolableSocketContainer.o KProcessManage.o KCmdPoolableRedirect.o KSubVirtualHost.o KIpVirtualHost.o KHttpPost.o KHtAccess.o KHtModule.o KHtRewriteModule.o KRewriteMarkEx.o EdcodeUtils.o KProcess.o KApiProcess.o KCmdProcess.o KVirtualHostProcess.o KExtendProgram.o KDynamicString.o kmysql.o KCdnMysqlMark.o KCdnRewriteMark.o KCdnContainer.o KTempleteVirtualHost.o KVirtualHostDatabase.o KDsoModule.o KList.o KListNode.o KLogHandle.o KRequestQueue.o KContext.o KCdnRedirect.o time_utils.o rbtree.o KVirtualHostContainer.o KSocketBuffer.o KAsyncFetchObject.o KSyncFetchObject.o KStaticFetchObject.o KDirectoryFetchObject.o KApiDso.o KUwsgiFetchObject.o KScgiFetchObject.o KHmuxFetchObject.o KTempFile.o KListenConfigParser.o KApacheVirtualHost.o KSSLSocket.o KAsyncWorker.o KInputFilter.o KMultiPartInputFilter.o KReplaceContentMark.o KReplaceContentFilter.o KConcatFetchObject.o KIpSpeedLimitMark.o KDynamicListen.o KCache.o KPerIpAcl.o KDiskCacheIndex.o KSqliteDiskCacheIndex.o ../module/whm/dllmain.o ../module/whm/WhmCallMap.o ../module/whm/WhmCommand.o ../module/whm/WhmContext.o ../module/whm/whm.o ../module/whm/WhmLog.o ../module/whm/WhmPackage.o ../module/whm/WhmPackageManage.o ../module/whm/KWhmService.o ../module/whm/stdafx.o ../module/whm/WhmDso.o ../module/whm/WhmExtend.o ../module/whm/WhmUrl.o ../module/whm/WhmShell.o ../module/whm/WhmShellProcess.o ../module/whm/WhmShellSession.o ../module/whm/whmdso/core/core.o KTimer.o KUrlParser.o KHttpFilterContext.o KHttpFilterDso.o KHttpFilterDsoManage.o KHttpFilterHookCollectRequest.o KHttpFilterHook.o KHttpFilterManage.o KTempFileStream.o KHttpFilterStream.o KHttpFilterHookCollectResponse.o KAccessDso.o KConnectionSelectable.o KReadWriteBuffer.o KResponseContext.o KUpstreamSelectable.o KSimulateRequest.o KCloudIpAcl.o   -lpthread -lpcre -lz -ldl
KConfig.o: In function `katom_cas':
/home/calvin/expack/kangle-3.4.8/src/katom.h:107: undefined reference to `__sync_bool_compare_and_swap_4'
KConfig.o: In function `katom_inc':
/home/calvin/expack/kangle-3.4.8/src/katom.h:39: undefined reference to `__sync_add_and_fetch_4'
HttpCore.o: In function `katom_inc':
/home/calvin/expack/kangle-3.4.8/src/katom.h:39: undefined reference to `__sync_add_and_fetch_4'
HttpCore.o: In function `katom_dec':
/home/calvin/expack/kangle-3.4.8/src/katom.h:49: undefined reference to `__sync_add_and_fetch_4'
HttpCore.o: In function `katom_dec':
/home/calvin/expack/kangle-3.4.8/src/KHttpRequest.h:353: undefined reference to `__sync_add_and_fetch_4'
HttpCore.o: In function `katom_inc':
/home/calvin/expack/kangle-3.4.8/src/katom.h:39: undefined reference to `__sync_add_and_fetch_4'
HttpCore.o:/home/calvin/expack/kangle-3.4.8/src/katom.h:49: more undefined references to `__sync_add_and_fetch_4' follow
...

7.2. 压测方案

考察现代浏览器默认配置的开启HTTP长连接Keep-Alive、开启gzip压缩、中型大小网页的GET性能

并发1000,共发起HTTP请求5万次,目标网页文件大小约3.3KB

准备网页文件press.html

-rwxrwxr-x 1 calvin calvin 3321 08-27 21:03 press.html

命令:

$ ab2 -kc 1000 -n 50000 -H "Accept-Encoding: gzip" http://192.168.6.111:????/press.html

7.3. 压测过程

先交替的各压一次热热身(可以预览一下性能)

$ ab2 -kc 1000 -n 50000 -H "Accept-Encoding: gzip" http://192.168.6.111:9527/press.html
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.6.111 (be patient)
Completed 10000 requests
Completed 20000 requests
Completed 30000 requests
Completed 40000 requests
Completed 50000 requests
Completed 60000 requests
Completed 70000 requests
Completed 80000 requests
Completed 90000 requests
Completed 100000 requests
Finished 100000 requests


Server Software:        hetao/0.2.0
Server Hostname:        192.168.6.111
Server Port:            9527

Document Path:          /press.html
Document Length:        281 bytes

Concurrency Level:      1000
Time taken for tests:   6.923 seconds
Complete requests:      100000
Failed requests:        0
Write errors:           0
Keep-Alive requests:    100000
Total transferred:      41709990 bytes
HTML transferred:       28242186 bytes
Requests per second:    14445.19 [#/sec] (mean)
Time per request:       69.227 [ms] (mean)
Time per request:       0.069 [ms] (mean, across all concurrent requests)
Transfer rate:          5883.87 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0   13 198.9      0    3080
Processing:     1   31 102.2     15    3456
Waiting:        0   31 102.2     15    3456
Total:          1   44 242.5     15    3479

Percentage of the requests served within a certain time (ms)
  50%     15
  66%     31
  75%     36
  80%     43
  90%     64
  95%     85
  98%    108
  99%    126
 100%   3479 (longest request)

$ ab2 -kc 1000 -n 50000 -H "Accept-Encoding: gzip" http://192.168.6.111:9528/press.html
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.6.111 (be patient)
Completed 10000 requests
Completed 20000 requests
Completed 30000 requests
Completed 40000 requests
Completed 50000 requests
Completed 60000 requests
Completed 70000 requests
Completed 80000 requests
Completed 90000 requests
Completed 100000 requests
Finished 100000 requests


Server Software:        nginx/1.9.13
Server Hostname:        192.168.6.111
Server Port:            9528

Document Path:          /press.html
Document Length:        293 bytes

Concurrency Level:      1000
Time taken for tests:   23.928 seconds
Complete requests:      100000
Failed requests:        0
Write errors:           0
Keep-Alive requests:    99004
Total transferred:      54195020 bytes
HTML transferred:       29300000 bytes
Requests per second:    4179.19 [#/sec] (mean)
Time per request:       239.281 [ms] (mean)
Time per request:       0.239 [ms] (mean, across all concurrent requests)
Transfer rate:          2211.83 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0   23 268.2      0    3167
Processing:     1  127 904.6     75   20671
Waiting:        0  127 904.6     75   20670
Total:          1  150 1058.7     75   23814

Percentage of the requests served within a certain time (ms)
  50%     75
  66%     86
  75%     87
  80%     87
  90%     92
  95%     96
  98%     96
  99%   2365
 100%  23814 (longest request)

$ ab2 -kc 1000 -n 50000 -H "Accept-Encoding: gzip" http://192.168.6.111:9529/press.html
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.6.111 (be patient)
Completed 10000 requests
Completed 20000 requests
Completed 30000 requests
Completed 40000 requests
Completed 50000 requests
Completed 60000 requests
Completed 70000 requests
Completed 80000 requests
Completed 90000 requests
Completed 100000 requests
Finished 100000 requests


Server Software:        Apache/2.2.14
Server Hostname:        192.168.6.111
Server Port:            9529

Document Path:          /press.html
Document Length:        281 bytes

Concurrency Level:      1000
Time taken for tests:   39.800 seconds
Complete requests:      100000
Failed requests:        0
Write errors:           0
Keep-Alive requests:    99119
Total transferred:      65363814 bytes
HTML transferred:       28101124 bytes
Requests per second:    2512.58 [#/sec] (mean)
Time per request:       397.998 [ms] (mean)
Time per request:       0.398 [ms] (mean, across all concurrent requests)
Transfer rate:          1603.83 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1  48.4      0    3001
Processing:     1   97 774.0      2   25875
Waiting:        0   97 773.8      2   25875
Total:          1   98 780.3      2   25897

Percentage of the requests served within a certain time (ms)
  50%      2
  66%      3
  75%      3
  80%      4
  90%      6
  95%     82
  98%   1398
  99%   2352
 100%  25897 (longest request)

$ ab2 -kc 1000 -n 50000 -H "Accept-Encoding: gzip" http://192.168.6.111:9530/press.html
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.6.111 (be patient)
Completed 10000 requests
Completed 20000 requests
Completed 30000 requests
Completed 40000 requests
Completed 50000 requests
Completed 60000 requests
Completed 70000 requests
Completed 80000 requests
Completed 90000 requests
Completed 100000 requests
Finished 100000 requests


Server Software:        Tengine/2.1.2
Server Hostname:        192.168.6.111
Server Port:            9530

Document Path:          /press.html
Document Length:        293 bytes

Concurrency Level:      1000
Time taken for tests:   25.203 seconds
Complete requests:      100000
Failed requests:        0
Write errors:           0
Keep-Alive requests:    99027
Total transferred:      51895135 bytes
HTML transferred:       29300000 bytes
Requests per second:    3967.81 [#/sec] (mean)
Time per request:       252.028 [ms] (mean)
Time per request:       0.252 [ms] (mean, across all concurrent requests)
Transfer rate:          2010.84 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0   16 233.6      0   21003
Processing:     1   91 485.5     33   21267
Waiting:        1   91 485.5     33   21267
Total:          1  107 637.0     33   24392

Percentage of the requests served within a certain time (ms)
  50%     33
  66%    101
  75%    112
  80%    112
  90%    132
  95%    182
  98%    213
  99%    337
 100%  24392 (longest request)

然后交替压10次,取“Requests per second”的值。

7.4. 压测结果

ROUND hetao/0.2.0 nginx/1.9.13 Apache/2.2.14 Tengine/2.1.2
1 13191.46 4208.08 2472.8 4282.31
2 14237.18 4395.69 2466.09 4013.62
3 14650.89 4245.99 2471.7 4346.52
4 16023.53 4234.76 2454.81 4152.04
5 14409.31 4206.19 2469.85 4381.55
6 15535.74 4184.32 2458.29 4013.12
7 14893.44 4110.75 2471.7 4313.44
8 14581.95 4406.23 2467.72 4014.66
9 14572.83 4171.1 2481.25 4250.61
10 13868.79 4100.61 2479.77 4209.85

Benchmark_hetao0_2_0_nginx_apache_tengine.jpg

ROUND hetao/0.7.0 nginx/1.9.13 Apache/2.2.14 Tengine/2.1.2
1 14661.97 4207.47 2498.64 4129.57
2 14751.86 4026.66 2474.3 4077.7
3 15424.81 4228.18 2482.39 4163.84
4 15393.55 4407.28 2477.92 4256.37
5 16013.01 4174.62 2480.06 3951.33
6 13500.85 4229.33 2474.44 4257.32
7 16960.94 4115.91 2480.42 4034.18
8 15429.98 4198.95 2475.3 3966.88
9 14829.60 4108.92 2483.37 4289.45
10 14901.13 4377.75 2485.03 3969.52

Benchmark_hetao0_7_0_nginx_apache_tengine.jpg

结论:

在开启Keep-Alive和gzip压缩、中型文件(约3.3KB)的场景下,hetao比nginx足足快了近3倍 ^_^
(现代浏览器一般都开启Keep-Alive和压缩,3.3KB也算是普遍的网页大小)

8. 内部实现

详细内部设计实现见 《如何设计一个Web服务器》

8.1. 进程和线程结构

process_and_thread_structure.png

hetao进程结构:

  • 管理进程,负责创建、监管工作进程,负责传递signal管理命令。
  • 工作进程,负责通讯协议处理。

工作进程结构:

  • 工作线程,负责多路复用IO管理,负责解析HTTP,负责静态文件的响应和缓存。
  • 定时器线程,负责定时更新用于日志输出的时间缓冲区。

8.2. 函数调用关系图

8.2.1. 启动与初始化

boot_and_initial.png

启动后,经过装载配置和初始化环境后,函数BindDaemonServer转换进程为守护进程,切换到管理进程角色。

8.2.2. 管理进程

monitor_process.png

创建所有管道和工作进程,然后监控工作进程结束事件,重启工作进程。

如果期间接收到signal,通过管道传递命令给所有工作进程。

8.2.3. 工作进程

worker_process.png

创建多路复用IO池,加入管道、文件缓存句柄、侦听端口,然后进入主循环,等待IO事件。

如果是侦听端口事件,接受连接放入多路复用IO池。

如果是通讯会话事件,收发数据,处理HTTP请求,加入文件监控句柄,并修改多路复用IO等待事件掩码。

如果是文件缓存事件,清理该文件监控句柄。

如果是管道事件,处理管理进程传递过来的事件。

9. 最后

hetao,一个高性能、功能完整、支持搭载应用的国产原创Web服务器。

欢迎使用hetao,如果你使用中碰到了问题请告诉我,谢谢 ^_^

源码托管地址 : 开源中国github

关于作者:厉华,左手C,右手JAVA,写过小到性能卓越方便快捷的日志库、HTTP解析器、日志采集器等,大到交易平台/中间件等,分布式系统实践者,容器技术专研者,目前在某城商行负责基础架构。

通过邮箱可以联系我 : 网易Gmail

Comments ( 31 )

Sign in for post a comment

About

一个高性能、功能完整、支持搭载应用的国产原创Web服务器 spread retract
C and 6 more languages
LGPL-2.1
Cancel

Releases

No release

Gitee Metrics

Contributors

All

Activities

load more
can not load any more
C
1
https://gitee.com/calvinwilliams/hetao.git
git@gitee.com:calvinwilliams/hetao.git
calvinwilliams
hetao
hetao
release

Search