1 Star 0 Fork 62

minphone / hetao

forked from calvinwilliams / hetao 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
LGPL-2.1

高性能、功能完整、支持搭载应用的国产原创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

GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. (This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.) Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. {description} Copyright (C) 2016 calvinwilliams This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. {signature of Ty Coon}, 1 April 1990 Ty Coon, President of Vice That's all there is to it!

简介

一个高性能、功能完整、支持搭载应用的国产原创Web服务器 展开 收起
C
LGPL-2.1
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
C
1
https://gitee.com/minphone/hetao.git
git@gitee.com:minphone/hetao.git
minphone
hetao
hetao
release

搜索帮助