又双叒一个HTTP服务端轮子

  现在看来,自己已经撸了好几个HTTP服务端的轮子了,就像是做前端的都爱做博客主题一样,估计做HTTP服务端也是很多服务端开发最爱干的事情了。事由是公司测试环境需要一个虚拟银行端来匹配打款系统做测试使用,而且后面可能也会用来匹配做压力测试和系统调优工作,虽说主要达到需求为目的,这东西可做简单可做复杂,但是本着进益求精(其实是不折腾不死心)的态度,自己还是想把这个功能能够模拟的真实一点。刚好自己之前做了很多的小组件,然后发现很多可以整理一下拿来直接使用,由此不禁感叹:工作年限多了,虽说感到技术和能力没啥长进,手头倒是积累了一大批的轮子、工具和脚手架,也能算得上是一笔小积累和小财富吧……
  总体来说,东西还是向着更好的、更成熟的方向发展的。
  经过前几次HTTP服务端的尝试,现在总算感觉把boost.asio和HTTP/1.x的GET/POST较好的融合起来了。通过GET方式返回文件系统内容基本很容易就能做一个静态Web服务器,而且之前用过FastCGI接口让其支持php动态语言也不是难事,但是总体开发HTTP服务端的应该都是作为接口开发的,将特定uri路由到特定接口的处理函数中去,所以开发中提供向指定uri注册回调函数的机制,将解析后的HTTP头部、uri和参数、POST数据体透传给这些函数,并约定处理结果的返回内容和格式,就算是形成了一个通用的HTTP服务器框架了,后面各种服务都可以注册进来,将请求路由到各个响应函数中,这个库把HTTP服务需要考虑的大多问题都帮忙解决了。后续的工作可以对更多的头属性做支持,HTTP/2暂时就不考虑了,不得不说这玩意儿实在是太复杂了……
  然后shared_ptr和weak_ptr组合实现KeepAlive超时机制实测也达到了自己预想中的效果。之前的文章说到好多种实现方式,由于C++没有现成好用的堆数据结构使用,所以自认为自己的解决方式还是比较理想的:通过使用map和hash两个容器,外加一个wrapper数据结构,在特定对象的查找和按照超时批量访问剔除都可以达到较好的访问效率。其详细实现细节可以参见AliveTimer。
  自己接触到的MySQL连接库都是基于C访问库封装的,其实MySQL官方也有一套原生的基于boost的客户端访问库mysqlcppconn,觉得挺好用的,但是却感觉GitHub上该项目没啥人气,也不知道是为什么。自己基于mysqlcppconn封装形成了一个连接池,同时对ResultSet等数据也采用RAII方式自动释放对象,用起来更方便更安全。还有,通过模板的方式,写了一个可以快速的进行元素提取的模板函数,虽然不算复杂但是感觉还是挺好用的,这以让我萌生了试想下一步踏入模板元编程的想法,虽然这条路不是一般的苦难,但也是通往C++高手和专家所无法回避的。
  之前提到的TimerService此处也亮相了。基于Libevent/1.x库,任何模块可以注册inline或者defer定时事件到这个服务中去,这些服务既可以one-shot执行,也可以间隔固定时间重复执行,作为一个基础服务就不多说了,但是用起来真的很舒心也很方便,其实现参见TimerService。
  现在自己的多线程服务都用自己封装的ThreadPool可升缩线程池了,虽然在生产上伸缩线程数目用的不是很多,必定生产环境还是能简单就不搞事,不过其一个特色就是主线程可以和工作线程相互同步状态,当主线发出graceful退出信号的时候,工作线程可以根据自己的业务逻辑决定什么时候退出线程执行函数,这对于对数据完整性要求高的业务是十分重要的。
  服务中任务的创建和处理是异步的,跟别人学的一招就是创建任务条目放到数据库中,然后处理线程通过SELECT FOR UPDATE的方式互斥的取出就绪的任务来执行。其好处就是处理进程可以随时下线,启动的时候随时加载待处理任务,而且通过数据库锁的方式,这为多进程部署创造了条件,多进程稳定性、伸缩性自然不必多言了吧。
  我们的程序中没有使用Redis存放重要数据,主要是Redis数据持久化有一定的延时,而我们的关键业务对于数据丢失几乎是0容忍的。不过Redis用于程序性能的统计和跟踪用起来真的很爽,主要还是得益于Redis支持丰富的数据结构。我会在关键的地方创建一个RAII对象,该对象在调用开始的位置创建,其构造函数记录一个开始时间戳,而在调用结束的时候该对象也正好被析构,其析构函数计算时间长度并推送到Redis的一个队列中。在Redis中每1分钟建立一个LIST数据结构,然后定时器服务每一分钟执行一次性能统计,对上一份分钟的调用次数、平均调用延时进行计算并记录显示在日志当中,人家说无法监控和评价的服务是不敢上线的,这里算是有了监控了调用频次和调用性能了吧!
  程序的参数配置文件,之前要目自己读行裸解析,要么直接写成Json的格式。现在发现了一个libconfig库,觉得还是挺好用的,原生支持C和C++接口。最主要的是这个库存在于RHEL的官方Base仓库,所以其稳定性和广泛性应该是不用质疑的。以后就用你啦!
  日志服务使用了rsyslog,这是Linux系统的标准日志系统,接口的话可以使用GNU syslog的标准接口,至于后端用rsyslog还是syslog-ng接管都是可以随意配置。之前我司都是用的一套自研的日志服务,虽然能用但是各种毛病多多,这一点我后面还是会单独吐槽一下的。
  其他,貌似也没啥了。
  最后还是强烈申诉:企业可以的话还是尽快将生产环境向新系统迁移。自己做了这么久的服务端,发现用Modern C++手法(包括使用boost进行模拟)写出来的程序,相比C和C++98的稳定性要好很多,比如自己之前重构的打款系统上线后除了业务逻辑改了两个小问题,程序一直稳定的运行着,而且没发现资源泄漏。通过RAII手法和智能指针拜托资源泄漏的苦恼,通过boost::function+boost::bind可调用对象的概念,凭感觉写程序可以做到自然而然得心应手了。

本文完!