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