作家
登录

如何实现一个Web Server

作者: 来源: 2017-12-12 17:12:27 阅读 我要评论

开辟者大年夜赛路演 | 12月16日,技巧立异,北京不见不散


比来重构了客岁造的一个轮子 Vino。Vino 旨在实现一个轻量并且可以或许包管机能的 Web Server,仅存眷 Web Server 的本质部分。在重构过程中,Vino 借鉴了很多优良开源项目标思惟,如 Nginx、Mongoose 和 Webbench。是以,比较上一个版本的 Vino,如今的 Vino 不仅机能获得晋升,并且设计也更为优雅、结实 :D。

本文将会对 Vino 今朝所具备的关键特点进行阐述,并总结开辟过程中的一点心得。

单线程 + Non-Blocking

Vino 整体采取了基于事宜驱动的单线程 + Non-Blocking 模型。采取单线程模型,避免了体系分派多线程及线程之间通信的开销,同时降低了内存的耗用。因为采取了单线程模型,为了更好的进步线程应用率,Vino 将默认 Blocking 的 I/O 设置为 Non-Blocking I/O,即在线程读/写数据的过程中,如不雅缓冲区为空/缓冲区满,线程不会壅塞,而是急速返回,并设置 errno。

Vino 最初的灵感来源竽暌冠 Computer Systems: A Programmer's Perspective 一书讲述收集编程时实现的一个简单的 Web Server,每到来一个请求,Web Server 都邑 fork 一个过程去处理。显然,在高并发的场景下,这种模型是不合理的。每次 fork 过程会带来巨大年夜的开销,并且体系中过程的数量是有限的。同时,伴随多过程带来的过程调剂的开销也弗成小觑,CPU 会花费大年夜量的时光用于决定调用哪一个过程。过程调剂激发的过程高低文之间的切换,也须要消费相昔时夜的资本。

很轻易联想到采取多线程模型来替代多过程模型,比拟于多过程模型,多线程模型占用的体系资本会大年夜大年夜降低,然则本质上并没有减小线程调剂带来的开销。为了减小由线程调剂导致的开销,我们可以采取线程池模型,即固定线程的数量,然则问题依旧存在:因为 Linux 默认 I/O 是浊宣(Blocking)的,如不雅线程池中所有的线程同时壅塞于正在处理的请求,那么新到来的请求就没有线程去处理了。是以,如不雅我们用 Non-Blocking 的 I/O 调换默认的 Blocking I/O,线程将不会壅塞于数据的读写,问题便可获得解决。

HTTP Keep-Alive

应用内存池分派内存,可以在必定程度上晋升内存分派的效力,不需液每次都调用 malloc/calloc 函数。同时,应用内存池使得内存治理加倍简单。在 Vino 中,针对每一个请求,Vino 都邑为其分派一或多个内存池(各个内存池形成一个单链表),在请求处理完毕后,一并释放所有的内存。

Vino 支撑 HTTP 长连接(Persistent Connections),即多个请求可以复竽暌姑同一个 TCP 连接,以此削减由 TCP 建立/断开连接所带来的机能开销。每到来一个请求,Vino 会对请求进行解析,断定请求头中是否存在 Connection: keep-alive 请求头。如不雅存在,在处理完一个请求后会保持连接,并对数据缓冲区(用于保存请求内容,响应内容)及状况标记进行重置,不然,封闭连接。

关于 HTTP Keep-Alive 的优势,RFC 2616 有着更完美的总结,引用如下。

  • By opening and closing fewer TCP connections, CPU time is saved in routers and hosts (clients, servers, proxies, gateways, tunnels, or caches), and memory used for TCP protocol control blocks can be saved in hosts.
  • HTTP requests and responses can be pipelined on a connection. Pipelining allows a client to make multiple requests without waiting for each response, allowing a single TCP connection to be used much more efficiently, with much lower elapsed time.
  • Network congestion is reduced by reducing the number of packets caused by TCP opens, and by allowing TCP sufficient time to determine the congestion state of the network.
  • Latency on subsequent requests is reduced since there is no time spent in TCP's connection opening handshake.
  • HTTP can evolve more gracefully, since errors can be reported without the penalty of closing the TCP connection. Clients using future versions of HTTP might optimistically try a new feature, but if communicating with an older server, retry with old semantics after an error is reported.

准时器 Timer

HTTP Parser

因为收集的不肯定性,我们并不克不及包管一次就能攫取所有的请求数据。是以,对于每一个请求,我们都邑开辟一段缓冲区用于保存已经攫取到的数据。同时,我们须要同时对攫取到的数据进行解析,以包管攫取到的数据都是合理的数据,例如,假设今朝缓冲区内的数据为 GET /index.html HTT,那么下一次攫取到的字符必须为 P,不然,应急速检测出当前请求是一个异常的请求,并主动封闭当前的连接。

基于以上分析,我们须要实现一个 HTTP 状况机(Parser)来保持当前的解析状况,Vino 状况机的实现参考了 Nginx 的设计,并对 Nginx 的实现做了简化。HTTP Parser 相干代码见 vn_http_parse.h 和 vn_http_parse.c。

Memory Pool

我们一般应用 malloc/calloc/free 来分派/释放内存,然则这些函数对于一些须要长时光运行的法度榜样来说会有一些弊病。频繁应用这些函数分派和释放内存,会导致内存碎片,不轻易让体系直接收受接收内存。典范的例子就是大年夜并发频繁分派和收受接收内存,会导致过程的内存产生碎片,并且不会立马被体系收受接收。

Vino 内存池的实现依旧参考了 Nginx 的实现,并做了简化,Memory Pool 相干代码见 vn_palloc.h 和 vn_palloc.c。


  推荐阅读

  分布式高并发缓存设计系统

分布式内存缓存分布式体系,具备故障主动恢复功能,无单点故障问题,稳定性佳好,支撑程度扩大好,对营业层供给通用接口,后端具体的缓存应用对营业透明代码侵人道一般,须要惹人通用的api平日有收集操作。 >>>详细阅读


本文标题:如何实现一个Web Server

地址:http://www.17bianji.com/lsqh/39720.html

关键词: 探索发现

乐购科技部分新闻及文章转载自互联网,供读者交流和学习,若有涉及作者版权等问题请及时与我们联系,以便更正、删除或按规定办理。感谢所有提供资讯的网站,欢迎各类媒体与乐购科技进行文章共享合作。

网友点评
自媒体专栏

评论

热度

精彩导读
栏目ID=71的表不存在(操作类型=0)