支撑日均百万订单的微服务架构应该这么搞!
最近几年微服务很火,大家都在建设微服务,如果不懂点微服务相关的技术,都不好意思跟同行打招呼了。
我也见过身边很多人在微服务踩过很多坑,我从 2016 年开始接触微服务,有多家大型企业的微服务分布式系统的架构经验,不过微服务和涉及的分布式计算非常的复杂,绝非是一篇文章就可以讲清楚的。
本文只是从最简单的概念的基本使用带你入门,如果后续还有兴趣的话,可以查阅相关的文献和技术书籍去深入学习。
本文的微服务分享以下三部分,总体大纲如下:
-
微服务的概念和原则(理论) -
Spring Cloud 如何低成本的实现微服务(实现) -
Spring Cloud 大型项目的架构方案(真实案例)
微服务的概念和原则
什么是微服务?
简单举例:看军事新闻的同学应该都知道,一艘航空母舰作战能力虽然很强,但是弱点太明显,就是防御能力太差,单艘的航空母舰很少单独行动,通常航空母舰战斗群才是主要军事力量。
你可以把单艘航母理解为的单体应用(防御差,机动性不好),把航母战斗群(调度复杂,维护费用高)理解为微服务。
-
单体应用: 简单,脆弱(某个模块出问题,整个系统不可用),战斗力弱,维护成本低。 -
微服务架构: 复杂,健壮(某个模块出问题,不会影响系统整体的可用性),战斗力强,维护成本高。
大部分的开发者经历和开发过单体应用,无论是传统的 SSM,还是现在的 Spring Boot/Rails,它们都是单体应用。
那么长期陪伴我们的单体应用有什么弊端?我们是面临了什么问题,导致我们要抛弃单体应用转向微服务架构?
个人总结主要问题如下:
部署成本高(无论是修改 1 行代码,还是 10 行代码,都要全量部署替换)。
改动影响大,风险高,测试成本高(不论代码改动多小,成本都相同)。
因为成本高,风险高,所以导致部署频率低(无法满足快速交付客户需求)。
解决什么问题,又引入了什么问题?
我们先看看微服务能带给我们什么?微服务架构的特点:
针对特定服务发布,影响小,风险小,成本低
频繁发布版本,快速交付需求
低成本扩容,弹性伸缩,适应云环境
我们知道一个朴素的理念,没有任何事物是完美的,任何东西都有两面性,有得必有失。
那么在选择微服务在解决了快速响应和弹性伸缩的问题同时,它又给我们带来了什么问题?
个人总结如下:
-
分布式系统的复杂性 -
部署,测试和监控的成本问题 -
分布式事务和 CAP 的相关问题
系统应用由原来的单体变成几十到几百个不同的工程,会所产生例如包括服务间的依赖,服务如何拆封,内部接口规范,数据传递等等问题。
尤其是服务拆分,需要团队熟悉业务流程,懂得取舍,要保证拆分的粒度服务既符合“高内聚,低耦合”的基本原则,还要兼顾业务的发展以及公司的愿景,要还要说服团队成员为之努力,并且积极投入,在多方中间取得平衡。
对于分布式系统,部署,测试和监控都需要大量的中间件来支撑,而且中间件本身也要维护。
原先单体应用很简单的事务问题 ,转到分布式环境就变得很复杂,分布式事务是采用简单的重试+补偿机制,还是采用二阶段提交协议等强一致性方法来解决,就要取决对业务场景的熟悉加上反复的权衡了。
微服务有哪些应该遵循哪些原则?
古人云:兵马未动,粮草先行。建设微服务是需要建立长远规划,不是像写 CMS 那样建好数据库表,然后就开始干活,这样十有八九是会失败的。
我们要进行微服务改造前,架构师要提前做好规划,我们把这里分为三步,前期阶段,设计阶段,技术阶段。
-
和多方充分沟通,确保能符合客户和组织的需求,并且得到认同。 -
和团队沟通,让队友(开发/测试/运维)理解,并且积极投入。 -
和业务部门沟通,指定版本计划和上线时间。
设计阶段,参考 Sam Newman 的著作《微服务设计》,单微服务必须要满足以下的条件,才符合微服务的基本要求:
-
标准的 REST 风格接口(基于 HTTP 和 JSON 格式)。 -
独立部署,避免共享数据库(避免因为数据库而影响整个分布式系统)。 -
业务上的高内聚,减少依赖(从设计上要避免服务过大或者太小)。
-
CI/CD 和自动化(分布式系统几乎不可能通过人工手动发布。) -
虚拟化技术(要保证微服务运行环境隔离,目前行业主流的是使用 Docker 容器)。 -
日志聚合,全链路监控(高度可观察和分析诊断问题)。
说了那么多,那什么样的情况下,你的团队不适合建设微服务?(请勿对号入座)
-
开发团队不具备自主性,所在组织对开发团队限制非常多(具体请参考 康威定律)。 -
团队不熟悉业务,无法识别出服务的边界,进行合理的拆分(请参考 DDD 领域驱动设计)。
微服务设计其实是很早就有的设计思想,因为随着虚拟化技术的崛起,微服务可以低成本的实现,所以也开始流行和兴起。
微服务的内涵很深,其中就包括,自动化,去中心化,独立性等等,其中细节无法用一篇文章概述清楚,我们在做技术选型或者方案的时候,尽可能多去了解技术的本身和起源再结合我们业务的特点,进行更好的选择。
如何低成本的实现微服务?
Spring Cloud 为什么是国内最流行的微服务框架,它提供哪些开箱即用的组件 ?
概览如下:
-
Srping Boot 服务应用 -
Spring Cloud Config 配置中心 -
Spring Cloud Eureka 服务发现 -
Spring Cloud Hystrix 熔断保护 -
Spring Cloud Zuul 服务网关 -
Spring Cloud OAuth 2 服务保护 -
Spring Cloud Stream 消息驱动 -
分布式全链路跟踪 -
部署微服务
①Spring Boot 微服务基石
Spring Boot 是构建微服务的理想框架,主要得益于 Spring Boot 可以打包成为单个可执行JAR文件交付服务,Spring Actuator 公开服务健康信息都是微服务的基石,为什么这么说 ?
我们先看看构建微服务的四个重要原则:
-
服务应该是独立和可独立部署的 -
应该从中央(配置中心)读取配置 -
对客户端是透明的 -
传达健康信息
微服务有优点和缺点,并非所有应用都适合用微服务架构,架构师需要能做到以下要求:
-
分解业务问题: 描述业务问题,注意动词,寻找数据内聚。 -
建立服务粒度: 讲大服务重构到更小的服务,重点关注服务如何相互交互,服务职责随时间改变。 -
定义服务接口: 拥抱 REST 的理念,使用 URI 来传达意图,使用 HTTP 状态码传达结果。
糟糕的微服务有哪些特征?
-
过于粗粒度: 服务承担过多的职责,服务跨大量表来管理数据,测试用例太多。 -
过于细粒度: 服务彼此依赖严重,服务内部没有逻辑。
何时不应该使用微服务 ?
-
不愿投入(需要高度成熟的运维,伸缩,复杂性问题)。 -
管理/监控散乱的服务器也需要很高的成本。 -
小型应用不适合,太昂贵。 -
数据事务(分布式系统处理事务非常困难)
②Spring Cloud Config 配置服务
-
应用程序的配置和部署的实际代码分离(配置中心和应用程序分离)。 -
集中(将配置中心集中在少量的存储库中)。 -
稳定(配置中心要保证高可用)。
Spring Config 这款配置中心提供的核心功能:
-
配置服务器允许使用环境特定值。 -
使用 Spring Profile 区分环境值。 -
可以使用基于文件或基于 Git 存储属性。 -
允许对称加密和非对称加密。
③Spring Cloud Eureka 服务发现
所以它是所有微服务上线/下线的必经之路,也掌握微服务的生杀大权,注册中心会根据 CAP 策略和对微服务的健康检查,作出对具体服务剔除,下线,恢复上线等操作。
主要还有以下几个核心功能:
-
快速对环境中服务数量水平伸缩(功能和 K8s 有些重合,不过也可以设定具体服务的运行时数量)。 -
抽象服务的物理位置(微服务通常运行在 Docker 容器内,没有固定 IP,只能通过注册中心才能找到它)。 -
提高程序的弹性,自动伸缩。 -
信息共享, 健康检测。
微服务架构里面要实现注册中心,需要达到哪些基本要求?
-
高可用,注册信息共享(注册中心集群部署),不可能因为注册中心挂了,导致所有集群不可用。 -
负载均衡(对服务间的动态请求负载均衡),合理在服务间分配流量。 -
有弹性(客户端缓存服务信息)。 -
容错,健康检查(检测坏掉的服务自动移除,无需人工干预)。
-
服务发现抽象服务的物理位置。 -
无感知添加和移除服务。 -
为服务间调用提供负载均衡。 -
使用 3 种不同机制来调用服务:DiscoveryClient,支持 Ribbon 的 RestTemplate,Netflix 的 FeignClient。
④Spring Cloud Hystrix 熔断保护
-
通常都是从小部分开始,到耗尽线程彻底崩溃。 -
服务间调用会长时间阻塞。 -
服务未关闭就会一直被调用,导致连锁效应。 -
一个性能不佳的服务可以迅速拖垮整个应用。
为什么熔断很重要 ?
-
每个节点(调用服务和数据库)实现断路器,可以避免服务崩溃的连锁效应。 -
实现只有出问题的服务受影响,其余的服务功能都是完整的(影响范围降到最小)。 -
熔断是服务器的灵活的基础。
-
快速失败。 -
功能降级(替代方案)。 -
无缝恢复(断路器定期检查,自动恢复服务)。
-
客户端负载均衡模式(检测服务出错,移除服务)。 -
断路器模式(出现超时抛出异常强行失败,超过阈值强行失败所有调用)。 -
后备模式(不是抛出异常而是执行替代方案,例如排队,稍后再试等)。 -
舱壁模式(把远程调用的资源分配到独立的线程池中,调用出问题只是线程池饱和并停止请求)。
跳闸会导致的三种结果:
-
服务 B 立即知道服务 C 有问题,不必等待,立即失败。 -
服务 B 执行服务 C 的替代代码来采取行动(后备模式)。 -
服务 C 可以在跳闸后,检查问题,快速恢复。
熔断的几个处理原则:
-
设计分布式应用必须考虑弹性。 -
服务的彻底故障是很容易检测和处理,只是需要时间,断路器给了这个时间窗口。 -
一个服务性能不佳,可能导致集群崩溃,因为相互调用会阻塞线程,耗尽资源。 -
Hystrix 支持两种隔离模型,即 THREAD 和 SEMAPHORE。
⑤Spring Cloud Zuul 网关
这样理解可能比较抽象,举一个生活的例子,微服务集群是一个大型公司,公司内部有很多个不同的职能部门(对应每个微服务)。
但是你如果要访问具体的职能部门,你必须先到前台登记,再由前台带你到你想访问的具体职能部门去办理实际的业务(智能路由)。
微服务对于网关实现的规范:
-
一个独立负责所有服务调用过滤和路由的服务。 -
服务和客户端的中间人,简化客户端开发。
网关通常要做哪些事情:
-
静态路由,从注册中心获取每个微服务的具体位置。 -
动态路由(根据参数特点,调用特定服务,少量用户体验新功能,通常用于灰度发布)。 -
验证和授权:验证访客的身份信息(统一验证,服务只需要关注业务逻辑)。 -
数据收集和日志(收集调用次数和响应时间等)。
-
结合 Spring Cloud Eureka 将服务发现的注册地址加入到 Zuul 路由。 -
Zuul 可以给所有服务轻松的添加/API 之类的前缀路由地址。 -
在全局上定制 Zuul 的 Spring Cloud Hystrix 和 Spring Cloud Ribbon (调度策略)的超时。 -
实现动态路由,不同版本进行 A/B 测试。 -
检查参数合法性等,例如 JWT,时间戳等等。
⑥Spring Cloud OAuth 2 服务保护
Oauth 2 是用于保证请求的合法和正确性,为了让微服务本身更加专注于业务,所以 OAuth 2 类似配置中心被单独抽离出来作为基础组件的统一认证中心来使用。
OAuth 2 的作用类似我们生活中的公安局的角色,当我们需要去正规机构办理业务的时候,我们需要提供有效的身份证(合法的身份认证标示)。
如果没有你就需要去公安局(OAuth)申请一张在有效期内的身份证(Token),然后带着这张身份证我们才能去购买机票,酒店等其他社会服务(微服务)。
社会服务机构在拿到你提供的身份证(Token)后,也会向公安局(OAuth)联网发送信息,来验证你的身份证的合法性(Token 合法性校验),身份认证不通过就会被拒绝服务,合法的身份才能进行业务的办理。
关于 OAuth 的工作流程,可以结合下图来理解:
-
安全框架,提供令牌生成,验证等逻辑。 -
开箱即用,和其他服务无缝集成。 -
行业标准,轻松与云服务商集成。
OAuth 2:/auth/oauth/token 的返回信息:
-
access_token(OAuth 2 令牌,每次调用出示)。 -
token_type(令牌类型,常用 bearer token)。 -
refresh_token(续约令牌)。 -
expires_in(过期描述,默认 12H)。 -
scope(令牌有效作用域)。
OAuth 2 支持 JWT (JSON Web Token)的规范,关于 JWT 的原理就不特别解释了。
简单的 JWT 有以下几个特点:
小巧(Base64编码)。
密码签名(防篡改)。
自包含(不需要调用验证服务确认内容,通过相同的密钥进行对称解密)。
可扩展(可在令牌内包含额外的信息)。
OAuth 2 的简单总结:
OAuth 2 是一个令牌验证框架。
使用 OAuth 2 需要建立 OAuth 2 验证服务。
调用受保护的资源都要通过 OAuth 2 验证。
⑦Spring Cloud Stream 消息驱动
我们和世界的互动不是同步的,很多时候是基于消息异步驱动模型,比如邮件,点餐,订票等等,想要了解 Spring Cloud Stream,必须先要理解基于事件(MQ)编程的模型,基于消息驱动有利于开发构建高度解耦的系统。
因为 Spring Cloud Stream 并不是自己实现了消息中间件,而是对于市场上主流(例如 RabbitMQ,KafKa)的 MQ 产品做了一层封装和抽象。
Spring Cloud Stream 做的事情并不是什么新鲜的事情,非常类似 ORM 所做的事情,了解 ORM 框架的同学应该都熟悉对于多种数据库(MySQL,Oracle,SQL Server)产品的抽象是何等重要。
面向 ORM 进行数据库访问,可以让你脱离对于指定数据库产品的深度依赖和绑定,而且可以不用特意去学习不同数据库的本地化特性和方言,降低学习成本。
假如你想从 Oracle 迁移到 MySQL 上面,几乎是不需要改动一行代码,只需要改动 ORM 的配置就可以实现了。
参考下图简单了解一下 ORM:
Spring Cloud Stream 类似 ORM,你只需要基于 Spring Cloud Stream 提供的消息模型进行编程。
至于底层的消息组件是用的 RabbitMQ 还是 Kafka 还是其他的消息中间件产品,都没有关系,甚至更换底层消息组件也不会对你的应用产生任何影响,这就是标准化所带来的收益。
关于如何更好的理解 Spring Cloud Stream 工作模型可以简单参考下图:
同步:通过 REST 端点接口进行请求:服务之间紧耦合(强依赖),服务之间的脆弱性(连锁效应),增加新的消费者不灵活。
异步:基于消息中间件通信:松耦合(无接口直接调用的依赖),耐久性(服务重启后可以消费历史消息),可伸缩性(消息过多可启动多服务来处理消息),灵活性(轻松添加新的消费者)。
消息传递架构的缺点:
消息处理语义:消息顺序处理,消息异常处理。
消息可见性:消息不会立刻被处理,事务关联 ID 在消息中的传递。
消息中放置什么数据 ?
消息体要尽可能的小,减少传输成本:通常只返回 action 类型和 id,然后用id获取最新数据。
只使用消息传递状态:在消息中包含版本号和时间戳,处理数据服务可以检查数据的版本号。
Spring Cloud Stream 的消息模型和概念:
发射器(Source):接收对象(对象表示要发布的消息),序列化对象,将消息发布到通道。
通道(Channel):队列的抽象,通道写在配置文件,更改配置切换通道(读取和写入队列)。
绑定器(Binder):与消息平台对话的 Spring 代码,不必依赖特定的 API 来发布和消费消息。
接收器(Sink):从队列接收消息,将消息反序列化为 POJO。
Spring Cloud Stream 的简单总结:
使用消息传递的异步通信是微服务架构的关键部分。
使用消息传递可以使服务能够伸缩并且更具有容错性。
Spring Cloud Stream 通过简单的注解抽象底层的消息平台细节。
⑧Sleuth 和 Zipkin 分布式跟踪
微服务分布式架构带来了复杂度,成本最高的就是跟踪检查和运维,分布式意味要在多个服务,机器跟踪一个事务,Sleuth 和 Zipkin 都是用于 Spring Cloud 服务体系的分布式跟踪技术,先直接看下最终效果。
下图一个简单的可视化链路跟踪调用,ZipKin 可以清晰的看到一个客户端请求在每个服务上面处理所消耗的事情,点击进去可以看到具体的事务执行内容,方便排查错误。
-
透明地创建并注入一个关联 ID 到服务调用中。 -
管理关联 ID 到出站服务调用的传播。 -
将关联信息添加到 Spring 的 MDC 日志记录(应用/跟踪 ID/跨度 ID/数据发送)。 -
将服务调用中的跟踪信息发布到 Zipkin 跟踪平台。
-
调用链使用一张干净简洁的图片,比一百万条日志要好看的多。 -
分布式跟踪平台,用于跟踪多个服务调用的事务。 -
图形的方式查看事务占用的时间量,分解每个服务所用的时间。 -
4 种不同的数据存储:内存数据/MySQL/Cassandra/Elasticsearch。
Spring Cloud Sleuth 可以无缝将关联 ID 添加到微服务中。
可以使用关联 ID 查看事务涉及的所有服务行为。
关联 ID 需要与日志聚合结合使用。
日志平台很重要,但是可视化跟踪事务也是有价值的工具。
⑨部署微服务
构建和部署管道是微服务架构最重要的部分,微服务的主要特点是快速构建,修改,发布。
自动的(自动构建和部署代码。
完整的(软件成品是镜像),不可变(发布过程不可人为干预)。
良好的微服务部署管道应该允许在几分钟部署新功能和修复 Bug。
Spring Cloud 大型项目的架构方案
这是一个真实用于国内某大型企业的微服务架构体系,支撑日均百万订单的项目,因为已经过了 2 年的保密期,所以可以拿出来分享。
刚好可以结合前面凌乱的知识点,看看 Spring Cloud 这套组件是如何搭建起来的。
具体每个组件的作用就不在这里详细说明了,在这套架构方案里面,我们没有完全照搬 Spring Cloud 全家桶的组建,还是根据自己的需求对其中组件进行的更换。
例如:
配置中心从 Spring Cloud Config 更换为 Apollo ,除了有更好的性能,还有更加简化的操作页面,修改配置文件毫秒级响应。
服务发现 Eureka 官网已经停止维护,我们后面更换为 Alibaba Nacos,服务注册和心跳检测都提升到毫秒级,Eureka 是 90 秒轮询。
分布式任务调度引入了 XXL-JOB,这是国内主流的分布式任务调度平台,没有特别需要说明的地方。
日志聚合也是用了主流的 ELK 技术方案,用于收集和检索日志。
PS:另外在值得补充的是,在写这篇文章的时候 Spring Cloud Zuul 已经不被官方推荐使用了,替代品是性能更好的 Spring Cloud Gateway ,大家可以在了解的时候需要注意一下。
总结
↓↓ 点击"阅读原文" 【加入云技术社区】
相关阅读:
更多文章请关注
文章好看点这里[在看]👇