微服务是什么

计算机自诞生以来,极大地影响了人类的生产和社会活动,软件生产以一种生产活动的方式进入了人们的生活。软件生产是知识密集型的智力活动,生产过程仍以手工劳动为主。随着软件生产活动的发展,不同时期生产力的需求促进着生产方式的变革,表现形式从程序设计方式逐渐转变为应用架构的创新,微服务便以应用架构的创新形式随着软件生产的发展逐渐演变而来,成为软件生产发展的必然产物,同时也是软件开发过程中的必然需求。

1、微服务是软件生产发展的必然产物

软件生产以程序设计为生产方式的表现形式发展经历了 3 个时期,分别是程序设计时代、软件系统时代和软件工程时代。

1) 程序设计时代(20世纪50年代~20世纪70年代)

程序设计时代的软件生产仅是程序设计,主要是按照需求编写用来计算的数学程序,编写者和使用者通常为同一人或同一组人,是一种自给自足、个体手工劳动的生产活动。编程语言主要以早期的命令式程序设计语言为表现方式。机器语言及汇编语言是最早的命令式语言,最早诞生的高级语言 Fortran I,也是一种命令式语言,命令式语言是基于动作的语言。

以冯·诺依曼计算机体系结构为背景,计算机被看作是动作的序列,程序就是用语言提供的操作命令书写的一个操作序列。命令式语言支持自然公式语法,如使用几条命令让计算机完成数学计算等,现在的高级语言都支持这种命令式语言的设计风格。

2) 软件系统时代(20世纪70年代~20世纪90年代)

软件系统时代有了数据结构的概念,程序与数据构成了软件,同时也产生了职业的软件开发人员,并由个体手工劳作逐渐转变成作坊式合作的真正软件生产活动。由于高级语言的诞生和 20 世纪 60 年代中期计算机硬件的飞速发展,计算机的应用领域及需求不断扩大,软件成为一种商品。这一时期由于生产力需求的增加,落后的程序设计方法严重阻碍了生产力的发展,导致了第一次软件危机的爆发。

为解决这一问题,人们提出了结构化程序设计方法,结构化程序设计约定软件开发者采用自上而下、逐步求精、模块化的方式进行程序设计,整个程序的各个模块通过顺序、选择、循环的控制结构进行连接,只有一个入口和一个出口。模块化的设计实现了有效的工作分工,每个模块可以被不同的人编写、重用并独立测试,使生产力得到巨大提升。结构化程序设计的典型代表莫过于C语言,每个程序由多个源文件构成,每个源文件就是一个模块,不同的源文件被入口文件 main.c 引入后,通过控制结构实现模块功能的调用。

3) 软件工程时代(20世纪90年代至今)

软件工程时代的软件生产引入了软件工程的概念,软件生产被定义了生命周期,程序开发也被要求遵守系统化、规范化、数量化的工程原则。随着社会的发展,人们对软件的需求量剧增,软件的复杂度也越来越高,大规模软件常常由数百万行代码组成,参与程序开发的人员数以百计,结构化程序设计的问题日益凸显。此时,以面向对象程序设计为代表的新的生产方式适应了生产力发展的需求,成为人们的新选择。

面向对象程序设计是将结构化程序设计中的数据及与数据有关的函数集成在一起,形成对象,而对象的类型就是类,类中可以定义方法和属性等,并将结构化程序设计中主程序与子程序间的从属关系,变为对象间相互发送消息的平等关系。现今流行的开发语言大多是面向对象的程序设计语言,采用面向对象程序设计思想开发的外部文件里可以有更加复杂的方法。通过类、方法、属性的定义,使其可以处理更加复杂的场景。

早期,软件生产方式变革的重点都在数据结构和算法的选择上,随着软件系统规模的变大及处理的场景越发复杂,软件生产进入软件工程时代,整个软件系统的架构设计和规范变得越来越重要,数据库、网络、分布式等应用架构技术也成为软件设计的重要组成部分。软件生产方式的表现形式逐渐由内在的程序设计向外在的应用架构转变,曾被使用的应用架构有垂直应用架构和面向服务架构。

① 垂直应用架构

最早的 LAMP(Linux、Apache、MySQL、PHP)是一种非常原始的垂直架构模式,由于早期互联网公司的业务规模小,LAMP 在很长一段时间内十分流行。随着互联网应用规模的增长,分层模式的垂直应用架构得到了广泛应用。

分层模式的最典型模型就是 MVC(Model-View-Controller)模型,MVC 模型充分利用面向对象的封装、继承及多态特性,把代码结构分为展示层(View)、控制层(Controller)和模型层(Model),控制层负责处理用户请求,通过模型层获取数据,并经过展示层渲染后展示给用户。MVC模型下的应用代码通常打包在一个发布包中进行部署。在高并发场景下,会使用 Nginx 等负载均衡对部署在多个机器上的应用进行负载分流。

② 面向服务架构

面向服务架构(Service Oriented Architecture,SOA)是一种松耦合、粗粒度的以服务为中心的架构,以服务为基本的业务功能单元,由平台接口契约来定义,将业务系统服务化。按照这种方式,可以将程序代码中的不同模块解耦,并通过网络实现服务调用、消息交换和资源共享。它的关注点是服务,注重服务的可用性、松耦合的独立性、可任意组合编排、无状态且可被自动发现,所有服务间可以通过网络、注册中心或企业服务总线(ESB)等技术方式进行通信。

上述两种应用架构下的每个功能应用仍以单体应用的方式存在,当软件规模复杂时,代码的复杂性、维护性、创新性、可扩展性等问题依然存在。

微服务架构基于面向服务架构,既在程序设计方法上限制了对象类模块的无限增长,使代码的复杂性得到有效控制,便于开发人员维护和扩展;同时又以服务化的形式增强应用架构的整合,使不同的应用以服务化的形式更易相互调用以实现不同的功能。微服务架构使软件生产方式再次发生变化,提升了软件生产效率,成为软件生产发展中的必然产物。

2、微服务是软件开发过程的必然需求

用户需求不明确是软件生产供需矛盾的一个重要表现形式,自软件诞生以来就一直存在于软件开发过程中。用户在见到开发出来的软件前,通常自己也不清楚软件的具体需求,对软件需求的描述也不够精确,甚至可能存在很多错误的描述。用户在软件开发期间也会有变更需求的情况发生,甚至因与开发人员所处的业务领域不同导致彼此间对需求的理解存在很大的差异。

首先,我们要认识到这种需求不明确存在的客观性,因为它不只存在于软件生产活动中,还是一种客观的社会环境特点。这一社会环境特点被称为 VUCA,VUCA 是易变性(Volatility)、不确定性(Uncertainty)、复杂性(Complexity)、模糊性(Ambiguity)的缩写。

VUCA 是如今整个社会环境的特点,尤其是信息科技方面,随着科技进步及互联网的快速发展,社会正处于信息化爆炸的年代,大数据、云计算、物联网、人工智能快速发展,人们对未来充满未知和疑惑,各种需求更加模糊、复杂且具有极大的不确定性。

在软件开发过程中,人们一直以不同的软件过程模型进行开发过程革新。最早出现的软件开发模型是瀑布模型,它以一种预见式的方式向用户确认需求,将开发周期从一个阶段向下个阶段逐级过渡。瀑布式的软件开发过程缺乏灵活性,当遇到用户需求不明确的问题时这一缺点最为突出。

敏捷开发模型顺应时代的发展成为人们的首选,在它的迭代式开发模式下,开发工作被组织为一系列称为迭代的实现周期短小、固定长度的小项目,每次迭代都包含需求分析、开发、测试等一系列动作,通过迭代开发的方式可以在每次迭代时向客户细化需求,并不断调整开发过程,使其更接近用户需求。

敏捷开发下的迭代周期都很短,通常在两周或三周左右。对于每次需求的变化,软件开发都要以最快的速度去调整。为适应这种变化,软件在程序设计上需要有更多可被重用的模块,应用架构方面也要能够应对其不断拆分或重组带来的变化。

微服务应用架构下,构成服务的粒度较小,代码逻辑简单、易维护、可替换性强。每个微服务都是独立运行的,每个产品功能由一个或多个微服务共同实现,对功能需求的变更只需增加或修改对应的微服务即可,其完全满足了敏捷式开发的需求,成为软件开发过程的必然选择。

3、微服务的技术特点

微服务是独立运行的、可被访问的服务单元。微服务架构是一种应用架构,架构中每个微服务可以独立部署,彼此之间是松耦合的。它集成了面向服务架构的诸多优点,且更注重以服务为单元的低复杂度、小体积形态,每个微服务代表一个较小的业务能力,多个不同的微服务可以被组织成可实现更复杂功能的集合。

微服务适应了客观社会环境,能够有效满足敏捷开发的需求。Spring Cloud 是一套完整的微服务架构实践方案,它利用 Java 语言 Spring Boot 框架的开发便利性,使开发人员的开发项目与其提供的各微服务组件可以很方便地进行集成,使微服务架构的项目可以快速实施。本节便以 Spring Cloud 集成的常见的微服务架构组件为例,介绍微服务架构的技术特点。微服务架构的主要技术特点如下。

1) 服务注册发现

微服务架构中,为确保每个服务的高可用性,每个微服务都由多个部署相同代码的节点构成,每个微服务都会把自己的所有节点注册到注册中心。对于服务调用方,可以通过注册中心查询并发现期望调用服务的节点调用地址,以实现服务访问通信。注册中心会提供相应的检测机制,以确保被发现的节点地址是可用的。常用的服务注册与发现组件有 Spring Cloud Euraka、ZooKeeper、etcd、Consul。

2) 服务网关

微服务架构中,每个微服务都提供了一个小的应用功能,对于客户端来讲,要想完成一个较复杂的功能需要调用不同的微服务。为了便于客户端的访问及访问管理,在客户端和服务端之间增加了服务网关组件。

服务网关为所有的微服务提供了一个唯一的入口,通过不同的路径将客户端的请求路由到不同的内部服务。通过服务网关还可以提供统一的用户鉴权、跨域访问、流量管控及数据整形等功能,既方便了微服务之间的访问,又减轻了开发工程师的工作量。常见的服务网关组件有 Spring Cloud Zuul、Kong(基于 OpenResty)及 Gravitee。

3) 配置中心

在传统模式下,每个应用都会存在对运行时的数据库、Redis等组件或不同硬件配置下的运行参数进行修改的需求,这些修改都以配置文件的形式保存在代码包中。当每个微服务被拆分为更小的体积并独立部署时,部署节点的数量急剧增加,每个节点配置的修改也变得非常复杂。为了方便配置的修改,配置中心提供了一种配置文件与应用代码分离、集中修改的方法实现配置修改操作。每个服务将配置存储在配置中心,在每次启动时按需读取配置内容,完成配置加载的需求。常见的组件有 Spring Cloud Config、Apollo 及 Disconf。

4) 服务容错保护

微服务架构将原有的单体应用拆分为多个可独立运行的服务,使很多以前在单应用内存级的调用变成了网络调用。由于网络调用的不确定性或被调用方的可用性等因素极大地增加了访问响应延迟等问题的发生,相应地,调用方自身在等待期间无法响应上级服务的当前调用,若此时仍不断有相同的请求被发送过来,便会造成请求积压,甚至导致服务瘫痪。

基于这种考量,Spring Cloud 在微服务架构中提供了断路器、线程隔离等一系列服务容错保护机制,以对调用的请求进行监控,当下游请求出错达到阈值时,将自动启动熔断,不再调用下游服务直接返回错误信息,当检测到下游服务器恢复时,则继续向下游服务器发送请求。常见的服务容错保护组件有 Spring Cloud Hystrix、Linkerd、Istio。

5) 分布式链路跟踪

在单体应用拆分为多个可独立运行服务的微服务架构中,服务节点不断增加,服务间的调用关系变得越发复杂。通常一个客户端请求会引发多个及多层级服务的调用,期间除了需要对容错保护机制进行监控,还需要对因调用关系而引发的链路性能进行分析监控。分布式链路跟踪会对客户端访问的每个请求创建一个唯一的跟踪标识,当请求在访问链路中流转时,跟踪系统将根据该跟踪标识实现对每个请求链路的监控。这些监控信息可以包括访问路径中的服务名称、请求耗时、方法错误等。常见的分布式链路跟踪组件有 Spring Cloud Sleuth、Jaeger和Zipkin。

6) 微服务进程间的通信

微服务是通过网络实现通信的,服务的相互调用是进程间的通信调用。对于进程间的通信在通信机制上有两种,一种是 IPC(Inter-Process Communication)机制,其以 REST 风格为代表,并完全通过 HTTP 协议实现,相对更加通用、规范;另一种是 RPC(Remote Procedure Call)机制,典型应用是 Google 开源的 gRPC 框架,它基于 HTTP/2 协议,使不同服务间的进程可以像调用本地方法一样调用远程方法。

很多语言都支持这两种机制的实现,不同语言编写的服务都可以实现跨语言的进程间通信。在通信模式上有同步和异步之分,在同步模式下,服务间调用需要被调用方即时响应,在高并发场景下会出现阻塞;在异步模式下,服务间通过消息组件实现间接通信,可有效避免阻塞,同时还支持一对多的通信实现,常用的消息组件有 RabbitMQ、Kafka。

7) 支撑平台

碎片化是微服务的主要特征,因而微服务及微服务架构的运维变得更加复杂。容器化技术以进程级别虚拟化使每个微服务运行在传统物理机上,基于容器的管理系统 Kubernetes 为微服务提供了自动化的管理解决方案。Kubernetes 提供了包括自动化部署、运维、监控、负载均衡、灰度访问等功能,有效解决了碎片化微服务的运维管理问题。

4、微服务的进化

微服务架构技术仍在不断创新,人们围绕微服务不断提出不同的部署和使用方式,使得微服务架构技术不断进化。

1) 服务网格

服务网格(Service Mesh)是一种微服务架构形式,它将微服务独立运行时所依赖的服务组件功能与业务进程分离,使其作为一种可配置的基础设施层存在,每个微服务都包含一个基础设施,并在微服务间为业务进程提供快速、可靠、安全的通信保障。

被分离的基础设施叫作 Sidecar,它实现了服务发现、负载均衡、链路跟踪、访问日志、身份验证、授权及容错保护等功能,使业务进程只关注于具体业务的实现即可。服务网格起源于开源项目 Linkerd,并因 Google 联合 IBM、Lyft 发起的 Istio 项目得到广泛推广。

Istio 是基于 Kubernetes 容器管理框架实现的,并与 Kubernetes 系统实现了紧密的结合,它使用了 Kubernetes 的服务名及服务发现机制。Istio的Sidecar 可实现自动注入 Pod,并使集群内服务间的通信完全可被 Istio 监控。
  • Istio 分为控制面板(Control Plane)和数据面板(Data Plane)。
  • 控制面板负责实现与用户间的交互,实现监控数据的展示和数据面板相关配置的修改及存储。
  • 数据面板由每个微服务的基础设施(Sidecar)组成,其负责与控制面板间的通信及具体微服务进程间的通信基础功能的实现,Istio 的 Sidecar 是通过 Envoy 实现的。
  • 在 Kubernetes 中部署 Istio 后,Service 间的通信将不再通过 Kube-proxy,而是被 Istio 通过 iptables 规则转由 Sidecar 接管。

服务网格将 Spring Cloud 微服务架构中诸多组件通过基础设施层利用 Kubernetes 系统的特点注入微服务每个节点的 Pod 中,该方式对业务代码无侵入性,使开发工程师可以更专注于业务功能的实现,极大地减轻了进行软件开发的工作量,提高了软件生产效率。

2) 无服务器化

无服务器化(Serverless)并不代表没有服务器,服务器作为底层资源仍是软件运行的基础,它并不是不需要服务器,而是共享服务器资源。每个用户只需要考虑自己业务应用所需要的计算资源,而不需要关心其运行在什么样的服务器上。无服务器化是公有云产品的一个延伸,它极大地改变了程序设计的方法,对于非无服务器化下的程序开发,开发工程师需要对实现的业务代码加载诸多基础函数、进行打包编译和部署发布等一系列的操作。

无服务器化则使开发工程师只需考虑具体代码的实现,甚至可以仅提供一段函数代码,就可以由无服务器化云平台完成一系列部署、发布、运行等操作。开源无服务器化应用 Kubeless 是基于 Kubernetes 系统实现的,它支持 Python、Node.js、Ruby、PHP、Go、.NET 等语言的运行时(runtime),也支持自定义运行时的方法。

当用户提交一段函数代码或文件后,它会将这段函数与其依赖的运行时封装成可运行的服务,并以 Pod 的形式运行在 Kubernetes 集群中,调用方只需要通过 Kubernetes 提供的 Service 及 Ingress 提供的端口,使用基于 HTTP 的 REST 方式即可实现相应函数方法的调用。

无服务器化架构方式更细粒度地拆解了微服务,它使每个函数都可成为一个微服务的最小功能单元,极大减少了开发工程师所需考虑的非业务类额外因素,更包括代码可复用的公共组件等,使开发工程师们更专注于业务功能的实现,可以更快速地完成开发任务。

3) 持续进化

微服务概念自出现以来,大家一直在思考什么是“微”,就是微服务到底有多小、如何对现有的单体应用进行拆分。这个问题似乎很复杂,也让初识微服务的人对其望而却步,但无论是 Spring Cloud 架构、服务网格还是无服务器化都是将软件生产过程中可被重用的部分与业务代码分离,其本质上仍是结构化程序设计思想的延续,就是将复杂任务按照功能进行拆分,逐步细化并通过模块化的方式提高代码的可重用性,可将这类微服务架构统一称为结构化微服务架构。

在我们的认知中,我们周围所有客观存在的都是物质,每个物质都有它的物理属性和化学属性,分子、原子、离子是构成物质最基本的微粒。在自然界,物质的种类形态万千,物质的性质多种多样,但它们都有其特性,那就是客观存在,并能够被观测。我们可以将微服务架构中的微服务看作一个物质对象,微服务的名称、分类、接口地址、参数说明被定义为它的物理属性,微服务的接口被传递不同参数时产生的不同返回结果被定义为它的化学属性。

对象类是组成微服务物质的分子,具有网络服务接口能力的一个或多个对象类构成一个微服务。这是以面向对象的思想构建微服务架构,多个对象类达到一定的规模就变成了单体应用,多个微服务之间被按照微服务架构的规则自动注册、彼此发现、共享数据、进行统一路由管理等则构成了更复杂的服务。

微服务在我们的现实世界里还需要不断进化,它已经变成客观存在,但以面向对象微服务架构的思想来看,它还不具备可被观测的特性,每个微服务的物理及化学属性应该形成一种标准和规范,可以在一定的授权范围内被用户观测和使用。例如 REST 风格或 gRPC 都是微服务化学反应的一种进程通信机制,无论使用哪一种,都应是物理属性中被声明的一部分,可以被外部用户直接观测。

人工智能技术已渗透到我们每个人的生活之中,未来计算机科学的各种应用都将以人工智能技术的方式体现。可被观测是微服务的一种基本特性,能够主动交流才是智能的体现,在具有智能特征的微服务架构体系中,每个微服务都应该可以智能地告诉服务中心:我是谁、我能做什么、如何和我交流并产生化学反应以及我的进化史。

以公司为实体范围的内部用户将共享每个微服务提供的功能,用户通过服务中心检索每个分类的微服务,并按照自己的需求组装更复杂的功能。当网络中不存在符合功能的微服务时,工程师们可以根据需求添加新的微服务或对相似的微服务进行升级。服务中心管理着每个微服务的版本,并根据智能算法和微服务提供的测试声明确保其化学属性的可用性。

每个微服务均以对象类为最小粒度进行构建,当功能扩展的版本升级后,被智能中心扫描发现所包含的对象类达到技术体系约定的数量时,便会被要求拆分为多个独立的微服务。由于微服务的体量足够小、更加便于阅读,所以每个工程师将不再受传统部门或项目组的约束,其可自由地添加或更改自己所需要的微服务版本,包括更换为自己熟悉的编程语言。每个微服务接口名称将像物质分类一样被社会标准统一制定,即便开发人员遇到跨领域的开发需求,也只需在服务中心检索通用类目获得相应解释和定义,并按照约定的名称定义接口。

总之,自然界中的物质形态万千,同样,微服务应用的功能也是无穷无尽的,所以按照应用的功能进行拆分是无法找到固定拆分方法的,只要以对象类为最小维度构建,并确保其有物理和化学属性的特征,就可以构建一个微服务。笔者认为面向对象的微服务架构将是微服务进化的方向,微服务的粒度也只应与包含对象类的数量有关。