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


猜你喜欢
- 一:取字符串中第几个字符print "Hello"[0] 表示输出字符串中第一个字符print "Hello&
- python清空命令行 !有时我们在命令行上运行一些代码时,觉得有些冗余了,可以通过以下代码进行清除命令行上的代码。import osdef
- 阅读上一篇:css基础教程属性篇 本篇主要介绍css对边框(border)的属性控制和链接(link)的伪类选择器.边框(border):
- 之前有写利用md5方式来做差异备份,但是这种md5方式来写存在以下问题:•md5sum获取有些软连接的MD5值存在问题 •不支持对空目录进行
- 本文实例为大家分享了微信小程序实现简单计算器与秒表的具体代码,供大家参考,具体内容如下实验内容:任务一:实现一个简单的加减乘除运算。首先输入
- 一、基本思想本文思想是基于用asp和DOM来读取和存储XML数据,并利用XML数据来存储留言信息,达到同用数据库存储数据的功能。二、XML留
- 本文实例讲述了go语言获取系统盘符的方法。分享给大家供大家参考。具体如下:涉及golang调用winapi获取系统盘符的技巧。代码如下://
- SQL Server日期计算 通常,你需要获得当前日期和计算一些其他的
- 本文实例讲述了python实现清屏的方法。分享给大家供大家参考。具体分析如下:一试:>>> import os>&g
- 1、pivot函数的定义pivot(index=None,columns=None,values=None) -> DataFrame
- 1. 换源,sohu的相当好用。 1.1备份CentOS-Base.repo cd /etc/yum.repos.d/ cp CentOS-
- 四、XML应用分类 总的说来的XML的应用可分为四类: (1)应用于客户需要与不同的数据源进行交互时。数据可能来自不同的数据库,他们都有各自
- 前言在做小程序列表展示的时候,接到了一个需求。需要在列表展示的时候加上动画效果。设计视频效果如下图:需要在进入列表页的时候,依次展示每一条卡
- 本文实例为大家分享了python实现12306图片验证效果的具体代码,供大家参考,具体内容如下思路:在鼠标点击位置加一个按钮,然后再按钮中的
- 大家都知道一些论坛的标题有高亮显示功能,在这里我不讨论也不研究论坛普遍的实现方法,下面是我的实现方法:实现思路:把要高亮显示的标题加上特定标
- requests 是一个非常小巧全面的库,应用它可以很容易写出与服务器进行交互的程序,今天遇到了一个问题,与服务器交互时,url都是http
- interfaceGo语言里面设计最精妙的应该算interface,它让面向对象,内容组织实现非常的方便,当你看完这一章,你就会被inter
- 一:使用where少使用having;二:查两张以上表时,把记录少的放在右边;三:减少对表的访问次数;四:有where子查询时,子查询放在最
- 我就废话不多说了,直接上代码吧!obj = Obj.objects.get(id=1)print obj.name #此时name的值假定为
- 本文实例讲述了JavaScript日期工具类DateUtils定义与用法。分享给大家供大家参考,具体如下:DateUtils = { &nb