不是后端也应该知道的「 web 服务、子服务、服务的部署」

web 服务是什么

1. 定义

我们先来看一个很通俗的定义,来自于wiki。

Web service 指的是,一个平台通过 web 向其它平台来提供服务。

更专业一点的定义怎么说呢?我们来看一下 W3C 对 web service 的定义。

Web service 是一个软件系统,使得不同机器可以在网络间进行互动操作。

2. 要素

想要实现一个平台在网络间调用另一个平台的服务,至少需要明确三点:

  • 如何将平台上的代码作为服务暴露出去供其它平台调用;

  • 使用什么样的网络协议通信;

  • 使用什么样的格式作为通信内容。


从 WSDL 理解 web service 的要素

要回答以上问题,我们可以先简单的了解一下什么是 WSDL,Web Services Description Language,网络服务描述语言。我们知道服务的提供方其实本质上是由代码编写而成,而服务的调用方通过发起一个网络请求来调用服务。那么通俗的说,WSDL 做的事情就是,描述了如何根据调用方发送的网络请求,找到服务提供方,进而找到要运行哪一段代码,从而得到结果返回给调用方。

WSDL 是基于 XML 格式的文档,包括两部分,抽象定义和具体定义。

WSDL的抽象定义

WSDL 的抽象定义,独立于提供服务的平台和服务实现的语言,定义了该服务通过什么样的网络协议、使用什么样的消息格式与调用方通信。网络协议是不受限制的,可以是 http、ftp、smtp 等其它网络传输协议,不过大部分情况下我们使用的是 http 协议。消息格式也是多种多样的,最初唯一被广泛使用的消息格式是基于 XML 格式的 SOAP,简单对象访问协议,后来 REST 流行了起来,出现了基于 REST + XML 的消息格式,再后来发展为 REST + JSON 的消息格式,也就是我们现在应用最广泛的一种。

WSDL 的具体定义

WSDL 的具体定义,与平台和语言相关,定义了一个具体的服务调用,请求参数和返回参数是怎么样的、以及通过哪一部分代码的运行可以得到结果等等。

咖啡馆的类比

我们使用一个咖啡馆来类比 WSDL 的工作原理,咖啡馆是服务提供方,提供了下单、取餐、付款等服务,咖啡馆的员工手册则相当于提供服务的代码,顾客是服务调用方。WSDL 的抽象定义,定义了顾客如何找到咖啡馆的位置,以及顾客和咖啡馆的员工使用哪国语言进行交流等等;而 WSDL 的具体定义,则定义了每一个具体的服务,如下单服务,顾客需要提供什么,工作人员在员工手册的哪一页可以找到下单的操作流程,以及工作人员会返回什么给顾客,等等。

WSDL 文档可以由服务的实现代码自动生成,反之也可以通过定义好的 WSDL 文档生成代码框架。

3. 应用方式

最常见的两种 web service 的组织形式是:RPC 远程过程调用,REST 表述性状态转移。从本质上来说,两者定义的都是规范,一个是面向过程的远程调用规范,一个是面向资源的远程调用规范。

RPC 远程过程调用

RPC,Remote Procedure Call,远程过程调用,定义了平台与平台之间面向过程进行服务调用的规范。它的本质思想是,将一个平台上的多个函数过程,作为一个服务,提供给另一个平台调用。所以以 RPC 为规范的服务,需要关心的是「我要做一件什么事」。RPC 规范是协议无关的,可以使用各种网络协议实现。

REST 表述性状态传递

那么 REST 又是什么?不知道 REST 没关系,如果你接触过 GET、POST、PUT、DELETE 这样的请求,不要怀疑,这种我们通常意义上所说的 http 请求大部分都是基于 REST 规范而来的。基于 REST 规范设计的 api 也称之为是 RESTful 的 api,这样的 api 的主题必须是资源,它关心的是「我要对某个资源进行什么样的操作」。为什么 REST 可以流行起来呢?这就跟我们为什么要用面向对象的思想进行编程是一个道理,万物皆对象,外物皆资源。这里推荐一篇非常通俗的讲解 REST 规范的文章 如何给老婆解释什么是RESTful。

RPC 与 REST 的比较

总的来说,PRC 与网络协议无关,关心的是过程;REST 基于 http 协议,关心的是资源。下图演示了针对相同的有关用户的操作,REST 形式的服务(左边)和 RPC 形式的服务(右边)设计上的区别。


不是后端也应该知道的「 web 服务、子服务、服务的部署」


那么在具体的使用场景下,对于两种设计规范,我们应该如何选择呢?我觉得二者的取舍,可以类比于函数式编程与面向对象的编程,各自有各自适合的场景,甚至在某些场景下,使用二者皆可且各有利弊。重要的是要理解这两个设计规范的本质和初衷,并根据实际场景和个人的使用习惯最初抉择。

web service 与子服务

在谈子服务之前,我们来继续之前咖啡馆的假设,从而理解什么是子服务以及我们为什么需要子服务。

为什么我们需要子服务

设想一个咖啡馆的正常运作,需要以下职能人员的参与。

  • 前台:负责创建、修改、删除客户的订单

  • 收银员:收取订单相应的费用、找零、管理咖啡馆的日常支出

  • 服务生:为客户配送咖啡到相应的座位上,回收餐具

  • 清洁工:维护店内清洁、桌椅摆放、空调灯光等硬件设施

  • 经理:保证店铺正常运行,解决问题和异常情况


对于这些职能人员来说,核心要素有三点:

  1. 他们所做的工作有明确的界限划分;

  2. 他们互相之间可能需要进行交流;

  3. 他们共同维护了咖啡馆的运作。


为什么咖啡馆不是由一个非常厉害的全能的人承担所有的工作呢?这个很容易理解:

  • 首先厉害的人比普通人更加难找到;

  • 而且要同时兼顾这么多的工作内容是更加容易出错的;

  • 还有最重要的一点是,如果他生病了,整个店就完全没有办法运作下去。


那么将咖啡馆的例子映射到 web 服务上,提供一个单一的 web 服务来支持整个咖啡馆的运作自然也是不合理的:

  • 想要维护好一个大型的系统比维护好一个小型的系统更加困难;

  • 业务逻辑冗杂的系统更容易出错;

  • 如果这个系统的一小块内容出现问题很容易导致整个系统的崩盘。


那么如何拆分一个服务系统呢,答案就是子服务了。我们将整个系统根据职能的划分拆分成5个子服务,分别对应到上文的5种职能人员。

  • 订单管理服务

  • 账户管理服务

  • 餐具管理服务

  • 店内环境管理服务

  • 性能监控与异常处理服务


同样的这些子服务的核心要素如下:

  • 这5个子服务所提供的接口有明确的界限划分;

  • 子服务之间可以互相调用;

  • 共同保证了整个咖啡馆的运作。


理解了子服务的概念以及 web service 为什么需要子服务之后,新的问题出现了:子服务如何进行合理的拆分?如何管理多个子服务?子服务间如何通信?

这里就不得不提到 SOA 了。

通过 SOA 架构组织子服务

SOA,Service Oriented Architecture,是一个面向服务的架构设计,通俗的说它也是一个规范,定义了如何管理服务的集合及他们之间的通讯方式。它本质上和 web service 以及子服务都没有绝对的依赖关系,它甚至比 web service 出现的更加早。然而人们在 web service 上发现了它的用武之地,也就是说 SOA 刚好可以在 web service 的管理上体现它的价值。于是乎,造成了几乎所有 SOA 的应用场景都与 web service 相关这样的现状,也导致了这两个概念一定程度上发生了混淆。

既然 SOA 框架是对服务的集合的管理,那么它究竟比单纯的服务拆分多做了哪些事情呢?

我们来看一下下图这个简单的例子。假设我们要将整个系统拆分成4个子服务:ACCOUNT、C2D、ASK、DESIGN。左边为单纯的服务拆分,右边为基于 SOA 框架的服务拆分。


不是后端也应该知道的「 web 服务、子服务、服务的部署」


  • 左边:单纯的进行了服务拆分,形成了4个互独立的服务。这里其实就出现了两个问题:客户端需要关心我请求的 api 到底是属于哪个服务的,然后再往相应的服务端发送请求;虽然服务做了拆分,但是如果其中一个服务出现问题挂掉了,那么整个架构中的服务都不可用。

  • 右边:将这4个子服务作为一个服务的集合,并简单地应用了 SOA 架构。可以看到除了四个子服务之外,最上层还多了一个 gateway,而最下层也多了三个底层模块。最下层的三个底层模块很好理解,有一些工作是每个子服务都需要做的,比如版本控制、性能监控等,底层就是抽出了这样的公共模块以便子服务复用。最上层的 gateway,网关,顾名思义,你们如果想访问我管理的这些子服务,直接访问我就好了;也就是说客户端只需要向 gateway 发送请求,gateway 会根据所配置的规则将请求转发到正确的子服务上,这也就解决了上文所述左边的设计中遇到的两个问题。


子服务及子服务的部署

1. 服务的实现

web 服务是一个软件系统,软件系统是通过代码形成的。那么这样一个软件系统是如何从一大坨代码转化为稳定的、可访问的、可更新的服务的呢?

一个有一定流量的服务一般是由类似这样的结构组成的。


不是后端也应该知道的「 web 服务、子服务、服务的部署」


上层是一个负载均衡器(load balancer),下层是多个相同的节点(node)。

  • 负载均衡器:将针对这个服务的请求,合理的分发到下面的某一个节点上,以尽量达到这样的目的:请求尽可能的被完成(例如其中一个节点没有正常运行不会导致请求失败)、每个节点承担均匀的压力(例如同时有一万个请求,不至于扎堆到同一个节点上去导致节点出现性能问题)。负责均衡器可以通过网络设备、虚拟 ip、nginx 反向代理、甚至仅仅是一段代码来实现。

  • 节点:每个节点都是等同的,每个节点上都运行着相同的服务,等待处理负载均衡器转发过来的请求。节点可以是一个物理机、虚拟机、也可以是一个 docker 容器。


2. 服务的部署

服务的部署,简单来说就是将该服务的软件系统的最新代码克隆到每一个节点上,再在每一个节点上将服务运行起来。那么服务的更新无非就是重新对每一个节点进行一次部署。

但是不要忘记,在一个节点上重新运行服务会导致该节点的服务有一个短暂的罢工,那么如何保证在完成对服务的更新的同时,保证对于客户端来说服务不会出现挂掉的状态?这时候就需要一定的部署策略。

注:下图中绿色节点均表示未更新节点,蓝色节点均表示已更新节点

1)滚动部署

滚动部署,每次只更新 n 个节点,等待前 n 个节点部署好了,再更新下 n 个节点,这样可以保证同一时间只可能最多有 n 个节点处在不可用状态。

下面四张图是一个滚动部署过程的例子,例子中 n 为 1。

首先更新第一个节点。

不是后端也应该知道的「 web 服务、子服务、服务的部署」


待第一个节点更新完毕之后,更新第二个节点。

不是后端也应该知道的「 web 服务、子服务、服务的部署」


待第二个节点更新完毕之后,更新第三个节点。

不是后端也应该知道的「 web 服务、子服务、服务的部署」


待第三个节点更新完毕之后,更新第四个节点。全部的节点都完成了更新。

不是后端也应该知道的「 web 服务、子服务、服务的部署」


滚动部署的缺点是,在部署过程中,客户端可以同时访问到更新前的服务和更新后的服务。

2)蓝绿部署

蓝绿部署,如下面三张图所示,分为三个步骤。

首先新增四个节点,并将新版的服务部署上去。

不是后端也应该知道的「 web 服务、子服务、服务的部署」


全部部署好之后将负载均衡器指向新的四个节点。

不是后端也应该知道的「 web 服务、子服务、服务的部署」


移除原有的旧版本服务的四个节点。

不是后端也应该知道的「 web 服务、子服务、服务的部署」


蓝绿部署解决了滚动部署会同时出现新旧服务并存的缺点,但是对资源的要求更高。

3)灰度发布

灰度发布是平滑过渡的一种发布方式。让一部分用户继续用旧版本的服务,一部分用户开始体验新版本的服务,如果用户对新版本没有什么反对意见,那么逐步扩大范围,将所有的旧版本服务更新为新版本服务。灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题,以避免产生无法挽回的影响。灰度发布的实现如下面三张图所示。

首先更新一个节点。

不是后端也应该知道的「 web 服务、子服务、服务的部署」


然后通过负载均衡器按照一定的规则筛选出 20% 的用户(比如用户 id 除以 5 余 0),并将这 20% 的用户所发出的全部请求都转发到已经更新过的节点。

不是后端也应该知道的「 web 服务、子服务、服务的部署」


再更新第二个节点,并将包含之前那 20% 的用户的 50% 的用户的请求转发到两个更新过的节点上去。

不是后端也应该知道的「 web 服务、子服务、服务的部署」


以此类推直到所有节点更新完毕,100% 的用户的请求都会请求到新的服务。这里需要注意的是,灰发的用户百分比最好和更新节点的占比相近,这样可以保证每个节点可以承受相似的压力。如果只更新了一个节点,而转发了 90% 的用户的请求到新服务上,那么这个节点很可能会出现性能问题。

“我们相信人人都可以成为一个java开发大神,现在开始,找个师兄,带你入门,学习的路上不再迷茫。这里是java开发修真院,初学者转行到互联网行业的聚集地。"

我做开发十多年的时间,如果大家对于学习java的学习方法,学习路线以及你不知道自己应该是自学还是培训的疑问,都可以随时来问我,大家可以加我的java交流学习qun:615741636。qun内有学习教程以及开发工具。

猜你喜欢

转载自blog.51cto.com/13719825/2379867