1. 编程圈首页
  2. 文库
  3. 后端开发

拆分后台服务的各种姿势

本文是This is How I Break Up的汉化版

上篇文章讲到了如何增加各种约的成功率。这篇文章继续讲怎么分。先看一个短故事。

最开始,我把整个后台放在一台机器(A),然后后台程序通过lo来访问数据库。直到有一天硬盘坏了,然后啥都没了。于是我认识到"单点"的危险性,所以我对服务程序在(B)上做了冗余,并且把数据库放到单独的一台C上。为了能同时访问A和B,我在D上配置了一个nginx的逆向代理。直到有一天,C的硬盘坏了,然后啥都没了。于是我认识到所有的"单点"都有风险,所以我给DB做了master-slave. 最后,我加了个cache层做加速。

我感觉这个虚拟故事中的真实的框架可以撑一段时间,所以就拿这个来做后续讨论的沙盘吧。

拆分后台服务的各种姿势

现在已经不流行把所有逻辑放一整坨了,大家都转而开始把服务器拆成一个一个的“服务”。但是这个就要引入额外的进程间/网络通信,增加管理服务间依赖关系的复杂性。那为啥要这么搞呢?咋分比较合适?请继续看故事。

最初动机

我最开始想拆分是在这套架构运用到一个在线游戏上。然后开发节奏加快,新版本一个接一个。

有一天,我接到一个需求是改排行榜系统的规则。这个规则已经是第三次改了,但是这次我写了个空指针异常,而且只会在非常微妙的情况下才会触发。由于太微妙了,这个问题逃过了所有的测试,但最终还是在生产环境上成功再现。A和B上的服务器程序都相继变成core dump。然后流量图就开始给我做鬼脸。大致是这样。

拆分后台服务的各种姿势

秒了bug。但是我还是担心哪天新版本又会爆。我更担心担心哪天新版本又会爆。但退一步想,这个排行版系统真没这么重要。其实没多少用户关注这个排行,而且它都不在关键路径中。就算这个系统出错,现有的逻辑处理会直接返回一个空,然后客户端读本地的缓存了事。所以这个事故就像一个音响系统的问题导致一辆车熄火,所以我决定把这个"音响系统"分离出来。技术上来说,我把排行的所有逻辑封装到另一个程序中,然后把所有对外提供的公共接口重构成RPC,其他一切不变。于是现在排行系统的异常就只会影响排行系统,我也能多睡点觉,少写点异常了。

第一条,非关键服务,尤其是常常发布的服务和关键服务分离。

我当然还记得"单点"的教训,所以我把新分离出来的排行服务冗余了几份,并且开启了RPC的负载均衡功能。

拆分后台服务的各种姿势

模块化编程的启示

然后,我继续拆分后台,把风险限制到更小的范围中。我也借用了模块化编程的思想,把相关的API都放到一个服务中。果然,开发和查bug的效率都大大提升,因为我只需要关注相关代码了。哦,编译时间也变短了。这个进一步的拆分导致了原先的服务进程退化成API层,仅仅负责整合新形成的逻辑层的返回结果。

第二条,服务器应用可以按软件模块的方式进行拆分。

拆分后台服务的各种姿势

快和慢

我对新架构挺满意的,直到有一天,我发现游戏币的接口越来越慢了。这些接口一般只会对cache进行操作,虽然cache会被定期刷到持久层(DB),但并不会阻塞调用返回。我发现写cache应该不会变的这么慢,而且其他类似的服务也确实并没有变慢。

稍稍深入了解了下这个服务,就发现问题了。在这个游戏中存在两种货币,一般游戏币和付费游戏币。一般游戏币可以通过游戏获取,而付费币则只能通过购买获得。而这个付费币的接口会对cache和数据库进行双写,并且等待写完成后才会返回接口。可想而知,这类接口会比一般游戏币慢很多。我忽略的这个速度差异,而把这两类接口放到一个服务中。于是,慢接口占满了资源(线程),并阻塞了快接口。就像如果给自行车和汽车公用车道,自行车会挡住汽车一样。

第三条,快服务和慢服务分离。

拆分后台服务的各种姿势

今天先写到这。如果您觉得这篇不错,可以点赞或关注这个专栏。👋

发布者:编程圈,转转请注明出处:https://www.bianchengquan.com/article/59468.html

发表评论

登录后才能评论