原文首发于『程序员精进』博客,原文链接:『Java微服务实践』微服务导读。
微服务是一种软件架构风格,它是以专注于单一责任与功能的小型功能区块为基础,利用模组化的方式组合出复杂的大型应用程序,各功能区块使用与语言无关的 API(例如 REST)集相互通讯,且每个服务可以被单独部署,以上便是Wikipedia上对于“微服务”的定义。从微服务的定义上我们可能会看到一些较为熟悉的定义,也许你在工作中应用了SOA(Service-oriented architecture,面向服务的架构设计),那么你可能已经使用了REST API做为服务间的通信标准;也许公司已经实行了DevOps实践,那么你可能已经接触到了持续集成或持续部署;如果你们团队是实践了敏捷开发,那么你可能已经实践了微服务建议的组织架构调整;那这样看来,是否“微服务”概念的提出只是新瓶装旧酒呢?另外,从2014年起,“微服务”的概念越来越火,在技术会议、各大公司分享的案例中,“微服务”架构设计风格被提到的次数也愈发多起来,在本文集将进行微服务的概念解读去理解微服务架构设计风格,同时会以实践的方式来介绍Java领域常用微服务框架的使用和部署,如Spring Boot、Dropwizard、NetflixOSS、Docker、Kubernetes等,以及在规模化微服务后需要进行怎样的调整才能够避免微服务在高并发下的陷阱。
微服务简介
在介绍微服务之前,我们简单回顾下微服务的由来,据“微服务”提出者之一的James Lewis回忆道,“在一次架构师协会的会议中,他们其中一人提出了关于如何将系统构建得‘可替换’,而不仅是‘可维护’,经过讨论后提出了‘micro app’的术语,而后演变为‘microservice’”。而后James Lewis和Martin Fowler关于微服务给出了他们的定义,如下图。
从他们二位给出的微服务定义中,我们不难看出有几个被标注的重点:
- suit of small services:由一系列小服务组成,没错,“微”即是小;
- running in its own process:每个服务运行于自己的独立进程;
- built around business capabilities:围绕着业务功能进行建模;
- independently deployable:每个服务可进行独立部署;
- bare minimum of centralized management:最小化的中心化管理。
基于二位对于“微服务”的定义,我们可以知晓微服务架构设计风格被提出之初,核心概念原则是为构建大型系统的专注于可替换的系统架构设计风格,接下来我们来根据“图1-1 示例微服务应用示意图”来理解微服务。
我们可以看到微服务应用与我们之前看到的其它架构设计风格应用有些类似但不完全一样,微服务应用有着一些特征:
- 每个微服务仅对单个业务负责,且为该业务的功能负责;
- 每个微服务可以进行独立部署,即不需要依赖其它微服务及其相关资源,如数据库、内存缓存系统等;
- 轻量级的通信协议,例如REST、STOMP、AMQP等;
- 服务的可替代性,代表着每个微服务原则上都可以使用不同的语言、框架进行技术实现,且更换实现后的微服务对于整个业务系统不会造成影响;
- 每个微服务拥有单独的数据存储;
- 每个微服务由小团队维护,服务以业务来进行拆分后,每个微服务的维护工作将有人数不多的小团队进行维护.
由于微服务架构设计风格产生的背景便是为了大型系统而生,将复杂的大型业务系统拆分至各个为单独业务所负责的系统,这便是“微”的由来,由此来降低整个业务系统的研发维护复杂程度;独立的数据存储、轻量级的通信协议、可单独部署的特性是因为微服务是“为可替代性来设计”的架构设计风格,我们假设业务系统会故障、会崩溃,如何以最小成本进行替换和恢复;由于微服务与DevOps的高度结合,具备天生云应用的特征,要完全践行微服务架构设计风格的话,以小团队来进行维护是必然的选择。
如何使用Java实现微服务?
我们可以先看下一个常见的微服务架构图。
从上图我们可以看到,一个完整的微服务架构包含:API网关、服务注册与发现、消息系统、配置管理系统、安全及访问控制、监控系统、日志系统等,另外REST API或AMQP等轻量级通信协议是连接其各个微服务的通道,下面先介绍下以上这些系统组件常见的解决方案,在后面的文章后会有更为详细的使用说明。
- 微服务框架,作为快速构建微服务的基础,现在Java领域常用的微服务框架为 Spring Boot、Dropwizard、WildFly Swarm等;
- 服务注册与发现:Consul、Eureka、Zookeeper等;
- API网关服务:Zuul、Nginx、AWS API Gateway、Kong等;
- 消息系统:Kafka、RabbitMQ、ActiveMQ等;
- 日志系统:Logstash、Kibana、Flume、ElasticSearch等;
- 部署与容器化:Docker、Kubernetes等;
在后面的文章中,会陆续介绍使用Java语言和常用微服务工具与框架来进行微服务的实践。
单体应用
在我们开始谈论“Java与微服务”之前,我们先简单回顾下Java的软件系统架构史,通常我们将微服务架构设计与单体(直译于英文单词 Monolithic)架构设计风格进行比较,所以在此先介绍下单体应用:一个单体应用即是一个单独的系统,通常在被集成在一个WAR包里(如 图2-1 示)。一个完整的企业应用主要分为以下三个部分:客户端(考虑到HTML、Javascript需要运行在浏览器上,也一并在此称为客户端)、数据库管理系统、服务端程序。服务端程序将负责处理HTTP请求、执行业务逻辑代码、从数据库获取数据或更新数据、选取渲染HTML页面并返回给客户端(浏览器或移动App)。这里的服务端程序便是一个典型的单体应用,该系统的任何改动需要生效的话,都需要对服务端程序进行新版本的构建和部署(一般情况下为WAR包)。
入上图所示,去构建一个单体系统的常用方法是一个单体服务器。处理请求的所有业务逻辑代码运行在一个单独的进程里面,通过类、方法和包名来进行拆分。小心地在开发电脑上运行并测试,然后通过部署流程确保变更修改被测试过后部署到生产环境,通过在负载均衡器后面部署多个实例来实现水平扩展。最开始单体应用也是很好用,我们更加关注于代码的模块化设计、包的拆分等,但是后来大家逐渐意识到它的一些问题(特别是在诸多应用开始部署于云端后):变更生效不够灵活——改变应用的一小部分时,需要将整个单体应用进行重新编译并部署;时间一长通常很难保证将应用保持良好的模块化结构,也使得对于一个模块的修改大概会对其它模块产生影响;在进行扩展时需要对整个应用进行部署,实际上需要扩展的可能只是整个应用里面的部分模块,相对于仅部署部分模块而言耗费了更多的资源。这些问题使得我们转向了微服务架构设计风格:应用由一系列的服务进行构建,每个服务都可以独立部署和扩展,且每个服务仅负责它该负责的业务功能,甚至服务间的实现语言可以并不相同,这些服务可以有多个不同的团队进行管理。其实熟悉Unix哲学(KISS:Keep It Simple and Stupid)的同学应该会感觉微服务没有什么新奇的,确实微服务架构设计风格的核心理念并非多么新奇或富有创造性,而是由Unix KISS演进而来。
我们不难看出单体应用的缺点有这些:
- 部署不够灵活:由于服务端程序代码都在一起,编译构建时间长,任何微小改动都需要进行整个工程的编译构建,然后进行完整发布;
- 代码维护难:工程里面代码耦合在一起,团队成员维护成本高,新人接手难度高;
- 开发效率低:由于代码都在一个工程里,多人并行开发时候,解决冲突花费了不必要的时间,代码如果分拆到多个工程通过jar引入会产生jar包版本问题;
- 稳定性不高:系统中存在些许小问题,就可能造成整个应用无法正常使用或对整体性能影响巨大;
- 扩展性不够:对于水平扩展,要进行完整的单体应用部署才可解决,面临资源浪费,在现在云计算时代,会产生无法在云端进行水平扩展的问题,并且单体应用没有做细致拆分的情况下,可能产生并发性能不足的问题。
在现在云计算时代,越来越多公司的应用系统部署于云上,其中有内部私有云、公有云(如Amazon Web Services、Google Cloud Platform、Microsoft Azure、阿里云、腾讯云等)以及混合云,单体应用在规模化部署上的弊端更加凸显,微服务架构设计风格能够良好的适配云计算时代的部署开发需求。
Java与微服务
本文集接下来会介绍Java在微服务领域常用的一些微服务框架的使用实践,如Spring Boot、Dropwizard和WildFly Swarm以及微服务工程的测试及部署等。
开发环境准备
我们将使用Maven进行Java项目工程管理,使用Git进行源代码管理,那么以下便是进行Java微服务开发的需安装软件列表,确保将相应的开发环境准备好(PS:在本文的最后会有附录,主要是介绍各个工具的安装过程)。
- Oracle JDK8
- Apache Maven 3.3+
- IDE:Eclipse for Java Developers 或 Spring Tool Suite
- SCM:Git 或 SourceTree
在此文集中我将使用MacOS做为桌面环境,会有些使用终端进行的命令行操作,所以大家准备Linux或MacOS做为桌面环境。在后面的文章中,会介绍使用Spring Boot作为微服务框架进行微服务开发,为了方便使用Spring生态的各类框架和工具,因此我们还需要准备相应的Spring Boot工具环境。
在IDE工具的选择上可以根据自己的个人爱好来,如:
- Netbeans
- IntelliJ IDEA
在『微服务部署』一文中,我们还将学习和了解微服务的部署,因此我们要进行些部署工具和环境的准备:
- Docker
- Kubernetes CLI
附录:工具安装
以下的安装教程以MacOS作为示例环境,对于Linux用户来说应该差不多,对于Windows来说就不太一样了。
Spring Boot CLI 安装
Spring Boot CLI 2.0.3安装教程如下,安装过程来源于官方网站,大家想了解详情,可以访问。https://docs.spring.io/spring-boot/docs/current/reference/html/getting-started-installing-spring-boot.html#getting-started-installing-the-cli
建议使用SDKMAN!进行安装。
手动安装
从Spring仓库下载Spring CLI二进制文件,以下两个压缩包选择一个即可:
下载完压缩包解压后,文件夹里面会有INSTALL.txt
文件,根据安装指引文件进行安装即可。
使用SDKMAN!安装
安装SDKMAN!
安装SDKMAN!在Unix-Like的系统上十分简单,在Mac OSX, Linux, Cygwin, Solaris和FreeBSD系统上可以使用终端进行安装,现在SDKMAN!安装脚本现在支持Bash和ZSH。
$ curl -s "https://get.sdkman.io" | bash
当提示安全完成后,在终端中输入以下命令,将SDKMAN!添加到环境变量中。
$ source "$HOME/.sdkman/bin/sdkman-init.sh"
然后便可以执行sdk命令,进行版本查看,版本查看返回版本号成功就代表SDKMAN!安装成功了。
$ sdk version
SDKMAN 5.5.2+183
安装Spring Boot CLI
$ sdk install springboot
$ spring --version
Spring Boot v2.0.3.RELEASE