【架构入门系列】从业务到平台的思惟转变

一个需求:抽奖系统

若是你接到了一个开发需求:开发一个抽奖活动的后台系统。第一反应若是是打开IDEA,新建一个项目,输入项目名:lottery。而后开始根据需求着手开始进行设计数据库,api,而后进行开发,这是太日常不过的开发流程。前端

过了一段时间,你又接到一个相似的需求:开发另外一个抽奖活动的后台系统,可是里面的一些业务逻辑、流程处理不太同样,而后就把前一个完成的后台系统复制过来,准备开始修改。git

是时候中止这种看起来很高效但实际上很愚蠢的开发方式了!本文将告诉你如何将你的思想转变过来,给你一个将业务、将系统、甚至将本身升华的机会。我会用偏后端的思惟、尽可能易懂的图文、尽可能精简的代码、尽可能多的例子来将你的等级从熟练的业务驯服者提高为初阶架构猎人github

反思:作错了什么

使用复制、粘贴、修改的方法去开发一个相似的新的业务系统,从开发单个系统上来看开发效率上确实挺高的,但仍然存在如下问题:web

  • 代码复用性低。相似的业务系统可能愈来愈多,复制粘贴致使代码的重复率过高,可复用性过低
  • 测试、部署效率过低。须要当作一个新的后台服务新建一套测试、构建、部署、发布流程并进行实施,对一些通用的业务逻辑进行了重复的测试,构建发布流程也须要单独管理
  • 收集数据难度大。相似的业务系统的数据被分散在不一样的数据库、日志中,若是想收集汇总分析一些通用的业务日志难度大
  • 业务系统太过私有化,没法提供相似SAAS服务那样让任何人都能建立本身的抽奖活动

简而言之,老鼠的视力不好,它没有办法看到远处的事物,可是咱们为了往后业务系统的重复利用,就须要尽可能避免鼠目寸光的重复开发。算法

因此,须要将业务系统抽象为业务平台,支持通用的业务流程,让业务平台为往后的相似的业务系统提供灵活多变的基础业务实现方案,至于特殊的业务流程就单独开发一个特殊的业务系统,二者共同结合提供完整的服务。数据库

另外,平台的使用者应该相似SAAS,具有为全部人服务的能力,提供给能让每一个用户甚至每一个组织都能建立属于本身的独有环境,能够经过在平台上的简单配置构建出属于个体的服务。后端

如图所示,业务系统与业务平台共同为用户服务。业务平台包含某个业务的通用流程,可能恰好能知足业务的全部需求,那么此时就不须要另外开发一个业务系统。但若是有一些比较特殊的业务流程或者必须单首创建一个业务方便管理控制的流程的话,那么能够将该业务系统与业务平台结合到一块儿使用,业务系统提供定制接口,业务平台提供通用接口,业务系统也能经过受权的调用接口来调用业务平台获取、修改数据。api

通用业务

首先咱们须要想清楚,如何从平台的角度考虑,将设计业务逻辑才能比较通用?如何区分是否为通用的业务逻辑,从而进行业务拆分?缓存

通用性

目的:解决业务实现的N种可能。在业务平台上尽可能支持多的、通用的业务逻辑。安全

如何解决:把眼光放长远一些,咱们这个业务系统如今须要支持哪些业务逻辑?往后须要支持哪些?而后进行有选择的取舍,不须要考虑太多,不然系统将被设计的特别复杂且用到的部分太少。一个简单有效的方法就是参照之前作过的系统和当前须要实现的系统,将通用的逻辑抽取出来。

举几个例子:

  • 在业务流程上的考量。好比上个活动只开了一天,奖品也都相同,可是此次的活动须要开两场,每场的奖品可能不同。因此咱们能抽象出:一个抽奖活动可能存在多个活动场次,每一个场次对应的奖品、时间可能都不一样,还须要支持随时进行修改,这就将活动拆分为活动与场次两个层次。因此在数据库设计的时候就须要添加一个字段来对应场次这个维度
  • 在数据库表字段上的考量。奖品的兑换方式多是经过快递发送实物奖品、直接展现兑换码,因此对于表字段的种类要考虑多一些,好比须要保存用户的收货地址、联系方式。在单个字段的属性选择也要适当考虑可扩展一些,好比字段长度、字段类型

边界处理、业务拆分

这里的边界指的是业务拆分时的边界,好比,哪些属于平台通用逻辑?就能把这些通用逻辑放到平台里。哪些属于业务特有逻辑?这些逻辑就不须要加到平台里。

好比,对于抽奖活动,配置奖品、分发奖品是抽奖平台通用逻辑,但用户排行榜则不够通用,除了游戏相关的场景,不多须要用到排行榜的功能,那么排行榜功能就应该属于业务特有逻辑。

在整个抽奖的业务流程中,还有一个业务流程是当抽奖次数用光,就能够经过分享的方式增长抽奖次数。因此抽奖业务通用逻辑依然能够继续拆分,对于分享功能,是不属于抽奖平台通用逻辑的,那么能够将分享功能也单独抽出来成为分享平台,使之也能服务于其余的平台或业务系统。

看似当前已经拆得比较清晰了,但尚未结束,由于这是仅仅针对于【分享成功->增长一次抽奖机会】这样的流程,若是须要分享屡次才能增长一次机会呢?若是分享以后每次增长不一样的抽奖机会呢?谁负责维护【分享-增长机会】这个奖励机制呢?这就须要一个引擎、流程控制中心来处理,不如就叫任务调度平台

任务调度平台的核心业务流程是【完成任务-触发奖励】,至因而谁完成的任务,触发谁的奖励对它来讲并不重要。只要它可以将任务的触发者与被触发者的对应关系、触发条件、被触发者的奖励接口调用方式维护在内部,当检测到触发者达到了触发条件,可以调用到对应的奖励便可

数据处理

大数据量的存储

既然是平台,会由于使用者使用时长、数据量方面就会比通常的特有业务系统多,在设计时须要考虑到提早为书数据量较大的某些表进行水平分表设计,贴一个简单的分表的例子:

在建立数据库时按上图进行建立,在代码中进行增删改查时用下面的方法根据分表字段(shard key)来获取表名:

func GetTableNameByActivityId(activityId int64) string {
	if activityId > 0 {
		return "activity_prize_redeem_" + strconv.FormatInt(activityId % 16, 10)
	}
	return "activity_prize_redeem"
}
复制代码

分表时,根据哪一个字段(shard key)进行分表也是须要考虑的,须要使数据可以均衡的分布在多张表中,而且不影响正常的查询,这样才能经过分表的方式将数据均匀分布到不一样的表中,根据shard key进行查询时效率与不分表时同样,但若是使用另外的一个字段查询数据可能须要遍历全部的表才能将数据查询到,因此shard key的选择是与业务查询需求、数据均匀分布相关。

分表以后,最直接的影响是开发的时候须要对SQL语句进行动态调整,某些ORM框架不支持致使的开发效率下降,可是改动的代码很少,一劳永逸。

高性能查询

对于平台来讲,并发访问量也可能较大,那么缓存、队列、ES必不可少。

对于缓存,如今基本上说的都是使用Redis,可使用的集群模式有主从、哨兵、集群。使用的策略也有延迟加载、直写,还须要考虑到一些击穿、失效的缓存问题,具体能够看这里

对于消息队列,在业务解耦、流量削峰方面是一个很是重要的中间件,具体能够看这里

对于Elastic Search,若是业务上有全文检索的需求就是要结合EFK一块儿上的一个模块,具体使用方式、场景请自行查阅。

运营埋点、报表

数据过重要了!分析用户行为,预测市场走向,仍是之后设计系统的参考指标,咱们做为技术开发人员,也须要为运营人员着想,须要统计出运营人员感兴趣的一些数据,最好是直接找他们提早询问清楚,哪些数据他们须要,以便咱们在设计数据库的时候将一些关键数据用一些字段保存下来。

好比在抽奖活动中,运营人员须要知道web平台、安卓平台、IOS平台分别参与抽奖的人分别有多少,可是设计出来的数据库字段、代码中打印出的日志可能都不存在或者难以经过现有设计进行统计,那么就须要对这个统计需求进行单独的设计。

日志、操做记录表

在代码中输出日志是必须的,好比一般须要在访问API、某个API里的关键逻辑、结合关键数据打印成功或失败信息。

另外,对于某些业务场景须要严格统计数据操做以前的状况、操做以后的状况、操做类型、操做人员、操做时间。虽然也能够经过输出日志的方式,但不够规范,查询统计难度大,容易丢失。因此须要单独设计出一张操做流水表,将重要信息持久化起来。

在每一个须要统计的重要操做执行以后,使用消息队列或者另外的线程去插入一条操做记录到这张表中,以下

go service.Record(&models.SysOperateRecord{
	BizType:     dao.BizType_Customer,
	OperType:    dao.OperType_INSERT,
	OperContent: fmt.Sprintf("batch insert one new customer: %s", customer),
	Operator:    userinfo.Id,
	CreateTime:  currentTime,
	UpdateTime:  currentTime,
})
复制代码

平台管理

类SAAS独立环境

如何让用户有本身的独立环境?让他们能够在平台上简单配置以后建立本身的应用?

其实很是简单,在只考虑用户而不是租户的命名空间下,在系统层面和数据库设计层面多进行一层考虑便可,好比在数据库的主表/主实体上增长两个字段:namespace_idapp_id

  • namespace_id表明一个用户拥有的惟一且与其余用户相互隔离的命名空间。一个用户建立的全部业务系统、数据都在一个namespace_id下面,因此该用户建立的全部业务系统的数据都能用namespace_id查询获得。
  • app_id表明具体的某个业务系统(应用)的惟一id。因此该用户建立的某个特定的业务系统的数据能用app_id查询到。

多平台统一管理

在多个平台(抽奖、分享、任务平台)创建起以后,须要一个后台管理平台将这几个平台统一管理起来,方便为用户提供统一的配置。

以建立一个业务系统为例,若是使用管理平台进行配置,那么用户只须要在管理平台上填写不一样平台的对应业务配置便可,在管理平台中将自动处理:

  1. 管理平台的建立应用功能将先建立一个统一的namespace_id、app_id
  2. 将namespace_id、app_id、不一样平台的对应配置经过内部调用写入到对应平台的数据库配置表中

对配置好平台配置进行修改、删除,对业务数据进行统计的操做可经过管理平台的调整配置、统计数据接口进行

技术选型

微服务

为何说适合用微服务架构来开发平台?

  • 平台业务的扩张。往平台的方向考虑,业务逻辑会愈来愈复杂,代码量会愈来愈庞大,为了服务的独立部署测试、调整,服务的拆分红为必然。
  • 平台的统一管理。经过API网关、配置中心、服务发现注册中心、熔断器等组件,能够将多个服务统一管理起来。好比在网关添加鉴权模块、收集全部请求日志,在配置中心统一管理服务的配置文件,在熔断器中对不一样服务进行管理配置。以上都是为了将零散的服务经过相关组件统一成总体

业务决定架构

业务如何决定架构?

  • 业务的并发量、稳定性。若是并发量是业务须要知足的一个条件,则须要可以支撑大量请求的高可用架构。好比每一个服务节点的多节点负载均衡、数据库使用MySQL仍是MongoDB的选择、数据库架构的主从复制读写分离、中间件的选择、前端的缓存与CDN技术
  • 业务的第三方支持。如推送、搜索功能本身实现的话比较麻烦,通常使用第三方服务,这种第三方与本身服务的结合也是架构中的体现
  • 业务需求引起的内部交互。如通常应用的注册功能,都会使用消息队列这个中间件将写数据库行为与其余行为(发短信、发邮件)解耦;还可能设计一些定时任务从某些数据源按期获取数据更新到另外一个数据源

这里给出另外一个比较能体现业务决定架构思想的一篇实例文章参考

性能

若是只考虑性能,那么不能选择微服务架构,由于单体应用在性能上是彻底碾压微服务架构的。若是为了全局考虑以后仍是得选择微服务,那么如何在使用微服务的状况下尽可能提高访问的性能呢?

首先须要经过压测的方式测试出性能的瓶颈在哪里,这篇文章可能有所帮助。

至少须要保证能在性能问题发生以后可以当即解决,因此至少要作到服务可以在任意时刻可以版本回滚、可以动态扩展到更多机器上。而后经过对应的方式如优化代码、使用缓存、队列、添加服务器、改进中间件架构的方式去解决性能问题。

部署方式

对一个架构师来说,代码从提交到上线也须要考虑到,甚至须要考虑为团队构建DevOps体系。简单来讲,使用自动化流水线将代码提交、测试、构建、部署自动化起来,并在项目上线的过程当中协助开发测试运维进行代码版本的切换、部署方式的实践。

为了往后服务的蓝绿部署、回滚、动态伸缩,必须使用容器技术如Docker与容器编排平台如K8S来实现。至于具体的搭建方式、维护细节不用彻底掌握,但必定要知道各类部署方式、服务维护方式和其利弊。

安全考虑

业务安全

对于抽奖系统,由于奖品有实际价值,因此直接跟钱挂钩。为了防止恶意用户撸羊毛,必须对可能存在的抽奖后门进行封杀。

对于抽奖系统,最大的后门是:中奖接口。如何判断用户是真正的玩了这个游戏并一路闯关达到终点夺得奖励的呢?对于web平台来讲真的是一个比较困难的问题,由于没办法跟游戏同样实时得到用户的数据进行详细的断定,因此只能在最大程度上增长中奖接口的校验规则。以下:

  • 游戏逻辑校验。根据前端设计,先测试出夺得奖品最快用时是多少,而后在后台加入判断:若是用时少于最快用时,封杀。另外能够根据游戏业务逻辑判断:必须经过点击【赞成协议】、【开始游戏】这两个按钮(访问这两个接口)才能成功调用中奖接口。
  • 验证码。防止恶意用户使用脚本自动刷奖,中奖接口添加验证码方式校验。但这极大程度上影响用的体验,谨慎使用。
  • 黑名单控制。对于反复调用不少次的用户,直接将该用户id加入到黑名单中,在一段时间内禁止接口访问。

网站安全

这是一些比较通用的web安全问题,通常在框架内部已经解决,但也须要确认是否开启相关安全机制。

  • SQL注入。若是使用的是ORM框架则不须要担忧这个问题,由于框架内部已经使用转义的方式帮你处理好会影响SQL语句的特殊字符。若是涉及到复杂的SQL手动拼接,那么必须在开发时使用占位符的方式来拼接字符串来解决该问题
  • csrf。像Go语言的Beego框架,Ruby语言的Ruby On Rails框架,Java语言的Spring Security中就已经提供解决方案,但并非默认开启,须要在先后端统一配置才能生效,后端配置以后,会在session存储一个csrf的token,等待下次请求前端传过来这个token来验证。因此对于某些安全性较高的操做是须要添加csrf防范的
  • xss。在框架里可能没有现成方案,那么须要在先后端都加以防范,好比在前端JS中要避免把不可信的数据看成代码执行了,在后端须要配置专门的过滤器,在过滤器中使用转义工具将影响JS执行的特殊字符进行转义
  • https。防止中间人攻击,提高网站安全性、搜索权重,众人皆知,再也不赘述
  • 权限控制。避免普通用户可以使用管理员的权限相似的问题,须要在后端进行限制
  • 密码管理。即便数据库被人破解,也不能让别人看到你的明文密码。对于不须要解密的密码,好比用户登陆的密码,使用bcrypt算法或md5加盐进行加密;对于须要解密的密码,使用AES或对称加密算法加密保存,加解密使用的密钥须要另外保存

等等等等,网站安全问题是在太多,没办法都写出来,不过以上问题比较常见,须要在开发时外加注意,这些虽然不会在架构设计图上展现出来,可是会是一个存在的架构隐患

总结

咱们从业务、数据、技术、安全方面综合考虑如何将简单的业务设计成为一个平台,看重的是长远的收益,提高的是本身的能力。做为开发人员的你也许已经烦透写业务代码,不妨换一种开发思想挑战一下本身吧。

来源:91Code

关注公众号获取更多内容!

在这里插入图片描述
相关文章
相关标签/搜索