我在做电商的时候遇到了很多颈瓶和缺乏一些概念性的知识,我感觉必须要有一些基础性的知识来支撑着我电商项目的开发,所以做了如下笔记,方便以后阅读
JEE 以面向对象的 Java 编程语言为基础,扩展了 Java 平台的标准版,是 Java 平台企业版 的简称。它作为企业级软件开发的运行时和开发平台,极大地促进了企业开发和定制信息化系 统的进展。
JEE 将企业级软件架构分为三个层级: Web 层、业务逻辑层和数据存取层,每个层次的职 责分别如下。
Web 层: 负责与用户交互或者对外提供接口 。
业务逻辑层:为了实现业务逻辑而设计的流程处理和计算处理模块。
数据存取层: 将业务逻辑层处理的结果持久化以待后续查询,并维护领域模型中对象的 生命周期。
JEE 平台将不同的模块化组件聚合后运行在通用的应用服务器上,例如 : WebLogic、 WebSphere, JBoss 等,这也包含 Tomcat, 但 Tomcat 仅仅是实现了 JEE Web 规范的 Web 容器。 JEE 平台是典型的二八原则的一个应用场景,它将 80%通用的与业务无关的逻辑和流程封装 在应用服务器的模块化组件里,通过配置的模式提供给应用程序访问,应用程序实现 20%的 专用逻辑,并通过配置的形式来访问应用服务器提供的模块化组件。事实上,应用服务器提 供的对象关系映射服务、数据持久服务、事务服务、安全服务、消息服务等通过简单的配置 即可在应用程序中使用 。
JEE 时代的架构已经对企业级应用的整体架构进行了逻辑分层,包括上面提到的 Web 层、业 务逻辑层和数据存取层,分别对应图 中的 Web 容器、 EJB 容器和数据存取 ORM 组件与数据 持久层 (数据库), 不同的层级有自己的职责,并从功能类型上划分层级,每个层级的职责单一。
JEE架构带来的问题
每个层次的 多个业务逻辑的实现会被放在同一应用项目中,并且运行在同一个JVM 中。尽管大多数公司会 使用规范来约束不同业务逻辑的隔离性来解祸,但是久而久之,随着复杂业务逻辑的选代增加 及开发人员的不断流动,新手工程师为了节省时间和赶进度,非法使用了其他组件的服务,业 务组件之间、 UI组件之间、数据存取之间的稿合性必然增加,最后导致组件与组件之间难以划 清界限,完全祸合在一起,将来的新功能迭代、增加和维护将难上加难。
另外,由于血E 主要应用于企业级应用开发,面向的用户较少,所以尽管 JEE 支持 Web 容器和 EJB 容器的分离部署,这个时代的大多数项目仍然部署在同一个应用服务器上并跑在一 个JVM进程中。
Sun 公司是 JEE 规范制定的始作俑者,早在传统企业全面信息化的初始阶段, Sun 公司看 到了企业信息化建设的巨大市场,在制定 JEE 规范时,更注重规范的全面性和权威性,对企业 级应用开发的方方面面制定了标准,并且联合 IBM 等大型企业进行推广。 然而,在铺天盖地的 规范和标准的限制下开发出来的符合 JEE 规范的应用服务器,不但没有简化在瘦客户环境下的 应用开发,反而加重了开发者使用的成本和负担,尤其是早期 EJB 版本(2.0)的实现由于大量 使用了 XML 配置文件,所以实现一个服务后的配置工作颇多, EJB 组件学习曲线较高,又难 以做单元测试,被称为超重量级的组件开发系统。
在 JEE 开始流行但没有完全奠定其地位时,开源软件 Struts、 Spring 和 Hibernate 开始崭露 头角, 很快成为行业内企业开发的开源框架标配(简称 SSH), JEE 规范中的各种技术如 EJB, 迅速失去了进一步发展的机会。 Web MVC 框架 Struts 在用户交互的 UI 层进一步划分了前端的 职责,将用户交互层划分为视图、模型和控制器三大块(简称 MVC 模型),其结构示意图如 下
在那个时代, Struts MVC 模型几乎服务于大多数企业服务的 Web 项目。
后来,开源框架 Spring 的发布,更加改变了 JEE 一开始制定的战略目标。 Spring 框架作为 逻辑层实现的核心容器,使用起来简单、方便又灵活,几乎大部分开发者完全倒向了 Spring 开 源派。 Spring 框架有两个核心思想: IOC 和 AOP,如下所述。
- 对 Java 字节码进行重新编译,将切面插入宇节码的某些点和面上,可以使用 cglib 库实 现。
- 定制类加载器,在类加载时对字节码进行补充,在字节码中插入切面,增加了除业务逻 辑外的功能, NM 自身提供的 Java Agent 机制就是在加载类的宇节码时,通过增加切 面来实现 AOP 的。
- NM 本身提供了动态代理组件,可以通过它实现任意对象的代理模式,在代理的过程中可 以插入切面的逻辑。可以使用 Java 提供的 APIProxy.newProxylnstanceO和 InvocationHandler 来实现。
另外, AspectJ是实现AOP 的专业框架和平台,通过AspectJ可以实现任意方式的字节码切 面, Spring 框架完全支持 AspectJ。
到现在为止, SSH 开源标配框架中有了四交互层的 Stru也框架和业务逻辑实现层的 Spring 框架,由于面向对象领域的模型与关系型数据库存在着天然的屏障,所以对象模型和关系模型之 间需要一个纽带框架,也就是我们常说的 ORM 框架,它能够将对象转化成关系,也可以将关系 转化成对象,于是, Hibernate 框架出现了。 Hibernate 通过配置对象与关系表之间的映射关系,来 指导框架对对象进行持久化和查询,并且可以让应用层开发者像执行 SQL 一样执行对象查找。 这 大大减少了应用层开发人员写 SQL 的时间。然而,随着时间的发展,高度抽象的 ORM 框架被证 明性能有瓶颈,因此,后来大家更倾向于使用更加灵活的 MyBatis 来实现 ORM 层。
SSH 时代的架构如下
这一时代的 SSH 架构与 JEE 时代的架构类似,可分为三个层次:实现交互 UI 接口的 Web MVC 层、实现业务逻辑的 Spring 层及实现对象关系映射的 Hibernate 层,每个层级的实现比 JEE 对应的层次更简单、更轻量级,不需要开启整个应用服务器即可测试和验证,极大提高了开发 效率,这得益于 Spring 框架的控制翻转理念。
由于这一时代的企业级软件服务的对象仍然是企业,用户量并不大,因此,大多数企业里 的 SSH 架构最终会被打包到同一个 JEE 规范的 War 包里,并且部署在 Apache Tomcat Web 容器 里,因此,整个结构还是趋向于传统的单体架构,业务逻辑仍然糯合在一个项目中,即使通过 规范来约束模块化组件的精合度,效果也往往适得其反。
这一时代的职能团队的划分仍然停留在层次上,与 JEE 架构下的职能团队划分类似,分为 前端团队、后端业务逻辑研发团队和 DBA 团队。
1:从JEE时代到SSH时代,服务的特点仍然是单体化,服务的粒度抽象为模块化组件,所有 组件精合在一个开发项目中,并且配置和运行在一个口叫进程中。如果某个模块化组件需要升 级上线,则会导致其他没有变更的模块化组件同样上线,在严重情况下,对某个模块化组件的 变更,由于种种原因,会导致其他模块化组件出现问题。
2:另外,在互联网异军突起的环境下,传统JEE和SSH无法满足对海量用户发起的高井发请 求进行处理的需求,无法突破稿合在一起的模块化组件的性能瓶颈,单一进程己经无法满足需 求,并且水平扩展的能力也是很有限的。
3:为了解决上述问题,SOA出现了。SOA代表面向服务的架构,俗称服务化,后面所说的 SOA、服务化、SOA服务化若没有特殊说明则指SOA。SOA将应用程序的模块化组件通过定义明确的接口和契约联系起来,接口是采用中立的方式进行定义的,独立于某种语言、硬件和 操作系统,通常通过网络通信来完成,但是并不局限于某种网络协议,可以是底层的TCP协议, 可以是应用层的HTTP,也可以是消息队列协议,甚至可以是约定的某种数据库存储形式。这 使得各种各样的系统中的模块化组件可以以一种统一和通用的方式进行交互。
4:对比JEE和SSH时代的模块化组件后发现,SOA将模块化组件从单一进程中进一步拆分, 形成独立的对外提供服务的网络化组件,每个网络化组件通过某种网络协议对外提供服务,这 种架构下的特点如下。
• SOA定义了良好的对外接口,通过网络协议对外提供服务,服务之间表现为松祸合性, 松祸合性具有灵活的特点,可以对服务流程进行灵活组装和编排,而不需要对服务本 身做改变。
·组成整个业务流程的每个服务的内部结构和实现在发生改变时,不影响整个流程对外提供 服务,只要对外的接口保持不变,则改变服务内部的实现机制对外部来说可以是透明的。
• SOA在这一时代的数据通信格式通常为XML,因为XML标记定义在大规模、高并发 通信过程中,元余的标记会给性能带来极大的影响,所以后来被JSON所取代。
• SOA通过定义标准的对外接口,可以让底层通用服务进行下沉,供多个上层的使用方
同时使用,增加了服务的可重用性。
• SOA 可以让企业最大化地使用内部和外部的公共服务,避免重复造轮子,例如:通过 SOA 从外部获取时间服务。
要彻底理解 SOA 时代的服务化发展情况,我们必须理解 SOA 的两个主流实现方式: Web Service 和ESB。
· 服务提供者 Web Service 2 和 Web Service 3 通过 UDDI 协议将服务注册到 Web Service 目录服务中。
· 服务消费者 Web Service l 通过 UDDI 协议从 Web Service 目录中查询服务,并获得服务 的 WSDL 服务描述文件。
· 服务消费者Web Service 1 通过WSDL语言远程调用和消费 Web Service 2和 Web Service 3 提供的服务。
UDDI是一种用于描述、发现、集成Web Service的技术,它是Web Service协议栈的一个重要部分。通过UDDI,企业可以根据自己的需要动态查找并使用Web服务,也可以将自己的Web服务动态地发布到UDDI注册中心,供其他用户使用。
SOAP一般指简单对象访问协议。简单对象访问协议是交换数据的一种协议规范,是一种轻量的、简单的、基于XML(标准通用标记语言下的一个子集)的协议,它被设计成在WEB上交换结构化的和固化的信息。
通过这个过程,要改造一个新的业务流程,可以从 Web Service 目录中发现现有的服务,井 最大限度地重用现有的服务,经过服务流程的编排来服务新的业务。
2. ESB
ESB 是企业服务总线的简称,是用于设计和实现网络化服务交互和通信的软件模型,是 SOA 的另一种实现方式,主要用于企业信息化系统的集成服务场景中。 Mule 是企业服务总线的一个 实现。
在 SOA 服务化发展之前,企业对信息化系统进行了初步建设,这些企业信息化系统由异构 技术技实现:不同的开发语言、操作系统和系统软件为了快速响应新的市场,完全使用新技术重 建所有的信息化系统是不现实的,在现有的服务系统上增加新的功能或者叠加新的服务化系统的 方法更加可行,这需要对这些现有的信息化系统和新增的信息化系统进行组合。 SOA 凭借其松藕 合的特性,正好应用于这一场景,使得企业可以按照服务化的方式来添加新服务或升级现有服务, 来解决新业务对流程编排的需要,甚至可以通过不同的渠道来获得外部服务,并与企业的现有应 用进行组合,来提供新的业务场景所需要的信息化流程。
ESB 也适用于事件处理、数据转换和映射、消息和事件异步队列顺序处理、安全和异常处 理、协议转换和保证通信服务的质量等场景。
ESB 的架构图
从图可以看出, ESB 服务没有中心化的服务节点,每个服务提供者都是通过总线的模 式插入系统,总线根据流程的编排负责将服务的输出进行转换并发送给流程要求的下一个服务 进行处理。
这里我们可以看到, ESB 的核心在于企业服务总线的功能和职责, 如下所述。
·监控和控制服务之间的消息路由。
· 控制可插拔的服务化的功能和版本。
· 解析服务之间交互和通信的内容和格式。
· 通过组合服务、资源和消息处理器来统一编排业务需要的信息处理流程。
· 使用冗余来提供服务的备份能力。
根据以上分析,我们看到企业服务总线是 ESB 的核心要素,所有服务都可以在总线上插拔, 并通过总线的流程编排和协议转接能力来组合实现业务处理能力。
随着互联网企业的不断发展,互联网产品需要服务的用户量逐渐增加,海量用户发起的大 规模、高并发请求是企业不得不面对的,前面介绍的 SOA 服务化系统能够分解任务,让每个服 务更简单、职责单一、更易于扩展,但无论是 Web Service 还是 ESB,都有时代遗留的问题。
Web Service 的问题如下。
· 依赖中心化的服务发现机制。
· 使用 SOAP 通信协议,通常使用 XML 格式来序列化通信数据, XML 格式的数据冗余 太大,协议太重。
· 服务化管理和治理设施并不完善。
ESB 的问题如下。
· ESB 虽然是 SOA 实现的一种方式,却更多地体现了系统集成的便利性,通过统一的服 务总线将服务组合在一起,并提供组合的业务流程服务。
· 组合在 ESB 上的服务本身可能是一个过重的整体服务,或者是传统的 JEE 服务等。
· ESB 视图通过总线来隐藏系统内部的复杂性,但是系统内部的复杂性仍然存在。
· 对于总线本身的中心化的管理模型,系统变更影响的范围经常会随之扩大。
awk -f”’{print $5 ) ’ I sort I uniq c I sort -nr
从对比来看,管道中的每个命令都代表一个微服务,它们高度自治并完成自己的职责,然 后将结果输出。管道类似于微服务的网络通信协议,负责微服务之间的交互。
· 微服务把每一个职责单一的功能放在一个独立的服务中。
· 每个服务运行在一个单独的进程中。
· 每个服务有多个实例在运行,每个实例可以运行在容器化平台内,达到平滑伸缩的效 果。
· 每个服务有自己的数据存储,实际上,每个服务应该有自己独享的数据库、缓存、消息 队列等资源。
· 每个服务应该有自己的运营平台,以及独享的运营人员,这包括技术运维和业务运营人 员:每个服务都高度自治,内部的变化对外透明。
· 每个服务都可根据性能需求独立地进行水平伸缩。
传统单体架构如图
通过对比微服务架构与传统单体架构,我们得知传统单体架构具有如下特点。
· 传统单体架构将所有模块化组件混合后运行在同一个服务 JVM 进程中。
· 可对包含多个模块化组件的整体JVM 进程进行水平扩展,而无法对某个模块化组件进 行水平扩展。
· 某个模块化组件发生变化时,需要所有的模块化组件进行编译、打包和上线。
· 久而久之,模块间的依赖将会不清晰,互相糯合、互相依赖成为家常便饭。
通过对比来看,微服务架构更灵活并且可水平伸缩,可以让专业的人来做专业的事。
我们看到微服务架构的一些特点与 SOA 服务化架构相似, 事实上微服务架构与 SOA 服务 化架构并不冲突,它们一脉相承,微服务架构是服务化架构响应特定历史时期的使用场景的延 续,是服务化进行升华井落地的一种实现方式。 SOA 服务化的理念在微服务架构中仍然有效, 微服务在 SOA 服务化的基础上进行了演进和叠加,形成了适合现代化应用场景的一个方法论。微服务架构与 SOA 服务化虽然一脉相承,却略有不同,如下所述。
平台的决策者一般倡导建设 API 网关,所有外部服务和内 部服务都由统一的 API 网关进行管理。在项目初期,中心化的 API 网关统一了所有 API 的入口, 这看起来很规范,但从技术角度来看限制了 API 的多样化。随着业务的发展, API 网关开始暴 露问题, 每个用户请求经过机房时只要有服务之间的交互,则都会从 API 网关进行路由,服务 上量以后, 由于内部服务之间的交互都会叠加在 API 网关的调用上,所以在很大程度上放大了 API 网关的调用 TPS, API 网关很快就遇到了性能瓶颈。
TPS:Transactions Per Second(每秒传输的事物处理个数),即服务器每秒处理的事务数。TPS包括一条消息入和一条消息出,加上一次用户数据库访问。
这个案例是典型的微服务的反模式,微服务倡导去中心化的治理,不推荐每个微服务都使 用相同的标准和技术来开发和使用服务。
微服务架构倡导去中心化的服务管理和治理,尽量不设置中心化的管理服务,最差也需要 在中心化的管理服务宕机时有替代方案和设计。在笔者工作的支付平台服务化建设中, 第 1 层 SOA 服务化采用 Dubbo 框架进行定制化,如果 Dubbo 服务化出现了大面积的崩溃,则服务化 体系会切换到点对点的 hessian 远程调用,这被称为服务化降级,降级后点对点的 hessian 远程 调用时没有中心化节点,整体上符合微服务的原理。
微服务之间交互的通用设计模式,这些设计模式对微服务之间的交互定义契约, 服务的生产者和调用者都需要遵守这些契约,才能保证微服务不出问题。
读者容错模式(Tolerant Reader)指微服务化中服务提供者和消费者之间如何对接口的改变 进行容错。从字面上来讲,消费者需要对提供者提供的功能进行兼容性设计,尤其对服务提供 者返回的内容进行兼容,或者解决在服务提供者改变接口或者数据的格式的情况下,如何让服务消费者正常运行。
消费者驱动契约模式用来定义服务化中服务之间交互接口改变的最佳规则。
服务契约分为: 提供者契约、消费者契约及消费者驱动的契约,它从期望与约束的角度描 述了服务提供者与服务消费者之间的联动关系。
在现实的服务交互设计中,上面这三种契约是同时存在的,比如支付平台里, 交易 系统在完成一笔支付后,需要到账务系统为商户入账,在这个过程中,服务契约表现如下。
· 生产者契约:账务系统提供 Dubbo 服务化接口, 参数为商户账户 D、入账订单号和入 账金额。
· 消费者契约: 账务系统返回 DTO,包含商户账户田、入账订单号、入账金额、入账时 间、账务流水号、入账状态等,而交易系统只需使用其中的入账订单号和入账状态。
· 消费者驱动的契约: 为了保证资金安全,交易系统作为入账的发起者向账务提出要求, 需要账务做幕等和滤重处理,对重复的入账请求进行拦截;账务系统在接受这个契约 后,即使将来有任何改变,也不能打破这个限制,否则就会造成资金的损失,这在金 融系统中是最严重的问题。
从图 可以看到,服务提供者契约是服务提供者单方面定下的规则,而一个消费者契约 会成为提供者契约的一部分,多个服务消费者可以对服务提供者提出约束,服务提供者需要在 将来遵守服务消费者提出的契约,这就是消费者驱动的契约。
与 SOA 服务化对比,微服务是去 ESB 总线、去中心化及分布式的;而 SOA 还是以 ESB 为核心实现遗留系统的集成,以及基于 Web Service 为标准实现的通用的面向服务的架构。在微 服务领域,微服务之间的交互通过定义良好的接口来实现,不允许使用共享数据来实现。
在实践的过程中,有些方案的设计使用缓存或者数据库作为两个微服务之间的纽带, 在业 务流程的处理过程中,为了处理简单,前一个服务将中间结果存入数据库或者缓存,下一个服 务从缓存或者数据库中拿出数据继续处理。 处理流程如下图
这种交互流程的缺点如下。
· 使得微服务之间的交互除了接口契约,还存在数据存储契约。
· 上游的数据格式发生变化时,可能导致下游的处理逻辑出现问题。
· 多个服务共享一个资源服务,对资源服务的运维难以划清职责和界限。
· 在做双机房独立部署时,需要考虑服务和资源的路由情况,跨机房的服务调用不能使用 独立的资源部署模式,因此难以实现服务自治。
因此,在设计微服务架构时,一定不要共享缓存和数据库等资源,也不要使用总线模式, 服务之间的通信和交互只能依赖定义良好的接口,通常使用REST样式的 API 或者透明的 RPC 调用框架。
使用微服务架构划分服务和团队是微服务架构实施的重要一步,良好的划分和拆分使系 统达到松辑合和高内聚的效果,然后通过微服务的灵活组装可以满足上层的各种各样的业务处理需求。
在微服务架构的需求分析和架构设计过程中,通常是用领域的动词和名词来划分微服务的。
例如,对于一个电商后台系统,可以分解为订单、商品、商品目录、库存、购物车、交易、支 付、发票、物流等子系统,每个名词和动词都可以是一个微服务,将这几个微服务组合在一起, 就实现了电商平台用户购买商品的整个业务流。
这样拆分以后,系统具有敏捷性、灵活性、可伸缩性等,拆分后有多个高度自治的微服务, 那么以什么方式组合微服务呢?
· 在新老系统上双写。
·迁移双写之前的历史遗留数据。
· 将读请求切换到新系统。
· 下调双写逻辑,只写新系统
服务代理模式常常应用到第 3 步, 一般会对读请求切换设计一个开关,开关打开时查询新 系统,开关关闭时查询老系统。
服务聚合模式是最常用的服务组合模式,它根据业务流程处理的需要,以一定的顺序调用 依赖的多个微服务,对依赖的微服务返回的数据进行组合、加工和转换,最后以一定的形式返 回给使用方。
这里,每个被依赖的微服务都有自己的缓存和数据库,聚合服务本身可以有自己的数据存 储,包括缓存和数据库等,也可以是简单的聚合,不需要持久化任何数据。
上图体现了 DRY (Don’t Repeat Yourself) 原则的设计理念,在设计或者构造应用时,最大 限度地重用了现有的实现。假如一块业务逻辑由三个独立的逻辑块组成,每个独立的逻辑块可 能有多个使用方,则 DRY 原则推荐将三个独立的逻辑块封装成三个独立运行的微服务,然后使 用服务聚合模式开发聚合服务,将三个独立的逻辑块聚合在一起提供给上层组合服务。 这样的设计原则有如下好处。
· 三个独立的子服务可以各自独立开发、敏捷变更和部署。
·聚合服务封装下层的业务处理服务,由三个独立的子服务完成数据持久化等工作,项目 结构清晰明了。
· 三个独立的子服务对于其他使用方仍然可以重用。
考虑到本节开头的例子,在对微服务进行拆分时,将电商后台系统大致拆分成订单、商品、 商品目录、库存、购物车、交易、支付、发票、物流等微服务,那么电商平台的前端应用就是 后端各个微服务的一个最大的聚合服务,前端应用通过调用商品和商品目录显示商品列表,提 供给用户选择商品的功能,用户选择商品后增加商品到购物车,在用户从购物车结算时,调用 交易系统完成交易和支付等。
电商前台的聚合模式的案例架构如下图
另外,聚合服务也可以是一个纯后台服务,通过聚合对使用方输出组合的服务,例如在上 面的电商系统案例中,在用户选择结算后,系统调用交易,交易系统会调用库存系统锁库存, 然后创建交易订单,引导用户去支付,支付成功后扣减库存,最后通过发票服务开具电子发票。
在上面提及的电商案例中, ui前端应用调用交易,交易调用商品库存系统锁定库存和扣减 库存,使用的就是服务串联模式。
服务串联模式案例的架构如下图
服务分支模式
服务分支模式是服务代理模式、服务聚合模式和服务串联模式相结合的产物。
分支服务可以拥有自己的数据库存储,调用多个后端的服务或者服务串联链,然后将结果 进行组合处理再返回给客户端。 分支服务也可以使用代理模式,简单地调用后端的某个服务或 者服务链,然后将返回的数据直接返回给使用方。
服务分支模式的架构如下图
在实际的业务平台建设中,由于业务的复杂性,抽象的微服务可能有多层的依赖关系,依 赖关系并不会太简单,经常呈现树形的分支结构。
以电商平台的支付服务架构为例,如下图,
支付服务对接两个外部的支付网关,都要经过各自的支付渠道网关,同时支持账户余额支 付,这个支付服务其实就是一个分支模式,在实际项目中这种服务分支模式很多。
服务异步消息模式
前面的所有服务组合模式都使用同步的 RESTful 风格的同步调用来实现,同步调用模式在 调用的过程中会阻塞线程,如果服务提供方迟迟没有返回,则服务消费方会一直阻塞,在严重 情况下会撑满服务的线程池,出现雪崩效应。
因此,在构建微服务架构系统时,通常会梳理核心系统的最小化服务集合,这些核心的系统服务使用同步调用,而其他核心链路以外的服务可以使用异步消息队列进行异步化。
服务异步消息模式的架构如下图
在上图中, 聚合服务同步调用服务 l 和服务 2,而服务 2 通过消息队列将异步消息传递 给服务 3 和服务 4
。
典型的案例就是在电商系统中,交易完成后向物流系统发起消息通知,通知物流系统发货, 如下图
服务共享数据模式
服务共享数据模式其实是反模式,我们提出了去数据共享模式,由于去掉了数据共 享, 所以仅仅通过服务之间良好定义的接口进行交互和通信,使得每个服务都是自治的,服务 本身和服务的团队包含全角色拔的技术和运营人员,这些人都是专业的人做专业的事,使沟通 在团队内部解决,因此可以使效率最大化。
服务共享数据模式的架构如下图
然而,在下面两种场景下,我们仍然需要数据共享模式。
单元化架构
一些平台由于对性能有较高的要求,所以采用微服务化将服务进行拆分,通过网络服务进 行通信,尽管网络通信的带宽己经很宽,但是还会有性能方面的损耗,在这种场景下,可以让 不同的微服务共享一些资源,例如:缓存、数据库等,甚至可以将缓存和数据在物理拓扑上与 微服务部署在一个物理机中,最大限度地减少网络通信带来的性能损耗,我们将这种方法称为 “单元化架构”。如下图
遗留的整体服务
对于历史遗留的传统单体服务,我们在重构微服务的过程中,发现单体服务依赖的数据库 表辑合在一起,对其拆分需要进行反规范化的处理,可能会造成数据一致性问题, 在没有对其 完全理解和有把握的前提下,会选择保持现状,让不同的微服务暂时共享数据存储。
除了上面提到的两个场景,任何场景都不能使用服务数据共享模式。
在使用了微服务架构以后,整体的业务流程被拆分成小的微服务,并组合在一起对外提供 服务,微服务之间使用轻量级的网络协议通信,通常是 RESTful风格的远程调用 。 由于服务与 服务的调用不再是进程内的调用,而是通过网络进行的远程调用,众所周知,网络通信是不稳 定、不可靠的, 一个服务依赖的服务可能出错、超时或者窑机,如果没有及时发现和隔离问题, 或者在设计中没有考虑如何应对这样的问题,那么很可能在短时间内服务的线程池中的线程被 用满、 资源耗尽,导致出现雪崩效应。
舱壁隔离模式
这里用航船的设计比喻舱壁隔离模式,若一艘航船遇到了意外事故,其中一个船舱进了水, 则我们希望这个船舱和其他船舱是隔离的,希望其他船舱可以不进水,不受影响。在微服务架 构中,这主要体现在如下两个方面。
1 ) 微服务容器分组
一个支付平台应用了微服务,将微服务的每个节点的服务池分为三组:准生产环境、 灰度环境和生产环境。 准生产环境供内侧使用;灰度环境会跑一些普通商户的流量: 大部分生产 流量和 VIP 商户的流量则跑在生产环境中。 这样,在一次比较大的重构过程中,我们就可以充分 利用灰度环境的隔离性进行预验证,用普通商户的流量验证重构没有问题后,再上生产环境。
另外一个案例是一些社交平台将名人的自媒体流量全部路由到服务的核心池子中,而将普 通用户的流量路由到另外一个服务池子中,有效隔离了普通用户和重要用户的负载。
其服务分组如下图
2 )线程池隔离
在微服务架构实施的过程中,我们不一定将每个服务拆分到微小的力度,这取决于职能团 队和财务的状况,我们一般会将同一类功能划分在一个微服务中,尽量避免微服务过细而导致 成本增加,适可而止。
这样就会导致多个功能混合部署在一个微服务实例中,这些微服务的不同功能通常使用同 一个线程池,导致一个功能流量增加时耗尽线程池的线程,而阻塞其他功能的服务。
线程池隔离如下图
熔断模式
可以用家里的电路保险开关来比喻熔断模式,如果家里的用电量过大,则电路保险开关就 会自动跳闸, 这时需要人工找到用电量过大的电器来解决问题,然后打开电路保险开关。在这 个过程中,电路保险开关起到保护整个家庭电路系统的作用。**对于微服务系统也一样,当服务的输入负载迅速增加时, 如果没有有效的措施对负载进行 熔断,则会使服务迅速被压垮,服务被压垮会导致依赖的服务都被压垮,出现雪崩效应,**因此, 可通过模拟家庭的电路保险开关,在微服务架构中实现熔断模式。
限流模式
服务的容量和性能是有限的,,然而,即使我们在设计阶段考虑到了性能压力的问题, 并从设计和部署上解决了这 些问题,但是业务量是随着时间的推移而增长的,突然上量对于一个飞速发展的平台来说是很 常见的事情。
针对服务突然上量,我们必须有限流机制,限流机制一般会控制访问的井发量,例如每秒 允许处理的并发用户数及查询量、请求量等。
有以下几种方法:计数器,令牌筒,信号量
失效转移模式
若微服务架构中发生了熔断和限流,则该如何处理被拒绝的请求呢?解决这个问题的模式 叫作失效转移模式,通常分为下面几种。
· 采用快速失败的策略,直接返回使用方错误,让使用方知道发生了问题并自行决定后续
处理。
· 是否有备份服务,如果有备份服务,则迅速切换到备份服务。
· 失败的服务有可能是某台机器有问题,而不是所有机器有问题,例如 OOM 问题,在这 种情况下适合使用 failover 策略,采用重试的方法来解决, 但是这种方法要求服务提供 者的服务实现了幕等性。
在微服务化架构中,软件项目被拆分成多个自治的服务,服务之间通过网络协议进行调用, 通常使用透明的 RPC 远程调用。
在 Java 领域,每个服务上线后,对外输出的接口为一个 Jar 包。在微服务领域, Jar 包被分 为一方库、二方库、 三方库。
· 一方库:本服务在JVM 进程内依赖的 Jar 包。
· 二方库:在服务外通过网络通信或者 RPC 调用的服务的 Jar 包。
· 三方库:所依赖的其他公司或者组织提供的服务或者模块。
Java 微服务项目的层级结构一般为:服务导出层、接口层和逻辑实现层,如图
其中,每个层级的职责和最终的表现形式如下。
· 服务导出层: 最后会打包成一个 War 包,包含服务的实现 Jar 包、接口 Jar 包,以及 Web 项目导出 RPC 服务所需要的配置文件等。
· 服务接口层:包含业务接口、依赖的 DTO 及需要的枚举类等,最后打包成 Jar 包,井 发布到 Maven 服务器上,也包含在服务导出层的 War 包中 。
· 服务实现层:包含业务逻辑实现类、依赖的第三方服务的包装类,以及下层数据库访问 的 DAO 类等,最后打包成 Jar 包,包含在服务导出层的 War 包中 。
Java 平台下微服务实现层的架构如下图
在上图中,本地服务层通过 DAO 层与数据库进行交互 。这里使用 了数据库事务,保 证了数据存取的强一致性,业务流程层通过组合本地服务和外部服务来完成业务逻辑的实现, 由于有远程服务的依赖,因此只能保证数据的最终一致性
这里有一个反模式,切记永远不要在本地事务中调用远程服务,在这种场景下如果远程服 务出现了问题,则会拖长事务,导致应用服务器占用太多的数据库连接,让服务器负载迅速攀 升,在严重情况下会压垮数据库。顺便说一下,虽然我们要竭力避免这种场景的发生,但是数 据库也应该有负载熔断机制。
Java 平台下微服务实现层的反模式架构如下图
微服务项目 需要实现自动化的持续部署和持续集成的功能,包括:代码管理、自动编译、 发布 QA、自动化测试、性能测试、准生产部署和测试、生产环境发布等。
SOA 服务化和微服务架构己经发展多年,市场上己经有很多成熟的商业和开源产品,我们 没有必要从头搭建一套服务化管理和治理平台,完全可以基于开源服务化框架进行定制化,以适应我们的业务需要。
下面我们介绍各种流行的 RPC 框架、服务化管理和治理、微服务框架,并通过讲解其特点来帮 助我们做技术选型。
RMI 采用 JDK 自带的专用序列化协议,不能跨语言。
· 使用了底层的网络协议, 不如基于文本的 HTTP 可读,也不如 HTTP 被广泛认可和应用。
· 开源框架的飞速发展,严重削弱了 JDK 资深技术的流行程度。
本节介绍 SOA 服务化时代的服务化框架和平台。
• Dubbo 开发得较早,近些年已经没有开发者维护和升级。
· 早期留下的 Bug 一直没有得到修复,需要使用者自己发现和修复。
• Dubbo 没有经过全面优化,在服务量级到达一定程度时,会出现通知系统携带过多的冗 余信息,在极端情况下会导致网络广播风暴。
• Dubbo 服务框架是 SOA 服务化时代的产物,对微服务化提出的各种概念如熔断、限流、 服务隔离等没有做精细的设计和实现。
• Dubbo 的监控和服务治理模块比较简单,难以满足复杂业务的需求。
本节主要介绍近年流行的 Spring Cloud 系列的微服务框架。
SpringClound入门概述
Spring Boot 的思路正好相反,它将容器嵌入自启动的 Jar 包中,在 Spring Boot 应用启动 时, 内部启动嵌入的容器,例如: Tomcat、 Jetty 和 Ne町等,然后通过内嵌的服务器将应用中 提供的服务暴露。
Spring Boot 的容器包含在应用 内的架构如下图
Spring Boot 这种设计在微服务架构下有如下明显的优点。
· 可以创建独立、自启动的应用程序。
· 不需要构建 War 包并发布到容器中,构建和维护 War 包、容器的配置和管理也是需要 成本的。
· 通过 Maven 的定制化标签,可以快速创建 Spring Boot 的应用程序。
· 可以最大化地自动化配置 Sprig,而不需要人工配置各项参数。
· 提供了产品化特点,例如: 性能分析、健康检查和外部化配置。
· 全程没有 XML 配置,也不需要代码生成。
Spring Boot 是 Spring Cloud 构建微服务架构的重要基础。
·服务在 Eureka 实例中注册,由 Spring 管理的 Bean 来发现和调用。
· 通过配置的方式可以启动嵌入式的 Eureka 服务器。
• Feign 客户端通过声明的方式即可导入服务代理。
• Zuul 使用 Ribbon 服务实现客户端的负载均衡。
· 通过声明的方式即可插入 Hystrix 的客户端。
· 通过配置的方式即可启动 Hystrix 面板服务器。
· 在 Spring 环境中可以直接配置 Netflix 的组件。
• Zuul 可以自动注册过滤器和路由器,形成一个反向代理服务器。
• Hystrix 面板可以对服务的状态进行监控,并提供了容错机制。
Spring Cloud Netflix 是当前最流行的微服务架构的落地和实现,由于发布的时间较晚, 理 念较新,如果读者所在的公司允许一定的试错成本,则也是一个不错的选择。
本章主要讲解从传统的单体架构到服务化的发展历程,并讲解从服务化到现在流行的微服 务架构的演化,以及微服务架构的特点、实现原理和最佳实践,井且提出了微服务是 SOA 服务 化的拓展和延续。和微服务就是 SOA 服务化在全新场景下的升华和叠加。
最后再次强调微服务架构的主要特点。
· 将传统单体应用拆分成网络服务,来实现模块化组件。
· 根据微服务架构的服务划分来分组职能团队,减少跨团队的沟通。
· 每个服务对应一个团队,团队成员负责开发、测试、运维和运营,开发后在团队内运维 和运营,不需要交付给其他团队。
去中心化、 去 SOA 服务化的中心服务治理和去企业服务总线。
· 微服务重视服务的合理拆分、分层和构造,可建设自动化持续发布平台,井进行敏捷开 发和部署。
· 具备兼容性设计、容错性设计和服务的契约设计。