第116章 要不这就算我的检查吧(数据再掉就恢復两更。)
  实验室在顶楼走廊的最深处。
  一进门就听到嗡嗡的伺服器风扇声。
  几台昂贵的sun ultra工作站一字排开。
  七八个博士生围在一个屏幕前,个个面如土色。
  胡鹏趴在键盘前,手指飞快地敲击著,调取系统底层的core dump文件。
  “segmentation fault……”
  胡鹏看著屏幕上的报错,眉头锁成了一个“川”字。
  “又是段错误。
  线程之间的资源爭抢太严重了。
  咱们用的corba標准,底层的orb通讯机制在处理大量短连接的时候效率太低了。”
  “要不加硬体?”
  刚才那个报信的男生小声提议。
  “再申请两台伺服器做负载均衡?”
  “加个屁!”
  胡鹏骂道。
  “这是软体架构的硬伤,你就是把机房堆满伺服器,锁竞爭的问题解决不了,一样得崩!
  国家给的指標是单机5000並发,现在连一半都跑不到,下个月验收怎么交代?”
  眾人一片死寂,不敢接话。
  陈浩站在人群最后面,目光扫过屏幕上的架构图和那几行关键的c++代码。
  他立刻看明白了。
  这套系统採用的是典型的“thread-per-request”(每个请求一个线程)模型。
  这是当时corba架构的標准做法。
  每一个客户端连接进来,伺服器就分配一个独立的线程去处理。
  在並发量小的时候,这种模型简单高效。
  但一旦並发量上来,成千上万个线程同时在作业系统里抢占cpu时间片,光是线程上下文切换的开销就能把cpu吃光。
  再加上他们为了保证数据一致性,在共享內存区加了大量的互斥锁。
  这不崩才怪。
  “胡院长。”
  一个突兀的声音打破了沉默。
  眾人回头,看到那个本该在写检查的大二学生,正双手插兜站在后面。
  胡鹏看到陈浩,火气又要上来:
  “谁让你进来的?出去!”
  “如果是corba架构下的线程阻塞,加再多伺服器也没用。”
  陈浩没有动,而是指了指屏幕上的一行代码。
  “你们用的是同步阻塞i/o模型(bio)。
  这种模型下,线程在等待网络数据的时候是掛起的,不仅占內存,还不干活。”
  胡鹏愣了一下,开始重新打量起陈浩。
  这番话切中要害,而且专业术语用得极准,绝不是一个大二学生能说出来的。
  “你懂corba?”
  胡鹏的声音沉了下来。
  “略懂一点。”
  陈浩走到屏幕前。
  “我兼职的公司就是做高並发网际网路应用的。
  前段时间我跟著出差到硅谷,跟sun公司负责java ee规范制定的一帮工程师聊过。
  现在的趋势是,瓶颈不在硬体,而在i/o模型。”
  陈浩顿了顿,看著胡鹏:
  “胡院长,能给我个白板吗?”
  周围的博士生面面相覷。
  一个大二的要在国家级实验室里给他们这些博士生讲课?
  “给他。”
  胡鹏盯著陈浩看了几秒,鬼使神差地挥了挥手。
  一个博士生从角落里推过来一块白板。
  陈浩拿起马克笔,没有废话,直接在白板上画了一个图。
  一个圆圈,周围连著无数线条,中间是一个单向的箭头。
  “既然多线程容易崩,那我们就不要用多线程。”
  陈浩一边画一边说。
  “目前的架构是,来一个客人,我们就派一个服务员全程跟著。
  客人点菜、吃饭、买单,服务员都得等著。
  客人多了,服务员就不够用了。”
  他在旁边画了另一个图。
  “我们可以换个思路。
  只留一个前台接待员。
  所有客人的请求先到前台登记。
  前台把请求分类,扔到后面的队列里。
  厨房做好了,再通知前台叫號。
  这就是io多路復用。”
  陈浩写下几个英文单词:i/o multiplexing。
  “利用unix系统底层的select或者poll机制,一个线程就可以监控成千上万个socket连接的状態。
  只有当socket真的有数据可读写时,才分配资源去处理。”
  陈浩转过身,看著胡鹏:
  “还需要把这块的同步锁去掉,换成无锁队列。”
  实验室里没人回应,博士生们有的皱眉沉思,有的还在发懵。
  在2000年,nio(非阻塞i/o)和reactor模式在学术界已经有了雏形,但在国內的工程实践中,还属於非常前沿甚至激进的技术。
  大部分人还在死磕多线程优化。
  胡鹏的眼睛却亮了。
  他是行家。
  陈浩画的这个图,虽然简单,但逻辑闭环非常完美。
  它从根本上避开了线程切换的开销。
  “无锁队列……”
  胡鹏喃喃自语。
  “你是说用cas指令原子操作来替代互斥锁?”
  陈浩点头。
  “是的。硬体级的原子操作,比作业系统级的锁快几个数量级。”
  胡鹏沉默了片刻。
  他看著陈浩,眼神复杂。
  “说起来容易,做起来难。”胡鹏指著屏幕。
  “这套系统的底层代码有十几万行,重构i/o模型等於换心臟。
  离验收只剩一个月,谁敢动?”
  “不用动全身。”
  陈浩把马克笔扔在桌上,走到那个操作电脑的博士生身后,拍了拍他的肩膀。
  “师兄,麻烦让个座。”
  那个博士生愣住了,下意识地看向胡鹏。
  胡鹏深吸了一口烟,把菸蒂狠狠按灭在菸灰缸里。
  “让他试!”
  代码在其他的电脑都有备份,出问题也不影响。
  博士生站起来,让出了位置。
  陈浩坐下,双手放在键盘上。
  那是一把老式的机械键盘,键程很长。
  他活动了一下手指,调出了底层的通讯模块代码:
  networkdispatcher.cpp。
  陈浩的眼神瞬间变得专注。
  他没有大改业务逻辑,而是直接刪掉了原本臃肿的线程池管理类。
  键盘敲击声开始在实验室里迴荡。
  噠噠噠,噠噠噠。
  陈浩直接引入了sys/select.h库。
  他开始手写一个简易的reactor事件分发器。
  fd_set master_set;
  fd_zero(&master_set);
  select(max_fd + 1, &read_fds, null, null, &timeout);
  一行行代码在黑色的屏幕上流淌。
  周围的博士生慢慢围了上来。
  一开始他们还带著怀疑,但隨著代码行数的增加,他们的表情变了。
  陈浩的代码风格极其老练。
  变量命名规范,注释清晰,逻辑结构紧凑得像教科书。
  更可怕的是,他几乎不思考,也不查文档,那些晦涩的unix系统调用函数,仿佛刻在他脑子里一样。
  胡鹏站在陈浩身后,双手抱胸。
  他越看越心惊。
  这哪是大二的学生?
  这分明是个浸淫底层开发十几年的老手!
  这种对內存指针的精准控制,对系统內核的理解,甚至超过了他带的很多博士生。
  仅仅半个小时。
  陈浩敲下最后一行代码,保存,退出编辑器。
  “编译。”
  陈浩按下回车。
  屏幕上开始滚动编译日誌。
  所有人的心都提到了嗓子眼。
  plete. no errors.
  编译通过。
  “跑一下测试吧。”
  陈浩站起身,把位置让了出来。
  那个博士生坐回去,重新启动了压力测试脚本。
  屏幕上的仪錶盘开始跳动。
  並发数:
  500……
  1000……
  系统运行平稳,没有报错。
  1500……
  2000……
  到了刚才崩溃的临界点。
  所有人都屏住了呼吸。
  曲线继续上扬,没有丝毫抖动。
  2500……
  3000……
  4000……
  最终,数字定格在5200。
  而旁边的cpu占用率,竟然只有60%!
  “臥槽……”
  一个博士生忍不住爆了句粗口。
  这不仅仅是解决了问题,还实现了性能翻倍!
  胡鹏死死盯著那个“5200”的数字,一脸的难以置信。
  他猛地转过头,看向站在一旁正在揉手腕的陈浩。
  陈浩从兜里掏出那包万宝路,抽出一根递给胡鹏:
  “胡院长,要不这就算我的检查吧?”