2015年到2016期间,随着业务流量增长,现有架构模式碰着了寻衅,公司开始朝着业务拆分和做事化方向迈进。开始采取java作为开拓措辞,做事化框架利用公司改进过的dubbox,支持跨措辞做事调用的nova框架(v2.0)。
2017年在做事化的根本上我们更近一步,向微做事架构渐变。拥抱社区供应的丰富组件(v3.0)。
我们碰着的问题

系统变的繁芜和薄弱,架构须要升级
开拓本钱高,学习本钱高,加大了merge冲突,排队等待,故障发生率等
测试本钱高,修正一处,也须要回归测试
运用难以做水平拓展,难以进行容量规则
第一个问题很棘手,我们先来说说架构升级会做些什么.
要明白做什么,首先须要考虑目标是什么?软件架构的目标是要设计软件系统来办理问题,以是架构要做的事从抽象的维度上看,便是:
根据问题域,界定系统的边界(Eric Evans的领域驱动设计,划定bounded context)
对系统进行切分,切分的目的是分工与协作(可以并行,以得到效率提升)
被切分的各部分之间建立协作与沟通的原则和机制
将各个部分连接合并成一个整体,完成系统的目标
上面是大的抽象原则,更详细一些来说,架构做得便是构造设计,在不同维度和层次上:
高维度:是系统、子系统或做事的切分与交互构造
中维度:是系统或做事内部的领域模块划分
低纬度:是代码构造、数据构造、表构造,技能方案选择,开拓用的爽不爽
架构实行过程中可能会涌现一些新问题,是在当初的架构设计中未能考虑到的,须要对此做剖析判断,并形成新的决策调度。而另一些问题,大概是实行过程中的走样,导致和当初的决策形成了偏差。架构师须要考虑所有这些关注点,并和开拓工程师找到办理这些关注点的各种选项,在适当的时候根据真实环境的情景去采纳得当的行动。有时,我们称这些行动叫作:重构或优化。当一个旧系统长期没有这样的行动,积累久了后,我们将迫不得已采纳其余一种行动,我们称之为 —— 架构升级。
软件系统或架构,不像建筑物会由于韶光的流逝而自然消耗腐坏,它只会由于变革而腐坏。一开始清晰整洁的架构与实现随着需求的变革而不断变得浑浊、混乱。打算机科学都爱借用一个物理学的术语「熵」,它表达体系的混乱程度,而软件系统的「熵」很随意马虎不经意间随着需求的变革而变得更高。
软件系统「熵」有个临界值,当达到并超过临界值后,软件系统的生命也基本到头了。这时,我们就要采纳那个迫不得已的行动了。图例展示了软件系统「熵」值的生命周期变革。
以是,不是所有的大型系统都是被很好的设计的,想要设计好一个巨型系统是非常困难的,而随着业务功能的叠加,原来的设计也会被堆砌的代码所淹没,以至冲破原来的设计。我们所能掌控的是一个有着特定边界的系统,以是根据业务属性拆分系统,将其限定在一个有边界的高下文中(Bouded Context),是一个最直不雅观也是最有效的方法。这也是领域驱动设计所追求的。在DDD欧洲大会上Eric也认可近年盛行的微做事架构有个很大的上风,做事粒度得当,做事物理隔离,单个做事的「熵」增问题被局限在单个微做事内部。单个微做事的更换与重构成本十分有限,使得「熵」增问题局部化,不随意马虎传染全局,甚至失落控。当然这有个条件,便是微做事的拆分和接口交互要合理,合理的考验标准便是随需求变革,总是实现变革或接口新增,而非总是调度接口交互。 架构始于系统生命之初,并伴随系统生命周期全程。每次需求变革带来的变动都应进行一次或大或小的重新架构过程。架构的关注点在于掌握软件系统变动时「熵」值的变革。
按照业务领域拆分后,已经能很好地知足了业务发展。但是v2.0对开拓职员包袱过重,须要做一些方便架构实行的事情
现有的java框架不能让开发职员只关注业务实现,框架本身没有供应一些开箱即用的三方的和公司的组件,须要大量地配置
框架没有供应一些common patterns辅导开拓职员编写代码
混乱的版本依赖
项目构造不标准化
运用康健检讨不标准化
编写测试繁芜,难以持续集成
为什么选择spring boot
开拓体检极大地提升
使运用配置变大略
使编码变大略
使编写测试代码更大略
本地启动,方便开拓调试
使支配变大略,内嵌容器
大略强大的spi机制,很随意马虎拓展自定义的autoconfiguer
spring-boot-starter-actuator使监控更大略
完善的生态圈,对主流框架无缝集成
社区生动,迭代迅速
符合我们的微做事目标,方便未来容器化
办理问题
youzan pom and youzan-boot-parent借鉴spring bom的做法,建立youzan bom,版本统一管理,彻底办理版本混乱问题。针对各个运用中重复配置问题,建立youzan-boot-parent,肃清重复,无需各个运用间copy。其余还额外带来一个好处,方便统一升级。
其余,针对我们现有的运维环境,标准化了4套环境:开拓,测试,预发,线上。
我们针对publish api jar deploy到maven仓库中做了严格的限定,api jar本应只包含一些DTO和一些接口,但由于开门职员常常是复制粘贴,也会把各种不须要的依赖(比如spring,各种log框架等) 加入到api中,导致利用该jar的运用方发生依赖冲突,通过会花一些不必要的韶光来找到冲突并办理冲突,我们希望通过技能手段来做末了一道防线,从源头上办理由于依赖其他系统api jar而导致的依赖冲突。
youzan application
我们希望运用对一些开源组件和公司自己开拓的组件利用起来更大略,降落接入本钱。为此我们拓展了spring boot的autoconfiger,添加了各种starter。
youzan-boot-dependencies
youzan-boot-parent
youzan-boot
nova-spring-boot-starter
nsq-spring-boot-starter
druid-spring-boot-starter
mybatis-spring-boot-starter
chameleon-spring-boot-starter
youzan-boot-starter-test
举个例子,如果我们想利用nova框架,只需一个表明@EnableNova即可
运用标准化的康健检讨
curl http://127.0.0.1:8080/health
{ status: \"大众UP\"大众, diskSpace: { status: \公众UP\公众, total: 249779191808, free: 61591195648, threshold: 10485760 }, redis: { status: \公众UP\"大众, version: \公众3.0.3\"大众 }, db: { status: \"大众UP\公众, database: \"大众MySQL\"大众, hello: 1 }, refreshScope: { status: \"大众UP\公众 }, hystrix: { status: \公众UP\"大众 } }
分布式系统设计中,有一条很主要的原则便是:为失落败而设计,缺点一定会发生。 为了防止系统涌现级连失落败,我们须要对依赖的做事所能够利用的资源做一定限定,保护运用本身。常日以下目标都是要考虑的:
保护调用方,不由于依赖的做事(特殊是网络做事)的问题(高延时和失落败)而影响到调用方运用
在繁芜的分布式系统中阻挡级联失落败
即时失落败(fail fast)和快速规复
fallback和优雅降级,如果可以的话
能够实时的监控,动态调度参数和干系操作
下面大略先容下hystrix实现事理,详细内容请参考官方文档
利用HystrixCommand 或者 HystrixObservableCommand 来包装外部系统调用,常日是在单独的一个线程中实行
每个dependency设置超时调用,可以自定义超时时间,也可以动态调度,常日超时时间设置的比丈量的第99.5个百分位的值稍高一些
为每个dependency掩护一个小的线程池,当线程池满了,直接谢绝要求
丈量记录要求成功数,失落败数,超时数和被谢绝数
触发断路器,针对某个特定的做事阻挡统统要求一段韶光(可以手动触发也可以自动触发,自动触发规则是这个dependency的缺点百分比超过阀值)
当要求失落败,超时,或者短路时实行fallback逻辑
监控丈量值和准实时配置变更
表示在代码上
@HystrixCommand(groupKey = \"大众RiskGroup\公众, commandKey = \公众RiskClient-containsSensitiveWord\公众, fallbackMethod = \"大众fallback\"大众, commandProperties = { @HystrixProperty(name = \公众execution.isolation.thread.timeoutInMilliseconds\"大众, value = \公众3000\"大众) }) public boolean containsSensitiveWord(List<String> words) { if(skipSensitiveWordValidation){ return false; } PlainResult<Boolean> result = sensitiveWordFilter.containSensitiveWord(words, RECEIPT_SCENE); LazyLogs.info(logger, \"大众RiskClient.containsSensitiveWord({}), result={}\"大众, -> words, -> JSON.toJSONString(result)); return result.getData; } / 风控接口调用涌现非常,则降级,是弱依赖 / public boolean fallback(List<String> words, Throwable e) { logger.warn(\"大众RiskClient.containsSensitiveWord({}) fallback\"大众, words, e); return false; }
通过配置中央可以动态掌握hystrix的参数
关于API文档的更新
作为api的利用者的开拓常常会创造文档是过期的,乃至是缺点的,听说程序员都不喜好写文档,由于他们喜好写代码,以是最好通过代码来自动天生文档。利用spring restdocs可以通过测试代码自动天生文档,还有一个好处时,如果接口中增加或减少字段时,如果不同步更新测试的话,测试就不会通过,这样就可以担保文档始终是最新的。
测试代码
@Test public void withdrawSummary throws Exception { given(withdrawQueryService.queryWithdrawStatus) .willReturn(PlainResults.success(WithdrawStatus.getStatusMap)); this.mockMvc.perform(get(\公众/withdraw/queryWithdrawStatus\公众)) .andExpect(status.isOk) .andExpect(content.contentType(MediaType.APPLICATION_JSON_UTF8)) .andExpect(jsonPath(\"大众success\"大众).value(true)) .andExpect(jsonPath(\公众code\"大众).value(0)) .andExpect(jsonPath(\"大众message\公众).value(\公众\"大众)) .andDo(document(\"大众withdraw-status\"大众, preprocessRequest(prettyPrint), preprocessResponse(prettyPrint), responseFields( subsectionWithPath(\公众requestId\公众).ignored.optional, subsectionWithPath(\"大众success\"大众).description(\公众要求结果\公众), subsectionWithPath(\公众code\公众).description(\"大众缺点码,0表示无缺点\"大众), subsectionWithPath(\"大众message\"大众).description(\"大众缺点提示,如果有缺点的话\"大众), subsectionWithPath(\公众data\公众).description(\公众提现状态列表\公众) ))); }
天生的API文档
关于AP供应者的烦恼
在公司或组织内部,api供应者最大的烦恼莫过于找不到消费者到底有哪些,以及消费者是如何利用他们api的。更不要说经由一些公司职员变动之后的情形。有个真实的案例,开拓职员将某个字段单词拼写缺点改动回来,结果发布上线后,有个依赖方由于利用到该字段,而导致依赖方做事不可用。实在这种场景和经历发生多次后,开拓职员就会畏手畏脚,对原来一些不合理的设计和缺点就会不去改进它,听之任之。
实在这种问题根本缘故原由是做事供应者与消费者协作模式的问题,我们希望有某种机制来减轻这种问题。如果做事消费方能够把利用api的场景关照给做事供应者,并落实在测试代码上,那是不是就可以让做事供应者感知到各个依赖方api利用场景。实在这是一种左券精神,做事供应方与依赖方要多多沟通互换,并将沟通互换的成果落实到测试代码上。当然了得有些得力的工具和框架来支持我们这种设想,左券测试(Contract Testing)便是来达成这些目标的。详细利用文档请参考 Spring Cloud Contract
持续集成
持续集成的好处不用多说,关键在于实行下去。
更多的收益
日志级别动态调度
curl -i -X POST -H 'Content-Type: application/json' -d '{\"大众configuredLevel\"大众: \公众DEBUG\公众}' http://localhost:8080/loggers/com.youzan.pay
更方便地网络系统metrics数据,方便监控
spring cloud config/consul 配置中央
做事创造/consul
spring cloud zuul api网关
spring cloud sluth & distributed tracing/zipkin 分布式tracing
参考
Eric Evans — Tackling Complexity in the Heart of Software
spring boot
spring cloud
https://martinfowler.com/microservices
microXchg 2017 - Juven Xu: AliExpress' Way to Microservices
Microservices at Netflix Scale
https://jenkins.io/
BoundedContext