- 我是一个线程(修订版)
- 我是一个 Java class
- Javascript:一个屌丝的逆袭
- Java : 一个帝国的诞生
- JSP 一个装配工的没落
- TCP/IP 之 大明王朝邮差
- TCP/IP 之大明内阁
- TCP/IP 之蓟辽督师
- CPU 阿甘
- CPU 阿甘之烦恼
- CPU 阿甘:函数调用的秘密
- 我是一个网卡
- 我是一个路由器
- 我是一个进程
- 我是一块硬盘(上)
- 我是一块硬盘(下)
- 我是一个键盘
- 张大胖的 socket
- 张大胖学递归
- 学习面向对象的令狐冲
- 张大胖学数据库
- 数据库村的旺财和小强
- 小李的数据库之旅(上)
- 小李的数据库之旅(下)
- 漫画:什么是机器学习?
- 那些烦人的同步和互斥问题
- IE 为什么把火狐和 Chrome 给打伤了?
- 对浏览器村的第二次采访
- 节约标兵 IE 的自述
- EMail 诞生记
- Email 诞生记(下)
- Http 历险记(上)
- Http 历险记(下)-- Struts 的秘密
- 动物王国的面向对象
- 冯·诺伊曼计算机的诞生
- Http Server : 一个差生的逆袭
- 张大胖的加法器
- 从 1 加到 100:一道简单的数学题挑战下你的大脑
- 编程语言
- Javascript:一个屌丝的逆袭
- 计算机语言之战
- 我和编程语言的爱恨情仇(上)
- 我和编程语言的爱恨情仇(下)
- Android 为什么选择了 Java
- iOS 为什么选择了 Object-C?
- Basic : 一个老兵的自述
- Node.js : 我只需要一个店小二
- 命令式编程 vs 声明式编程
- 编译还是解释?
- 程序人生
- “架构师"小赵
- 师兄说
- 师姐说
- 小王的架构师之路
- 小李的版本管理系统
- 小超穿越记
- 小李的 Build 之路(上)
- 小李的 Build 之路(下)
- 张大胖改 Bug
- 我的编程之路--大学趣事
- 码农小王的一天
- 小李在外企
- 张大胖的需求估算
- 从厨师到码农
- 聊一聊那些神一样的程序员们(上)
- 聊一聊那些神一样的程序员们(中)
- 聊一聊那些神一样的程序员们(下)
- 谁是互联网之父?
- 一个价值百万的创业教训
- 让自己与众不同 - 提升工作的价值
- 看看你的“易燃性”
- 从无聊的工作中寻找价值
- 什么样的学生适合报考计算机?
- 谈谈程序员的职业方向(上)
- 谈谈程序员的职业方向(中)
- 谈谈程序员的职业方向(下)
- 谈谈培训班的作用
- 码农需要知道的“潜规则”
- 学习编程的加速度
- 码农在工作中的必备能力
- 码农和英语
- 老司机经验
- 假如时光能够倒流, 我会这么学习 Java
- 假如我是计算机系老师
- 学会编程, 而不是学会 Java
- 从增删改查中突围
- 抽象:程序员必备的能力
- 懒就一个字
- 编程的自学方法
- 小王买房记
- 从一道面试题谈谈一线码农应该具备的基本素质
- 想写框架的看过来
- 苹果手机变砖头以后
- 如何快速的学习一门技术?
- 唯一不变的是变化: 谈谈微信应用号
- 什么是企业应用?
- 勿以浮沙筑高台
- 为什么敏捷开发难于成功?
- localhost vs 127.0.0.1
- GitHub/Stackoverflow 找工作时有什么用?
- 动词 or 名词 :这是一个问题
- 如何选择入行语言
- 有时候,沉默是金
- 零 Bug 的代码是怎么炼成的?
- 浮点数为什么不精确?
- 文章错误大全
- Open Source--不要为了开源而开源
- 一不留神,代码就腐化了
- 先做个“键盘侠”, 再来写程序
- 不加断点调试的程序员是好程序员
- 码农必备技能:烂代码的处理之道(上)
- 码农必备技能:烂代码的处理之道(下)
- 学习数据结构有用吗?
- 从现在开始,丰富你的简历
- 那些永不过时的书,你看过几本吗?
- 学好编程必备的一个品质你知道吗?
- 你最爱的 Java
- 搞懂了这几点,你就学会了 Web 编程
- Spring 的本质系列(1) -- 依赖注入
- Spring 本质系列(2)-AOP
- 三层架构和 MVC 那点事儿
- Java 帝国之拨云见日识回调
- 小张的 Duck Typing
- JDBC 的诞生
- JDBC 后传
- 一个不安分的 JDBC 驱动
- Java 帝国之 Java bean (上)
- Java 帝国之 Java bean(下)
- Java 帝国之函数式编程
- Java 帝国之函数式编程(下)
- 关于 Java 初学者需要知道的 10 件事
- JUnit 你不知道的那些事儿
- 圣诞礼物:Java EE 的历史
- Java EE 读书指南
- 给小白的 Java EE 指南
- 给小白的 Java EE 指南(2)
- 给小白的 Java EE 生存指南(3) : XML
- 给小白的 Java EE 生存指南(4) : 一只叫 Tom 的猫
- 给小白的 Java EE 指南(5) : AJAX
- 给小白的 Java EE 生存指南(6) :Java 反射
- 闲聊
- "饿了么"初体验
- 来自大脑的控诉
- 一个高中生是怎么玩自媒体的?
- 尝试 分答
- 到底应不应该上培训班?
- 自学编程中遇到问题怎么办?
- 据说 99%的初级程序员看完后都不迷茫了
- 一行代码引发的“血案”
- 对一个死锁问题的思考
- 通过外包进入名企
- 请开往十年前的今天
- 为什么自学中最好有个师傅指导一下?
- 这个网站值得你花时间投入
- 为什么你无法坚持自学编程?
Http Server : 一个差生的逆袭
我刚毕业那会儿,国家还是包分配工作的, 我的死党小明被分配到了一个叫数据库的大城市,天天都可以坐在高端大气上档次的机房里, 在那里专门执行 SQL 查询优化 , 工作稳定又舒适;
隔壁宿舍的小白被送到了编译器镇,在那里专门把 C 源文件编译成 EXE 程序, 虽然累,但是技术含量非常高, 工资高,假期多。
我成绩不太好,典型的差生,四级补考了两次才过, 被发配到了一个不知道什么名字的村庄,据说要处理什么 HTTP 请求, 这个村庄其实就是一个破旧的电脑, 令我欣慰的是可以上网,时不时能和死党们通个信什么的。
不过辅导员说了, 我们都有光明的前途。
1
Http Server 1.0
HTTP 是个新鲜的事物, 能够激起我一点点工作的兴趣, 不至于沉沦下去。
一上班,操作系统老大扔给我一大堆文档: “这是 HTTP 协议, 两天看完!”
我这样的英文水平, 这几十页的英文 HTTP 协议我不吃不喝不睡两天也看不完, 死猪不怕开水烫,慢慢磨吧。
两个星期以后, 我终于大概明白了这 HTTP 是怎么回事: 无非是有些电脑上的浏览器向我这个破电脑发送一个预先定义好的文本(Http request), 然后我这边处理一下(通常是从硬盘上取一个后缀名是 html 的文件), 然后再把这个文件通过文本方式发回去(http response), 就这么简单。
唯一麻烦的实现, 我得请操作系统给我建立 Http 层下面的 TCP 连接通道, 因为所有的文本数据都得通过这些 TCP 通道接收和发送, 这个通道是用 socket 建立的。
弄明白了原理,我很快就搞出了第一版程序, 这个程序长这个样子:
看看, 这些 socket, bind, listen , accept... 都是操作系统老大提供的接口, 我能做的也就是把他们组装起来: 先在 80 端口监听, 然后进入无限循环,如果有连接请求来了,就接受(accept),创建新的 socket, 最后才可以通过这个 socket 来接收,发送 http 数据。
老大给我的程序起了个名称, Http Server, 版本 1.0 。
这个名字听起来挺高端的, 我喜欢。
我兴冲冲的拿来实验, 程序启动了, 在 80 端口“蹲守”, 过了一会儿就有连接请求了, 赶紧 Accept ,建立新的 socket, 成功 ! 接下来就需要从 socket 中读取 Http Request 了。
可是这个 receive 调用好慢, 我足足等了 100 毫秒还没有响应 ! 我被阻塞(block) 住了!
操作系统老大说: “别急啊, 我也在等着从网卡那里读数据,读完以后就会复制给你。 ”
我乐的清闲, 可以休息一下。
可是操作系统老大说:“别介啊, 后边还有很多浏览器要发起连接, 你不能在这儿歇着啊。”
我说不歇着怎么办? receive 调用在你这里阻塞着, 我除了加入阻塞队列, 让出 CPU 让别人用还能干什么?
老大说: “唉, 大学里没听说过多进程吗? 你现在很明显是单进程, 一旦阻塞就完蛋了, 想办法用下多进程, 每个进程处理一个请求! ”
老大教训的是, 我忘了多进程并发编程了。
2
Http 2.0 :多进程
多进程的思路非常简单,当 accept 连接以后,对于这个新的 socket , 不在主进程里处理, 而是新创建子进程来接管。 这样主进程就不会阻塞在 receive 上, 可以继续接受新的连接了。
我改写了代码, 把 Http server 升级为 V2.0, 这次运行顺畅了很多, 能并发的处理很多连接了。
这个时候 Web 刚刚兴起, 我这个 Http Server 访问的人还不多, 每分钟也就那么几十个连接发过来,我轻松应对。
由于是新鲜事物, 我还有资本给搞数据库的小明和做编译的小白吹吹牛, 告诉他们我可是网络高手。
没过几年, Web 迅速发展, 我所在的破旧机器也不行了, 换成了一个性能强悍的服务器, 也搬到了四季如春的机房里。
现在每秒中都有上百个连接请求了, 有些连接持续的时间还相当的长,所以我经常得创建成百上千的进程来处理他们,每个进程都得耗费大量的系统资源, 很明显操作系统老大已经不堪重负了。
他说: “咱们不能这么干了, 这么多进程,光是做进程切换就把我累死了。”
“要不对每个 Socket 连接我不用进程了, 使用线程? ”
“可能好一点, 但我还是得切换线程啊, 你想想办法限制一下数量吧。”
我怎么限制? 我只能说同一时刻,我只能支持 x 个连接, 其他的连接只能排队等待了。
这肯定不是一个好的办法。
3
Http Server 3.0 : Select 模型
老大说: “我们仔细合计合计, 对我来说, 一个 Socket 连接就是一个所谓的文件描述符(File Descriptor ,简称 fd , 是个整数) , 这个 fd 背后是一个简单的数据结构, 但是我们用了一个非常重量级的东西-- 进程 --来表示对它的读写操作, 有点浪费啊 。”
我说: “要不咱们还切换回单进程模型? 但是又会回到老路上去, 一个 receive 的阻塞就什么事都干不了了”
“单进程也不是不可以, 但是我们要改变一下工作方式。”
“改成什么?” 我想不透老大在卖什么关子。
“你想想你阻塞的本质原因, 还不是因为人家浏览器还没有把数据发过来, 我自然也没法给你, 而你又迫不及待的想去读, 我只好把你阻塞。 在单进程情况下, 一阻塞,别的事儿都干不了。“
“对,就是这样”
“所以你接受了客户端连接以后, 不能那么着急的去读, 咱们这么办, 你的每个 socket fd 都有编号, 你把这些编号告诉我, 就可以阻塞休息了 ”
我问道:“这不和以前一样吗? 原来是调用 receive 时阻塞, 现在还是阻塞”
“听我说完, 我会在后台检查这些编号的 socket, 如果发现这些 socket 可以读写, 我会把对应的 socket 做个标记, 把你唤醒去处理这些 socket 的数据, 你处理完了,再把你的那些 socket fd 告诉我, 再次进入阻塞,如此循环往复。”
我有点明白了: “ 这是我们俩的一种通信方式, 我告诉你我要等待什么东西, 然后阻塞, 如果事件发生了, 你就把我唤醒, 让我做事情。”
“对, 关键点是你等我的通知, 我把你从阻塞状态唤醒后, 你一定要去遍历一遍所有的 socket fd,看看谁有标记, 有标记的做相应处理。 我把这种方式叫做 select ”
我用 select 的方式改写了 Http server, 抛弃了一个 socket 请求对于一个进程的模式, 现在我用一个进程就可以处理所有的 socket 了。
4
Http Server4.0 : epoll
这种称为 select 的方式运行了一段时间, 效果还不错, 我只管把 socket fd 告诉老大, 然后等着他通知我就行了。
有一次我无意中问老大:“我每次最多可以告诉你多少个 socket fd?”
“1024 个”
“那就是说我一个进程最多只能监控 1024 个 socket 了? ”
"是的, 你可以考虑多用几个进程啊"
这倒是一个办法, 不过"select"的方式用的多了, 我就发现了弊端, 最大的问题就是我从阻塞中恢复以后,需要遍历这 1000 多个 socket fd, 看看有没有标志位需要处理。
实际的情况是, 很多 socket 并不活跃, 在一段时间内浏览器并没有数据发过来, 这 1000 多个 socket 可能只有那么几十个需要真正的处理, 但是我不得不查看所有的 socket fd, 这挺烦人的。
难道老大不能把那些发生了变化的 socket 告诉我吗?
我把这个想法给老大说了下, 他说:“嗯, 现在访问量越来越大, select 方式已经不满足要求, 我们需要与时俱进了, 我想了一个新的方式,叫做 epoll ”
“看到没有, 使用 epoll 和 select 其实类似“ 老大接着说 : ” 不同的地方是第 3 步和第 4 步, 我只会告诉你那些可以读写的 socket , 你呢只需要处理这些'ready' 的 socket 就可以了“
“看来老大想的很周全, 这种方式对我来说就简单的多了。 ”
我用 epoll 把 Http Server 再次升级, 由于不需要遍历全部集合, 只需要处理哪些有变化的, 活跃的 socket 文件描述符, 系统的处理能力有了飞跃的提升。
我的 Http Server 受到了广泛的欢迎, 全世界有无数人在使用, 最后死党数据库小明也知道了, 他问我:“ 大家都说你能轻松的支持好几万的并发连接, 真是这样吗? ”
我谦虚的说: “过奖, 其实还得做系统的优化啦。”
他说:“厉害啊,你小子走了狗屎运了啊。”
我回答: “毕业那会儿辅导员不是说过吗, 每个人都有光明的前途。”
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论