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


    微服务架构与实践
    王磊


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

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

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


    分布式架构

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


    微服务通信

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

    服务节点之间通信,可以采用同步通信方式或异步通信方式。


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

    传统的远程过程调用框架主要包括 Sun RPC(主要基于 C语言实现,以函数调用为主。随着面向对象语言的快速发展,序列化/反序列化等特性的诞生,基于不同语言的远程过程调用框架开始提供对象远程访问的功能,譬如 Java RMI、 Thrift 或者 protocol b uffers等。同传统的远程过程调用框架相比,有一类框架能够允许客户端通过面向对象的调用方式,调用远端的实现,我们称这类调用为远程方法调用( Remote Method Invocation, RMI)。换句话说,远程方法调用是远程过程调用的一种面向对象的实现。
    优点:远程过程调用通过使用代理存根( Stub/ Skeleton)的方式,屏蔽了通信双方底层的调用细节,让客户端不必显式地区分当前代码级别的方法调用是本地调用还是远程调用,因此使分布式节点间的通信变得简单。
    弊端:1、耦合度高大部分远程过程调用的实现机制,是依赖于编程语言或者特定平台的,因此限制了客户端和服务器端采用的技术,耦合度较高。一旦使用后,通信的双方就很难再切换到其他语言或者平台上。譬如,如果使用了 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种资源的操作方式:1、 GET用来获取资源。 2、 POST用来新建资源。 3、PUT用来更新资源。 4、DELETE用来销毁资源。
    随着团队或者组织业务的不断增长,服务器端响应内容复杂度的增加, REST的使用面临如下两个挑战: 1、如何标准化资源结构,返回的JSON字符串格式没有标准 2、如何处理资源的相关链接,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,是从用户使用系统的角度出发,对系统的行为进行正确性验证的测试。
    微服务架构的端到端测试,会对系统内多个服务之间的协作进行较全面的覆盖,这同之前我们描述的集成测试、消费者驱动的契约测试相比,对服务间协作的测试更加充分和全面。

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


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

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

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

  • 相关阅读:
    c# 自定义事件和委托
    C#委托之个人理解(转)
    invokeRequired属性和 invoke()方法
    .NET(C#)连接各类数据库
    Mobile Web Development with ASP.NET 2.0
    移动飞信WEB发送服务接口
    4行C#代码打造专业数据库连接配置界面
    domino 中 UniversalID 和NoteID的区别
    VB中preserve的用法
    LOTUS Note ID 剖析
  • 原文地址:https://www.cnblogs.com/Mike_Chang/p/11627314.html
Copyright © 2020-2023  润新知