• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

    restserver: 一个轻量级、高性能、耗资源极少的RESTful应用服务平台 ...

    原作者: [db:作者] 来自: 网络 收藏 邀请

    开源软件名称:

    restserver

    开源软件地址:

    https://gitee.com/calvinwilliams/restserver

    开源软件介绍:

    一个小巧、高效、低耗的C技术栈的RESTful应用服务平台(restserver)

    1. 诞生

    我家中的PC配置强劲,我使用一直喜欢的firefox浏览器浏览网页,公司里笔记本配置较弱,只能跑chrome浏览器,于是跨浏览器书签同步是个老大难问题,搜了很久也没找到一款的通吃主流浏览器、免费好用的书签插件,于是想自己开发一个,采用RESTful接口风格。

    我不懂浏览器端开发,那就先把服务器端写好部署到网上,对外提供服务,其他熟悉浏览器端开发同学有兴趣有时间都可以开发自己品牌的浏览器端插件,对接我的服务端,这会不会是另一种项目协作方式呢。

    目前服务器端应用服务平台C技术栈有Apache,JAVA技术栈有Tomcat,不过这些都是HTTP服务器,没有对RESTful层做封装,开发起来很累,而且将来计划部署到我的阿里云服务器上,但只有1GB内存,采用JAVA技术栈的话能启动起来就不错了,不指望能对外提供正常规模的服务,一盘算,先研发一个C技术栈的RESTful应用服务平台,再在上面开发浏览器书签SaaS服务,这就是本项目的起源。

    首先取个简单好记的名字,由于是RESTful接口风格的服务平台,就叫做“restserver”吧 ^_^

    2. 简介

    restserver是一个小巧、高效、低耗的C技术栈的RESTful应用服务平台。

    小巧是因为链接出来的可执行程序只有300多KB,应用接口库80KB,本体源码都在一个目录中,手写的大概一千行左右,用预置好的makefile一条命令就能完成源码编译安装。

    高效是因为她完全用C编写而成,采用多进程+多路复用模型,参考Nginx。

    低耗是因为空载运行只占了几MB内存,特别适合买不起高配云服务器的个人开发者。对于企业来说,现在动不动就要求8、16、32GB内存配置,如果软件能低耗运行,节省下来的硬件支出也是相当可观,或者说相同配置的硬件上能对外提供更大容量的应用服务。

    C技术栈在前面已经提到了,考虑到现实情况,我要在网上唯一拥有的服务器上运行,只能用C写,所以,“缺乏资金”和“懒”一样,都是人类文明进步的原动力。

    经过两个晚上和周末两天的集中研发,感谢老婆、孩子的不打扰之恩,也感谢以前的我留下来那么多封装良好的库、工具、框架,我只手写了大概一千多行就组装出了一个可运行的版本,又经过几个晚上的打磨,restserver横空出世,还是那句话,脑子里想想和动手去做是两件完全不同的事,实现核心功能和打造完整软件又是另外两件完全不同的事。

    restserver功能特性如下:(截止版本v0.8.0)

    • HTTP核心功能:如侦听IP、PORT、域名匹配、超时控制。
    • HTTP安全控制:防御巨量HTTP头选项、防御巨大HTTP头、防御巨大HTTP体。
    • 平台封装至RESTful层:与Apache、Tomcat封装HTTP层相比,封装层次更高,应用无需处理HTTP层的众多细节,自带RESTful控制器直接分派到RESTful服务入口,应用接口直接提供RESTful编程接口。你也可以编写自己的控制器替换自带控制器。
    • 多进程+多路复用模型:充分利用多核环境,防御慢速TCP,支持巨量TCP连接和同时收发,且性能卓越。
    • 可执行程序+动态库模式:restserver是应用服务平台(可执行程序),启动后装载应用(动态库),外来请求被平台接收和解析,转交给应用动态库处理,处理完后返回平台,发送响应回去,平台和应用的部署运行边界解耦清晰。
    • 运行模式:以前给公司研发的多款平台框架沉淀下来的优秀设计思想,测试模式即时装卸应用,重构应用后无需重启平台,生产模式预装载应用,性能无损耗,谁说鱼与熊掌不可兼得?那是教条!
    • 平台自有日志设施:可配置日志文件名、日志等级,同时应用也能使用。

    3. "Hello world"

    上一节功能特性里有说到应用服务平台(可执行程序)restserver启动后装载应用(动态库)工作,因为restserver比Apache、Tomcat多封装了一层RESTful,所以应用要实现的内容就很少了,这有助于减少应用开发量,也尽可能瘦化应用代码结构,简洁的就是最好的。

    本节Hello示例代码在源码路径example/hello/,你可以在安装完restserver后自行到hello目录中编译运行、修改代码再编译运行,体验绕过HTTP直接编写RESTful服务的方便性。

    只有一个源文件hello.c,直接上代码:

    #include "restserver_api.h"funcRestServiceEntry GET_hello;int GET_hello( struct RestServerContext *ctx ){	char		response[1024] ;	int		response_len ;		int		nret = 0 ;		/* 初始化临时缓冲区 */	memset( response , 0x00 , sizeof(response) );	response_len = 0 ;		/* 填充hello信息 */	response_len = snprintf( response , sizeof(response) , "Hello restserver\n" ) ;		/* 构造HTTP缓冲区 */	nret = RSAPIFormatHttpResponse( ctx , response , response_len , NULL ) ;	if( nret )		return nret;		return 0;}static struct RestServiceConfig		g_rest_services_config[] = {		{ "GET" , "/hello" , GET_hello } ,		{ "" , "" , NULL }	} ;funcInitRestApplication InitRestApplication;int InitRestApplication( struct RestServerContext *ctx ){	struct RestServiceControler	*ctl = NULL ;		/* 创建RESTful服务控制器 */	ctl = RSAPICreateRestServiceControler( g_rest_services_config ) ;	if( ctl == NULL )		return RESTSERVER_FATAL_CREATE_RESTSERVICECONTROLER;		/* 设置RESTful服务控制器到restserver动态库对象运行实例中 */	RSAPISetUserData( ctx , ctl );		return 0;}funcCallRestApplication CallRestApplication;int CallRestApplication( struct RestServerContext *ctx ){	struct RestServiceControler	*ctl = NULL ;		int				nret = 0 ;		/* 从restserver动态库对象运行实例中取出RESTful服务控制器 */	ctl = RSAPIGetUserData( ctx ) ;	if( ctl == NULL )		return RESTSERVER_FATAL_GET_RESTSERVICECONTROLER;		/* 让RESTful服务控制器分派服务处理入口 */	nret = RSAPIDispatchRestServiceControler( ctl , ctx ) ;	if( nret )		return nret;		return 0;}funcCleanRestApplication CleanRestApplication;int CleanRestApplication( struct RestServerContext *ctx ){	struct RestServiceControler	*ctl = NULL ;		/* 从restserver动态库对象运行实例中取出RESTful服务控制器 */	ctl = RSAPIGetUserData( ctx ) ;		/* 销毁RESTful服务控制器 */	RSAPIDestroyRestServiceControler( ctl );		return 0;}

    每一个应用(动态库)中必须有三个函数InitRestApplication、CallRestApplication和CleanRestApplication,分别在动态库装载时、HTTP请求发生时、卸载时调用。

    当HTTP请求是"GET /hello",RESTful服务控制器从配置中查出服务处理入口函数是GET_helloHello服务处理逻辑为填充临时缓冲区,最后构造HTTP响应缓冲区。

    运行过程:

    1. 先打开第一个窗口,启动restserver
    $ cd $HOME$ restserver src/restserver/example/hello/restserver.conf

    注意:指定的配置文件路径根据你放源码的目录按需调整。注意:生产运行需要用nohup放置后台跑。

    1. 再打开第二个窗口,用curl发起HTTP请求

    测试示例Hello的脚本是src/restserver/example/hello/hello.sh,也可以直接执行,里面就一行

    $ curl "http://localhost:7911/hello"Hello restserver

    看到打招呼了没?测试成功!

    4. 架构设计

    4.1. 开发架构

    images/deve_arch_1.png

    启动restserver时装载动态库对外提供服务,restserver是应用服务平台,动态库放所有应用服务逻辑。

    images/deve_arch_2.png

    完整的开发架构左边是第三方库/工具(其实大多数也是我的开源项目^_^),右边是public library(项目内公共模块)、rest server context(平台环境上下文模块)、restserver api(平台提供应用API模块)、rest service controler(服务控制器模块),中间从下到上、按调用顺序分别是main(启动入口层)、tcpdaemon(进程与通讯管理层)、tcpmain(HTTP通讯收发层)、process http request(HTTP应用处理接口层)和restful service application(RESTful应用逻辑层)。

    第三方库/工具说明表:

    库/工具项目名及版本简介说明备注
    list链表函数库负责链表数据结构实现从Linux Kernel里挖出来的
    LOGC v1.3.2日志函数库负责输出日志;已复制到restserver里我的开源项目,传送门 开源中国github
    fasterjson v1.1.8JSON解析函数库和DirectStruct配合,负责解析配置文件(JSON格式);需要自行安装我的开源项目,传送门 开源中国github
    DirectStruct v1.14.0报文序列化/反序列化代码自动生成器根据定义文件,自动生成调用fasterjson的源代码;改造开发时才需要安装我的开源项目,传送门 开源中国github
    tcpdaemon v1.5.0TCP通讯服务端框架函数库负责指定通讯服务端模型的进程、通讯管理;需要自行安装我的开源项目,传送门 开源中国github
    fasterhttp v1.5.0HTTP解析函数库负责接收、解析和发送HTTP通讯数据;需要自行安装我的开源项目,传送门 开源中国github

    模块/层说明表:

    模块/层名简介说明
    public library项目内公共模块本项目内公共宏、函数等代码资源
    rest server context平台环境上下文模块包裹了平台内部流转的状态、数据、第三方库实例对象等,便于函数间调用
    restserver api平台提供应用API模块平台内各模块功能的API包装层,提供给RESTful应用逻辑层调用,作为应用获取HTTP、RESTful等信息的接口
    rest service controler服务控制器模块根据HTTP请求方法和URI分派调用对应RESTful服务入口的路由;用户可替换
    main启动入口层初始化环境、装载配置文件等启动时处理
    tcpdaemon进程与通讯管理层负责多进程+多路复用模型的进程、通讯管理;由第三方库tcpdaemon实现,静态库未libtcpdaemon.a,主要函数为tcpdaemon
    tcpmainHTTP通讯收发层作为tcpdaemon回调函数,当TCP事件发生时被调用,负责新连接到来、数据可读、数据可写、对端连接关闭时的处理
    process http requestHTTP应用处理接口层作为平台与应用之间的调用层,负责装载应用动态库,调用动态库应用构造函数、应用析构函数、RESTful请求处理函数
    restful service applicationRESTful应用逻辑层应用实现的层,提供应用构造函数、应用析构函数、RESTful请求处理函数给平台调用,调用平台服务控制器模块,让其根据HTTP方法和URI分派到指定的RESTful服务逻辑入口

    4.2. 过程架构

    images/proc_arch_2.png

    restserver启动后,装载配置文件,构造tcpdaemon通讯管理引擎参数,以多进程+多路复用模型调用tcpdaemon。

    tcpdaemon创建TCP服务端侦听,创建多进程,创建多路复用环境,注册TCP侦听事件,进入事件处理主循环,当有事件发生时调用之前设置的回调函数tcpmain。

    tcpmain分支不同的事件做相应处理:如果是TCP侦听事件,创建TCP会话和会话的HTTP环境,注册已连接会话结构到tcpdaemon,通知tcpdaemon注册TCP会话数据可读事件;如果是TCP会话数据可读事件,非堵塞接收HTTP报文,如果接收完整,调用ProcessHttpRequest,通知tcpdaemon改注册TCP会话数据可写事件;如果是TCP会话数据可读事件,非堵塞发送HTTP报文,如果发送完成,再判断是否需要保持连接,是就改注册TCP会话数据可读事件,否就通知关闭tcpdaemon触发关闭事件;如果是TCP连接关闭事件,清理HTTP环境和关闭TCP会话,注销已连接会话结构。

    ProcessHttpRequest创建平台上下文环境,然后解析HTTP请求到平台上下文环境中,然后如果没有装载动态库则装载之,然后如果没有调用过动态库中的应用构造函数InitRestApplication则调用之,然后调用动态库中的RESTful请求处理函数CallRestApplication,然后如果是测试模式调用动态库中的应用析构函数CleanRestApplication及卸载动态库。

    应用动态库的构造函数InitRestApplication创建RESTful服务控制器,装载RESTful服务路由表,最后设置RESTful服务控制器到restserver平台上下文环境中。

    应用动态库的RESTful请求处理函数CallRestApplication从restserver平台上下文环境中取出RESTful服务控制器,最后让RESTful服务控制器分派服务处理入口。

    应用动态库的细节函数CleanRestApplication从restserver平台上下文环境中取出RESTful服务控制器,最后销毁RESTful服务控制器。

    RESTful服务控制器分派函数RSAPIDispatchRestServiceControler在RESTful路由表查询符合当前RESTful请求的服务,调用服务入口。

    注意:restserver自带的RESTful服务控制器并不是必须的,你可以自己写一个替代自带版本。

    5. 安装部署

    5.1. 源码编译安装

    restserver依赖我的另外几个开源项目,首先下载那几个开源项目源码编译安装。

    5.1.1. 源码编译安装依赖项目

    5.1.1.1. fasterjson

    开源中国github下载解压源码或clone项目。

    进入src目录编译链接

    $ cd src$ make -f makefile.Linux install

    如果没有报错则说明源码编译安装成功,头文件安装在$HOME/include/fasterjson/,库文件安装在$HOME/lib/libfasterjson.so。

    5.1.1.2. tcpdaemon

    开源中国github下载解压源码或clone项目。

    进入src目录编译链接

    $ cd src$ make -f makefile.Linux install

    如果没有报错则说明源码编译安装成功,头文件安装在$HOME/include/tcpdaemon/,库文件安装在$HOME/lib/libtcpdaemon.a。

    5.1.1.3. fasterhttp

    开源中国github下载解压源码或clone项目。

    进入src目录编译链接

    $ cd src$ make -f makefile.Linux install

    如果没有报错则说明源码编译安装成功,头文件安装在$HOME/include/fasterhttp/,库文件安装在$HOME/lib/libfasterhttp.so。

    5.1.2. 源码编译安装restserver

    开源中国github下载解压源码或clone项目。

    进入src目录编译链接

    $ cd src$ make -f makefile.Linux install

    如果没有报错则说明源码编译安装成功,头文件安装在$HOME/include/restserver/,库文件安装在$HOME/lib/librestserver_api.so,可执行程序安装在$HOME/bin/restserver。

    5.1.3. 源码编译安装restserver的应用示例example

    进入example目录编译链接

    $ cd src$ make -f makefile.Linux install

    如果没有报错则说明源码编译安装成功,应用示例动态库文件安装在$HOME/so/RS_hello.so、$HOME/so/RS_rsapi。

    6. 开发应用

    6.1. 应用和服务

    从前面我们知道,可执行程序restserver启动后装载动态库实现业务逻辑对外服务,业务开发也就是开发一批服务,构建成一个应用,运行时restserver服务平台装载应用(动态库)即可。

    应用动态库中必须存在三个函数,一个是动态库的应用构造函数InitRestApplication,第二个是RESTful请求处理函数CallRestApplication,最后一个是应用析构函数CleanRestApplication,分别用于服务平台restserver装载动态库时、RESTful服务到来时和卸载动态库时调用。由于restserver提供了服务控制器模块(也可以自行研发),在构造函数中根据预配置的RESTful方法和URI路由表创建一个服务控制器实例,当RESTful请求到来时,平台调用RESTful请求处理函数,函数执行该服务控制器分派函数,分派函数用当前RESTful请求方法和URI查询控制器路由表,找到RESTful服务入口函数,从入口进入业务逻辑,当然最后卸载动态库时调用析构函数中销毁之。

    6.2. 一个通用代码模板

    一般的,一个应用动态库有如下代码框架:

    #include "restserver_api.h"funcRestServiceEntry GET_hello;int GET_hello( struct RestServerContext *ctx ){	char		response[1024] ;	int		response_len ;		int		nret = 0 ;		/* 初始化临时缓冲区 */	memset( response , 0x00 , sizeof(response) );	response_len = 0 ;		...		/* 构造HTTP缓冲区 */	nret = RSAPIFormatHttpResponse( ctx , response , response_len , NULL ) ;	if( nret )		return nret;		return 0;}static struct RestServiceConfig		g_rest_services_config[] = {		{ "GET" , "/hello" , GET_hello } ,		{ "" , "" , NULL }	} ;funcInitRestApplication InitRestApplication;int InitRestApplication( struct RestServerContext *ctx ){	struct RestServiceControler	*ctl = NULL ;		/* 创建RESTful服务控制器 */	ctl = RSAPICreateRestServiceControler( g_rest_services_config ) ;	if( ctl == NULL )		return RESTSERVER_FATAL_CREATE_RESTSERVICECONTROLER;		/* 设置RESTful服务控制器到restserver平台上下文环境中 */	RSAPISetUserData( ctx , ctl );		return 0;}funcCallRestApplication CallRestApplication;int CallRestApplication( struct RestServerContext *ctx ){	struct RestServiceControler	*ctl = NULL ;		int				nret = 0 ;		/* 从restserver平台上下文环境中取出RESTful服务控制器 */	ctl = RSAPIGetUserData( ctx ) ;	if( ctl == NULL )		return RESTSERVER_FATAL_GET_RESTSERVICECONTROLER;		/* 让RESTful服务控制器分派服务处理入口 */	nret = RSAPIDispatchRestServiceControler( ctl , ctx ) ;	if( nret )		return nret;		return 0;}funcCleanRestApplication CleanRestApplication;int CleanRestApplication( struct RestServerContext *ctx ){	struct RestServiceControler	*ctl = NULL ;		/* 从restserver平台上下文环境中取出RESTful服务控制器 */	ctl = RSAPIGetUserData( ctx ) ;		/* 销毁RESTful服务控制器 */	RSAPIDestroyRestServiceControler( ctl );		return 0;}

    开发真正要做的,就是写RESTful服务函数实现业务逻辑,配置进RESTful控制器路由配置表g_rest_services_config。

    在hello示例中,RESTful服务函数简单的把打招呼信息压入HTTP缓冲区

    funcRestServiceEntry GET_hello;int GET_hello( struct RestServerContext *ctx ){	char		response[1024] ;	int		response_len ;		int		nret = 0 ;		/* 初始化临时缓冲区 */	memset( response , 0x00 , sizeof(response) );	response_len = 0 ;		/* 填充hello信息 */	response_len = snprintf( response , sizeof(response) , "Hello restserver\n" ) ;		/* 构造HTTP响应缓冲区 */	nret = RSAPIFormatHttpResponse( ctx , response , response_len , NULL ) ;	if( nret )		return nret;		return 0;}

    注意:RSAPIFormatHttpResponse是restserver_api提供的构造HTTP响应缓冲区函数,第二个参数和第三个参数输入HTTP体数据和长度,第四个参数及以后是可变参数序列,类似snprintf的format机制,用于输入HTTP头(Content-length随HTTP体数据输入自动附加)。

    注意:构建restserver应用时比如makefile,编译需要指定restserver_api头文件目录,链接需要指定librestserver_api.so库文件。示例hello自带的makefile是基于make工具mktpl2的依赖makefile,其无依赖版本是同目录里的makefile.Linux。

    注意:针对大型项目而言,你应该把RESTful服务函数拆到一个个独立的.c文件,这样就要跟随自己的经验,创建头文件声明它们,构建时分别编译和最后一起链接。

    注意:开发数据库应用时,一般会在应用动态库构造函数中加入打开数据库逻辑,析构函数中加入断开数据库逻辑,以便在RESTful服务函数里直接发送SQL。构造函数和析构函数里面还能加入其它需要初始化和销毁时操作的逻辑,平台保证会按时序完整执行。

    在下一个示例rsapi中,RESTful服务函数演示了调用众多restserver_api函数获取RESTful信息,把它们都返回给前端

    int GET_rsapi( struct RestServerContext *ctx ){	char		response[4096] ;	int		response_len ;		char		*method = NULL ;	int		method_len ;	char		*uri = NULL ;	int		uri_len ;		int		uri_paths_count ;	int		uri_path_index ;	char		*uri_path = NULL ;	int		uri_path_len ;		int		queries_count ;	int		query_index ;	char		*key = NULL ;	int		key_len ;	char		*value = NULL ;	int		value_len ;		int		nret = 0 ;		/* 初始化临时缓冲区 */	memset( response , 0x00 , sizeof(response) );	response_len = 0 ;		/* 获取HTTP方法 */	method = RSAPIGetHttpMethodPtr( ctx , & method_len ) ;	BUFPRINTF( response , response_len , "method[%.*s]\n" , method_len,method )		/* 获取HTTP路径 */	uri = RSAPIGetHttpUriPtr( ctx , & uri_len ) ;	BUFPRINTF( response , response_len , "uri[%.*s]\n" , uri_len,uri )		/* 获取已分解后的路径段 */	uri_paths_count = RSAPIGetHttpUriPathsCount( ctx ) ;	BUFPRINTF( response , response_len , "uri_paths_count[%d]\n" , uri_paths_count ) ;	for( uri_path_index = 1 ; uri_path_index <= uri_paths_count ; uri_path_index++ )	{		uri_path = RSAPIGetHttpUriPathPtr( ctx , uri_path_index , & uri_path_len ) ;		BUFPRINTF( response , response_len , "uri_path[%.*s]\n" , uri_path_len,uri_path )	}		/* 获取已分解后的参数段 */	queries_count = RSAPIGetHttpUriQueriesCount( ctx ) ;	BUFPRINTF( response , response_len , "queries_count[%d]\n" , queries_count ) ;	for( query_index = 1 ; query_index <= queries_count ; query_index++ )	{		key = RSAPIGetHttpUriQueryKeyPtr( ctx , query_index , & key_len ) ;		value = RSAPIGetHttpUriQueryValuePtr( ctx , query_index , & value_len ) ;		BUFPRINTF( response , response_len , "query[%d][%.*s][%.*s]\n" , query_index , key_len,key , value_len,value ) ;	}		/* 构造HTTP缓冲区 */	nret = RSAPIFormatHttpResponse( ctx , response , response_len , NULL ) ;	if( nret )		return nret;		return 0;}

    注意:填充临时缓存区时使用了restserver_api提供的格式化字符串宏,简化了代码,但这些宏还比较初级,用户可以使用自己所拥有的更完善更成熟的字符串/缓冲区格式化库来代替之。

    注意:如果你设计的接口规范中,HTTP体塞入JSON作为业务报文格式,你可以使用restserver自带的fasterjson+DirectStruct(前面安装依赖时没有提到)组合来帮助你实现JSON报文的快速序列化和反序列化,仅仅只需要调用一个函数即可,参阅restserver内部实现是如何读取配置文件,详细参见fasterjson和DirectStruct开源项目说明文档。

    6.3. 自研RESTful服务控制器

    如果对自带的RESTful服务控制器不满意,可以自行研发一个,在应用动态库中使用之,代码框架提供如下:

    #include "restserver_api.h"funcInitRestApplication InitRestApplication;int InitRestApplication( struct RestServerContext *ctx ){	...	return 0;}funcCallRestApplication CallRestApplication;int CallRestApplication( struct RestServerContext *ctx ){	...	return 0;}funcCleanRestApplication CleanRestApplication;int CleanRestApplication( struct RestServerContext *ctx ){	...		return 0;}

    注意:RESTful信息在平台上下文环境ctx中,可以通过restserver_api层函数访问之;HTTP信息在ctx深处,调用RSAPIGetHttpEnv传入ctx传出struct HttpEnv *http,然后使用fasterhttp函数库函数访问之,别忘了编译时包含fasterhttp.h。

    7. 部署运行

    假设现在一组RESTful服务函数包裹成的一个应用(动态库)已经构建完毕,你肯定亟不可待地想跑一把,你还需要学习运行前的最后一块内容:

    7.1. 配置文件

    restserver配置文件一般取名为"restserver.conf",当然你也可以命名成其它诸如"this_is_the_restserver_config.ini"。

    {	"log" :	{		"log_pathfilename" : "$HOME$/log/restserver.log" ,		"log_level" : "DEBUG"	} ,	"server" :	{		"test_mode" : 1 ,		"workers_count" : 2 ,		"application_so_pathfilename" : "$HOME$/so/RS_hello.so"	} ,	"http" :	{		"listen_ip" : "0" ,		"listen_port" : 7911 ,		"domain" : "localhost:7911" ,		"timeout_seconds" : 60 ,		"headers_count_hardmax" : 128 ,		"headers_len_hardmax" : 4096 ,		"header_content_length_val_hardmax" : 1024000	}}

    我们拿示例hello的配置文件来讲解。

    restserver配置文件里面分三个段:log(日志配置)、server(进程和服务器配置)、http(HTTP和RESTful配置),至少v0.8.0是这样的。

    配置项路径简介说明
    log.log_pathfilename日志文件名可以配成相对路径或绝对路径,还支持内嵌环境变量"$...$"
    log.log_level日志等级枚举空间:DEBUG、INFO、WARN、ERROR、FATAL
    server.test_mode是否是测试模式1:测试模式
    0:生产模式
    当设置成测试模式时,每个RESTful请求前都会装载应用动态库,每个RESTful请求后都会卸载应用动态库,是不是特别适合调试?
    server.workers_count多进程+多路复用模型的进程数决定了应用处理最多并发数,但不是通讯最大并发数,多路复用模型支持成千上万的通讯并发数
    server.application_so_pathfilename应用动态库文件名格式同日志文件名
    http.listen_ipHTTP或者说RESTful服务器侦听地址根据网络规划和安全要求设置成合适的IP;"0"代表内外兼修
    http.listen_portRESTful服务器侦听端口
    http.domain域名参考其它HTTP服务器;客户端请求的域名必须和服务端设置的域名一致
    http.timeout_seconds超时时间单位:秒
    防止一些连接占着茅坑不拉屎
    http.headers_count_hardmaxHTTP报文头选项的最大数量安全防御用
    http.headers_len_hardmaxHTTP报文头的最大长度安全防御用
    http.header_content_length_val_hardmaxHTTP报文体的最大长度安全防御用

    7.2. 跑起来

    其实就是指定某个配置文件运行restserver,restserver装载应用动态库,对外提供RESTful服务。

    restserver命令行参数很简单

    $ restserverUSAGE : restserver config_file

    我把源码包中的配置文件复制到$HOME/etc/里改了改,就可以运行了

    $ restserver etc/restserver.conf

    此时命令行会卡住,可以开启另外一个窗口查看日志文件$HOME/log/restserver.log,再用curl发起一个HTTP请求测试一下

    $ curl "http://localhost:7911/hello"Hello restserver

    注意:第一个"//"和"/"之间会被浏览器/curl取出来当作域名上送,restserver会根据配置文件匹配域名。注意:第一个"/"后面的是URI,测试前请确保当前装载的应用动态库路由表里已预置。

    好了,测试没问题的话,就用ctl+c中断运行。正式运行命令应该长这个样子

    $ nohup restserver etc/restserver.conf &$

    8. 平台应用接口开发参考

    8.1. 错误码宏

    宏名宏说明
    RESTSERVER_ERROR_URI_FIRST_CHARACTER当前HTTP请求的URI地址首字符必须是'/'
    RESTSERVER_ERROR_TOO_MANY_HTTP_URI_PATHS当前HTTP请求的URI的目录文件端数太多
    RESTSERVER_ERROR_URI_FIRST_CHARACTER_IN_CONFIG源码RESTful路由表的URI地址首字符必须是'/'
    RESTSERVER_ERROR_TOO_MANY_HTTP_URI_PATHS_IN_CONFIG源码RESTful路由表的URI的目录文件端数太多
    RESTSERVER_ERROR_RESTSERVICE_ENTRY_RETURNRESTful服务入口函数返回处理失败
    RESTSERVER_ERROR_HTTP_DOMAIN当前HTTP请求的域名与配置文件中的不一致
    RESTSERVER_ERROR_PLACEHOLDER_LACK_OF_CLOSE配置文件中的目录路径中的环境变量占位符前后'$'不能匹配
    RESTSERVER_ERROR_BUFFER_OVERFLOW应用缓冲区溢出
    RESTSERVER_ERROR_SOMETHING其它错误
    RESTSERVER_FATAL_CREATE_RESTSERVICECONTROLER构造RESTful服务控制器失败
    RESTSERVER_FATAL_GET_RESTSERVICECONTROLER得到RESTful服务控制器失败
    RESTSERVER_FATAL_ENV_VAR_NOT_FOUND环境变量未找到
    RESTSERVER_FATAL_SOMETHING其它致命错误

    8.2. 工具宏

    8.2.1. 简单缓冲区格式化宏

    8.2.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) ) ){	...}

    8.2.1.2. STRNCMPSTR

    宏定义#define STRNCMPSTRN(_str1_,_str1_len_,_cmp_,_str2_) STRNCMPSTRN( (_str1_) , (_str1_len_) , _cmp_ , (_str2_) , (strlen(_str2_)) )
    宏说明带长度的比较两个字符数组,其中第二个字符数组自动计算长度
    输入参数_str1_ : 字符数组1
    _str1_len_ : 字符数组1长度
    _cmp_ : 比较符
    _str2_ : 字符数组2
    返回值0 : 构造成功
    大于0 : 字符数组1大
    小于0 : 字符数组2大

    示例

    char	buf1[...] ;char	buf2[...] ;...if( STRNCMPSTR( buf1 , strlen(buf1) , == , buf2 ) ){	...}

    8.2.1.3. STRNCMPRSTR

    宏定义#define STRNCMPRSTR(_str1_,_str1_len_,_cmp_,_const_str2_) STRNCMPSTRN( (_str1_) , (_str1_len_) , _cmp_ , (_literal_str2_) , (sizeof(_literal_str2_)-1) )
    宏说明带长度的比较两个字符数组,其中第二个字符数组为字面量
    输入参数_str1_ : 字符数组1
    _str1_len_ : 字符数组1长度
    _cmp_ : 比较符
    _literal_str2_ : 字符数组2
    返回值0 : 构造成功
    大于0 : 字符数组1大
    小于0 : 字符数组2大

    示例

    char	buf1[...] ;...if( STRNCMPRSTR( buf1 , strlen(buf1) , == , "hello" ) ){	...}

    8.2.1.4. HTTP_NEWLINE

    宏定义#define HTTP_NEWLINE "\r\n"
    宏说明便于格式化HTTP时加入换行

    8.2.1.5. HTML_NEWLINE

    宏定义#define HTML_NEWLINE "
    "
    宏说明便于格式化HTML时加入小换行

    8.2.1.6. HTML_RETURN_NEWLINE

    宏定义#define HTML_RETURN_NEWLINE "

    "
    宏说明便于格式化HTML时加入大换行

    8.2.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会自动累加和封顶。

    8.2.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会自动累加和封顶。

    8.2.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会自动累加和封顶。

    8.2.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会自动累加和封顶。

    8.3. 函数原型

    8.3.1. 应用动态库函数原型

    8.3.1.1. funcInitRestApplication

    函数原型typedef int funcInitRestApplication( struct RestServerContext *ctx );
    函数说明当应用动态库第一次装载时被调用
    输入参数struct RestServerContext *ctx : 平台上下文环境
    输出参数(无)
    返回值0 : 构造成功
    非0 : 失败,具体失败原因见错误宏

    8.3.1.2. funcCallRestApplication

    函数原型typedef int funcCallRestApplication( struct RestServerContext *ctx );
    函数说明当每次HTTP请求到来时被调用
    输入参数struct RestServerContext *ctx : 平台上下文环境
    输出参数(无)
    返回值0 : 构造成功
    非0 : 失败,具体失败原因见错误宏

    8.3.1.3. funcCleanRestApplication

    函数原型typedef int funcCleanRestApplication( struct RestServerContext *ctx );
    函数说明当应用动态库最后卸载时被调用
    输入参数struct RestServerContext *ctx : 平台上下文环境
    输出参数(无)
    返回值0 : 构造成功
    非0 : 失败,具体失败原因见错误宏

    8.4. API函数

    8.4.1. 查询RESTful请求信息类

    8.4.1.1. RSAPIGetHttpMethodPtr

    函数原型char *RSAPIGetHttpMethodPtr( struct RestServerContext *ctx , int *p_method_len );
    函数说明从平台上下文环境中得到当前HTTP请求的方法
    输入参数struct RestServerContext *ctx : 平台上下文

    鲜花

    握手

    雷人

    路过

    鸡蛋
    该文章已有0人参与评论

    请发表评论

    全部评论

    专题导读
    热门推荐
    阅读排行榜

    扫描微信二维码

    查看手机版网站

    随时了解更新最新资讯

    139-2527-9053

    在线客服(服务时间 9:00~18:00)

    在线QQ客服
    地址:深圳市南山区西丽大学城创智工业园
    电邮:jeky_zhao#qq.com
    移动电话:139-2527-9053

    Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap