编辑推荐: |
本文来自于简书,本文重点从微服务的演化、建模微服务、微服务的配置以及规划来讲解如何构建微服务等等。 |
|
微服务是什么
微服务就是一个独立的可部署的占有自己进程的一个实体, 我们一般把这个实体称之为服务。 微服务的核心思维和SRP原则是一样的:
聚合所有因为相同原因而改变的元素, 分割那些因为不用原因而改变的元素。 只不过是站在更加宏观的角度来思考的。?
一个微服务是完全自治的,它对外提供API, 并且自身的改变(不包括API的改变)不会影响依赖者的改变。?
微服务的好处
服务的异构性 - 不同的服务可以使用不同的且适合当前服务的技术栈,这样的话公司也有机会在某些服务上尝试一些新的技术
弹性 -可以对错误进行隔离, 可以通过用多台机器增加可用性。
缩放 - 可以通过用多台机器分担LOAD
易于部署 - 部署的最小单元缩减为单个服务
组织架构的安 - 就算是大公司,人员年分布在不同的地点甚至是国家,通过微服务,我们可以把开发相同微服务的人员放在一块。
可组合性 - 通过编排微服务,可以很方便的搭建客户化的需求。
可替代性 - 微服务一般都很小,重写的难度没有那么大
微服务的到底应该多小呢
理论上微服务越小上述的好处越明显,但是所带来的管理问题也越大,这就是一个平衡的取舍,一般来说这取决域公司的组织架构,管理成本,服务治理的能力。?例如如果公司有高效的福利治理工具,且能够自动化的部署构建微服务,那么就可以增大一些微服务分割里粒度。?又或者微服务的数量可以等于据公司软件部门职责的划分出来项目组的数量。
微服务的问题
一方面是上诉所说的管理的问题, 一方面由于微服务体系的分布式系统的本质,所有分布式系统的难题在微服务系统架构中都有体现,例如数据一致性问题,分布式事务,服务依赖,异常传递等。
演化的架构师
一个软件架构师最重要的职责是负责规划系统的区域,已经区域之间的交互,而不应该纠结区域内部的建设。 对于微服务来说最重要则是服务的切分(那些服务应该成为独立的微服务),
以及这个微服务之间的依赖和调用关系。 但是架构师在做系统设计的时候有很多时候需要在信息不完全,所以架构师需要建立一些指导原则(Principle)和实践(Practice)去指导开发者去进行服务设计和开发。我们的企业有对于软件系统是有战略目标的,Principle是为了履行这些目标所定下的原则,普通情况下是不变的。
而 Practice是履行这些原则所定下的具体的日常实践惯例。 这些工作包括例如
1.选择一个标准的接口技术。 例如如果我们选择了REST/HTTP,
那么就要接下来规范化接口定义, 如何支持分页, 如何支如何Versioning 等
2.平台安全性。可能需要所有的接口有自己的连接池,或者实现Circuit
Breake. 还有就是定义良好的错误规则也很重要,例如返回异常还是返回错误代码。
3.定义处理技术欠债的方法
4.使用模板代码规范化模块的开发等。
当然我们的系统里面可以有一些特例不符合规定的原则,但是这些特例是少数存在的,如果我们发现特例越来越多可能我们就需要修改我们的原则了。
软件架构师处理做上述所说还需要去了解新的科技知道什么时候做出正确的平衡,
和团队坐在一起去了解自己做出的决定对团队的影响,或者对代码的影响。 这些工作难度大,工作量多最好能和一群人一起做,
而各组的leader是非常好的人选。 有的时候架构师可以让团队犯一些小错让队伍学会在错误中成长, 但是如果他们做出了非常错误决定会造成大的影响的时候,架构师需要站出来阻止。
如果建模微服务
最重要的而两个原则是:松耦合,高内聚
松耦合对于微服务的最简单的衡量标准是:我变我的客户不需要变, 我重启我的上游客户不会被影响。
高内聚对于微服务的最简单的衡量标准是:服务逻辑全部由微服务收拢。 例如库存微服务,那么所有和库存相关的逻辑都应该由库存微服务提供。
我们开发微服务的时候服务的对外接口是什么,内部实现是什么一定要确定清除。
有的时候太早分割系统模块做微服务也不好,不如一开始用系统模块实现,等自己对系统了解足够好了,再分解为微服务。
建模的时候,先像这个模型行为是什么,再想数据存储,千万不要以数据为导向来建模
有的时候微服务的边界还是要取决于组织结构,例如,如果一个团队负责仓库的所有服务,那么我们可以创建如下架构的微服务其包涵三个模块
订单履行, 库存服务,货品接受。
如果一个团队负责仓库的所有服务
如果我们有三个团队分别对三个服务负责,那么就可以变为三个微服务。
有三个负责三个服务
由于MVC 架构的影响我们的系统一般很容易变为技术分层
技术分层
对技术分层进行业务拆分会非常的困难,所以微服务话的难度也会很大。
技术分层可以在微服务内部去做。或者如果公司的服务架构不复杂,且性能成为问题的时候可以采用。
集成
API
微服务之间的交互或者叫功能继承级技术选择一定要符合如下的原则:
1. 避免破坏性的修改 (服务变, 客户端也要变)
2. 保证API的技术无关性?
3. 服务易于消费方使用
4. 隐藏内部实现细节
共享数据库
共享数据库一般不适合于微服务架构理由如下:
1. 表结构于多个服务,间接造成服务耦合
2. 消费方绑定了特定的技术(和关系型数据库绑定了)
3. 如果发现一个读取表的BUG, 那么很可能多个地方如果都是COPY的逻辑的时候,那么就需要修改很多地方,
内聚性规则被打破。
同步和异步
这也是服务于服务之间要考量的调用模式, 根据业务而定。
编排和协同
服务编排: 组件作为中心大脑去调用管理调用小于服务
服务协同: 组件使用一个事件去触发下游服务,大家一起协作完成任务
RPC
目前来说方便调用的RPC技术容易造成技术耦合,而没有技术耦合的RPC技术客户端不能很方便的调用, 例如Protobuf,
而且使用RPC技术特别容易破坏服务变动客户端不需要修改的原则。 所以一般情况下不推荐使用。但是在电商环境下,考虑到性能问题有时候还是必须使用RPC作为集成技术。
REST
最佳的集成方式,不过性能要比RPC低。
微服务DRY原则的危险性
例如如果多个服务使用一个共享库,这个共享库升级, 那么所有的相关的库都需要升级。 作者建议微服务内需要DRY原则,但是微服务之间可以适当的违反这一原则。
有的时候我们对服务使用客户端,这里的原则是千万不要把逻辑放到客户端,这样服务端一升级,客户端就需要改动。客户端最好就实现,日志,故障,负载均衡之类的和逻辑无关的功能。
用户界面相关
如果微服务按业务划分,那么其实相关业务的UI也必须是微服务团队负责。 对于这点我们可以利用UI嵌入功能, 也就是说例如单独的UI
team做大的框架, 然后微服务团队返回自己功能的UI, 嵌入到UI TEAM的框架中。
当然如果我们的前端是本地手机端App,上述方法就不适用了。
BFF 模式
Backend for frontend 模式, 建立一个服务编排层服务于负责的业务场景。
第三方软件集成
可以使用绞杀者模式,或者就把第三方服务包装成微服务来使用。
分解monolithi程序
如果分解单块系统
单块系统很大,我们如果下手呢,可以考虑下面的因素
1. 如果我们对库存代码后期改动很大, 我们可以把库存代码抽出来做一个微服务,独立的改动, 对后期开发速度有促进作用
2. 如果团队在不同的区域, 把那些能让相同位置一共开发的功能剥离出来当微服务。
3. 安全方面的代码,也可以剥离出来做一个独立的服务做监控, 传输数据,和静态数据的保护
4. 有一些高尖端技术 独立出来, 独立演变。
处理杂乱的依赖
我们通过上面的方案在单块系统找出了要分解的部分, 下一步就是弄清楚当前要分解的部分和其他部分的关系了
可以通过一个有向无环图来查看关系,并且可以找出哪些部分是最难以处理的。一般来说,数据库是最难处理的组件。下面给出一下常见的数据库耦合的场景以及解决方法:
例子1: 打破外键的关系
那些用外键的表,就显示了表与表的耦合,也就变成了业务域和业务域的耦合, 例如表A, 和表B有外键,我们可以重构为这样。
A 的服务首先通过A表得到相关信息,而那些B表的那些原先通过外键得到的信息,可以向B用微服务调用的方式得到,而不是通过直接数据库JOIN的方式
。
例子2: 共享静态数据
有的时候一些静态的数据保存在数据库表中供多个服务访问,那么一个极端的方式是做一个专门的微服务,或者就为每个服务建立一个ENUM来保存静态数据。作者认为使用ENUM是比较折中的方式。
例子3: 共享数据
一些业务数据可能被多个服务共享,那么需要为这些业务数据单独做一个微服务。
例子4:共享表
系统可能有一些基础表能够存储例如产品目录和仓库信息,那么我们可以建立两张结构一样的表,分别为产品目录和仓库两个微服务服务。
报告系统
微服务体系下的报告系统肯定是需要单独拿出来做微服务的。以前单体系统的报告可能就是直接读取本身的大型数据库得到的。
而在微服务的环境下报告系统可以有自己的独立的存储系统。
我们的报告系统可以用定时的JOB去调用多个微服务得到数据,存储到本地的存储环境中去。 又或者可以异步的调用系统,例如第一次我们调用的时候系统返回202表示我接受了,然后系统异步去处理,
我们的报告系统去polling, 知道得到201 为止。?
上面的方式是报告系统自己去拉, 但是也可以让各个服务自己周期性的去导出数据到报告系统中去。或者服务可以以事件的形式把自己的数据发送出去,
让报告系统去订阅。
部署
持续集成CI
一个真正的CI必须要满足下面的情况
1. 你是否每天签入代码到主线?
2. 你是否有一组测试来验证修改?
3. 构建失败后团队是否把修复CI当作第一优先级的事情去做
理想的CI架构如下,每个微服务代码分离,且都有自己的构建任务,打包成不同的部署文件
构建流水线和持续交付
理解这一节需要弄清楚几个概念
集成是指软件个人研发的部分向软件整体部分交付,以便尽早发现个人开发部分的问题;
部署是代码尽快向可运行的开发/测试节交付,以便尽早测试;
交付是指研发尽快向客户交付,以便尽早发现生产环境中存在的问题。
一般我们的jenkis工具只负责集成,当然我们可以在jenkis上创建一个Job,
完成持续部署和持续交付的任务。
一个持续交付的流程如下:
分阶段处理的好处, 例如可以让一些快速的测试先进行然后出错了可以快速的反馈不用等到耗时测试完成才给反馈。
而且使用可视化的方式可以为运维人员带来管理的便利。上图中,不同的构建阶段是使用不同的环境的,有的时候环境的不同会隐藏一些问题知道生产环境中才会发现,所以我们越接近后面就需要越接近生产环境。
但是生产环境的成本较高,所以我们就要取平衡。
服务配置
我们并不想把环境配置嵌入到APP中去, 让APP包涵一些例如数据库密码的配置。 最佳的方式是使用单独的配置server,
构建的时候从配置服务器中动态拉取配置。
服务于主机之间的映射
一 单主机多服务一 单主机多服务
优点:
一般情况下一个团队管理软件 一个团队管理主机部署。 如果多个服务部署到一个主机,主机团队的工作会变小。
成本减少
缺点:
监控不方便,服务之间还是会相互影响的
部署有问题,例如用PUPPET这样的软件部署,不可避免的出现冲突
增加单个服务的扩展性的难度。 如果一个服务需要一个特殊的网络环境,但是由于和其他的部署在一起所以最后还需要搞一些特殊化
二 应用程序容器
优点:
1.有单机的优点
2.用容器简化管理, 可以对多服务来监控。
3.节省语言运行时的开销
缺点:
1.限制技术栈的选择
2.自动化的管理受到限制
三 每个主机一个服务
优点:
1.简化监控和错误恢复
2.减少单点故障
3.可以采用不同的部署技术
缺点:
1.管理复杂性
虚拟机
虚拟化技术允许把我们把一台物理机分成独立的主机。
一 传统的虚拟化技术
hypervisor的两个作用
1. CPU 内存,资源从虚拟机到物理机上的映射
2. 提供一个控制虚拟机的层
但是需要暂用不少的资源
Vagrant: 一个能在开发机上生成一个测试云的工具, 它的底层使用标准的虚拟化系统(virtualbox
例如)。 利用这个工具可以方便的模拟出生产环境云环境。
?但是使用它会占用不少机器的资源
二 Linux容器
;
linux 创建一个隔离的进程空间,在这个空间中运行其他的子进程, 容器就成了这个进程上的子进程树。
每个容器里面操作系统可以是不一样的但是必须使用相同的内核。例如可以是UBUNDU或者是CENTOOS但是他们必须使用相同的内核。
由于没有HYOERVISOR层,且映射也不需要了,所以启动速度和能够拥有的虚拟数目比传统的虚拟技术快的多多的多。
缺点是很难把每个容器里的虚拟直接对外暴露服务, 且可能出现容器之间相互影响的情况。
Docker
基本上你可以认为目前的Docker是LXC的一个高级封装,提供了各种辅助工具和标准接口方便你使用LXC,你可以依靠LXC和各种脚本实现与docker类似的功能
监控
如果我们提供给客户的服务,是由很多小服务组合而成的,那么我们的监控是监控小的服务, 然后聚合起来看整体。
单一服务器, 单一服务
首先监控主机本身 cpu, 内存,
然后查看服务程序的日志 (扫描)
监控RT, 监控日志-出错的次数
单一服务多个服务器
CPU 占用率每台都很高就是服务的问题,不然就可能是主机的问题,或者是某些恶意访问的问题。
聚合所有主机的数据 , 一般的监控工具都可以满足这个功能
查看日志 ssh-multiexecutor 功能可以查看多个主机的日志
查看响应时间:可以直接在负载均衡器上加。 负载均衡器自己也可以监控底下的节点然后去除那些不良节点
多个服务,多个服务器
如何在在多个主机跟踪一个错误的调用链, 然后找出引起这个错误的原因。
答案是从应用程序指标,日志,集中收集聚合尽可能多的数据
使用logstash 把日志放在一起, 然后用KIBANA 一个基于elasticSearch的系统用查询语句来查询日志,
还能生成图表
关联标识
系统中使用关联表示可以便于我们追踪我们系统中的调用链路,从而让我们能够快速的发现调用链中哪个错误,
目前比较的方便的工具是zipkin.
级联
有的时候服务A, 服务B都是正常的, 但是服务A,B 之间的网络是坏的,所以每个系统都应该监控自己的下游服务的可用性。
我们可以用hystrix 断路器优雅的降级, 也可以用spring boot 的hyxtrix来显示系统的短路断路状态。
标准化
监控领域的标准化很重要,例如标准格式记录日志。 提供一个镜像理由就有logstash和collectd,
还有一个公共的库使得和graphite 变得容易。
考虑受众
他我们是为不同的人收集数据,我们需要为受众考虑
1.他们现在需要什么
2.他们以后想要什么
3.他们怎么消费数据
未来
业务指标和系统指标最好以相同的方式处理, 因为以前我们把业务指标扔到一个地方然后几天后才管,而现在的业务需要我们能对这些业务指标快速实时的做出反应和系统指标一样。
需要用一个伸缩性好的路由系统,把这些数据分发到不同的系统中,STORM, HADOOP或者日志分析KIBANA.?
康威定律和系统设计
一个奇怪的定律:
任何组织在设计一套系统时,所交付的设计方案在结构上都和改组织的沟通结构保持一致。?
例如你有四个小组开发一个编译器,你哟一个四步编译器
1.松耦合组合和紧耦合组织
紧耦合组织: 例如普通的公司, 商业组织
宋耦合组织: 例如开源社区
宋耦合组织一般系统模块话会好一些。
AMAZON 和 NETFLIX 两个例子:amazon 想让团队管理自己的服务和资源,于是AWS就这么产生了。
NetfliX 也就是这样才出现了其微服务的结构
服务所有权
拥有服务的团队对服务负全责
大家使用共享服务的原因
之所以很大系统还是倾向于单体系统
1. 难以拆分系统
2 特性团队
例如分为了数据库团队,服务团队, UI团队
3 交付瓶颈
如果一个小团队挂了,能轻易的让其他人来干
内部开源
上面的共享服务存在是有合理原因的,我们克服的方法是内部开源,服务的人负责MERGE,
其他人负责PULL REQUEST,服务守护者对提交代码的使用惯例,样式要严格把关,不要让现有的修改使未来更加的困难。
规模化微服务
多少是太多?
我们对于系统的一些操作,例如容忍多少故障,?系统需要多快。需要根据场景,系统的用户来看。?有的时候我们的客户不清楚他们自己的需求?那么我们就需要自己去问:
1.响应时间/延迟
2.可用性要达到多少
3.数据的持久性
功能降级
要弄清楚每个微服务挂掉之后,我们的降级策略。
架构性安全措施
有可能一个缓慢的应用会拖垮整个网站的threadpool,?所以要么做连接池隔离要么用hystrix
如何应对随时可能出现的系统故障
1.正确的设置超时
2.使用断路器
扩展
1 使用更强大的主机
2 负载均衡
客户端用HTTPS 访问我们的负载均衡器, 然后负载均衡器在自己的内网用HTTP和底下的机器通信。
负载均衡器可以使用硬件也可以使用软件。?
最重要的是最好把配置存放在版本系统中去, 并且可以自动化使用。?
3 基于worker的系统
就是另外一种实现分担负载和降低脆弱性的一种方式,所有的请求需要保存在一个临时列表中去, 这个列表需要安全,例如ZOOKEEPER或者是消息中间件。
然后我们的机器开启线程WORKER去消费他们, 消费完成就需要去在列表中删除请求。?
4 重新设计
任何系统不可能一开始就设计为为大规模并发量服务的,我们开始的时候可能只要考虑我们的用户10倍量的时候该如何设计,只有在用户100倍量的时候,我们才需要对系统进行重构。?
互联网公司快速实现,验证用户使用是非常重要的。?
扩展数据库
扩展读: 例如MYSQL 的SLAVES, 然后可以读写分离
扩展写: 最简单的方式就是分片,先HASH, 找到对应的SHARD, 然后去写。 查询的时候可能会很麻烦,需要搜索很多分片,
这个时候我们可以采用58同城沈剑介绍的一些方法。?
但是现在越来越多的非关系型数据库,会自动的帮我们弹性增加分片。?
CQRS
查询和命令使用不同的模型。 两部分可以独立的发展。 命令发出来的可以通知查询的MODEL去变化。 而我们的查询的model
可以是多种多样的。
例子:
Customer服务和Order服务是命令端服务,提供了创建和更新Customer和Order的API。Customer
View服务是查询端服务,它们提供查询Customer的API。Customer View服务订阅了由命令端服务发布的Customer和Order事件。该服务更新由MongoDB实现的视图存储。服务维护了MongoDB所存储一系列文档,每个用户一个文档。每个文档中包括了客户细节信息的属性,还包括存储客户近期订单的属性。这一系列文档支持多种查询,包括之前所提到的查询
自动伸缩
能够根据使用量自动的伸缩
总结: 微服务的原则
围绕业务概念建模
更加的稳定, 也能确保我们更好的反应业务流程的变化。?
实现自动化
构建部署,等需要自动化
隐藏内部实现细节
为了让服务独立于其他服务, 最大化的独自演化的能力
一切去中心化
团队对服务的所有权
内部开源来包邮其他人修改自己代码的权力
团队和组织保持一致
独立部署
隔离失败
对远程调用警惕,使用HYXTRIX 等
高度可观察
1.使用关联标识跟踪系统的调用
2.使用语义监控看系统是否正常
3.聚合日志和数据
4.用合成事务到系统,模拟真实的用户行为
|