微服务架构与实践 —— 读书摘抄、笔记

微服务架构与实践
王磊html


单一架构,不考虑服务器集群,全部的代码最终运行在一个进程当中算法

单层架构,用户 --> 表示层、业务层、数据访问层混合在一块儿 --> 数据库数据库

二层架构,用户 --> 表示层、业务层、数据访问层(分离) --> 数据库
三层架构,用户 --> 表示层(分离)、业务层(分离)、数据访问层(分离) --> 数据库编程


分布式架构浏览器

SOA面向服务架构WebService
微服务架构业务独立,高内聚,单一职责,轻量级通讯机制、与语言、平台无关服务器


微服务通讯架构

在微服务架构中,服务和服务之间通讯时,一般是经过轻量级的通讯机制,实现彼此间的互通互联,互相协做。所谓轻量级通讯机制,一般是指与语言无关、与平台无关的这类协议。经过轻量级通讯机制,使服务与服务之间的协做变得更加简单、标准化。并发

服务节点之间通讯,能够采用同步通讯方式或异步通讯方式。框架


RPC远程过程调用,远程过程调用采用客户端/服务器端的模式,请求的发起者是客户端,提供响应的是服务器端。客户端经过客户代理存根( Stub),传递函数参数,向服务器端发起函数调用。服务器端经过服务器代理存根( Skeleton),接收到客户端的请求后,对请求进行处理,并在结束后向客户端返回响应,从而完成一次通讯。异步

传统的远程过程调用框架主要包括 Sun RPC(主要基于 C语言实现,以函数调用为主。随着面向对象语言的快速发展,序列化/反序列化等特性的诞生,基于不一样语言的远程过程调用框架开始提供对象远程访问的功能,譬如 Java RMI、 Thrift 或者 protocol b uffers等。同传统的远程过程调用框架相比,有一类框架可以容许客户端经过面向对象的调用方式,调用远端的实现,咱们称这类调用为远程方法调用( Remote Method Invocation, RMI)。换句话说,远程方法调用是远程过程调用的一种面向对象的实现。
优势:远程过程调用经过使用代理存根( Stub/ Skeleton)的方式,屏蔽了通讯双方底层的调用细节,让客户端没必要显式地区分当前代码级别的方法调用是本地调用仍是远程调用,所以使分布式节点间的通讯变得简单。
弊端:一、耦合度高大部分远程过程调用的实现机制,是依赖于编程语言或者特定平台的,所以限制了客户端和服务器端采用的技术,耦合度较高。一旦使用后,通讯的双方就很难再切换到其余语言或者平台上。譬如,若是使用了 Java RMI做为通讯机制,就意味着对应的通讯双方都必须运行在 JVM平台上,这一点在很大程度上限制了未来技术的替换。 Thrift和 protocol buffers支持多种语言,能够稍稍地弱化这个缺点。

远程过程调用,是一种典型的分布式节点间同步通讯的实现方式。其为客户端提供了简洁、透明的调用接口,但同时因为其依赖于开发语言以及特定的平台,所以耦合度较高,而且灵活性不太好,并不适合做为一种轻量级的机制知足服务之间通讯的要求。

REST(表述性状态传递)是近几年使用较普遍的分布式节点间同步通讯的实现方式。
REST从语义层面将响应结果定义为资源,并使用 HTTP的标准动词映射为对资源的操做,造成了一种以资源为核心、以 HTTP为操做方式的,与语言无关、平台无关的服务间的通讯机制。
REST列为同步通讯的实现方式,主要是由于 REST是基于 HTTP的,而 HTTP的通讯是同步过程(虽然可使用 AJAX等方式将通讯的过程异步化,但其本质上仍是同步的过程),因此能够认为 REST的通讯过程是同步的。不过,也能够将 REST的架构风格应用于异步通讯过程当中,譬如在后台任务系统中,可使用 REST风格做为服务调用的协议。
REST的核心资源( Resource)是一个抽象的概念,是指对某类信息实体的抽象。
实体是指服务器端须要处理的具体信息,它能够是一段文本、一张图片、一首歌曲,
与面向对象设计的概念相似,资源一般以名词为核心来定义。每一个资源对应一个特定的 URI做为标识。对某个资源感兴趣的客户端应用,能够访问资源的 URI与其进行交互。
资源的表述( Representation)是对资源在某个特定时刻的状态的描述。
咱们知道,资源是一种信息实体,实体在客户端与服务器端进行信息交换时,能够有多种表现形式。譬如,文本能够用 TXT格式表现,也能够用 HTML格式、 XML格式、 JSON格式表现,甚至能够采用二进制格式;图片能够用 JPG格式表现,也能够用 PNG格式表现。这种资源的表述格式能够在客户端与服务器端经过请求-响应的协商机制来肯定。
URI仅表明资源的实体,并不表明它的表述。譬如,有些 URL最后的. html后缀名并不属于表述范畴,表述应该在 HTTP请求的头信息中用 Accept和 Content- Type字段指定,这两个字段才是对“表述”的描述
Accept表明发送端(客户端)但愿接受的数据类型。 好比:Accept:text/xml; 表明客户端但愿接受的数据类型是xml类型。 Content-Type表明发送端(客户端|服务器)发送的实体数据的数据类型。 好比Content-Type:text/html; 表明发送端发送的数据格式是html。
统一接口( Uniform Interface)客户端操做资源的方式,一般是基于 HTTP的 4个动词( Verb): GET、 POST、 PUT、 DELETE。它们分别对应 4种资源的操做方式:一、 GET用来获取资源。 二、 POST用来新建资源。 三、PUT用来更新资源。 四、DELETE用来销毁资源。
随着团队或者组织业务的不断增加,服务器端响应内容复杂度的增长, REST的使用面临以下两个挑战: 一、如何标准化资源结构,返回的JSON字符串格式没有标准 二、如何处理资源的相关连接,JSON最大的遗憾在于没有对超连接处理作内置的支持。


HAL( Hypertext Application Language)是一种轻量级超文本应用描述协议。 HAL的实现基于 REST,并有效地解决了 REST中资源结构标准化和如何有效定义资源连接的问题。

在 HAL中,任何服务器端的响应都被定义成一种资源( Resource),这是遵循 REST原则对资源的定义的。同 REST不一样的是,在每一个资源中, HAL又将其分红了以下三个标准的部分。
状态( State)一般是指资源自己固有的属性。譬如,对于商品列表资源的响应而言,其状态的定义可能以下所示:
连接( Links)定义了与当前资源相关的一组资源的连接的集合。一般列表页返回的响应都是分页的结果,所以须要在资源中定义上一页、下一页、第一页以及最后一页等相关连接。
子资源( Embedded Resource)描述在当前资源的内部,其嵌套资源的定义。

使用 HAL,还有一个很大的优点,那就是可使用 HAL浏览器( HAL Browser)来可视化资源的信息。咱们知道, HAL将资源分红三个基本的部分:状态、连接和子资源。基于这个标准的结构, HA L浏览器可以将资源的每一部分,经过可视化的方式显示出来。


消息队列( Message Queue)是一种处理节点之间异步通讯的实现方式。发送消息的一端称为发布者,接收消息的一端称为消费者。经过消息队列,发布者将消息放入队列中保存,而后消费者会在未来某个时间点获取消息并处理。消息队列使消息的发布者和消费者不须要同时交互,从而达到异步通讯的效果。
消息队列的核心特征
消息队列会指定相应的标准和算法,保障消息进入或者出队列的优先级。譬如最多见的 FIFO(先进先出)算法。
消息队列提供某种通知机制,帮助消息发布者知道什么时候部分或所有接收者收到了消息。
消息队列的访问,通常存在以下两种方式。
拉模式( Pull M ode)要求消费者按期检查队列上的消息。
推模式( Push Mode)是每当发布者将消息添加到队列中时,会经过某种机制通知消费者。所以消费者可知道消息队列的变化状况,这意味着数据的处理更及时,但同时也意味着发布者、消费者以及队列必须依赖语言或者平台的某些特性,作更多的开发和配置工做。一般在推模式下,通常存在多个消费者,也称他们为订阅者。

对于微服务架构的系统而言,使用消息队列的优势很是明显

异步通讯,当构建复杂的分布式系统时,消息队列是经常使用的异步通讯方式。

但消息队列会增长整个系统的开发及维护成本,譬如,对一个用户注册的场景,当用户单击注册按钮后,但愿后台服务进行处理并发送欢迎电子邮件,但同时要求该操做不会影响用户在页面上的其余操做。对于这个场景,若是须要安装 ActiveMQ、 RabbitMQ,定义消息的发布者和消费者,显然太过复杂,所以可使用后台任务处理系统。


经常使用的后台任务处理系统有 Resque、 Sidekiq以及 Delayed_ job等

后台任务处理系统主要包括: ●任务 ●队列 ●执行器 ●定时器
队列( Queue)主要用于存储任务,并提供任务执行失败后的错误处理机制,譬如失败重试、任务清理等,一般咱们也称队列为任务队列。每一个任务队列都有一个全局惟一的名称做为标识。目前大多后台任务处理系统一般都采用 Redis做为队列的实现机制,譬如像 Resque、 Sidekiq等。

对于微服务架构的系统而言,使用后台任务系统的优势很明显,主要包括以下几项。
请求服务经过定义任务,将交互的请求存在任务队列中,执行器根据定时器的配置,从任务队列中取出任务并执行,再回调相关的服务。在整个交互过程当中,后台任务系统只是承担了定时触发回调请求的责任,具体的实现均由被调用服务完成,从开发、测试以及问题定位和调试的角度而言,能够更集中在被调用服务自己。所以,相比消息队列,后台任务系统提供了更轻量级的方式,完成服务间的异步通讯。
一般,后台任务系统的开发和维护成本比起消息队列要低得多。
经过使用后台任务系统,使不一样服务之间可以有效地完成业务异步通讯。相比消息队列的方式,后台任务系统更简洁易用,也更轻量级。


测试

单元测试是针对程序单元进行正确性的检验。理论上,程序单元是指应用中最小的可测部分,譬如函数、代码片断等。一般,单元测试都能经过自动化的方式运行。经常使用的单元测试框架有 Java语言的 JUnit,可以使用工具或脚本彻底自动化运行。

接口是服务间的通讯方式和协议,也能够称为契约( Contract)。契约的两端分别是消费者( Consumer)和提供者( Provider)。使用契约的一方称为消费者,提供契约的一方称为提供者。契约测试是针对服务接口进行的测试,它能验证提供者提供的契约是否知足消费者的指望。
Pact是一个基于消费者驱动的契约测试框架。
Pact的使用主要包括两步:消费者端生成契约与提供者端完成校验。
消费者端使用 Pact框架提供的 DSL以及相关的配置文件,运行一个本地的模拟服务,模拟服务的提供者端。
提供者端使用 Pact框架提供的 DSL以及相关的配置文件,配置 Pact。

集成测试( Integration Testing),是将不一样的单元按照指望组合起来,对其接口进行正确性检验的测试工做。一般,集成测试在单元测试以后、端到端测试以前进行。集成测试的本质是确保当多个单元组合在一块儿时,可以按照指望协做运行。

组件的概念你们必定不陌生,当将庞大的系统拆分红不一样的部分时,每一个独立的部分就是一个组件。在微服务架构中,每一个服务也能够看做是一个组件。
对于微服务的组件测试而言,它关注的是当指定输入时,被测服务是否能提供指望的输出。实际上,组件测试并不会对服务内部的逻辑作任何模拟或者打桩。所以,经过组件测试,咱们能有效保证当前被测服务的功能是正常的。

端到端测试,又称 End- To- End Testing或者 System Testing,是从用户使用系统的角度出发,对系统的行为进行正确性验证的测试。
微服务架构的端到端测试,会对系统内多个服务之间的协做进行较全面的覆盖,这同以前咱们描述的集成测试、消费者驱动的契约测试相比,对服务间协做的测试更加充分和全面。

在金字塔的顶端,是探索性测试。探索性测试并无具体的测试方法,一般是团队成员基于对系统的理解以及基于现有测试没法有效覆盖的部分,作出的系统性的验证。譬如跨浏览器的测试,或者一些视觉效果的测试。由于这类功能变动较频繁,并且所有实现自动化成本较高。所以,小范围的探索性测试仍是比较有效的。实际上,探索性测试强调测试人员的主观能动性,其一般不太容易经过自动化的方式实现,更多的是由团队成员手动完成。所以,探索性测试的成本最高,自动化难度大,测试以手动执行为主,同时还须要根据具体的场景改变策略。另外,探索性测试的反馈周期也是最慢的。


使用微服务架构改造遗留系统
因为是遗留系统,熟悉该代码的人早已离职多时,新团队对其望而却步,只能作些周边的修补工做。
在没法中断业务处理的状况下,为了解决当前面临的问题,团队制定了以下策略。 ●最小修改 ●功能剥离 ●数据解耦 ●数据同步 ●迭代替换

一、对于大部分非紧急的、非重要的任务,都禁止在原有的系统上进行修改。
二、在现有合同管理系统的外围,逐步构建功能服务接口,将系统核心的功能分离出来。同时,使用代理机制,将用户对原有系统的访问,转发到新的服务中,从而解耦原合同系统与用户之间的依赖。
三、随着部分功能的解耦,已经存在一些微服务可以独立为用户提供功能。这时,逐渐考虑将数据解耦,从原有的单块架构数据库中剥离相关的业务数据。尽可能知足对于每一个服务,有独立的、隔离的业务数据系统。
四、可能在很长一段时间内,因为新的服务(业务逻辑、数据)独立出来,致使没法同现有系统协做。在这种状况下,为了持续交付价值,笔者一般会采用 ETL( Extract、 Transform、 Load)的机制,将服务中的业务数据同步到单块架构的数据库中,保障原有的功能可以被继续使用。
五、经过不断地迭代替换,最终将原有的合同管理系统替换成使用微服务架构解耦的新的合同管理系统。

在微服务的开发实践中,团队的微服务快速开发模板( Microservice Template)诞生了。该模板是一个帮助快速构建 Ruby微服务应用的开发框架,主要包括 4部分:快速开发模板、代码生成工具、持续集成模板以及一键部署工具。

相关文章
相关标签/搜索