微服务架构是当前很热门的一个概念,是技术发展的必然结果。微服务架构也不是一个缥缈、空洞的术语,它的核心理念与架构原则是实实在在的,虽然微服务架构没有公认的技术标准和规范草案,但业界已经有一些很有影响力的开源微服务架构平台,架构师可以根据公司的技术实力并结合项目的特点来选择某个合适的微服务架构平台,稳妥地实施项目的微服务化改造或开发进程。
微服务架构(Microservice Architecture) 是近两年来最流行的架构术语之一,大名鼎鼎的Martin Flower曾这样描述它:
“微服务”只不过是满大街充斥的软件架构中的一个新名词而已。尽管我们非常鄙视这样的名词,但其描述的软件风格越来越吸引我们的注意。在过去几年里,我们发现越来越多的项目开始使用这种风格,以至于我们的同事在构建企业级应用时,理所当然地认为这是一种默认开发形式。然而,很不幸,微服务风格是什么,应该怎么开发,关于这样的理论描述却很难找到。
在互联网时代,在极端情况下每天都有新需求要开发上线。随着代码量及团队成员的增加,传统单体式架构的弊端日益凸显,严重制约了业务的快速创新和敏捷交付,与互联网所追求的“唯快不破”的目标越来越远。这就是微服务架构兴起的时代大背景。
微服务架构兴起的原因
为什么微服务架构会快速流行?为什么越来越多的人认为微服务架构是一种默认的开发形式?
为了弄明白上面两个问题,我们需要先弄明白另外两个基本问题,即我们通常讲的单体架构是怎样一种架构? 微服务架构又是怎样一种架构?下面这张图显示了两者间的区别。
传统的应用架构又被称为单体架构(Monolithic), 表现为业务系统的各个模块是紧耦合的关系,各模块运行在一个进程中,每次升级系统时基本上都要重启整个应用进程,如果某个模块有问题,则可能导致整个系统无法正常启动。微服务架构则是将业务系统中的不同模块以微服务的方式进行拆分,使每个微服务都变成-一个独立的Project,独立编译并且部署为一一个独立的进程,每个微服务都可以被部署为多个独立的进程对外提供服务,对外的接口方式通常是REST或者RPC,不同的微服务进程也可以被部署到多个服务器上。
我们通过对比就会发现,微服务架构通过将-一个庞大的单体进程分解为相互独立的多个微小进程,以分布式的思想巧妙解决了传统单体架构在互联网时代遭遇的各种问题。所以微服务架构这种新的理念被大家快速接受并且迅速流行,是有深刻原因的。
为了解决传统单体架构面临的挑战,软件架构先后演进出了SOA架构、RPC架构、分布式服务架构,最后演进为微服务架构。但我们需要牢记一点:软件开发从来不存在银弹,因此微服务化架构也不是银弹,它更多的是一种架构思想与开发模式的改变,而在具体实施过程中还存在大量的技术问题及团队问题需要妥善解决。
微服务架构实施过程中所面临的最大技术问题之一就是开 发运维过程中的自动化。假如我们要把原来某个中等规模的系统架构改造为微服务架构,则最少也能拆分出十几个微服务,于是这么多微服务进程的编译、部署、调测及升级就演化成-一个浩大的工程了,如果没有自动化的手段,则微服务化是无法推动的。说到自动化,就不得不提到容器技术,它是促进微服务架构发展的重要动力,也是微服务架构得以快速流行的重要原因。
不得不提的容器技术
容器技术其实很早就被一些互联网公司广泛使用了,早在Docker兴起前的十几年内,Google就一直采用容 器技术支撑着世界上最大的分布式集群,只是一直对外保守秘密,直到祭出微服务架构利器Kubernets。Google 之所以开源该技术,是因为容器技术已经被业界公认为IT界最重要的平台级技术,如果不能抢占先机和掌握话语权,就会逐步失去技术领先性所带来的市场份额。
我们先来回顾Docker的发展历史。Docker 在2014年才发布1.0 版本,成为2014年的热门技术之一。从2004年年初的B轮融资到该年年末的DockerCon欧洲大会,Docker 在这一年里顺风顺水,就连微软、Google、AWS也对其青睐有加,彼时,微服务架构、云计算、DevOps等技术理念如日中天,Docker恰恰完美地从技术上驱动了这些概念的落地。2015 年,CoreOS发布了自己的容器引擎Rocket,引发容器技术分裂与统-一的大争论, 随后在Linux基金会的干预下,Docker公司与CoreOS公司握手言和,成立了0CI (Open Container Initiative)标准委员会,它类似于当年Java的JCP组织,参与者包括Google、RedHat 等巨头,OCI 组织负责制定容器技术标准规范。2016年发布的Docker 1.11成为第一一个符合OCI标准的容器引擎。从2014年到2016年,DockerHub 的下载量从1亿增加到了60 亿! 2017年,Docker 公司将Docker 的版本区分为企业版和社区版,开始面向企业收费,随后又将Docker开源项目的代码迁移到新的Moby项目上,Moby项目被推给社区维护。Docker公司则提供两个产品,其中Docker-ce是基于Moby项目的免费的容器产品,Docker-ee 是它的商业产品。我们知道,在软件开发过程中有很多环节是靠人工的,比如搭建环境、发布安装包(发布安装包到某个FTP服务器上或者以U盘方式复制)、部署应用、升级系统等,这些过程都比较耗时耗力,很难保证任务的质量与完成时间。在分布式系统中,一旦集群上了规模,上述人工操作就 极易因为大量重复性的劳动导致各种难以排查的错误。Docker公司敏锐地察觉到了传统软件开发中的上述痛点,以创新性的标准化镜像(Docker Image)打包发布应用技术为突破口,成功定义了“软件生命周期中的标准化与自动化”的新标准。下面这张图给出了Docker的标准化镜像的示意图。
Docker镜像是一个包含了目标程序所有依赖文件的“All in one"的分层压缩包,你可以认为它是一个没有Linux 内核但有Linux 文件系统和基础命令的-一个最精简版的虚机,在这个虚机里包括了已经安装和配置好的目标应用二进制代码,以及运行目标程序中的所有其他依赖包,比如一个运行在Torncat中的完整应用的Docker镜像组成如下。
启动镜像的过程就是启动打包制作镜像时指定的目标程序,比如对于Tomcat来说,就是运行tomcat.sh命令。此外,由于镜像本身已经固化了安装过程及配置参数,所以通过Docker镜像创建和启动一个容器就变成了一个非常简单并且不会出错的命令:
docker. run xxximage
而运行期间需要指定的参数可以通过环境变量及启动命令的参数等方式传递到容器中,不同的容器之间相互隔离,可以在一个主机上同时启动不同镜像版本的容器并测试信息。更进一步地,Docker 将制作镜像(build image)、创建容器,以及启动、停止、挂起、恢复、销毁容器的所有功能都做成了RESTAPI,于是我们可以通过编程来实现自动化控制,后面提到的Kubernetes就采用了Docker的API实现了全自动的微服务架构平台。
打包好的Docker镜像是否可以像源码一样进行版本管理、集中托管并且被全球任意联网的机器下载运行呢? Docker公司的第二个创意就模仿了GitHub 的做法,创建了全球唯一的开放性Docker仓库一Docker Hub,任何组织和个人都可以注册账号,并且分享自已打包的Docker镜像,现在你所能想到的任何中间件或基础应用几乎都在Docker Hub.上存在镜像,比如下面这行命令就自动从DockerHub拉下来-一个MySQL镜像,并且在本机上启动了一个MySQL服务器,可以让远程机器访问。DockerHub的存在大大加速了Docker技术的普及和发展。
docker run -it -e MYSQL_ ROOT PASSWORD=123456
mysql/mysql-server
私有的镜像仓库被称为Docker Registry,通常每个使用Docker 的公司都需要自己建立一个私有的Docker Regitry,存放从Docker Hub拉取的标准基础镜像,以及基于这些基础镜像而打包的私有镜像。下图给出了Docker 镜像打包、运行过程中与Docker Registry之间的交互过程。
综上所述,我们通过Docker技术可以很容易地将软件开发从源码编译、镜像打包、测试环境部署、版本发布、系统升级到生产环境发布等生命周期中的所有重要环节自动化,这是加速微服务架构实施的重要技术保障手段,下图是这个过程的一个简单示意图。
如何全面理解微服务架构
微服务架构是从SOA架构、RPC架构、分布式服务架构演变而来的,它也具有以下特点。
首先,微服务架构是一个分布式系统架构。也就是说,分布式系统设计的原则、经验,以及常用的分布式基础设施和中间件依然是微服务架构中的重要组成部分,如果抛开分布式架构中的这些技术,只是空谈微服务架构,则好像空中楼阁。
其次,与SOA架构- -样, 微服务架构与开发语言无关,它并没有公认的技术标准规范与实施方案,更多地体现了一种被 普遍接受的新的设计理念和指导思想,归纳下来有以下几点。
●轻量级的服务: 每个服务实例都只提供密切相关的一种或几种服务,粒度小、轻量级,便于微团队快速开发、部署、测试与升级。
●松耦合的系统:微服务之间的调用也是客户端的一种调用方式,仅限于接口层的耦合,避免了服务实现层的深耦合,因此服务之间的依赖性被降到最低,系统的整体稳定性与平衡升级(滚动升级)能力得到切实保障。
●平滑扩容能力:由于在微服务架构平台中原生地提供了某种微服务负载均衡机制,因此对于无状态的微服务,可以通过独立部署多个服务进程实例来提升整体的吞吐量。由于每个微服务都可以单独扩容,因此微服务架构具有很强的运行时的性能调优能力。
●积木式的系统:每个微服务通常都被设计为复杂业务流程中一个最小粒度的逻辑单元(积木),某个完整的业务流程就是合理编排(搭积木)这些微服务而形成的工作流,升级或者重新开发一一个新业务流程变成了简单的积木游戏,而随着微服务越来越多,业务单元(微服务)的复用价值越来越大,因此新业务快速上线的需求变成了一个可准确评估和预测的计划任务。
最后,微服务架构也有某些事实上公认的框架与工具,目前最经典的有以下三个微服务架构开源平台。
上述这三个经典微服务架构平台能提供完备的微服务架构与管理工具,在技术上各有千秋, 从总体来看,Google 出品的Kubermetes是很优秀的微服务架构,也是本章要重点分析和讲解的微服务架构。
接下来,我们一起看看在实施--个微服务架构项目的过程中可能遇到的问题及应对策略。
首先,架构师需要对项目组的全体成员进行培训,让大家明白微服务架构的思想和优点,培训的一个重要目标是要让大家明白,微服务架构在实施过程中会给项目组带来很明显的技能升级需求,不管是对于开发人员还是对于运维测试人员来说,这都是一个难 得的新技术学习与自我技能提升的好机会,希望大家顶住压力完成项目。
其次,要正确选择一个合适的微服务架构平台而不是自己研发。这种大型基础平台的研发成本很高、开发周期长而且平台可持续升级的可能性较低,因此目前很少有公司会自己进行研发,即使是RedHat这样有实力、有经验的开源软件服务型公司,也放弃了自己的微服务架构,转而应用Kubernetes。 那么,如何选择适合自己公司和项目组的微服务架构平台呢?以本节提到的三个典型的微服务架构平台为例,可参考如下条件进行选择。
●如果整个团队对 容器技术没有什么经验,则排除Kubermnetes,否则优先选择它。
●如果系统的性能要求很高,同时很多高频流程中涉及大量微服务的调用,以及微服务之间也存在大量调用,则先考虑以RPC二进制方式通信的微服务平台,优先考虑Ice,其次是Kubernetes,最后是Spring Cloud。
●如果系统中更多的是自己内部开发的各种服务之间的远程调用,很少使用中间件,只需要高性能的通信及水平扩展能力,则Ice可能是最佳选择,其次是Spring Cloud, 最后才是Kubernetes。因为Kubernetes 并没有提供-一个RPC架构,所以在这种情况下,反而增加了系统的复杂性。
●如果项目是用多个语言协同开发的,则在这种情况下,可以优先选择Kubernetes架构与Ice。
再者,在微服务架构项目的实施过程中经常需要考虑如下工作。
(1)引入自动化工具与集中运维管理工具。自动化工具被用于程序编译打包、自动化部署和升级等工作过程中。在集中化的运维监控工具方面主要包括日志收集与查询展示系统,用于收集分布在各个节点上的系统日志、应用日志,以及资源监控与故障系统,用于展示资源使用状态与应用告警。
(2)研究、测评大量相关开源产品(与工具)并引入微服务架构中。微服务架构本质上是一种分布式架构,所以之前在单体架构开发中所写的一些通用代码是无法被应用到微服务架构系统中的,比如最常见的配置模块、定时任务、同步逻辑等。此外,对于很多中间件来说,原先可能只用了单节点的方式,而在微服务架构下往往要切换成集群模式,在这种情况下也需要对这些中间件进行更为深入的研究测试,甚至可能会因此转向其他类似的中间件。
(3)团队的重构。在微服务模式下,整个系统从架构层来看基本只分为展现层与微服务层。
考虑到微服务在整个系统中的重要性,建议团队中的骨干技术人员成为微服务层的开发主力,大家作为-一个总体对所有微服务代码负责,--起设计每个微服务接口,一起评审所有微服务代码,而在具体的开发过程中,则可以将相似度较高的几个微服务模块交由同一个人研发。这种模式基本符合二八定律。
(4)高质量的文档。在微服务架构下,文档特别是每个微服务的接口文档的重要性越来越高,因为每个使用微服务的人都要清楚当前所要调用的微服务是哪个、应该调用哪个接口、参数有什么含义,以及返回值的意义。因此,我们需要一份详细 并且准确的微服务接口文档,还,要保持文档与代码同步更新。
接下来看看如何设计系统中的微服务。一开始, 我们其实并不很清楚要将哪些功能和服务设计成一个微服务, 以及一个微服务究竟应该包括多少个接口,每个接口应该如何设计。笔者的建议是先粗粒度地划分微服务,每个微服务包括比较多的接口以减少微服务的个数,这样可以减少开发、程序打包、测试及部署的工作量,有利于快速推进项目。
系统中的微服务按照调用客户端的不同,可以划分为流程控制类、接口类及基础核心类三种,如下图所示。
一般来讲,这三种不同的微服务的接口设计也有所不同。
流程控制类微服务主要面向UI调用,所以它的接口设计应该以页面展示的便利性为第一目标,即大部分情况下采用JSON或TEXT文本的方式传递参数与返回值,并且考虑在调用逻辑出错的情况下告诉客户端错误的代码与原因,于是这类微服务的返回值通常会是下面的结构体:
public class CallResult
int resultCode; //返回代码, 0为成功,其他为调用错误
String resultData;//调用结果, 通常为JSON的字符串
String errmsg; //调用错误的时候,展示给用户的可读错误信息
接口类微服务主要面向第三方系统,所以特别需要注意安全问题,因此在接口设计中必须有安全措施,比较常见的方案是在调用参数中增加Token,并考虑参数加密的问题,同时建议接口类微服务在实现过程中重视日志的输出问题,以方便接口联调,并方便在运行期间排查接口故障,在日志中应该记录入口参数、关键逻辑分支、返回结果等重要信息。
基础核心类微服务主要被UI及其他两种微服务所调用,在这类微服务的接口设计中主要考虑效率和调用的方便性。建议将其设计得与普通Java类的接口看起来一样,这样可以避免将很多复杂Bean对象作为参数及返回值时不但增加调用者的负担而且降低接口性能。
在微服务设计中,我们还需要考虑接口兼容性的问题,比如如下微服务接口设计:
public void doBusiness (paraml, param2, param3)
如果参数的个数存在增加的可能性,那么为了兼容旧版本,最好改为如下设计:
public class XXXBean
{
private String paraml;
private String param2;
private String param3;
private String param4;
}
public void doBusiness (XXXBean thebean)
这样一来, 对旧接口无需重新编译,只要升级XXXBean所在的JAR文件即可兼容旧版本。