阿里P8 谈微服务的应用架构设计

首先咱们经过一个最简单的例子来看下如何构建一个微服务应用。git

图 1 是一个完整服务的代码,它和普通的应用程序没什么区别,只是功能很是少,业务很是简单。把它编译以后部署在服务端就能跑起来,咱们从上往下解释一下这段代码干了什么事情:github

  1. 在第 6 行咱们引入一个包 “github.com/koding/kite”,这是一个开源的微服务框架包,使用它能够快速方便的构建一个微服务应用;数据库

  2. 第 11 和 12 行咱们定义一个微服务,并将其取名为 first;后端

  3. 第 15 行开始咱们为这个微服务添加了一个 square 操做指令供外部调用,这是负责完成具体功能的业务单元,在这里它负责将外部传入过来的值进行求平方计算后返回给调用方;设计模式

  4. 最后在第 21 行咱们把它注册到框架提供的某个地方,并声明了它的访问地址和协议。缓存

阿里P8 “布道师”,谈微服务的应用架构设计(附微服务教程)

图 1. 服务端代码网络

这个服务实现了一个供外部调用的功能,咱们把它称之为 server 端。接下来咱们再实现一个调用这个它的客户端,咱们称之为 client 端。在真实的微服务环境中,并无严格区分 server 端和 client 端,只要有须要,它们均可能会相互调用。架构

一样的,咱们从上往下看看图 2 这段代码干了什么事情:负载均衡

阿里P8 “布道师”,谈微服务的应用架构设计(附微服务教程)

图 2. 客户端的代码框架

  1. 除了第 6 行引入的 “github.com/koding/kite” 包以外,咱们还引入了另一个 “github.com/koding/kite/protocol” 包,它用于作环境中的微服务发现;

  2. 第 15 行开始咱们经过配置环境中的信息在环境中查找全部名字为 “first” 的微服务。之因此说「查找全部」,是由于在更复杂的生产环境中,一个微服务每每可能包含多个副本,而且这个副本数是动态伸缩的,所以须要专门的服务注册和发现过程来动态注册和动态查找,咱们后面会讲到更具体的模式;

  3. 在第 22 行中咱们选择第 1 个结果,也就是第一个微服务进行访问;

  4. 在第 26 行中传入参数 4,调用指令 “square” 求平方,获得结果;


这是一个服务调用另外一个服务的演示过程,从中能够看出它涉及到服务的构建、注册和发现。后面这个 client 端例子在演示中虽然没有监听端口,只是访问别的微服务,可是它也能监听端口对外提供服务,本质上 server 和 client 两端从微服务架构角度说并无严格区分,只是在不一样时候扮演不一样的角色履行不一样的职责。

上面两个示例中咱们提到服务注册和发现。实际生产环境中,微服务的种类和每一个微服务副本数可能都很是多,特别是在相互调用以后,它们杂糅在一块儿的业务可能很是复杂。同时,复杂多变的环境决定了系统中运行的微服务随时可能被启停,这也是拥抱微服务的价值点之一:主动拥抱系统的不稳定性,经过消除这种不稳定性带来的影响来达到高可用的目的。为此,须要引入服务注册和发现机制来帮助系统治理这些服务。

服务注册与发现

咱们再来看一下,为何这么简单的代码中还须要多一个服务注册的步骤?

一个微服务起来以后,要对外提供服务,必须让别人知道它的存在,所以须要有一个地方让它「注册」,咱们把它称为「注册中心」。因为微服务的启停是随时均可能发生的,和启停对应的是服务的注册和解除注册,所以在这里咱们说到注册的时候默认也隐含解除注册,后续再也不赘述。解除注册是为了保证下线的服务不会被调用方访问到。

假如这两个服务都有多个实例副本,它部署起来如图 3 所示。在实际生产环境中,每一个服务实例 A、B 和 C 都是动态变化的,整个服务的过程当中可能一直有多个副本在那里跑着,但不必定都是相同的实例,它们的 IP 在服务动态伸缩过程当中会发生变化。那么如何让客户端及时的知道这种变化呢?咱们知道,微服务提倡多个服务解耦比较干净,所以让一个服务主动通知另一个服务它的变化是不太现实的,这样侵入对方业务太深了,耦合太紧。同时,在后端有多个服务实例的状况下,如何将客户端的请求负载均衡的分发到各个实例中呢?

阿里P8 “布道师”,谈微服务的应用架构设计(附微服务教程)

图 3. 没有服务发现的问题

在这些问题面前,和传统的单体架构相比,微服务中服务的注册和发现能力很是重要,甚至有些微服务框架自带了服务的注册和发现功能(好比咱们给的例子中用到的 kite 这个框架就自带了这样的功能)。

一个服务起来以后,它的注册和解除注册过程能够由本身去完成,也能够由第三方工具去完成。好比在客户端和微服务端之间的负载均衡器可能会帮助微服务完成服务的注册和解除注册等操做,客户端和微服务自身都不须要关心,也即不须要他们各自的业务逻辑里面实现这些操做。还有一些状况是在客户端自带服务查询模块,它先从服务注册中心查询可用的服务,而后再按照这个查询结果去请求后端服务实例。下面咱们分别解释这两种模式。

阿里P8 “布道师”,谈微服务的应用架构设计(附微服务教程)

图 3. 没有服务发现的问题

图 4 中给出的是以客户端查询为主的服务注册和发现机制,后端服务实例起来以后,会以主动或者被动的形式注册到「注册中心」。客户端自带的「查询模块」会从「注册中心」查询可用的服务,而后按照查询结果去请求后端服务实例。

这样作的好处是:

  • 「注册中心」在服务以外维护,使用简单,对已有的微服务架构侵入小;

  • 客户端直接请求后端实例,查询完成后请求链路不须要通过其它中间环节。

其缺点在于:

  • 客户端和「注册中心」绑定;

  • 客户端的实现取决于具体语言或者框架,每一个客户端都得本身去实现。

阿里P8 “布道师”,谈微服务的应用架构设计(附微服务教程)

图 5. 服务端服务发现

咱们再来看下服务端实现的服务注册和发现机制,如图 5 所示。一样的,后端服务实例起来以后,会经过主动或者被动的方式注册到「注册中心」,可是客户端不须要实现查询模块去查询服务了,取而代之的是咱们在服务端添加一个「路由」环节,它负责代理客户端的全部请求,从后端实例获取结果以后返回给客户端。与客户端服务发现机制相比,它具备如下优势:

  • 客户端不须要作额外的变动;

  • 有些云服务公司已经提供相似产品能够知足需求了,能够直接接入。

  • 但它也有它的缺点:

  • 除非是托管在云服务提供商那里,不然还须要额外的服务端来部署「路由」或者「负载均衡器」部分,这也就意味着须要保证这个部分的可靠性和可用性,须要额外的系统设计和运维工做;

  • 请求多了一个路由代理的步骤,增长系统整体耗时。

在这里给你们提供一个交流,讨论的平台,JAVA微服务讨论群:671017482 ,

微服务测试

阿里P8 “布道师”,谈微服务的应用架构设计(附微服务教程)

图 6. 测试金字塔

咱们今天介绍微服务,首先假设系统到了必定的复杂度,使用传统的单体架构已经不能知足需求了,必须将单体架构上的功能拆分红职责和功能单一的微服务才能更好的跟上业务的发展。所以,由微服务构建的系统每每是一个复杂的分布式系统,它的测试涉及到从里到外的各个环节。

接下来咱们分三部分来介绍分布式系统中涉及到的测试:常规测试、混沌测试和流量重现。

咱们先来看一个测试金字塔,如图 6 所示,它包含:单元测试、集成测试、组件测试、端到端测试和探索性测试。接下来咱们要介绍的「常规测试」包含金字塔地下的四部分,它还包含另一种不太常见类型的测试,也即契约测试,下面会介绍到。而混沌测试或者猴子测试则属于探索性测试。

常规测试

1. 单元测试

单元测试是几乎全部系统开发都会涉及到的环节,它覆盖最细粒度的测试范围,通常基于类甚至是方法来进行自动化测试。它测试的范畴通常不涉及跨网络的调用,所以相对简单。在传统的 Web 开发中,很是流行的测试驱动开发 TDD 里面讲的测试通常基于单元测试,单元测试用例不只定义了业务边界,还将业务模块进行了细分。

2. 集成测试

集成测试将相关模块组合在一块儿,在业务上构成一个子系统进行测试,它的主要目的在于验证构成一个子系统的全部路劲上的调用是否正确,模块组合在一块儿后调用产生的结果是否符合预期。好比两个服务之间的调用,或者服务和数据库之间的通讯,通常都属于集成测试的范畴。

3. 组件测试

与集成测试相反,组件测试关注的是组件内部功能的完备性。对于微服务应用来讲,一个微服务即一个组件,为了测试这个组件内部功能的完备性,须要尽可能减小外部环境对其形成的影响。当该微服务依赖于一些外部服务或者数据库调用的时候,咱们能够 mock 一些外部服务,同时使用内存数据来代替数据库数据,这样能够尽可能减小外部服务调用对它产生的影响,只关注单个微服务自身的测试。

4. 契约测试

契约测试和组件测试的所需的边界很是像,对于微服务来讲,它们的所涉及的边界都是微服务自己。但和组件测试不一样的是,契约测试更关注输入参数和输出结果的合法性,必定的合法输入必须获得必定的合法输出,也即测试一个服务是否知足必定的契约;另外,契约测试还关注相应的结果是在多长时间内获得的,也即服务响应的性能。这时候对一个服务的测试,其所依赖的服务不能经过 mock 或者内存数据来代替。

5. 端到端测试

为了验证一个系统是否符合客户需求和商业目标,须要一个覆盖产品完整链路的测试。对于有用户 UI 界面的产品,端到端的测试意味着包括 UI 界面的测试和后端服务的测试。因为涉及到的环节最多,所以这样的测试运行起来也比较慢,出现故障的时候排查问题也比较麻烦。为了加快这个测试环节,一般建议:

  1. 将尽量多的测试需求自动化。

  2. 没必要每一个组件的修改都触发端到端测试,能够在全部更小粒度的测试完成以后再作一遍端到端测试。

之因此把上面全部的测试称为「常规测试」,是由于这些测试基本上在全部类型的现代化软件开发中均可能涉及到,它其实不是微服务架构都有的,微服务架构下的这些测试只是边界不太同样。统计对比代表,上线以前完整的测试能够避免 90% 以上的错误致使的故障。

关于常规的测试,咱们只在这里作简单的介绍


混沌测试

在分布式系统领域,Netflix 发明了一种更为古怪的测试,叫混沌测试,Netflix 称之为猴子测试。什么意思呢?它假设,若是你的系统足够健壮,那么随便启停某些服务并不会影响系统的总体运行,用户并不会感知到服务的故障。咱们常常讲要构建容忍故障的高可用服务,可是若是故障没有来,就无法验证这样的服务是否能够容忍某些极端状况的故障。为此,Netflix 在系统中引入了一系列搞破坏的「猴子」,它会主动给系统的各个部分制造麻烦,好比随时不当心关闭一台机器,可是你的服务还得继续运行,全部故障必须自动恢复,而且不能被用户感知到。

阿里P8 “布道师”,谈微服务的应用架构设计(附微服务教程)

图 7. Netflix猴子军团

Netflix 的猴子军团:

  • Chaos Monkey: 随机杀死实例

  • Latency Monkey: 人为引入延迟

  • Chaos Gorilla: 模拟整个可用区忽然断电

猴子军团中的几种不一样类型的机器人分别表明了不一样的破坏性,它在稳定的线上环境中随机选择破坏。这样的测试模拟了天然灾难,对线上环境进行了全黑盒式的演练,让线上系统产生「免疫」。遗憾的是,即使所幸躲过全部这些「天然灾难」,也没法说明系统是没问题的。


流量重现

咱们前面提到端到端的测试很难作,主要是难在它整个链路太长,耗时太长,环节也难以控制。另一个难点在于,咱们一般很难得到和线上同样真实的流量去进行端到端的测试,即使某个环节可以成功模拟相应的请求,但也不是全部请求的比例和线上都是一致的。

为了模拟线上请求,咱们能够讲线上流量截获后导入到测试环境中经过「流量重现」的方式,在一个更加真实的模拟环境中观察和调整。在这里介绍一个用 Go 写的开源工具:Gor https://github.com/buger/gor

阿里P8 “布道师”,谈微服务的应用架构设计(附微服务教程)

图 8. 流量重现工具 Gor

经过监听线上服务的请求,它可以截获线上环境的流量,并将其在测试和开发环境中重现,如上图所示。

典型微服务架构设计模式

阿里P8 “布道师”,谈微服务的应用架构设计(附微服务教程)

图 9. 典型微服务设计模式

图 9 是一个典型的微服务架构图。服务注册和发现的架构图前面讲过,为了简化图的结构,此图再也不说起。客户端的请求首先会到达一个负责对外沟通的 API Gateway,它负责识别客户端类型,解析和理解客户端的请求,并将请求分发到后端对应的微服务中完成。

通常来说,基于 HTTP REST API 的请求都是同步执行的,可是有些场景使用异步的方式更为合理,好比大视频转码服务,它很难在很短期内完成。同时,为了最大化微服务之间的解耦,应尽可能减小和简化微服务之间的通讯。为此,咱们引入消息队列做为异步通讯的通道。

在微服务的后端,若是每一个微服务都按照三层结构来部署,除了服务自己以外,还包括缓存层和持久化的数据库层。

这样就构成了一个典型的微服务应用架构,各项服务能够自由伸缩,相互之间依赖最小化,经过共享队列的方式来进行数据共享和信息同步。固然,这样一个架构要经过猴子军团的测试,首先得保证每一个组件都是高可用甚至是跨机房部署的,好比 API Gateway、消息队列以及每一个服务依赖的 Cache 和 DB 都随时可能挂掉。

七牛的微服务架构实践

阿里P8 “布道师”,谈微服务的应用架构设计(附微服务教程)

图 10. 七牛微服务设计实践

图 10 展现的是七牛数据处理平台的微服务架构示意图。七牛的数据处理平台天天处理接近百亿的数据处理请求,这些请求包括图片缩放裁剪等能够实时完成的同步请求,也包括音视频转码等没法实时完成的异步请求。全部这些请求经过统一的网关进入负载均衡器,再由负载均衡器分发给后端的处理实例。后端的处理实例中,每一个实例只部署一种类型的服务,同时维持一个 Agent 能够从存储中读取数据,以及一个 Cache 用于维持状态。因为业务相对简单,Cache 能够直接基于内存,而且没有持久化的要求。全部持久化的工做都由客户端主动发起,由后端 Agent 完成后对存储进行读写。

问答环节

  • :关于微服务的注册和发现,目前有哪些成熟的产品?

  • :比较成熟的用于协同的是 Zookeeper,在开源容器产品里面用的比较多的是 etcd。

  • :微服务健康情况,运行状况监控是如何作的?

  • :若是是使用容器化的部署方式,监控方面目前不少都是基于 Google 的 cAdvisor 来作的。

  • :请问微服务是否部署了多个实例,同服务的不一样实例间是否有分布式锁来保护?

  • :若是须要保证服务之间的原子性,可使用分布式锁,但应该和微服务的初衷有点冲突。建议尽可能改为异步通讯的方式,借助 message queue 等来通讯。

  • :微服务的部署,老师比较推荐哪一种方式?蓝绿、金丝雀,或者是其余?

  • :咱们的业务是灰度。

  • :针对服务总线、API GateWay、OpenAPI之间的区别是什么呢?您刚才所讲的服务发现属于这里面谁的范畴?

  • :服务总线是针对数据传输讲的。API Gateway 的针对请求处理和分发的。OpenAPI 功能上应该差很少,主要是面向第三方平台的开发者。API Gateway 除了终端(Web/iOS/Android)提供 API 功能以外,还提供了统一的入口、微服务接口聚合以及受权认证、后端服务负载均衡等功能。

  • :怎么解决动态拓展Docker,网络问题?

  • :要看具体是啥问题…… 网络方面 Docker 官方有提供方案,k8s 也有,通常私有部署能够直接使用开源的版本。

  • :若是应用已经基于Spark这样的分布式框架构建了,再想作微服务拆分有什么比较好的方案吗?

  • :我理解你跑在 Spark 上面的计算任务更依赖的是 Spark,而不是微服务的优点。所以若是你要考虑微服务话或者为了充分利用容器云平台的能力,能够考虑先把你依赖的 Spark 容器化,后面再考虑计算任务容器化的事情。

  • :以什么标准来进行微服务颗粒度划分?

  • :你们都说以业务的最小单元来拆分服务,但对业务的最小单元却没有统一的标准,也不可能有统一的标准,好比咱们作存储业务的和你电商业务的标准就无法统一块儿来。我我的的见解是,就像康威定律里面说的,服务的复杂性决定了服务的大小和拆分的合理性,除了「以业务的最小单元来拆分」以外,维护某个微服务的团队不该该太大。还有,拆分以后原本应该是尽可能解耦的,因此能够看拆分以后是否带来更复杂的异步通讯。

 

阿里P8 “布道师”,谈微服务的应用架构设计(附微服务教程)

原创不易,若是感受不错,但愿给个推荐!您的支持是我写做的最大动力!版权声明:做者:Ken企鹅交流群:790845561 791372870腾讯课堂直播:https://ke.qq.com/course/144677?flowToken=1003848若是你对生活感受到了绝望,请不要气馁。由于这样只会让你更加绝望! 所谓的但愿每每都是在绝望中萌发的,因此,请不要放弃但愿!