2022-03-04

目标

现在协程+异步的web服务器并不多,nginx 仍然占据极大的优势。不知道是不是线程池 + epoll 就足以应付目前的 web 服务器需求。我正在学习rust,想着rust的基础库这么多,为什么不去试试写一个协程+异步的web服务器呢?最后就有了这个计划。

目前对这个项目的设想是作为自己的毕设来做,尽量不做玩具项目,因为我还希望自己用上这个项目呢。基于这些理由,我现在认为这个项目应该要有:

  • 反向代理
  • 轻松配置(我感觉 nginx 配置起来比较复杂)
  • 静态 Web 服务器
  • 负载均衡
  • 模块支持
  • 支持 FastCGI (不支持这个我也没办法用)
  • 尽可能详细易用的日志(本来就应该有)
  • 支持tls

另外我还想加上这些:

  • DPDK 支持 (但是我根本没有支持DPDK的硬件来着。。先做梦)
  • 云原生支持 (其实我也不知道到底什么是真正的云)
    • 监控容器进行负载均衡
    • 把自己部署到容器里面

先实现静态 Web 服务器这样简单点的功能,等做出静态 Web 服务器就先发布 0.1 版本。纯静态 Web 服务器做好之后我就可以先不用 Python3 的 http 服务器了。

开发

今天就先新建了工程,项目依赖主要就是 tokio 。根据示例完成一个 Toy Web Server 花了半小时,之后我发现自己不知道怎么写了。连忙下单了一本关于 nginx 的书。虽然手头目前没有书,但是看了一下 nginx 代码和解析,我发现大家主要用状态机来实现请求解析。查阅了 Http 资料后我把状态机设计为这些状态:

  • Start
  • RequestLine
  • RequestHeaders
  • RequestBody
  • End

考虑到静态 Web 服务器肯定不支持 post 之类的操作(如果以后发现要支持那就以后再说),我需要实现的就是解析 请求行请求头请求体

tokio 提供的 api 里面有两个读取,一个是读取到缓冲区一个是读取到一个 vector 里面。如果为了省事方便,后者比较方便。但是考虑到网络可能存在的丢包,客户端分段请求和可能的恶意攻击。如果攻击者一直发送数据,服务器就需要一直存储这些数据,完全接收完毕之后才会处理。这样做大概率会把内存爆掉,效率也不高。所以我选择了相对比较麻烦的前者。另外我发现 tokio 大部分示例里面都把 buffer 开在栈上面。这样虽然能运行得快一点,但是栈切换的消耗变大了,也容易爆栈。实际工程中 buffer 最好放在堆上,虽然多了一次指针引用的消耗,但是能避免放在栈上面的问题。

服务器还要在 accept 之后关掉那些不发送信息的连接,那就需要一个计时器来对连接计时。不过这个任务先放到后面再实现。

首先实现了一个根据 "\r\n" 识别分段的函数,用这个对输入的数据进行分段,然后扔到处理 请求行请求头请求体 的函数里面。测试了一下好像没什么问题。

我有点纠结这个项目用什么语言写注释比较好?

2022-03-05

开发

今天上线先把昨天写的 bug 修了。首先是状态机不需要 Start 状态,先给扬了。然后是昨天写的代码实际上完全不能处理超出 buffer 长度的请求。把这部分代码改成能处理的代码。测试后,只要请求的每一行不超过 buffer 的长度就没问题了。还需要一个总长度判断避免恶意攻击。决定拖到明天再写,今天好困先摸了。

2022-03-06

今天博客炸了,修了一天,而且我还要写别的项目所以摸了,只完成了总长度判断。

2022-03-08

定时器

需要设计一个定时器来关闭那些已经建立但长时间没有接收到数据的连接。nginx的做法很巧妙,但是用最小堆或者四叉堆在性能上可能会更好一点。

事件循环

事件循环中应该先用 peek 函数判断请求前端是不是可用的http请求。

开发

使用 bytes 库实现了0拷贝分析请求头,但目前的实现产生的内存碎片比较大。以后再解决这个内存碎片的问题。

文章目录