应对海量并发请求,首席布道师谈微服务的应用架构设计

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
云原生网关 MSE Higress,422元/月
注册配置 MSE Nacos/ZooKeeper,118元/月
简介:
  20160805091901834.jpg 何李石

七牛云首席布道师

 

  • 《Go语言程序设计》译者,Go语言/容器虚拟化技术布道师、实践者。

  • 5年以上互联网创业经验和企业级产品研发、运营经验,同时也是互联网产品基础架构解决方案专家。

 


随着互联网网民数的爆发式增加以及人们对随时随地接入互联网诉求的加强,互联网产品需要面对的并发请求量越来越大,云计算的诞生和普及为海量并发请求的应用提供了弹性的硬件支撑。


本案例分享基于微服务的应用架构设计,内容涉及如何构建一个微服务应用,服务注册与发现,微服务测试和典型的微服务架构设计模式,以及微服务架构在七牛的实践案例。


1大纲


  • 构建一个微服务应用

  • 服务注册与发现

  • 微服务测试

  • 典型微服务架构设计模式

  • 七牛微服务架构实践


2构建一个微服务应用


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

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


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

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

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

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


20160805091936170.jpg

图 1. 服务端代码


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


同样的,我们从上往下看看图 2 这段代码干了什么事情:


20160805091953518.jpg
图 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服务注册与发现


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


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


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


20160805092003120.jpg

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


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


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


20160805092012382.jpg

图 4. 客户端服务发现


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


这样做的好处是:

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

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


其缺点在于:

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

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


20160805092023437.jpg

图 5. 服务端服务发现


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


  • 客户端不需要做额外的变更;

  • 有些云服务公司已经提供类似产品可以满足需求了,可以直接接入。

  • 但它也有它的缺点:

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

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


4微服务测试

20160805092031692.jpg

图 6. 测试金字塔


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


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


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


5常规测试


1. 单元测试


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


2. 集成测试


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


3. 组件测试


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


4. 契约测试


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


5. 端到端测试


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


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

  2. 不必每个组件的修改都触发端到端测试,可以在所有更小粒度的测试完成之后再做一遍端到端测试。


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


关于常规的测试,我们只在这里做简单的介绍,想详细了解每种测试覆盖的范畴或者最佳实践的同学可以参考这个 Martin Fowler 网站上 Toby Clemson 关于微服务测试的分享:http://martinfowler.com/articles/microservice-testing/


6混沌测试


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


20160805092043918.jpg

图 7. Netflix猴子军团


Netflix 的猴子军团:

  • Chaos Monkey: 随机杀死实例

  • Latency Monkey: 人为引入延迟

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


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


更多关于猴子军团的信息可以参考以下链接:

  • Chaos Monkey Released Into The Wild: http://techblog.netflix.com/2012/07/chaos-monkey-released-into-wild.html

  • What is Simian Army? https://github.com/Netflix/SimianArmy/wiki

  • Automated Failure Testing: http://techblog.netflix.com/2016/01/automated-failure-testing.html


7流量重现


我们前面提到端到端的测试很难做,主要是难在它整个链路太长,耗时太长,环节也难以控制。另外一个难点在于,我们通常很难获得和线上一样真实的流量去进行端到端的测试,即便某个环节能够成功模拟相应的请求,但也不是所有请求的比例和线上都是一致的。


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

20160805092053206.jpg

图 8. 流量重现工具 Gor


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


8典型微服务架构设计模式


20160805092101742.jpg

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


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


一般来讲,基于 HTTP REST API 的请求都是同步执行的,但是有些场景使用异步的方式更为合理,比如大视频转码服务,它很难在很短时间内完成。同时,为了最大化微服务之间的解耦,应尽量减少和简化微服务之间的通信。为此,我们引入消息队列作为异步通信的通道。


在微服务的后端,如果每个微服务都按照三层结构来部署,除了服务本身之外,还包括缓存层和持久化的数据库层。


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


9七牛的微服务架构实践



20160805092109105.jpg

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


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


Q&A


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

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

 

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

A2:如果是使用容器化的部署方式,监控方面目前很多都是基于 Google 的 cAdvisor 来做的。

 

Q3:微服务测试,打桩是怎么做的?

A3:打桩测试不太了解,有朋友介绍使用这个工具来做http://www.mbtest.org/

 

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

A4:如果需要保证服务之间的原子性,可以使用分布式锁,但应该和微服务的初衷有点冲突。建议尽量改成异步通信的方式,借助 message queue 等来通信。

 

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

A5:我们的业务是灰度。

 

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

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

 

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

A7:要看具体是啥问题…… 网络方面 Docker 官方有提供方案,k8s 也有,一般私有部署可以直接使用开源的版本。

 

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

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

 

Q9:老师,一个客户端应用调用多个微风服务,数据一致性怎么解决?

A9:我理解多个微服务之间的数据一致性是指他们共用了相同的数据或者数据库,有多个微服务对他们进行读写。这个问题可以使用事件驱动的编程模型来解决,简单来讲就是维护一个事件服务端,让各个服务对数据的修改都以事件的形式通知它,再由它去通知其它所有相关的服务。参考 Event Sourcinghttp://martinfowler.com/eaaDev/EventSourcing.html

 

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

A10:大家都说以业务的最小单元来拆分服务,但对业务的最小单元却没有统一的标准,也不可能有统一的标准,比如我们做存储业务的和你电商业务的标准就没法统一起来。我个人的看法是,就像康威定律里面说的,服务的复杂性决定了服务的大小和拆分的合理性,除了「以业务的最小单元来拆分」之外,维护某个微服务的团队不应该太大。还有,拆分之后本来应该是尽量解耦的,所以可以看拆分之后是否带来更复杂的异步通信。 


本文来自云栖社区合作伙伴"DBAplus",原文发布时间:2016-08-05

相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
目录
相关文章
|
12天前
|
安全 应用服务中间件 API
微服务分布式系统架构之zookeeper与dubbo-2
微服务分布式系统架构之zookeeper与dubbo-2
|
12天前
|
负载均衡 Java 应用服务中间件
微服务分布式系统架构之zookeeper与dubbor-1
微服务分布式系统架构之zookeeper与dubbor-1
|
8天前
|
Kubernetes Cloud Native 持续交付
探索云原生架构:打造弹性可扩展的应用
【9月更文挑战第29天】在云计算的浪潮中,云原生架构成为企业追求高效、灵活和可靠服务的关键。本文将深入解析云原生的概念,探讨如何利用容器化、微服务和持续集成/持续部署(CI/CD)等技术构建现代化应用。我们将通过一个简易的代码示例,展示如何在Kubernetes集群上部署一个基于Node.js的应用,从而揭示云原生技术的强大能力和潜在价值。
21 6
|
9天前
|
监控 Cloud Native 持续交付
云原生架构:构建弹性与高效的现代应用##
随着云计算技术的不断成熟,云原生架构逐渐成为企业技术转型的重要方向。本文将深入探讨云原生的核心概念、主要技术和典型应用场景,以及如何通过云原生架构实现高可用性、弹性扩展和快速迭代,助力企业在数字化转型中保持竞争优势。 ##
30 6
|
10天前
|
运维 Cloud Native 持续交付
云原生架构:构建未来应用的基石
本文将深入探讨云原生架构的核心概念、主要优势以及实际应用案例,揭示其在现代IT领域的重要性。通过详细解析云原生技术的各个方面,帮助读者更好地理解和应用这一前沿技术。
|
15天前
|
JSON 监控 安全
探索微服务架构中的API网关模式
【9月更文挑战第22天】在微服务架构的海洋中,API网关如同一位智慧的守门人,不仅管理着服务的进出,还维护着整个系统的秩序。本文将带你一探究竟,看看这位守门人是如何工作的,以及它为何成为现代云原生应用不可或缺的一部分。从流量控制到安全防护,再到服务聚合,我们将一起解锁API网关的秘密。
|
11天前
|
Cloud Native 持续交付 云计算
探索云原生架构:构建现代应用的新范式
在当今数字化浪潮中,云原生架构以其敏捷性、弹性和可扩展性成为企业技术转型的核心驱动力。本文将引领读者深入理解云原生的概念,剖析其关键技术组件——微服务、容器化、DevOps实践及持续交付/持续部署流程,并揭示这些技术如何相互协作,共同构建高效、可靠且易于管理的现代软件系统。通过对云原生架构的全面解读,我们旨在为开发者、架构师乃至企业决策者提供有价值的见解与指导,助力其在快速变化的市场环境中保持竞争力。
|
17天前
|
Kubernetes Cloud Native Serverless
探索云原生技术:从基础架构到应用实践
本文深入探讨了云原生技术的各个方面,包括其定义、核心原则、关键技术组件以及在现代企业中的应用。通过分析云原生如何推动数字化转型和提高业务敏捷性,文章旨在为读者提供对这一领域的全面了解和实际应用的指导。
45 7
|
19天前
|
Kubernetes Cloud Native 持续交付
深入理解云原生技术及其在现代IT架构中的应用
【9月更文挑战第18天】云原生技术,作为推动企业数字化转型的引擎,正以它独特的魅力重塑着信息技术的未来。本文将带你一探究竟,从云原生的基础概念出发,逐步深入到其核心组件、设计理念以及如何在实际应用中发挥巨大作用。你将了解到容器化、微服务架构、持续集成与持续部署(CI/CD)等关键实践,并见证它们如何帮助企业构建更加灵活、高效和可靠的应用。
|
10天前
|
前端开发 测试技术 API
探索微前端架构:构建现代化的前端应用
在软件开发中,传统单体架构已难以满足快速迭代需求,微前端架构应运而生。它将前端应用拆分成多个小型、独立的服务,每个服务均可独立开发、测试和部署。本文介绍微前端架构的概念与优势,并指导如何实施。微前端架构具备自治性、技术多样性和共享核心的特点,能够加速开发、提高可维护性,并支持灵活部署策略。实施步骤包括定义服务边界、选择架构模式、建立共享核心、配置跨服务通信及实现独立部署。尽管面临服务耦合、状态同步等挑战,合理规划仍可有效应对。
下一篇
无影云桌面