http服务器
浏览器地址栏里敲下“example.com”,服务回车。服务几百毫秒后,服务页面出现了。服务这背后至少有一台机器在监听某个端口,服务等着你的服务请求砸过来——这就是HTTP服务器干的事。 说白了,服务HTTP服务器就是服务一个程序,它绑定在某个IP地址和端口上(通常是服务80或443),然后进入死循环:等待连接、服务读取请求、服务返回响应、服务关闭连接。服务听起来简单得不像话,服务但真正写一个能用的服务,坑多到你怀疑人生。 当TCP连接建立好,服务器开始读数据。第一行是请求行,比如 我刚开始折腾的时候,用Python的socket模块手写了一个玩具服务器。最头疼的不是解析请求,而是处理并发。单线程的服务器一次只能服务一个客户端,第二个人来访问就得排队等着。你打开一个网页,浏览器可能同时发几十个请求要CSS、JS、图片,单线程直接卡死。 解决办法无非几种:多进程、多线程、IO多路复用、或者异步。Nginx选了epoll(Linux上的IO多路复用),一个worker进程能扛几万个连接。Apache默认用多进程/多线程模型,每个连接占一个进程或线程,并发一高内存就爆。这不是谁好谁坏的问题,是设计取舍。 请求 找到文件后,读出来,加上Content-Type、Content-Length这些头部,写回给客户端。Content-Type得根据扩展名猜:.html是text/html,.css是text/css,.jpg是image/jpeg。猜错了浏览器渲染就出问题。MIME类型表长得要命,但常用的就那么几十个。 还有缓存。第一次访问返回200,带上Last-Modified或ETag。下次浏览器再请求同一个文件,带上If-Modified-Since,服务器发现文件没变,直接回304 Not Modified,省了传输时间。这个机制看起来不起眼,但对页面加载速度影响巨大。 静态文件谁都能伺候,真正让HTTP服务器变得复杂的是动态请求。用户登录、发帖、查数据库——这些需要服务器把请求转给后端程序处理。最早的CGI协议就是为这个设计的:服务器把请求信息塞进环境变量,启动一个进程跑脚本,脚本输出什么就返回什么。简单粗暴,但每个请求都fork一个进程,性能惨不忍睹。 后来出现了FastCGI、WSGI、Servlet这些接口规范。Nginx不直接跑PHP,而是把请求通过FastCGI协议转发给PHP-FPM进程池。Tomcat本身就是个Servlet容器,Java代码跑在里面,HTTP服务器和应用程序耦合在一起。选哪种架构,取决于你的技术栈和性能需求。 反向代理也是HTTP服务器的常见角色。Nginx挡在前面,把请求分发给后面好几台应用服务器,顺便做SSL终止、负载均衡、限流。客户端只知道Nginx的IP,后面的拓扑完全透明。这种架构在微服务里几乎是标配。 我试过用Go写一个简易HTTP服务器,net/http包几行代码就能跑起来。但一旦加上超时控制、连接池、日志、优雅关闭这些生产环境必需的东西,代码量翻好几倍。超时设置尤其关键——不设读写超时,一个慢客户端就能占住一个连接不放,连接数一满服务器就废了。 HTTP/1.1默认开启keep-alive,一个TCP连接可以发多个请求。这减少了握手开销,但也意味着服务器得管理连接的生命周期。HTTP/2更进一步,多路复用、头部压缩、服务器推送,实现复杂度又上了一个台阶。 说到底,HTTP服务器就是互联网的搬运工。它不生产内容,只负责把字节从磁盘或后端程序搬到用户的浏览器里。但这个搬运过程涉及的细节——并发模型、缓存策略、安全防护、协议兼容——足够一个人研究好几年。请求进来之后发生了什么
GET /index.html HTTP/1.1。接着是一堆头部字段,Host、User-Agent、Accept……空行之后是请求体(GET一般没有)。服务器得把这些东西解析出来,知道客户端到底想要什么。静态文件服务是最基本的活
GET /style.css,服务器得去磁盘上找对应的文件。路径拼接要小心,别让用户用 ../../etc/passwd 这种路径穿越攻击读到系统文件。我见过有人写的服务器直接把URL拼到根目录后面,结果整个文件系统暴露了。动态内容才是重头戏
写一个能用的没那么容易
-
上一篇
-
下一篇
- 随机阅读
- 搜索
-
- 友情链接
-