💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
在[从输入网址到浏览器返回内容(一),服务器处理篇](http://www.cnblogs.com/dy2903/p/8305881.html "从输入网址到浏览器返回内容(一),服务器处理篇")提到了Web服务器以及应用服务器,它主要用来接收从浏览器发过来的HTTP请求,并进行页面的呈现。 本文主要讲一下HTTP服务器的进化过程 # HTTP服务器1.0:单进程 所谓`HTTP协议`实际上就是浏览器向服务器发送一个HTTP Request请求,然后HTTP服务器进行处理,从硬盘拿一个HTML文件,然后以文本的形式返回过去(HTTP response) 麻烦的是需要请操作系统建立HTTP协议下面的TCP连接通道。这个通道是通过Socket建立的: - 先在80端口监听 - 进入无限循环,如果有连接请求来,就接受(accept) - 创建新的socket,然后通过这个新的socket来接收,发送HTTP数据 这就是HTTP Server 1.0版本。 但是网络传输是不稳定的,有可能很长时间都没有响应,所以服务器的receive进程很可能因为得不到数据而**阻塞**。但是进程也不能歇着,因为还有很多浏览器的连接在那里等着。 解决方案就是为每个请求开一个进程或者线程 # HTTP服务器2.0:多进程 多进程的思路是当accept获得到连接的时候,生成了新的socket,但是不在主进程里面处理,而是**新创建一个子进程**来接管。这样主进程就不会阻塞在receive上,可以继续接受新的连接了。 但是如果有成千上万个连接来了以后,如果每个连接都开一个进程,而每个进程都**耗费大量的系统资源,而且进程切换也非常消耗资源。** 如果把多进程换为多线程,只能说可能会好一点,但是治标不治本。 ![](http://p8a6vmhkm.bkt.clouddn.com/img/20181029141615.png?imageslim) # HTTP 服务器3.0:Select模型 之前的方法存在一个非常大的问题,一个Socket连接就是一个文件描述符,这个描述符是个指针指向一个简单的数据结构,但是我们用了一个重量级的进程来对它进行读写,非常的浪费。 那么思路又回到了单进程上,只需要**改变一下工作方式**就可以用单进程进行连接的处理。 在1.0的阶段,为什么会阻塞,因为浏览器只是和我们建立了连接,但是没有把**数据**发送过来,但是我们又迫不及待的去读,所以只能阻塞。所以,问题的症结就在于此。 所以,可以接收了客户端连接以后,**不急着读**,而是拿到socket fd的编号,就可以处理其他的连接了。 下面操作系统会在后台检查socket,如果发现有socket可以读写了,就会做个**标记**,然后通知HTTP服务器。 HTTP服务器遍历一遍所有的socket 描述符,看看谁有标记,有标记的才做处理。 处理完了,把socket的描述符告诉操作系统,然后再继续等待, 这种方式就是Select ![](http://p8a6vmhkm.bkt.clouddn.com/img/20181029141628.png?imageslim) # HTTP服务器4.0:epoll Select方法存在一个弊端,每次被唤醒之后,需要遍历所有的socket 描述符,看看是否有标志位。 所以要如果能把**发生了变化的socket**告诉HTTP服务器就好了。这种方式就是epoll。 使用了epoll,就不需要遍历全部的集合,只需要处理哪些有变化的。 ![](http://p8a6vmhkm.bkt.clouddn.com/img/20181029141637.png?imageslim) # 参考 [Http Server:一个差生的逆袭](http://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513467&idx=1&sn=178459f4bb9891c9cf471a28e7c340be&chksm=80d679b8b7a1f0aea8f6e3f09acb6969993825753170dc3db63f8ef35c95cce98aa40a0c7097&scene=21#wechat_redirect)