当前位置: 首页 > 科技观察

去小公司从0到1搭建后端架构,做个总结!

时间:2023-03-19 19:06:38 科技观察

来腾讯之前,我在之前的公司做了3年后端开发。经历了一个SaaS产品从0到10(还没到100,哈哈哈)的过程。实践过程中遇到的问题越来越多,这里总结一下。该产品是针对人力资源的SaaS在线服务。HR有多个WebAndroid/iOS小程序客户端,后端使用RESTful风格的API提供服务。Python语言主要用于方便快速迭代。架构的演进经历了4大阶段:1.MVC2.服务拆分3.微服务架构4.领域驱动设计1.MVC项目开始时,后端同事不超过5人。工作是实现产品的原型,不用过多考虑架构,使用Django快速实现功能。DB的表结构设计好后,抽象出功能View。因为产品设计的不完善,后台需要做很多预留设计,避免产品逻辑的变化导致整个表结构的变化。代码在这个阶段最重要的是确定适合团队的代码规范和代码检查规则。整体架构如上图所示。Nginx负责负载均衡,将流量分配给多个Django服务,Django处理逻辑。如果需要异步任务,Celery交给他们。Redis用于缓存数据量比较大的地方。同时,也有使用NginxPushModule进行实时消息通知的需求。问题及优化方法:1.Django并发性能较差。使用uWSGIMaster+Worker配合gevent携程支持高并发。2.Redis连接过多。使用redis-py自带的连接池实现连接复用。3.MySQL连接太多使用djorm-ext-pool(https://github.com/djangonauts/djorm-ext-pool)连接池重用连接4.Celery配置gevent支持并发任务随着功能的开发越来越多,Django下的app越来越多,给发布带来了不便。每发布一个版本,都需要重启所有的Django服务。如果发布有问题,只能通过加班来解决。而且,单个Django项目下的代码量也在不断增加,不易维护。2.服务拆分随着后端团队的壮大,分配给每个同事的需求也越来越细。如果继续把所有的代码都放在一个项目里开发,维护成本太高,而我们之前的架构在Django中,每个app都被分成模块,app内部集群性高,app之间耦合度低,这就带来了便于服务的拆分。拆分的过程并没有遇到太多的问题。最初的分裂只是代码的分离。提取了公共代码以实现公共Python库。数据库和Redis仍然是共享的。随着负载的增加,数据库也做了多实例。如上图所示,尽量避免服务之间互相调用,在需要交互的地方使用http请求,使用hosts指向内网地址进行内网调用。问题及优化方法:NginxPushModule长期没有维护,最大长连接数不够。Tornado+ZeroMQ用于实现tormq(https://github.com/zhu327/tormq)服务,支持消息通知服务之间的调用,采用http方式,需要依赖的服务host配置hosts指向调用地址,给维护带来不便。并且在调用链的过程中没有重试、错误处理、限流等策略,导致服务可用性差。随着业务的拆分,继续使用Nginx维护配置非常麻烦,调用错误往往是因为修改了Nginx的配置。每个服务都有完整的鉴权流程,鉴权依赖于用户中心的数据库。修改鉴权时,需要重新发布多个服务。3、微服务架构首先在接入层引入基于OpenResty的KongAPIGateway,实现鉴权、限流等定制化插件。接入层承担和解除应用层的公共认证和限流功能。发布新服务时,在发布脚本中调用Kongadminapi将服务地址注册到Kong,使用插件方式加载api。为了解决相互调用的问题,维护了一个基于gevent+msgpack的RPC服务框架doge,使用etcd进行服务管理,在rpc客户端实现了限流、高可用、负载均衡等功能.现阶段最难的技术选型,开源的API网关大多是用Golang和OpenResty(lua)实现的,需要定制来满足我们的业务需求。前期花了一个月的时间学习了OpenResty和Golang,用OpenResty实现了一个短网址服务shorturl,用于业务。最终选择Kong是基于Lua分发方便,Kong的开箱即用和插件开发都比较容易。性能方面的考虑并不是最重要的。为了支持更多的并发,还使用了云平台提供的LB服务,将流量分发到一个由两台Kong服务器组成的集群中。配置在集群之间自动同步。饿了么维护着纯Python实现的thrift协议框架thriftpy,并提供了很多配套工具。如果团队足够大,这套RPC方案其实是合适的,但是我们团队人手不足,水平参差不齐,很难推广这套学习成本高的方案。最终我们开发了一个类Duboo的RPC框架doge,代码主要参考了微博开源的motan。4.领域驱动设计在这个架构中,我们尝试从应用服务中抽取数据服务层。每个数据服务都包含一个或多个限界上下文。限界上下文类只有一个聚合根来暴露RPC调用的方法。数据服务不依赖应用服务,应用服务可以依赖多个数据服务。通过数据服务层,应用解耦它们的依赖关系,高层服务只依赖底层服务。在我离开的时候,领域驱动设计还在学习设计阶段,还没有落地,但是我相信前公司的后端架构会继续往这个方向演进。综上所述,架构的设计和技术的选择,不能完全按照流行的技术来走。最后,它仍然服务于产品和客户的需求。在设计过程中,由于团队和人员的结构问题,有很多妥协。如何在妥协中找到最优解是最大的挑战。新一代微服务架构ServiceMesh正在成为主流。虽然目前的工作与微服务无关,但我们会持续关注学习。