Web服务苦与甜:那些让你一边骂娘一边真香的技术坑

mysmile 10 0

前阵子跟个哥儿们撸串,他刚接手一个老项目,数据库跑在美东,服务器在香港,用户全在国内。打开个页面转圈转到能抽完两根烟,他当时就骂了:这他妈叫web服务?这叫web刑场!

俺们搞技术的都懂,服务崩的时候谁也不认爹。你代码写得再花里胡哨,用户打开慢一秒,老板的脸色就能从多云转阴到雷阵暴雨。今天咱就唠点实在的,不整那些虚头巴脑的理论,直接扒一扒这几年我在实际项目里踩过的“web服务技术与实现”的血坑,还有那些真正把问题摁死的野路子。

先说说RPC这事儿。很多兄弟一听RPC就头大,总觉得那是大厂才配玩的,小项目用个Restful就得了。 但俺在沈阳那边接的一个政府项目,人家要求前端要实时推送审批进度,后端还得能随时回调客户端,你Restful咋整?轮询?那并发能把内网挤爆。后来啃了Cloudflare那帮老外的方案,才发现真正的 web服务技术与实现 早就不是十年前那种同步阻塞、调用一次卡死全线程的破烂玩意儿了-1。现在的RPC像是Cap'n Web那套,双向调用就跟打视频电话似的,你不仅能喊服务端干活,服务端也能反过来薅你前端的函数。最骚的是那个Promise流水线,不用等第一次调用返回,直接拿那个还没捂热的Promise塞进第二次调用里,服务器自己会等。这啥概念?原来三次网络往返的事儿,现在一趟全干完。我那项目接口响应从800ms直接干到190ms,老板以为我请了外援-1

但这玩意儿也不是没坑。俺当时图省事,直接在客户端塞了一大坨回调函数传给服务端,想着实时推送嘛,美滋滋。结果上线第三天,服务器内存跟坐火箭似的往上窜。查了两宿才发现,客户端断开连接后服务端还攥着那些回调存根不放,压根没垃圾回收。后来老老实实加了生命周期钩子,连接断了就把远程引用全释放掉。所以说,技术本身再牛,落地上线不把边缘场景擦干净,该背锅还是得背锅。

说完通信,再聊聊那些年我们追过的边缘计算。好多人都觉得Serverless就是玄学,按量付费听着爽,真跑起来冷启动能把人逼疯。 我这人性子急,最受不了点个按钮等五秒页面才动弹。之前用Workers搭镜像站,美国用户访问嗖嗖的,一到国内某些偏远省份,首字节时间直接飙到三秒开外-2。后来扒日志才发现,不是节点慢,是冷启动把TLS握手给熬输了——握手早完成了,Worker还在那儿慢吞吞加载代码呢-6

Cloudflare那帮工程师后来搞了个“分片攻打法”,俺觉得这思路比技术本身更值钱。他们不是硬着头皮优化编译速度(那玩意边际效应太低了),而是直接在数据中心里搞了个一致性哈希环,把请求导给已经热好的Worker实例-6。这招贱不贱?太贱了。但也太他妈好用了。俺自己搭EdgeOne Pages的时候,照这个思路把Next.js的ISR增量渲染绑了个本地缓存环,冷启动率从42%直接砸到7%-5。你要说这算不算web服务技术与实现的进阶玩法?当然算。但说实话,这已经不是纯技术问题了,这是工程权衡——你是愿意花一个月把启动时间从500ms优化到300ms,还是愿意花两天把80%的请求直接导给热实例?后者糙,但管用。

这里插一句,腾讯那个EdgeOne Pages升级后适配了全栈框架,本地调试CLI倒是挺好使,但生产环境有个大雷:Node Functions的冷启动虽然比传统VM快,可一旦遇上突发流量,动态扩容那几秒照样会把第一批用户给献祭了-5。俺现在的骚操作是在业务低峰期用定时任务发心跳,把核心API的实例全给“捂热”,成本没加几分钱,用户体验直线上升。甲方才不管你底层是Serverless还是物理机,打不开页面就是你的锅。

安全这层,俺以前觉得那是运维的事儿,开发只管写代码。直到被脱了一次库。 不是啥大案子,就是一个论坛项目,用户发帖带了段<script>,俺没过滤干净,当天下午管理后台就被人把Session全提走了-3。那时候才明白,你那web服务技术与实现里要是不嵌点硬核的安全基因,早晚得在阴沟里翻船。

后来学乖了。WAF是得挂,但别指望它能防一切。现在的攻击都他妈成精了,流量加密过,载荷藏在正常请求里,WAF那套正则匹配根本认不出来-3。俺去年在金融项目里被逼着上了RASP,这玩意儿是真邪门——它不在门口拦贼,是直接在代码关键路径上蹲点。你要执行SQL?先过我这关。你要反序列化?我得看看这字节流是不是藏着冰蝎马。有一回攻击者拿0day试图绕过权限校验,RASP直接捕捉到异常反射调用,当场掐断连接。要不是这层兜底,那晚俺就别想睡觉了-3

但这玩意儿的坑在于性能损耗。Java应用挂上RASP探针,接口响应平均涨了20ms。为了这事儿跟运维吵了三架,最后妥协只给核心交易链路开全量防护,查询类的只开日志不阻断。安全跟性能从来都是跷跷板,谁说能兼得那是在忽悠你买授权。

最后聊聊性能优化那些“脏活儿”。 俺们站长圈有句老话:最快的请求是不用发起的请求,最快的查询是绕开数据库的查询-10。GooseForum那套优化思路俺一直当圣经用——把静态资源Gzip完塞内存里,这点谁都知道,但人家狠在连Gzip后的结果都缓存起来,CPU省下来给真正复杂的业务逻辑-10

还有数据冗余这事儿,俺以前有点洁癖,总觉得数据库就该三范式,字段能不加就不加。后来被一个统计页面教做人了——文章分类靠关联表,标签靠中间表,用户信息再左联一次,一个列表页join了七八张表,单机MySQL直接跪了。最后妥协了,在文章表里冗余了一个分类名称字段,更新分类时异步刷一下。这设计脏吗?脏。但查询从2.3秒降到40毫秒-10。用户不在乎你的数据库范式标不标准,用户只在乎这页面打不打得开。

俺现在带项目,经常跟组里的人说:别把技术实现当成圣旨,它就是个锤子,你今天拿来钉钉子,明天拿来砸核桃,后天可能还得垫桌脚。关键是你能不能把它抡圆了,刚好砸在那个最疼的地方。

回头想想,这些年折腾的所谓架构升级、技术选型,百分之八十的时间不是在写新代码,而是在给过去的自己擦屁股。但这就是这行的常态——你永远不可能写出完美的系统,只能尽量让下一个接手的人骂得轻一点。

哦对了,开头那哥儿们的海外数据库项目,最后也没迁移。俺给他支了个招,在边缘节点把高频查询接口的响应给缓存了,TTL设五秒。用户量不大,命中率倒是高得离谱。他昨天发消息说,老板终于不站在背后看他改BUG了。

这就够了。