Spring微服务实战

978-7-115-48118-4
作者: [美]约翰•卡内尔(John Carnell)
译者: 陈文辉
编辑: 杨海玲

图书目录:

详情

本书以一个实际项目为主线,介绍云、微服务等概念以及Spring Boot和Spring Cloud等诸多Spring项目,并介绍如何将该项目一步一步地从单体架构重构成微服务架构,最终将这个项目拆分成众多微服务,让它们运行在各自的Docker容器中,实现持续集成/持续部署,并最终自动部署到云环境(亚马逊云)中。

图书摘要

版权信息

书名:Spring微服务实战

ISBN:978-7-115-48118-4

本书由人民邮电出版社发行数字版。版权所有,侵权必究。

您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。

我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。

如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。

著    [美] 约翰·卡内尔(John Carnell)

译    陈文辉

审  校 张卫滨

责任编辑 杨海玲

人民邮电出版社出版发行  北京市丰台区成寿寺路11号

邮编 100164  电子邮件 315@ptpress.com.cn

网址 http://www.ptpress.com.cn

读者服务热线:(010)81055410

反盗版热线:(010)81055315


Original English language edition, entitled Spring Microservices in Action by John Carnell published by Manning Publications Co., 209 Bruce Park Avenue, Greenwich, CT 06830. Copyright © 2017 by Manning Publications Co.

Simplified Chinese-language edition copyright © 2018 by Posts & Telecom Press. All rights reserved.

本书中文简体字版由Manning Publications Co.授权人民邮电出版社独家出版。未经出版者书面许可,不得以任何方式复制或抄袭本书内容。

版权所有,侵权必究。


本书以一个名为EagleEye的项目为主线,介绍云、微服务等概念以及Spring Boot和Spring Cloud等诸多Spring项目,并介绍如何将EagleEye项目一步一步地从单体架构重构成微服务架构,进而将这个项目拆分成众多微服务,让它们运行在各自的Docker容器中,实现持续集成/持续部署,并最终自动部署到云环境(Amazon)中。针对在重构过程中遇到的各种微服务开发会面临的典型问题(包括开发、测试和运维等问题),本书介绍了解决这些问题的核心模式,以及在实战中如何选择特定Spring Cloud子项目或其他工具解决这些问题。

本书适合拥有构建分布式应用程序的经验、拥有Spring的知识背景以及对学习构建基于微服务的应用程序感兴趣的Java开发人员阅读。对于希望使用微服务构建基于云的应用程序,以及希望了解如何将基于微服务的应用部署到云上的开发人员,本书也具有很好的学习参考价值。

致我的兄弟Jason!即使在你处在最黑暗的时刻,你也向我展示了力量和尊严的真正含义。作为兄弟、丈夫和父亲,你是一个好榜样。


让我们把时间调回到2003年6月。那一年,承载着“传统J2EE寒冬之后的崭新起点”美好愿景的Spring项目开始立项,并以1.0版本进行推进。时光荏苒,从Spring Framework 1.0发展到现在的Spring Framework 5.0,Spring早已从当初Java企业级开发领域的挑战者、颠覆者,变成了标准的制定者,成为Java企业级开发事实上的标准开发框架。

经过十多年的发展,Spring家族现在已枝繁叶茂,涵盖J2EE开发、依赖维护、安全、批处理、统一数据库访问、大数据、消息处理、移动开发以及微服务等众多领域。在Spring家族的诸多项目里面,最耀眼的项目莫过于Spring Framework、Spring Boot和Spring Cloud。Spring Framework就像是Spring家族的树根,是Spring得以在Java开发领域屹立不倒的根本原因,它的目标就是帮助开发人员开发出好的系统;Spring Boot就像是树干,它的目标是简化新Spring应用的初始搭建以及开发过程,致力于在蓬勃发展的快速应用开发领域成为领导者;Spring Cloud就如同是Spring这棵参天大树在微服务开发领域所结出的硕果。

在近几年,微服务这一概念十分火热,因为它确实能解决传统的单体架构应用所带来的顽疾(如代码维护难、部署不灵活、稳定性不高、无法快速扩展),以至于涌现出了一批帮助实现微服务的工具。在它们之中,Spring Cloud无疑是最令人瞩目的,不仅是因为Spring在Java开发中的重要地位,更是因为它提供一整套微服务实施方案,包括服务发现、分布式配置、客户端负载均衡、服务容错保护、API网关、安全、事件驱动、分布式服务跟踪等工具。

本书对微服务的概念进行了详细的介绍,并介绍了微服务开发过程中遇到的典型问题,以及解决这些问题的核心模式,并介绍了在实战中如何选择特定Spring Cloud子项目解决这些问题。本书非常好地把握了理论和实践的平衡,正如本书作者所言,本书是“架构和工程学科之间良好的桥梁与中间地带”。相信读者阅读完本书之后,会掌握微服务的概念,明白如何在生产环境中实施微服务架构,学会在生产中运用Spring Cloud等工具,并将项目自动部署到云环境中。

我第一次接触Spring Cloud,是由于我所负责的一个项目需要从典型的单体应用架构重构成微服务架构,而当时部门主管选定的技术方案就是Spring Cloud。从那时起,我才真正开始深入了解Spring Cloud。当时,Spring Cloud算是比较新的技术,国内有关Spring Cloud和微服务方面的优秀技术书籍凤毛麟角,我只能选择参阅Spring的官方文档以及国外的一些技术博客。当时Manning出版社尚未出版的Spring Microservices in Action走入了我的视野。通读完这本书的早期预览版之后,我认为它是目前市面上将微服务和Spring Cloud结合介绍得最好的技术书籍,于是我便毛遂自荐,向人民邮电出版社的杨海玲编辑表达了希望成为这本书的中文译者的意愿。不久之后,她回复了我,请我担任这本书的译者,我欣然答应,从此开启了披星戴月的翻译日子。

虽然翻译本书花费了我大量的业余时间,但我也在这个过程中学到了许多。感谢杨海玲编辑和张卫滨老师在翻译过程中对我的指导与指正。同时,我想要感谢我的爱人在这个过程中对我的支持与奉献,还要感谢我那即将出生的孩子,你们是我坚持的动力来源。

限于时间和精力,也囿于我本人的知识积累,在翻译过程中难免犯错。如果读者发现本书翻译中存在哪些不足或纰漏之处,欢迎提出宝贵意见。读者可以通过memphychan@gmail.com联系我。希望本书能够对您有用!

陈文辉

2018年4月于东莞


具有讽刺意味的是,在写书的时候,所写的书的最后一部分往往是这本书的前言。这往往也是最棘手的部分。为什么?因为你必须向所有人解释为什么你对一个主题如此热情,以至于你最后花了一年半的时间来写一本关于这个主题的书。很难说清楚为什么有人会花这么多的时间在一本技术书上。人们很少会为名或为利撰写软件开发书籍。

我写本书的原因就是——我热爱编码。这是对我的一种召唤,也是一种创造性的活动,它类似于绘画或演奏乐器。软件开发领域之外的人很难理解这一点。我尤其喜欢构建分布式应用程序。对我来说,看到一个应用程序跨几十个(甚至数百个)服务器工作是一件令人惊奇的事情。这就像看着一个管弦乐队演奏一段音乐。虽然管弦乐队的最终作品很出色,但完成它往往需要大量的努力与练习。编写大规模分布式应用程序亦是如此。

自从25年前我进入软件开发领域以来,我就目睹了软件业与构建分布式应用程序的“正确”方式做斗争。我目睹过分布式服务标准(如CORBA)兴起与陨落。巨型公司试图推行大型的而且通常是专有的协议。有人记得微软公司的分布式组件对象模型(Distributed Component Object Model,DCOM)或甲骨文公司的J2EE企业Java Bean 2(EJB)吗?我目睹过技术公司和它们的追随者涌向沉重的基于XML的模式来构建面向服务的架构(SOA)。

在各种情况下,这些用于构建分布式系统的方法常常在它们自身的负担下崩溃。我并不是说这些技术无法用来构建一些非常强大的应用程序。它们陨落的真相是它们无法满足用户的需求。10 年前,智能手机刚刚被引入市场,云计算还处于起步阶段。另外,分布式应用程序开发的标准和技术对于普通开发人员来说太复杂了,以至于无法在实践中理解和使用。在软件开发行业,没有什么能像书面代码那样说真话。当标准妨碍到这一点时,标准很快就会被抛弃。

当我第一次听说构建应用程序的微服务方法时,我是有点儿怀疑的。“很好,另一种用于构建分布式应用的银弹方法。”我是这样想的。然而,随着我开始深入了解这些概念,我意识到微服务的简单性可以成为游戏规则的改变者。微服务架构的重点是构建使用简单协议(HTTP和JSON)进行通信的小型服务。仅此而已。开发人员可以使用几乎任何编程语言来编写一个微服务。在这种简单中蕴含着美。

然而,尽管构建单个微服务很容易,实施和扩展它却很困难。要让数百个小型的分布式组件协同工作,然后从它们构建一个弹性的应用程序是非常困难的。在分布式计算中,故障是无从逃避的现实,应用程序要处理好故障是非常困难的。套用我同事Chris Miller和Shawn Hagwood的话:“如果它没有偶尔崩溃,你就不是在构建。”

正是这些故障激励着我写这本书。我讨厌在不必要的时候从头开始构建东西。事实上,Java是大多数应用程序开发工作的通用语言,尤其是在企业中。对许多组织来说,Spring框架已成为大多数应用程序事实上的开发框架。我已经用Java做了近 20 年的应用程序开发(我还记得Dancing Duke applet),并且使用Spring近10年了。当我开始我的微服务之旅时,我很高兴看到Spring Cloud的出现。

Spring Cloud框架为许多微服务开发人员将会遇到的常见开发和运维问题提供开箱即用的解决方案。Spring Cloud可以让开发人员仅使用所需的部分,并最大限度地减少构建和部署生产就绪的Java微服务所需的工作量。通过使用其他来自Netflix、HashiCorp以及Apache基金会等公司和组织的久经考验的技术,Spring Cloud实现了这一点。

我一直认为自己是一名普通的开发人员,在一天结束的时候,需要按期完成任务。这就是我写这本书的原因。我想要一本可以在我的日常工作中使用的书。我想要一些直接简单的(希望如此)代码示例。我总是想要确保本书中的材料既可以作为单独的章使用也可以作为整体来使用。我希望读者会觉得本书很有用,希望读者会喜欢读它,就如同我喜欢写它一样。


本书由异步社区出品,社区(https://www.epubit.com/)为您提供相关资源和后续服务。

本书提供如下资源:

要获得以上配套资源,请在异步社区本书页面中点击,跳转到下载界面,按提示进行操作即可。注意:为保证购书读者的权益,该操作会给出相关提示,要求输入提取码进行验证。

作者和编辑尽最大努力来确保书中内容的准确性,但难免会存在疏漏。欢迎您将发现的问题反馈给我们,帮助我们提升图书的质量。

当您发现错误时,请登录异步社区,按书名搜索,进入本书页面,点击“提交勘误”,输入勘误信息,点击“提交”按钮即可。本书的作者和编辑会对您提交的勘误进行审核,确认并接受后,您将获赠异步社区的100积分。积分可用于在异步社区兑换优惠券、样书或奖品。

我们的联系邮箱是contact@epubit.com.cn。

如果您对本书有任何疑问或建议,请您发邮件给我们,并请在邮件标题中注明本书书名,以便我们更高效地做出反馈。

如果您有兴趣出版图书、录制教学视频,或者参与图书翻译、技术审校等工作,可以发邮件给我们;有意出版图书的作者也可以到异步社区在线提交投稿(直接访问www.epubit.com/ selfpublish/submission即可)。

如果您是学校、培训机构或企业,想批量购买本书或异步社区出版的其他图书,也可以发邮件给我们。

如果您在网上发现有针对异步社区出品图书的各种形式的盗版行为,包括对图书全部或部分内容的非授权传播,请您将怀疑有侵权行为的链接发邮件给我们。您的这一举动是对作者权益的保护,也是我们持续为您提供有价值的内容的动力之源。

“异步社区”是人民邮电出版社旗下IT专业图书社区,致力于出版精品IT技术图书和相关学习产品,为作译者提供优质出版服务。异步社区创办于2015年8月,提供大量精品IT技术图书和电子书,以及高品质技术文章和视频课程。更多详情请访问异步社区官网https://www.epubit.com。

“异步图书”是由异步社区编辑团队策划出版的精品IT专业图书的品牌,依托于人民邮电出版社近30年的计算机图书出版积累和专业编辑团队,相关图书在封面上印有异步图书的LOGO。异步图书的出版领域包括软件开发、大数据、AI、测试、前端、网络技术等。

异步社区

微信服务号


当我坐下来写下这些致谢时,我忍不住回想起2014年我第一次跑马拉松的情景。写一本书就如同跑马拉松。写这本书的提案和大纲很像训练过程,它会让你的想法成型,会把你的注意力集中在未来的事情上。是的,在这个过程接近尾声的时候,它可能会变得有点乏味和残酷。

开始写这本书的那一天就像是比赛日。你充满活力与激情地开始了马拉松比赛。你知道你在尝试做比以往所有事情都要重大的事情,这既令人兴奋又让人神经紧张。虽然这是你已经训练过的,但同一时间,在你的脑海中总会有一些怀疑的声音,说你完成不了你开始的事情。

我从跑步中学到了比赛不是一次一公里地完成的,相反,跑步是一只脚在另一只脚前面地跑。长跑是个人脚步的总和。当我的孩子们在为某件事而挣扎时,我笑着问他们:“你如何写一本书?那就是一次一词,一次一步。”他们通常会不以为然,但到最后,除了这条无可争辩的铁律之外就没有别的办法了。

然而,当你跑马拉松的时候,你可能是一名竞赛的参与者,但你永远不会孤军奋斗。在这个过程中,有整个团队在那里为你提供支持、时间和建议。撰写这本书的经历亦是如此。

我首先想感谢Manning出版社为我撰写这本书所给予的支持。策划编辑Greg Wild耐心地与我一起工作,帮助我精炼了本书中的核心概念,并引导我完成了整个提案过程。在此过程中,我的开发编辑Maria Michaels让我保持坦诚,并鞭策我成为一名更优秀的作者。我还要感谢我的技术编辑Raphael Villela和Joshua White,他们不断检查我的工作,并确保我编写的示例和代码的整体质量。我非常感谢这些人在整个项目中投入的时间、才华和承诺。我还要感谢在撰写和开发过程中对书稿提供反馈意见的审稿人:Aditya Kumar、Adrian M. Rossi、Ashwin Raj、Christian Bach、Edgar Knapp、Jared Duncan、Jiri Pik、John Guthrie、Mirko Bernardoni、Paul Balogh、Pierluigi Riti、Raju Myadam、Rambabu Posa、Sergey Evsikov和Vipul Gupta。

致我的儿子Christopher:你正成长为一个不可思议的年轻人。我迫不及待地想要到你真正点燃热情的那一天,因为这个世界上没有什么能阻止你实现你的目标。

致我的女儿Agatha:我愿付出我所有的财富来换取用仅仅10min的时间透过你的眼睛去看这个世界。这段经历会让我成为一名更好的作家,更重要的是,成为一个更好的人。你的智慧,你的观察力和创造力让我谦逊。

致我4岁的儿子Jack:小伙子,感谢你在我每次说“我现在不能玩,因为爸爸必须要投身于这本书的写作”的时候对我保持耐心。你总是逗我笑,让整个家庭变得完整。没有什么比我看到你是一个“开心果”并与家里的每个人一起玩耍更让我开心的了。

我和这本书的赛跑已经完成了。就像我的马拉松一样,我在写这本书时已倾尽全力。我非常感激Manning团队,以及早早地买下了这本书并给予我许多珍贵反馈的MEAP版读者。最后,我希望读者喜欢这本书,就像我喜欢写这本书一样。谢谢你!


本书是为工作中的Java/Spring开发人员编写的,他们需要实际的建议以及如何构建和实施基于微服务的应用程序的示例。写这本书的时候,我希望它基于与Spring Boot和Spring Cloud示例结合的核心微服务模式,这些示例演示了这些模式。因此,读者会发现几乎每一章都会讨论特定的微服务设计模式,以及使用Spring Boot和Spring Cloud实现的模式示例。

本书包含10章和2个附录。

本书每一章中的所有代码示例都可以在作者的GitHub存储库中找到,每一章都有自己的存储库。读者可以通过到每一章代码存储库的链接http://github.com/carnellj/spmia-overview找到概述页面。包含所有源代码的zip文件也可从Manning出版社的网站[1]获取。

本书中的所有代码使用Maven作为主要构建工具进行构建以运行在Java 8上。有关编译和运行代码示例所需的软件工具的完整详细信息,参见附录A。

我在写这本书时遵循的一个核心概念是,每章中的代码示例应该独立于其他章中的代码示例。因此,我们为某一章创建的每个服务将构建到相应的Docker镜像。当使用前几章的代码时,它包括在源代码和已构建的Docker镜像中。我们使用Docker compose和构建的Docker镜像来保证每章都具有可重现的运行时环境。

本书包含许多源代码的例子,它们有的在带编号的代码清单中,有的在普通的文本中。在这两种情况下,源代码都以等宽字体印刷,以将其与普通文本分开。有时,代码还会加粗,以突出显示与这一章前面的步骤相比有变化的代码,例如,将新功能添加到现有代码行时。

在很多情况下,原始的源代码已被重新调整了格式。我们添加了换行符和重新加工了缩进,以适应书的页面空间。在极少数情况下,甚至还不止如此,代码清单还包括行连续标记(➥)。此外,在文本中描述代码时,源代码中的注释通常会从代码清单中移除。许多代码清单附带了代码注解,突出重要的概念。

[1] 读者可登录异步社区(https://www.epubit.com),在本书页面免费下载。——编者注


约翰·卡内尔(John Carnell)在Genesys的PureCloud部门工作,担任Genesys的高级云工程师。他大部分时间都在使用AWS平台构建基于电话的微服务。他的日常工作主要围绕在设计和构建跨Java、Clojure和Go等多种技术平台的微服务。

他是一位高产的演讲者和作家。他经常在当地的用户群体发表演讲,并且是“The No Fluff Just Stuff Software Symposium”的常规发言人。在过去的20年里,他撰写、合著了许多基于Java的技术书籍,并担任了许多基于Java的技术书籍和行业刊物的技术审稿人。

他拥有马奎特大学(Marquette University)艺术学士学位和威斯康星大学奥什科什分校(University of Wisconsion Oshkosh)工商管理硕士(MBA)学位。

他是一位充满激情的技术专家,他不断探索新技术和编程语言。不演讲、写作或编码时,他与妻子Janet和3个孩子(Christopher、Agatha和Jack)生活在北卡罗来纳州的卡里。

在极少的空闲时间里,他喜欢跑步、与他的孩子嬉戏,并研究菲律宾武术。

读者可以通过john_carnell@yahoo.com与他取得联系。


本书封面插画的标题为《克罗地亚男人》。该插画取自克罗地亚斯普利特民族博物馆2008年出版的Balthasar Hacquet的Images and Descriptions of Southwestern and Eastern Wenda, Illyrians, and Slavs的最新重印版本。Hacquet(1739—1815)是一名奥地利医生及科学家,他花数年时间去研究奥匈帝国很多地区的植物、地质和人种,以及伊利里亚部落过去居住的(罗马帝国的)威尼托地区、尤里安阿尔卑斯山脉及西巴尔干等地区。Hacquet发表的很多论文和书籍中都有手绘插图。

Hacquet的出版物中丰富多样的插图生动地描绘了200年前阿尔卑斯东部和巴尔干西北地区的独特性和个体性。那时候相距几公里的两个村庄村民的衣着都迥然不同,当有社交活动或交易时,不同地区的人们很容易通过着装来辨别。从那之后着装的要求发生了改变,不同地区的多样性也逐渐消亡。现在很难说出不同大陆的居民有多大区别,例如,现在很难区分斯洛文尼亚的阿尔卑斯山地区那些美丽小镇或村庄或巴尔干沿海小镇的居民和欧洲其他地区的居民。

Manning出版社利用两个世纪前的服装来设计书籍封面,以此来赞颂计算机产业所具有的创造性、主动性和趣味性。正如本书封面的图片一样,这些图片也把我们带回到过去的生活中。


本章主要内容

作为软件开发者,我们一直处于一片混乱和不断变化的海洋之中,这已是软件开发领域中的一个常态。新技术与新方案的突然涌现会让我们受到强烈的冲击,使我们不得不重新评估应该如何为客户搭建和交付解决方案。使用微服务开发软件被许多组织迅速采纳就是应对这种冲击的一个例子。微服务是松耦合的分布式软件服务,这些服务执行少量的定义明确的任务。

本书主要介绍微服务架构,以及为什么应该考虑采用微服务架构来构建应用。我们将看到如何利用Java以及Spring Boot和Spring Cloud这两个Spring框架项目来构建微服务。Spring Boot和Spring Cloud为Java开发者提供了一条从开发传统的单体的Spring应用到开发可以部署在云端的微服务应用的迁移路径。

在微服务的概念逐步形成之前,绝大部分基于Web的应用都是使用单体架构的风格来进行构建的。在单体架构中,应用程序作为单个可部署的软件制品交付,所有的UI(用户接口)、业务、数据库访问逻辑都被打包在一个应用程序制品中并且部署在一个应用程序服务器上。

虽然应用程序可能是作为单个工作单元部署的,但大多数情况下,会有多个开发团队开发这个应用程序。每个开发团队负责应用程序的不同部分,并且他们经常用自己的功能部件来服务特定的客户。例如,我在一家大型的金融服务公司工作时,我们公司有一个内部定制的客户关系管理(CRM)应用,它涉及多个团队之间的合作,包括UI团队、客户主团队、数据仓库团队以及共同基金团队。图1-1展示了这个应用程序的基本架构。

图1-1 单体应用程序强迫开发团队人工同步他们的交付,因为他们的代码需要被作为一个整体单元进行构建、测试和部署

这里的问题在于,随着单体的CRM应用的规模和复杂度的增长,在该应用程序上进行开发的各个团队的沟通与合作成本没有减少。每当各个团队需要修改代码时,整个应用程序都需要重新构建、重新测试和重新部署。

微服务的概念最初是在2014年前后悄悄蔓延到软件开发社区的意识中,它是对在技术上和组织上扩大大型单体应用程序所面临的诸多挑战的直接回应。记住,微服务是一个小的、松耦合的分布式服务。微服务允许将一个大型的应用分解为具有严格职责定义的便于管理的组件。微服务通过将大型代码分解为小型的精确定义的部分,帮助解决大型代码库中传统的复杂问题。在思考微服务时,一个需要信奉的重要概念就是:分解和分离应用程序的功能,使它们完全彼此独立。如果以图1-1所示的CRM应用程序为例,将其分解为微服务,那么它看起来可能像图1-2所示的样子。

图1-2 使用微服务架构,CRM应用将会被分解成一系列完全彼此独立的微服务,让每个开发团队都能够按各自的步伐前进

由图1-2可以发现,每个功能团队完全拥有自己的服务代码和服务基础设施。他们可以彼此独立地去构建、部署和测试,因为他们的代码、源码控制仓库和基础设施(应用服务器和数据库)现在是完全独立于应用的其他部分的。

微服务架构具有以下特征。

我经常和同事开玩笑,说微服务是构建云应用程序的“诱人上瘾的毒药”。你开始构建微服务是因为它们能够为你的开发团队提供高度的灵活性和自治权,但你和你的团队很快就会发现,微服务的小而独立的特性使它们可以轻松地部署到云上。一旦服务运行在云中,它们小型化的特点使启动大量相同服务的实例变得很容易,应用程序瞬间变得更具可伸缩性,并且显而易见也会更有弹性。

在基于Java的应用程序构建中,Spring已经成为事实上的标准开发框架。Spring的核心是建立在依赖注入的概念上的。在普通的Java应用程序中,应用程序被分解成为类,其中每个类与应用程序中的其他类经常有明显的联系,这些联系是在代码中直接调用类的构造器,一旦代码被编译,这些联系点将无法修改。

这在大型项目中是有问题的,因为这些外部联系是脆弱的,并且进行修改可能会对其他下游代码造成多重影响。依赖注入框架(如Spring),允许用户通过约定(以及注解)将应用程序对象之间的关系外部化,而不是在对象内部彼此硬编码实例化代码,以便更轻松地管理大型Java项目。Spring在应用程序的不同的Java类之间充当一个中间人,管理着它们的依赖关系。Spring本质上就是让用户像玩乐高积木一样将自己的代码组装在一起。

Spring能够快速引入特性的特点推动了它的实际应用,使用J2EE技术栈开发应用的企业级Java开发人员迅速采用它作为一个轻量级的替代方案。J2EE栈虽然功能强大,但许多人认为它过于庞大,甚至许多特性从未被应用程序开发团队使用过。此外,J2EE应用程序强制用户使用成熟的(和沉重的)Java应用程序服务器来部署自己的应用程序。

Spring框架的迷人之处在于它能够与时俱进并进行自我改造——它已经向开发社区证明了这一点。Spring团队发现,许多开发团队正在从将应用程序的展现、业务和数据访问逻辑打包在一起并部署为单个制品的单体应用程序模型中迁移,正转向高度分布式的模型,服务能够被构建成可以轻松部署到云端的小型分布式服务。为了响应这种转变,Spring开发团队启动了两个项目,即Spring Boot和Spring Cloud。

Spring Boot是对Spring框架理念重新思考的结果。虽然Spring Boot包含了Spring的核心特性,但它剥离了Spring中的许多“企业”特性,而提供了一个基于Java的、面向REST[1]的微服务框架。只需一些简单的注解,Java开发者就能够快速构建一个可打包和部署的REST 微服务,这个微服务并不需要外部的应用容器。

注意


虽然本书会在第2章中更详细地介绍REST,但REST背后最为核心的概念是,服务应该使用HTTP动词(GET、POST、PUT和DELETE)来代表服务中的核心操作,并且使用轻量级的面向Web的数据序列化协议(如JSON)来从服务请求数据和从服务接收数据。

在构建基于云的应用时,微服务已经成为更常见的架构模式之一,因此Spring社区为开发者提供了Spring Cloud。Spring Cloud框架使实施和部署微服务到私有云或公有云变得更加简单。Spring Cloud在一个公共框架之下封装了多个流行的云管理微服务框架,并且让这些技术的使用和部署像为代码添加注解一样简便。本章随后将介绍Spring Cloud中的不同组件。

本书是关于使用Spring Boot和Spring Cloud构建基于微服务架构的应用程序的,这些应用程序可被部署到公司内运行的私有云或Amazon、Google或Pivotal等运行的公有云上。在本书中,我们将介绍一些实际的例子。

阅读完这本书,读者将具备构建和部署基于Spring Boot的微服务所需的知识,明白实施微服务的关键设计决策,了解服务配置管理、服务发现、消息传递、日志记录和跟踪以及安全性等如何结合在一起,以交付一个健壮的微服务环境,最后读者还会看到如何在私有云或公有云中部署微服务。

如果你已经仔细阅读了本书前面的内容,那么我假设你:

我写这本书出于两个原因。第一,我已经看过许多关于微服务概念方面的好书,但我并没有发现一本如何基于Java实现微服务的好书。虽然我总是认为自己是一个精通多门编程语言的人,但Java是我的核心开发语言,Spring是我构建一个新应用程序时要使用的开发框架。第一次发现Spring Boot和Spring Cloud,我便被其迷住了。当我构建运行在云上的基于微服务的应用程序时,Spring Boot和Spring Cloud极大地简化了我的开发生活。

第二,由于我在职业生涯中一直是架构师和工程师,很多次我都发现,我购买的技术书往往是两个极端,它们要么是概念性的,缺乏具体代码示例,要么是特定框架或者编程语言的机械概览。我想要的是这样一本书:它是架构和工程学科之间良好的桥梁与媒介。在这本书中,我想向读者介绍微服务的开发模式以及如何在实际应用程序开发中使用它们,然后使用Spring Boot和Spring Cloud来编写实际的、易于理解的代码示例,以此来支持这些模式。

让我们转移一下注意力,使用Spring Boot构建一个简单的微服务。

我一直以来都持有这样一个观点:如果一个软件开发框架通过了被我亲切地称为“卡内尔猴子测试”[2]的试验,我就认为它是经过深思熟虑和易于使用的。如果一只像我(作者)这样的“猴子”能够在10 min或者更少时间内弄明白一个框架,那么这个框架就通过了这个试验。这就是我第一次写Spring Boot服务示例的感觉。我希望读者也有同样的体验和快乐,所以,让我们花一点儿时间,看看如何使用Spring编写一个简单的“Hello World”REST服务。

在本节中,我们不会详细介绍大部分代码。这个例子的目标是让读者体会一下编写Spring Boot服务的感受。第2章中会深入更多的细节。

图1-3展示了这个服务将会做什么,以及Spring Boot微服务将会如何处理用户请求的一般流程。

图1-3 Spring Boot抽象出了常见的REST微服务任务(路由到业务逻辑、从URL中解析HTTP参数、JSON与对象相互映射),并让开发人员专注于服务的业务逻辑

这个例子并不详尽,甚至没有说明应该如何构建一个生产级别的微服务,但它同样值得我们注意,因为它只需要写很少的代码。在第2章之前,我不打算介绍如何设置项目构建文件或代码的细节。如果读者想要查看Maven pom.xml文件以及实际代码,可以在第1章对应的代码中找到它。第1章中的所有源代码都能在本书的GitHub存储库找到。

注意


在尝试运行本书各章的代码示例之前,一定要先阅读附录A。附录A涵盖本书中所有项目的一般项目布局、运行构建脚本的方法以及启动Docker环境的方法。本章中的代码示例很简单,旨在从桌面直接运行,而不需要其他章的信息。但在后面的几章中,将很快开始使用Docker来运行本书中使用的所有服务和基础设施。如果读者还没有阅读附录A中与设置桌面环境相关的内容,请不要过多自行尝试,避免浪费时间和精力。

在这个例子中,创建一个名为Application的Java类(在simpleservice/src/com/thoughtmechanix/application/simpleservice/Application.java的Java类,它公开了一个名为/hello的REST端点。Application类的代码,如代码清单1-1所示。

代码清单1-1 使用Spring Boot的Hello World:一个简单的Spring微服务

package com.thoughtmechanix.simpleservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController; 
import org.springframework.web.bind.annotation.PathVariable;

@SpringBootApplication  ⇽--- 告诉Spring Boot框架,该类是Spring Boot服务的入口点
@RestController  ⇽--- 告诉Spring Boot,要将该类中的代码公开为Spring RestController类
@RequestMapping(value="hello")  ⇽--- 此应用程序中公开的所有URL将以/ hello前缀开头
public class Application {

    public static void main(String[] args) { 
        SpringApplication.run(Application.class, args);
    }

    @RequestMapping(value="/{firstName}/{lastName}",  ⇽---  Spring Boo公开为一个基于GET方法的REST端点,它将使用两个参数,即firstName和lastName
    ➥  method = RequestMethod.GET)
    public String hello( @PathVariable("firstName") String firstName,   ⇽--- 将URL中传入的firstName和lastName参数映射为传递给hello方法的两个变量
    ➥  @PathVariable("lastName") String lastName) { 
        return String.format("{\"message\":\"Hello %s %s\"}",  ⇽--- 返回一个手动构建的简单JSON字符串。在第2章中,我们不需要创建任何JSON
        ➥  firstName, lastName);
        }
    }

代码清单1-1中主要公开了一个GET HTTP端点,该端点将在URL上取两个参数(firstNamelastName),然后返回一个包含消息“Hello firstName lastName”的净荷的简单JSON字符串。如果在服务上调用了端点/hello/john/carnell,返回的结果(马上展示)将会是:

{"message":"Hello john carnell"}

让我们启动服务。为此,请转到命令提示符并输入以下命令:

mvn spring-boot:run

这条mvn命令将使用Spring Boot插件,然后使用嵌入式Tomcat服务器启动应用程序。

Java与Groovy以及Maven与Gradle!

Spring Boot框架对Java和Groovy编程语言提供了强力的支持。可以使用Groovy构建微服务,而无须任何项目设置。Spring Boot还支持Maven和Gradle构建工具。我将本书中的例子限制在Java和Maven中。作为一个长期的Groovy和Gradle的爱好者,我对语言和构建工具有良好的尊重,但为了保持本书的可管理性,并使内容更聚焦,我选择使用Java和Maven,以便于照顾到尽可能多的读者。

如果一切正常开始,在命令行窗口中应该看到图1-4所示的内容。

图1-4 Spring Boot服务将通过控制台与公开的端点和服务端口进行通信

检查图1-4中的内容,注意两件事。首先,端口8080上启动了一个Tomcat服务器;其次,在服务器上公开了/hello/{firstName}/{lastName}的GET端点。

这里将使用名为POSTMAN的基于浏览器的REST工具来调用服务。许多工具(包括图形和命令行)都可用于调用基于REST的服务,但是本书中的所有示例都使用POSTMAN。图1-5展示了POSTMAN调用http://localhost:8080/ hello/john/carnell端点并从服务中返回结果。

图1-5 /hello端点的响应,以JSON净荷的形式展示了请求的数据

显然,这个简单的例子并不能演示Spring Boot的全部功能。但是,我们应该注意到,在这里只使用了25行代码就编写了一个完整的HTTP JSON REST服务,其中带有基于URL和参数的路由映射。正如所有经验丰富的Java开发人员都会告诉你的那样,在25行Java代码中编写任何有意义的东西都是非常困难的。虽然Java是一门强大的编程语言,但与其他编程语言相比,它却获得了啰唆冗长的名声。

完成了Spring Boot的简短介绍,现在必须提出这个问题:我们可以使用微服务的方式编写应用程序,这是否意味着我们就应该这么做呢?在下一节中,将介绍为什么以及何时适合使用微服务方法来构建应用程序。

[2] “卡内尔猴子测试”对应的英文为“Carnell Monkey Test”,是作者John Carnell设想出来的一个判断框架是否易于使用的试验。——译者注

我们正处于历史的拐点。现代社会的几乎所有方面都可以通过互联网连接在一起。习惯于为当地市场服务的公司突然发现,他们可以接触到全球的客户群,全球更大的客户群一起涌进来的同时也带来了全球竞争。这些竞争压力意味着以下力量正在影响开发人员考虑构建应用程序的方式。

为了满足这些期望,作为应用开发人员,我们不得不接受这样一个悖论:构建高可伸缩性和高度冗余的应用程序。我们需要将应用程序分解成可以互相独立构建和部署的小型服务。如果将应用程序“分解”为小型服务,并将它们从单体制品中转移出来,那么就可以构建具有下面这些特性的系统。

为此,当我们开始讨论微服务时,请记住下面一句话:小型的、简单的和解耦的服务=可伸缩的、有弹性的和灵活的应用程序

术语“云”已经被过度使用了。每个软件供应商都有云,每个软件供应商的平台都是支持云的,但是如果穿透这些天花乱坠的广告宣传,我们就会发现云计算有3种基本模式。它们是:

为了更好地理解这些概念,让我们将每天的任务映射到不同的云计算模型中。当你想吃饭时,你有4种选择:

(1)在家做饭;

(2)去食品杂货店买一顿预先做好的膳食,然后你加热并享用它;

(3)叫外卖送到家里;

(4)开车去餐厅吃饭。

图1-6展示了各种模型。

图1-6 不同的云计算模型归结于云供应商或你各自要负责什么

这些选择之间的区别是谁负责烹饪这些膳食,以及在哪里烹饪。在内部自建模型中,想要在家里吃饭就需要自己做所有的工作,还要使用家里面的烤箱和食材。商店购买的食物就像使用基础设施即服务(IaaS)计算模型一样,使用店内的厨师和烤箱预先烘烤餐点,但你仍然有责任加热膳食并在家里吃(然后清洗餐具)。

在平台即服务(PaaS)模型中,你仍然需要负责烹饪膳食,但同时依靠供应商来负责与膳食制作相关的核心任务。例如,在PaaS模型中,你提供盘子和家具,但餐厅老板提供烤箱、食材和厨师来做饭。在“软件即服务”(SaaS)模型中,你去到一家餐厅,在那里,所有食物都已为你准备好。你在餐厅吃饭,然后在吃完后买单,你也不需要自己去准备或清洗餐具。

每个模型中的关键项都是控制:由谁来负责维护基础设施,以及构建应用程序的技术选择是什么?在IaaS模型中,云供应商提供基础设施,但你需要选择技术并构建最终的解决方案;而在SaaS模型中,你就是供应商所提供的服务的被动消费者,无法对技术进行选择,同时也没有任何责任来维护应用程序的基础设施。

新兴的云平台

本书已经介绍了目前正在使用的3种核心云平台类型(即IaaS、PaaS和SaaS)。然而,新的云平台类型正在出现。这些新平台包括“函数即服务”(Functions as a Service,FaaS)和“容器即服务”(Container as a Service,CaaS)。基于FaaS的应用程序会使用像亚马逊的Lambda技术和Google Cloud函数这样的设施,应用会将代码块以“无服务器”(serverless)的形式部署,这些代码会完全在云提供商的平台计算设施上运行。使用FaaS平台,无须管理任何服务器基础设施,只需支付执行函数所需的计算周期。

使用容器即服务模型,开发人员将微服务作为便携式虚拟容器(如Docker)进行构建并部署到云供应商。与IaaS模型不同,使用IaaS的开发人员必须管理部署服务的虚拟机,而CaaS则是将服务部署在轻量级的虚拟容器中。云供应商会提供运行容器的虚拟服务器,以及用于构建、部署、监控和伸缩容器的综合工具。亚马逊的弹性容器服务(Amazon’s Elastic Container Service,Amazon ECS)就是一个基于CaaS平台的例子。在第10章中,我们将看到如何部署已构建的微服务到Amazon ECS。

需要重点注意的是,使用云计算的FaaS和CaaS模型,开发人员仍然可以构建基于微服务的架构。请记住,微服务概念的重点在于构建有限职责的小型服务,并使用基于HTTP的接口进行通信。新兴的云计算平台(如FaaS和CaaS)是部署微服务的替代基础设施机制。

微服务架构的核心概念之一就是每个服务都被打包和部署为离散的独立制品。服务实例应该迅速启动,服务的每一个实例都是完全相同的。

作为编写微服务的开发人员,我们迟早要决定是否将服务部署到下列某个环境之中。

基于云的微服务的优势是以弹性的概念为中心。云服务供应商允许开发人员在几分钟内快速启动新的虚拟机和容器。如果服务容量需求下降,开发人员可以关闭虚拟服务器,而不会产生任何额外的费用。使用云供应商部署微服务可以显著地提高应用程序的水平可伸缩性(添加更多的服务器和服务实例)。服务器弹性也意味着应用程序可以更具弹性。如果其中一台微服务遇到问题并且处理能力正在不断地下降,那么启动新的服务实例可以让应用程序保持足够长的存活时间,让开发团队能够从容而优雅地解决问题。

本书会使用Docker容器将所有的微服务和相应的服务基础设施部署到基于IaaS的云供应商。下面列出的是用于微服务的常见部署拓扑结构。

为什么不是基于PaaS的微服务

本章前面讨论了3种云平台(基础设施即服务、平台即服务和软件即服务)。对于本书,我选择专注于使用基于IaaS的方法构建微服务。虽然某些云供应商可以让开发人员抽象出微服务的部署基础设施,但我选择保持独立于供应商并部署应用程序的所有部分(包括服务器)。

例如,亚马逊、Cloud Foundry和Heroku可以让开发人员无须知道底层应用程序容器即可部署服务。它们提供了一个Web接口和API,以允许将应用程序部署为WAR或JAR文件。设置和调优应用程序服务器和相应的Java容器被抽象了出来。虽然这很方便,但每个云供应商的平台与其各自的PaaS解决方案有着不同的特点。

IaaS方案虽然需要更多的工作,但可跨多个云供应商进行移植,并允许开发人员通过产品覆盖更广泛的受众。就个人而言,我发现基于PaaS的云解决方案可以快速启动开发工作,但一旦应用程序拥有足够多的微服务,开发人员就会开始需要云服务商提供的IaaS风格的灵活性。

本章前面提到过新的云计算平台,如函数即服务(FaaS)和容器即服务(CaaS)。如果不小心,基于FaaS的平台就会将代码锁定到一个云供应商平台上,因为代码会被部署到供应商特定的运行时引擎上。使用基于FaaS的模型,开发人员可能会使用通用的编程语言(Java、Python、JavaScript等)编写服务,但开发人员仍然会将自己严格束缚在底层供应商的API和部署函数的运行时引擎上。

本书中构建的服务都会打包为Docker容器。本书选择Docker的原因之一是,作为容器技术,Docker可以部署到所有主要的云供应商之中。稍后在第10章中,本书将演示如何使用Docker打包微服务,然后将这些容器部署到亚马逊云平台。

尽管构建单个微服务的概念很易于理解,但运行和支持健壮的微服务应用程序(尤其是在云中运行)不只是涉及为服务编写代码。编写健壮的服务需要考虑几个主题。图1-7强调了这些主题。

图1-7 微服务不只是业务逻辑,还需要考虑服务的运行环境以及服务的伸缩性和弹性

下面我们来更详细地了解一下图1-7中提及的要点。

本书采用基于模式的方法来回答这些问题。通过基于模式的方法,本书列出可以跨不同技术实现来使用的通用设计。虽然本书选择了使用Spring Boot和Spring Cloud来实现本书中所使用的模式,但开发人员完全可以把这些概念和其他技术平台一起使用。具体来说,本书涵盖以下6类微服务模式:

让我们深入了解一下这些模式。

核心微服务开发模式解决了构建微服务的基础问题,图1-8突出了我们将要讨论的基本服务设计的主题。

图1-8 在设计微服务时必须考虑服务是如何通信以及被消费的

微服务路由模式负责处理希望消费微服务的客户端应用程序,使客户端应用程序发现服务的位置并路由到服务。在基于云的应用程序中,可能会运行成百上千个微服务实例。需要抽象这些服务的物理IP地址,并为服务调用提供单个入口点,以便为所有服务调用持续强制执行安全和内容策略。

服务发现和路由回答了这个问题:如何将客户的服务请求发送到服务的特定实例?

图1-9 服务发现和路由是所有大规模微服务应用的关键部分

因为微服务架构是高度分布式的,所以必须对如何防止单个服务(或服务实例)中的问题级联暴露给服务的消费者十分敏感。为此,这里将介绍4种客户端弹性模式。

图1-10展示了这些模式如何在服务表现不佳时,保护服务消费者不受影响。第5章将会介绍这些主题。

图1-10 使用微服务时,必须保护服务调用者远离表现不佳的服务。记住,
慢速或无响应的服务所造成的中断并不仅仅局限于直接关联的服务

写一本微服务的书绕不开微服务安全性。在第7章中我们将介绍3种基本的安全模式。这3种模式具体如下。

图1-11展示了如何实现上述3种模式来构建可以保护微服务的验证服务。

图1-11 使用基于令牌的安全方案,可以实现服务验证和授权,而无须传递客户端凭据

本书现在不会太深入图1-11中的细节。需要一整章来介绍安全是有原因的(实际上它本身就可以是一本书)。

微服务架构的优点是单体应用程序被分解成可以彼此独立部署的小的功能部件,而它的缺点是调试和跟踪应用程序和服务中发生的事情要困难得多。

因此,本书将介绍以下3种核心日志记录和跟踪模式。

图1-12展示了这些模式如何配合在一起。第9章中将会更加详细地介绍日志记录和跟踪模式。

图1-12 一个深思熟虑的日志记录和跟踪策略使跨多个服务的调试事务变得可管理

微服务架构的核心原则之一是,微服务的每个实例都应该和其他所有实例相同。“配置漂移”(某些文件在部署到服务器之后发生了一些变化)是不允许出现的,因为这可能会导致应用程序不稳定。

一句经常说的话

“我在交付准备服务器上只做了一个小小的改动,但是我忘了在生产服务器中也做这样的改动。”多年来,我在紧急情况团队中工作时,许多宕机系统的解决方案通常是从开发人员或系统管理员的这些话开始的。工程师(和大多数人一般)是以良好的意图在操作。工程师并不是故意犯错误或使系统崩溃,相反,他们尽可能做到最好,但他们会变得忙碌或者分心。他们调整了一些服务器上的东西,打算回去在所有环境中做相同的调整。

在以后某个时间点里,出现了中断状况,每个人都在搔头挠耳,想要知道其他环境与生产环境之间有什么不同。我发现,微服务的小规模与有限范围的特点创造了一个绝佳机会——将“不可变基础设施”概念引入组织:一旦部署服务,其运行的基础设施就再也不会被人触碰。

不可变基础设施是成功使用微服务架构的关键因素,因为在生产中必须要保证开发人员为特定微服务启动的每个微服务实例与其他微服务实例相同。

为此,本书的目标是将基础设施的配置集成到构建部署过程中,这样就不再需要将软件制品(如Java WAR或EAR)部署到已经在运行的基础设施中。相反,开发人员希望在构建过程中构建和编译微服务并准备运行微服务的虚拟服务器镜像。部署微服务时,服务器运行所需的整个机器镜像都会进行部署。

图1-13阐述了这个过程。本书最后将介绍如何更改构建和部署管道,以便将微服务及运行的服务器部署为单个工作单元。第10章将介绍以下模式和主题。

图1-13 开发人员希望微服务及其运行所需的服务器成为在不同环境间作为整体部署的原子制件

使用这些模式和主题的目的是,在配置漂移影响到上层环境(如交付准备环境或生产环境)之前,尽可能快地公开并消除配置漂移。

注意


本书中的代码示例(除了第10章)都将在本地机器上运行。前两章的代码可以直接从命令行运行,从第3章开始,所有代码将被编译并作为Docker容器运行。

本节将简要介绍在构建微服务时会使用的Spring Cloud技术。这是一个高层次的概述。在书中使用各项技术时,我们会根据需要为读者讲解这些技术的细节。

从零开始实现所有这些模式将是一项巨大的工作。幸好,Spring团队将大量经过实战检验的开源项目整合到一个称为Spring Cloud的Spring子项目中。

Spring Cloud将Pivotal、HashiCorp和Netflix等开源公司的工作封装在一起。Spring Cloud简化了将这些项目设置和配置到Spring应用程序中的工作,以便开发人员可以专注于编写代码,而不会陷入配置构建和部署微服务应用程序的所有基础设施的细节中。

图1-14将1.9节中列出的模式映射到实现它们的Spring Cloud项目。

图1-14 可以将这些直接可用的技术与本章探讨的微服务模式对应起来

下面让我们更详细地了解一下这些技术。

Spring Boot是微服务实现中使用的核心技术。Spring Boot通过简化构建基于REST的微服务的核心任务,大大简化了微服务开发。Spring Boot还极大地简化了将HTTP类型的动词(GET、PUT、POST和DELETE)映射到URL、JSON协议序列化与Java对象的相互转化,以及将Java异常映射回标准HTTP错误代码的工作。

Spring Cloud Config通过集中式服务来处理应用程序配置数据的管理,因此应用程序配置数据(特别是环境特定的配置数据)与部署的微服务完全分离。这确保了无论启动多少个微服务实例,这些微服务实例始终具有相同的配置。Spring Cloud Config拥有自己的属性管理存储库,也可以与以下开源项目集成。

通过Spring Cloud服务发现,开发人员可以从客户端消费的服务中抽象出部署服务器的物理位置(IP或服务器名称)。服务消费者通过逻辑名称而不是物理位置来调用服务器的业务逻辑。Spring Cloud服务发现也处理服务实例的注册和注销(在服务实例启动和关闭时)。Spring Cloud服务发现可以使用Consul和Eureka作为服务发现引擎。

Spring Cloud与Netflix的开源项目进行了大量整合。对于微服务客户端弹性模式,Spring Cloud封装了Netflix Hystrix库和Netflix Ribbon项目,开发人员可以轻松地在微服务中使用它们。

使用Netflix Hystrix库,开发人员可以快速实现服务客户端弹性模式,如断路器模式和舱壁模式。

虽然Netflix Ribbon项目简化了与诸如Eureka这样的服务发现代理的集成,但它也为服务消费者提供了客户端对服务调用的负载均衡。即使在服务发现代理暂时不可用时,客户端也可以继续进行服务调用。

Spring Cloud使用Netflix Zuul项目为微服务应用程序提供服务路由功能。Zuul是代理服务请求的服务网关,确保在调用目标服务之前,对微服务的所有调用都经过一个“前门”。通过集中的服务调用,开发人员可以强制执行标准服务策略,如安全授权验证、内容过滤和路由规则。

Spring Cloud Stream(https://cloud.spring.io/spring-cloud-stream/)是一种可让开发人员轻松地将轻量级消息处理集成到微服务中的支持技术。借助Spring Cloud Stream,开发人员能够构建智能的微服务,它可以使用在应用程序中出现的异步事件。此外,使用Spring Cloud Stream可以快速将微服务与消息代理进行整合,如RabbitMQ和Kafka。

Spring Cloud Sleuth允许将唯一跟踪标识符集成到应用程序所使用的HTTP调用和消息通道(RabbitMQ、Apache Kafka)之中。这些跟踪号码(有时称为关联ID或跟踪ID)能够让开发人员在事务流经应用程序中的不同服务时跟踪事务。有了Spring Cloud Sleuth,这些跟踪ID将自动添加到微服务生成的任何日志记录中。

Spring Cloud Sleuth与日志聚合技术工具(如Papertrail)和跟踪工具(如Zipkin)结合时,能够展现出真正的威力。Papertail是一个基于云的日志记录平台,用于将日志从不同的微服务实时聚合到一个可查询的数据库中。Zipkin可以获取Spring Cloud Sleuth生成的数据,并允许开发人员可视化单个事务涉及的服务调用流程。

Spring Cloud Security是一个验证和授权框架,可以控制哪些人可以访问服务,以及他们可以用服务做什么。Spring Cloud Security是基于令牌的,允许服务通过验证服务器发出的令牌彼此进行通信。接收调用的每个服务可以检查HTTP调用中提供的令牌,以确认用户的身份以及用户对该服务的访问权限。

此外,Spring Cloud Security支持JSON Web Token。JSON Web Token(JWT)框架标准化了创建OAuth2令牌的格式,并为创建的令牌进行数字签名提供了标准。

要实现代码供应,我们将会转移到其他的技术栈。Spring框架是面向应用程序开发的,它(包括Spring Cloud)没有用于创建“构建和部署”管道的工具。要实现一个“构建和部署”管道,开发人员需要使用Travis CI和Docker这两样工具,前者可以作为构建工具,而后者可以构建包含微服务的服务器镜像。

为了部署构建好的Docker容器,本书的最后将通过一个例子,阐述如何将整个应用程序栈部署到亚马逊云上。

在本章最后这一节中,我们概要回顾一下要使用的各种Spring Cloud技术。因为每一种技术都是独立的服务,要详细介绍这些服务,整整一章的内容都不够。在总结这一章时,我想留给读者一个小小的代码示例,它再次演示了将这些技术集成到微服务开发工作中是多么容易。

与代码清单1-1 中的第一个代码示例不同,这个代码示例不能运行,因为它需要设置和配置许多支持服务才能使用。不过,不要担心,在设置服务方面,这些Spring Cloud服务(配置服务,服务发现)的设置是一次性的。一旦设置完成,微服务就可以不断使用这些功能。在本书的开头,我们无法将所有的精华都融入一个代码示例中。

代码清单1-2中的代码快速演示了如何将远程服务的服务发现、断路器、舱壁以及客户端负载均衡集成到“Hello World”示例中。

代码清单1-2 Hello World Service使用Spring Cloud

package com.thoughtmechanix.simpleservice;

// 为了简洁,省略了其他import语句
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; 
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty; 
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;

@SpringBootApplication
@RestController
@RequestMapping(value="hello")
@EnableCircuitBreaker  ⇽--- 使服务能够使用Hystrix和Ribbon库
@EnableEurekaClient
public class Application {  ⇽--- 告诉服务,它应该使用Eureka服务发现代理注册自身,并且服务调用是使用服务发现来“查找”远程服务的位置的

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @HystrixCommand(threadPoolKey = "helloThreadPool")  ⇽--- 包装器使用Hystrix断路器调用helloRemoteServiceCall方法
    public String helloRemoteServiceCall(String firstName, String lastName){
    ResponseEntity<String> restExchange = 
    ➥  restTemplate.exchange(
        ➥  "http://logical-service-id/name/  ⇽--- 使用一个装饰好的RestTemplate类来获取一个“逻辑”服务ID,Eureka在幕后查找服务的物理位置
            ➥   [ca]{firstName}/{lastName}",
        ➥  HttpMethod.GET,
        ➥  null, String.class, firstName, lastName);

    return restExchange.getBody();

    }
    @RequestMapping(value="/{firstName}/{lastName}", method = RequestMethod.GET)
    public String hello(@PathVariable("firstName") String firstName,
    ➥  @PathVariable("lastName") String lastName) {
        return helloRemoteServiceCall(firstName, lastName);
    }
}

这段代码包含了很多内容,让我们慢慢分析。记住,这个代码清单只是一个例子,在第1章的GitHub仓库源代码中是找不到的。把它放在这里,是为了让读者了解本书后面的内容。

开发人员首先应该要注意的是@EnableCircuitBreaker@EnableEurekaClient注解。@EnableCircuitBreaker注解告诉Spring微服务,将要在应用程序使用Netflix Hystrix库。@EnableEurekaClient注解告诉微服务使用Eureka服务发现代理去注册它自己,并且将要在代码中使用服务发现去查询远程REST服务端点。注意,配置是在一个属性文件中的,该属性文件告诉服务要进行通信的Eureka服务器的地址和端口号。读者第一次看到使用Hystrix是在声明hello方法时:

@HystrixCommand(threadPoolKey = "helloThreadPool")
public String helloRemoteServiceCall(String firstName,String lastName)

@HystrixCommand注解做两件事。第一件事是,在任何时候调用helloRemoteService Call方法,该方法都不会被直接调用,这个调用会被委派给由Hystrix管理的线程池。如果调用时间太长(默认为1 s),Hystrix将介入并中断调用。这是断路器模式的实现。第二件事是创建一个由Hystrix管理的名为helloThreadPool的线程池。所有对helloRemoteServiceCall方法的调用只会发生在此线程池中,并且将与正在进行的任何其他远程服务调用隔离。

最后要注意的是helloRemoteServiceCall方法中发生的事情。@EnableEurekaClient的存在告诉Spring Boot,在使用REST服务调用时,使用修改过的RestTemplate类(这不是标准的Spring RestTemplate的工作方式)。这个RestTemplate类允许用户传入自己想要调用的服务的逻辑服务ID:

ResponseEntity<String> restExchange = restTemplate.exchange
➥  (http://logical-service-id/name/{firstName}/{lastName}

在幕后,RestTemplate类将与Eureka服务进行通信,并查找一个或多个“name”服务实例的实际位置。作为服务的消费者,开发人员的代码永远不需要知道服务的位置。

另外,RestTemplate类使用Netflix的Ribbon库。Ribbon将会检索与服务有关的所有物理端点的列表。每当客户端调用该服务时,它不必经过集中式负载均衡器就可以对客户端上不同服务实例进行轮询(round-robin)。通过消除集中式负载平衡器并将其移动到客户端,可以消除应用程序基础设施中的其他故障点(故障的负载平衡器)。

我希望此刻读者会印象深刻,因为只需要几个注解就可以为微服务添加大量的功能。这就是Spring Cloud背后真正的美。作为开发人员,我们可以利用Netflix和Consul等知名的云计算公司的微服务功能,这些功能是久经考验的。如果在Spring Cloud之外使用这些功能,可能会很复杂并且难以设置。Spring Cloud简化了它们的使用,仅仅是使用一些简单的Spring Cloud注解和配置条目。

我想要确保本书提供的示例都是与开发人员的工作息息相关的。为此,我将围绕一家名为ThoughtMechanix的虚构公司的冒险(不幸事件)来组织本书的章节以及对应的代码示例。

ThoughtMechanix是一家软件开发公司,其核心产品EagleEye提供企业级软件资产管理应用程序。该产品覆盖了所有关键要素:库存、软件交付、许可证管理、合规、成本以及资源管理。其主要目标是使组织获得准确时间点的软件资产的描述。

该公司成立了大概10年,尽管营收增长强劲,但在内部,他们正在讨论是否应该革新其核心产品,将它从一个单体内部部署的应用程序转移到云端。对该公司来说,与EagleEye相关的平台革新是“生死”时刻。

该公司正在考虑在新架构上重构其核心产品EagleEye。虽然应用程序的大部分业务逻辑将保持原样,但应用程序本身将从单体设计中分解为更小的微服务设计,其部件可以独立部署到云端。本书中的示例不会构建整个ThoughtMechanix应用程序。相反,读者将从问题领域构建特定的微服务,然后使用各种Spring Cloud(和一些非Spring Cloud)技术来构建支持这些服务的基础设施。

成功采用基于云的微服务架构的能力将影响技术组织的所有成员。这包括架构团队、工程团队、测试团队和运维团队。每个团队都需要投入,最终,当团队重新评估他们在这个新环境中的职责时,他们可能需要重组。让我们开始与ThoughtMechanix的旅程,读者将开始一些基础工作——识别和构建EagleEye中使用的几个微服务,然后使用Spring Boot构建这些服务。

[1] 虽然本书在稍后的第2章中会介绍REST,但是Roy Fielding阐述如何基于REST构建应用的博士论文仍然值得一读(http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm)。在对REST概念的阐述上,它依然是最棒的材料之一。


本章主要内容

软件开发的历史充斥着大型开发项目崩溃的故事,这些项目可能投资了数百万美元、集中了行业里众多的顶尖人才、消耗了开发人员成千上万的工时,但从未给客户交付任何有价值的东西,最终由于其复杂性和负担而轰然倒塌。

这些庞大的项目倾向于遵循大型传统的瀑布开发方法,坚持在项目开始时界定应用的所有需求和设计。这些项目的开发人员非常重视软件说明书的“正确性”,却很少能够满足新的业务需求,也很少能够重构并从开发初期的错误中重新思考和学习。

但现实情况是,软件开发并不是一个由定义和执行所组成的线性过程,而是一个演化过程,在开发团队真正明白手头的问题前,需要经历与客户沟通、向客户学习和向客户交付的数次迭代。

使用传统的瀑布方法所面临的挑战在于,许多时候,这些项目交付的软件制品的粒度具有以下特点。

基于微服务的架构采用不同的方法来交付功能。具体来说,基于微服务的架构具有以下特点。

为什么这些微服务架构属性对基于云的开发很重要?基于云的应用程序通常有以下特点。

本章的内容会包含在业务问题中构建和识别微服务的基础知识,构建微服务的骨架,然后理解在生产环境中成功部署和管理微服务的运维属性。

要想成功设计和构建微服务,开发人员需要像警察向目击证人讯问犯罪活动一样着手处理微服务。即使每个证人看到同一事件发生,他们对犯罪活动的解释也是根据他们的背景、他们所看重的东西(例如,给予他们动机的东西),以及在那个时刻目睹这个事件所带来的环境压力塑造出来的。每个参与者都有他们自己认为重要的视角(和偏见)。

就像一名成功的警察试图探寻真相一样,构建一个成功的微服务架构的过程需要结合软件开发组织内多个人的视角。尽管交付整个应用程序需要的不仅仅是技术人员,但我相信,成功的微服务开发的基础是从以下3个关键角色的视角开始的。

本章将演示如何从这些角色的视角使用Spring Boot和Java设计和构建一组微服务。到本章结束时,读者将有一个可以打包并部署到云的服务。

架构师在软件项目中的作用是提供待解决问题的工作模型。架构师的工作是提供脚手架,开发人员将根据这些脚手架构建他们的代码,使应用程序所有部件都组合在一起。

在构建微服务架构时,项目的架构师主要关注以下3个关键任务:

(1)分解业务问题;

(2)建立服务粒度;

(3)定义服务接口。

面对复杂性,大多数人试图将他们正在处理的问题分解成可管理的块。因为这样他们就不必努力把问题的所有细节都考虑进来。他们将问题抽象地分解成几个关键部分,然后寻找这些部分之间存在的关系。

在微服务架构中,架构师将业务问题分解成代表离散活动领域的块。这些块封装了与业务域特定部分相关联的业务规则和数据逻辑。

虽然我们希望微服务封装执行单个事务的所有业务规则,但这并不总是行得通。我们经常会遇到需要跨业务领域不同部分的一组微服务来完成整个事务的情况。架构师通过查看数据域中那些不适合放到一起的地方来划分一组微服务的服务边界。

例如,架构师可能会看到代码执行的业务流程,并意识到它们同时需要客户和产品信息。存在两个离散的数据域时,通常就意味着需要使用多个微服务。业务事务的两个不同部分如何交互通常成为微服务的服务接口。

分离业务领域是一门艺术,而不是非黑即白的科学。读者可以使用以下指导方针将业务问题识别和分解为备选的微服务。

(1)描述业务问题,并聆听用来描述问题的名词。在描述问题时,反复使用的同一名词通常意味着它们是核心业务领域并且适合创建微服务。第1章中EagleEye域的目标名词可能会是合同许可证资产

(2)注意动词。动词突出了动作,通常代表问题域的自然轮廓。如果发现自己说出“事务X需要从事物A和事物B获取数据”这样的话,通常表明多个服务正在起作用。如果把注意动词的方法应用到EagleEye上,那么就可能会查找像“来自桌面服务的Mike安装新PC时,他会查找软件X可用的许可证数量,如果有许可证,就安装软件。然后他更新了跟踪电子表格中使用的许可证的数量”这样的陈述句。这里的关键动词是查找和更新。

(3)寻找数据内聚。将业务问题分解成离散的部分时,要寻找彼此高度相关的数据。如果在会话过程中,突然读取或更新与迄今为止所讨论的内容完全不同的数据,那么就可能还存在其他候选服务。微服务应完全拥有自己的数据。

让我们将这些指导方针应用到现实世界的问题中。第1章介绍了一种名为EagleEye的现有软件产品,该软件产品用于管理软件资产,如软件许可证和安全套接字层(SSL)证书。这些软件资产被部署到组织中的各种服务器上。

EagleEye是一个传统的单体Web应用程序,部署在位于客户数据中心内的J2EE应用程序服务器。我们的目标是将现有的单体应用程序梳理成一组服务。

首先,我们要采访EagleEye应用程序的所有用户,并讨论他们是如何交互和使用EagleEye的。图2-1描述了与不同业务客户进行的对话的总结。通过查看EagleEye的用户是如何与应用程序进行交互的,以及如何将应用程序的数据模型分解出来,可以将EagleEye问题域分解为以下备选微服务。

图2-1 采访EagleEye用户,了解他们如何做日常工作

图2-1强调了与业务用户对话时出现的一些名词和动词。因为这是现有的应用程序,所以可以查看应用程序并将主要名词映射到物理数据模型中的表。现有应用程序可能有数百张表,但每张表通常会映射回一组逻辑实体。

图2-2展示了基于与EagleEye客户对话的简化数据模型。基于业务对话和数据模型,备选微服务是组织、许可证、合同和资产服务。

图2-2 简化的EagleEye数据模型

拥有了一个简化的数据模型,就可以开始定义在应用程序中需要哪些微服务。根据图2-2中的数据模型,可以看到潜在的4个微服务基于以下元素:

我们的目标是将这些主要的功能部件提取到完全独立的单元中,这些单元可以独立构建和部署。但是,从数据模型中提取服务需要的不只是将代码重新打包到单独的项目中,还涉及梳理出服务访问的实际数据库表,并且只允许每个单独的服务访问其特定域中的表。图2-3展示了应用程序代码和数据模型如何被“分块”到各个部分。

图2-3 将数据模型作为把单体应用程序分解为微服务的基础

将问题域分解成不同的部分后,开发人员通常会发现自己不确定是否为服务划分了适当的粒度级别。一个太粗粒度或太细粒度的微服务将具有很多的特征,我们将在稍后讨论。

构建微服务架构时,粒度的问题很重要,可以采用以下思想来确定正确的解决方案。

(1)开始的时候可以让微服务涉及的范围更广泛一些,然后将其重构到更小的服务——在开始微服务旅程之初,容易出现的一个极端情况就是将所有的事情都变成微服务。但是将问题域分解为小型的服务通常会导致过早的复杂性,因为微服务变成了细粒度的数据服务。

(2)重点关注服务如何相互交互——这有助于建立问题域的粗粒度接口。从粗粒度重构到细粒度是比较容易的。

(3)随着对问题域的理解不断增长,服务的职责将随着时间的推移而改变——通常来说,当需要新的应用功能时,微服务就会承担起职责。最初的微服务可能会发展为多个服务,原始的微服务则充当这些新服务的编排层,负责将应用的其他部分的功能封装起来。

糟糕的微服务的“味道”

如何知道微服务的划分是否正确?如果微服务过于粗粒度,可能会看到以下现象。

服务承担过多的职责——服务中的业务逻辑的一般流程很复杂,并且似乎正在执行一组过于多样化的业务规则。

该服务正在跨大量表来管理数据——微服务是它管理的数据的记录系统。如果发现自己将数据持久化存储到多个表或接触到当前数据库以外的表,那么这就是一条服务过于粗粒度的线索。我喜欢使用这么一个指导方针——微服务应该不超过3~5个表。再多一点,服务就可能承担了太多的职责。

测试用例太多——随着时间的推移,服务的规模和职责会增长。如果一开始有一个只有少量测试用例的服务,到了最后该服务需要数百个单元测试用例和集成测试用例,那么就可能需要重构。

如果微服务过于细粒度呢?

问题域的一部分微服务像兔子一样繁殖——如果一切都成为微服务,将服务中的业务逻辑组合起来会变得复杂和困难,因为完成一项工作所需的服务数量会快速增长。一种常见的“坏味道”出现在应用程序有几十个微服务,并且每个服务只与一个数据库表进行交互时。

微服务彼此间严重相互依赖——在问题域的某一部分中,微服务相互来回调用以完成单个用户请求。

微服务成为简单CRUD(Create,Read,Update,Delete)服务的集合——微服务是业务逻辑的表达,而不是数据源的抽象层。如果微服务除了CRUD相关逻辑之外什么都不做,那么它们可能被划分得太细粒度了。

应该通过演化思维的过程来开发一个微服务架构,在这个过程中,你知道不会第一次就得到正确的设计。这就是最好从一组粗粒度的服务而不是一组细粒度的服务开始的原因。同样重要的是,不要对设计带有教条主义。我们可能会面临两个单独的服务之间交互过于频繁,或者服务的域之间不存在明确的边界这样的物理约束,当面临这样的约束时,需要创建一个聚合服务来将数据连接在一起。

最后,采取务实的做法并进行交付,而不是浪费时间试图让设计变得完美,最终导致没有东西可以展现你的努力。

架构师需要关心的最后一部分,是应用程序中的微服务该如何彼此交流。使用微服务构建业务逻辑时,服务的接口应该是直观的,开发人员应该通过学习应用程序中的一两个服务来获得应用程序中所有服务的工作节奏。

一般来说,可使用以下指导方针思考服务接口设计。

(1)拥抱REST的理念——REST对服务的处理方式是将HTTP作为服务的调用协议并使用标准HTTP动词(GET、PUT、POST和DELETE)。围绕这些HTTP动词对基本行为进行建模。

(2)使用URI来传达意图——用作服务端点的URI应描述问题域中的不同资源,并为问题域内的资源的关系提供一种基本机制。

(3)请求和响应使用JSON——JavaScript对象表示法(JavaScript Object Notation,JSON)是一个非常轻量级的数据序列化协议,并且比XML更容易使用。

(4)使用HTTP状态码来传达结果——HTTP协议具有丰富的标准响应代码,以指示服务的成功或失败。学习这些状态码,并且最重要的是在所有服务中始终如一地使用它们。

所有这些指导方针都是为了完成一件事,那就是使服务接口易于理解和使用。我们希望开发人员坐下来查看一下服务接口就能开始使用它们。如果微服务不容易使用,开发人员就会另辟道路,破坏架构的意图。

本书用这一章来谈论为什么微服务是构建应用程序的强大的架构模式。但是,本书还没有提及什么时候不应该使用微服务来构建应用程序。接下来,让我们了解一下其中的考量因素:

(1)构建分布式系统的复杂性;

(2)虚拟服务器/容器散乱;

(3)应用程序的类型;

(4)数据事务和一致性。

因为微服务是分布式和细粒度(小)的,所以它们在应用程序中引入了一层复杂性,而在单体应用程序中就不会出现这样的情况。微服务架构需要高度的运维成熟度。除非组织愿意投入高分布式应用程序获得成功所需的自动化和运维工作(监控、伸缩),否则不要考虑使用微服务。

微服务最常用的部署模式之一就是在一个服务器上部署一个微服务实例。在基于微服务的大型应用程序中,最终可能需要50~100台服务器或容器(通常是虚拟的),这些服务器或容器必须单独搭建和维护。即使在云中运行这些服务的成本较低,管理和监控这些服务器的操作复杂性也是巨大的。

注意


必须对微服务的灵活性与运行所有这些服务器的成本进行权衡。

微服务面向可复用性,并且对构建需要高度弹性和可伸缩性的大型应用程序非常有用。这就是这么多云计算公司采用微服务的原因之一。如果读者正在构建小型的、部门级的应用程序或具有较小用户群的应用程序,那么搭建一个分布式模型(如微服务)的复杂性可能太昂贵了,不值得。

开始关注微服务时,需要考虑服务的数据使用模式以及服务消费者如何使用它们。微服务包装并抽象出少量的表,作为执行“操作型”任务的机制,如创建、添加和执行针对存储的简单(非复杂的)查询,其工作效果很好。

如果应用程序需要跨多个数据源进行复杂的数据聚合或转换,那么微服务的分布式性质会让这项工作变得很困难。这样的微服务总是承担太多的职责,也可能变得容易受到性能问题的影响。

还要记住,在微服务间执行事务没有标准。如果需要事务管理,那就需要自己构建逻辑。另外,如第7章所述,微服务可以通过使用消息进行通信。消息传递在数据更新中引入了延迟。应用程序需要处理最终的一致性,数据的更新可能不会立即出现。

在构建微服务时,从概念到实现,需要视角的转换。具体来说,开发人员需要建立一个实现应用程序中每个微服务的基本模式。虽然每项服务都将是独一无二的,但我们希望确保使用的是一个移除样板代码的框架,并且微服务的每个部分都采用相同的布局。

在本节中,我们将探讨开发人员从EagleEye域模型构建许可证微服务的优先事项。许可证服务将使用Spring Boot编写。Spring Boot是标准Spring库之上的一个抽象层,它允许开发人员快速构建基于Groovy和Java的Web应用程序和微服务,比成熟的Spring应用程序能够节省大量的配置。

对于许可证服务示例,这里将使用Java作为核心编程语言并使用Apache Maven作为构建工具。

在接下来的几节中,我们将要完成以下几项工作。

(1)构建微服务的基本框架并构建应用程序的Maven脚本。

(2)实现一个Spring引导类,它将启动用于微服务的Spring容器,并启动类的所有初始化工作。

(3)实现映射端点的Spring Boot控制器类,以公开服务的端点。

首先,要为许可证服务创建一个骨架项目。读者可以从本章的GitHub存储库拉取源代码,也可以创建具有以下目录结构的许可证服务项目目录:

一旦拉取或创建了这个目录结构,就可以开始为项目编写Maven脚本。这就是位于项目根目录下的pom.xml文件。代码清单2-1展示了许可证服务的Maven POM文件。

代码清单2-1 许可证服务的Maven POM文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns=http://maven.apache.org/POM/4.0.0
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
    ➥  http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.thoughtmechanix</groupId>
  <artifactId>licensing-service</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>EagleEye Licensing Service</name>
  <description>Licensing Service</description>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>    ⇽---  告诉Maven包含Spring Boot起步工具包依赖项
    <version>1.4.4.RELEASE</version>
    <relativePath/>
  </parent>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>    ⇽---  告诉Maven包含Spring Boot Web依赖项
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>    ⇽---  告诉Maven包含Spring Actuator依赖项
    </dependency>
</dependencies>
<!—- 注意:某些构建属性和Docker构建插件已从此pom中的pom.xml中排除掉了(GitHub存储库的
     源代码中并没有移除),因为它们与这里的讨论无关。
    -->

<build>
  <plugins>
     <plugin>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-maven-plugin</artifactId>    ⇽---  告诉Maven包含Spring特定的Maven插件,用于构建和部署Spring Boot应用程序
     </plugin>
    </plugins>
  </build>
</project>

这里不会详细讨论整个脚本,但是在开始的时候要注意几个关键的地方。Spring Boot被分解成许多个独立的项目。其理念是,如果不需要在应用程序中使用Spring Boot的各个部分,那么就不应该“拉取整个世界”。这也使不同的Spring Boot项目能够独立地发布新版本的代码。为了简化开发人员的开发工作,Spring Boot团队将相关的依赖项目收集到各种“起步”(starter)工具包中。Maven POM的第一部分告诉Maven需要拉取Spring Boot框架的1.4.4版本。

Maven文件的第二部分和第三部分确定了要拉取Spring Web和Spring Actuator起步工具包。这两个项目几乎是所有基于Spring Boot REST服务的核心。读者会发现,服务中构建功能越多,这些依赖项目的列表就会变得越长。

此外,Spring Source还提供了Maven插件,可简化Spring Boot应用程序的构建和部署。第四部分告诉Maven构建脚本安装最新的Spring Boot Maven插件。此插件包含许多附加任务(如spring-boot:run),可以简化Maven和Spring Boot之间的交互。

最后,读者将看到一条注释,说明Maven文件的哪些部分已被删除。为了简化,本书没有在代码清单2-1中包含Spotify Docker插件。

注意


本书的每一章都包含用于构建和部署Docker容器的Docker文件。读者可以在每章代码部分的README.md文件中找到如何构建这些Docker镜像的详细信息。

我们的目标是在Spring Boot中运行一个简单的微服务,然后重复这个步骤以提供功能。为此,我们需要在许可证服务微服务中创建以下两个类。

如刚才所见,Spring Boot使用注解来简化设置和配置服务。在代码清单2-2中查看引导类时,这一点就变得显然易见。这个引导类位于src/main/java/com/thoughtmechanix/licenses/Application.
java文件。

代码清单2-2 @SpringBootApplication注解简介

package com.thoughtmechanix.licenses;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication ⇽--- @SpringBootApplication告诉Spring Boot框架,这是项目的引导类
public class Application {
    public static void main(String[] args) {
 SpringApplication.run(Application.class, args);  ⇽--- 调用以启动整个Spring Boot服务
    }
}

在这段代码中需要注意的第一件事是@SpringBootApplication的用法。Spring Boot使用这个注解来告诉Spring容器,这个类是在Spring中使用的bean定义的源。在Spring Boot应用程序中,可以通过以下方法定义Spring Bean。

(1)用@Component@Service@Repository注解标签来标注一个Java类。

(2)用@Configuration注解标签来标注一个类,然后为每个我们想要构建的Spring Bean定义一个构造器方法并为方法添加上@Bean标签。

在幕后,@SpringBootApplication注解将代码清单2-2中的Application类标记为配置类,然后开始自动扫描Java类路径上所有的类以形成其他的Spring Bean。

第二件需要注意的事是Application类的main()方法。在main()方法中,Spring Application.run(Application.class, args)调用启动了Spring容器,然后返回了一个Spring ApplicationContext对象(这里没有使用ApplicationContext做任何事情,因此它没有在代码中展示。)。

关于@SpringBootApplication注解及其对应的Application类,最容易记住的是,它是整个微服务的引导类。服务的核心初始化逻辑应该放在这个类中。

现在已经有了构建脚本,并实现了一个简单的Spring Boot引导类,接下来就可以开始编写第一个代码来做一些事情。这个代码就是控制器类。在Spring Boot应用程序中,控制器类公开了服务端点,并将数据从传入的HTTP请求映射到将处理该请求的Java方法。

遵循REST

本书中的所有微服务都遵循REST方法来构建。对REST的深入讨论超出了本书的范围[1],但对于本书,我们构建的所有服务都将具有以下特点。

使用HTTP作为服务的调用协议——服务将通过HTTP端点公开,并使用HTTP协议传输进出服务的数据。

将服务的行为映射到标准HTTP动词——REST强调将服务的行为映射到POST、GET、PUT和DELETE这样的HTTP动词上。这些动词映射到大多数服务中的CRUD功能。

使用JSON作为进出服务的所有数据的序列化格式——对基于REST的微服务来说,这不是一个硬性原则,但是JSON已经成为通过微服务提交和返回数据的通用语言。当然也可以使用XML,但是许多基于REST的应用程序大量使用JavaScript和JSON。JSON是基于JavaScript的Web前端和服务对数据进行序列化和反序列化的原生格式。

使用HTTP状态码来传达服务调用的状态——HTTP协议开发了一组丰富的状态码,以指示服务的成功或失败。基于REST的服务利用这些HTTP状态码和其他基于Web的基础设施,如反向代理和缓存,可以相对容易地与微服务集成。

HTTP是Web的语言,使用HTTP作为构建服务的哲学框架是构建云服务的关键。

第一个控制器类LicenseSerriceController位于src/main/java/com/thoughtmechanix/licenses/controllers/LicenseServiceController.java中。这个类将公开4个HTTP端点,这些端点将映射到POST、GET、PUT和DELETE动词。

让我们看一下控制器类,看看Spring Boot如何提供一组注解,以保证花最少的努力公开服务端点,使开发人员能够集中精力构建服务的业务逻辑。我们将从没有任何类方法的基本控制器类定义开始。代码清单2-3展示了为许可证服务构建的控制器类。

代码清单2-3 标记LicenseServiceController为Spring RestController

package com.thoughtmechanix.licenses.controllers;
 
// 为了简洁,省略了import语句
 
@RestController    ⇽---  @Restcontroller告诉Spring Boot这是一个基于REST的服务,它将自动序列化/反序列化服务请求/响应到JSON
@RequestMapping(value="/v1/organizations/{organizationId}/licenses")    ⇽---  在这个类中使用/v1/organizations{organizationId}/ licenses的前缀,公开所有HTTP端点
public class LicenseServiceController {
  // 为了简洁,省略了该类的内容
}

我们通过查看@RestController注解来开始探索。@RestController是一个类级Java注解,它告诉Spring容器这个Java类将用于基于REST的服务。此注解自动处理以JSON或XML方式传递到服务中的数据的序列化(在默认情况下,@RestController类将返回的数据序列化为JSON)。与传统的Spring @Controller注解不同,@RestController注解并不需要开发者从控制器类返回ResponseBody类。这一切都由@RestController注解进行处理,它包含了@ResponseBody注解。

为什么是JSON

在基于HTTP的微服务之间发送数据时,其实有多种可选的协议。由于以下几个原因,JSON已经成为事实上的标准。

首先,与其他协议(如基于XML的SOAP(Simple Object Access Protocol,简单对象访问协议))相比,它非常轻量级,可以在没有太多文本开销的情况下传递数据。

其次,JSON易于人们阅读和消费。这在选择序列化协议时往往被低估。当出现问题时,开发人员可以快速查看一大堆JSON,直观地处理其中的内容。JSON协议的简单性让这件事非常容易做到。

最后,JSON是JavaScript使用的默认序列化协议。由于JavaScript作为编程语言的急剧增长以及依赖于JavaScript的单页互联网应用程序(Single Page Internet Application,SPIA)的同样快速增长,JSON已经天然适用于构建基于REST的应用程序,因为前端Web客户端用它来调用服务。

其他机制和协议能够比JSON更有效地在服务之间进行通信。Apache Thrift框架允许构建使用二进制协议相互通信的多语言服务。Apache Avro协议是一种数据序列化协议,可在客户端和服务器调用之间将数据转换为二进制格式。

如果读者需要最小化通过线路发送的数据的大小,我建议查看这些协议。但是根据我的经验,在微服务中使用直接的JSON就可以有效地工作,并且不会在服务消费者和服务客户端间插入另一层通信来进行调试。

代码清单2-3中展示的第二个注解是@RequestMapping。可以使用@RequestMapping作为类级注解和方法级注解。@RequestMapping注解用于告诉Spring容器该服务将要公开的HTTP端点。使用类级的@RequestMapping注解时,将为该控制器公开的所有其他端点建立URL的根。

在代码清单2-3中,@RequestMapping(value="/v1/organizations/{organizationId}/licenses")使用value属性为控制器类中公开的所有端点建立URL的根。在此控制器中公开的所有服务端点将以/v1/organizations/{organizationId}/licenses作为其端点的根。{organizationId}是一个占位符,表明如何使用在每个调用中传递的organizationId来参数化URL。在URL中使用organizationId可以区分使用服务的不同客户。

现在将添加控制器的第一个方法。这一方法将实现REST调用中的GET动词,并返回单个License类实例,如代码清单2-4所示(为了便于讨论,将实例化一个名为License的Java类)。

代码清单2-4 公开一个GET HTTP端点

@RequestMapping(value="/{licenseId}",method = RequestMethod.GET)  ⇽--- 使用值创建一个GET端点v1/organizations/  {organizationId}/licenses{licenseId}
public License getLicenses(
➥  @PathVariable("organizationId") String organizationId,
➥  @PathVariable("licenseId") String licenseId) {    ⇽---  从URL映射两个参数(organizationId和licenseId)到方法参数
    return new License()
        .withId(licenseId)
        .withProductName("Teleco")
        .withLicenseType("Seat")
        .withOrganizationId("TestOrg");
}

这一代码清单中完成的第一件事是,使用方法级的@RequestMapping注解来标记getLicenses()方法,将两个参数传递给注解,即valuemethod。通过方法级的@Request Mapping注解,再结合类顶部指定的根级注解,我们将所有传入该控制器的HTTP请求与端点/v1/organizations/{organizationId}/licences/{licensedId}匹配起来。该注解的第二个参数method指定该方法将匹配的HTTP动词。在前面的例子中,以RequestMethod. GET枚举的形式匹配GET方法。

关于代码清单 2-4,需要注意的第二件事是getLicenses()方法的参数体中使用了@PathVariable注解。@PathVariable注解用于将在传入的URL中传递的参数值(由{parameterName}语法表示)映射为方法的参数。在代码清单2-4所示的示例代码中,将两个参数organizationIdlicenseId映射到方法中的两个参数级变量:

@PathVariable("organizationId") String organizationId,
@PathVariable("licenseId") String licenseId)

端点命名问题

在编写微服务之前,要确保(以及组织中的其他可能的团队)为服务公开的端点建立标准。应该使用微服务的URL(Uniform Resource Locator,统一资源定位器)来明确传达服务的意图、服务管理的资源以及服务内管理的资源之间存在的关系。以下指导方针有助于命名服务端点。

(1)使用明确的URL名称来确立服务所代表的资源——使用规范的格式定义URL将有助于API更直观,更易于使用。要在命名约定中保持一致。

(2)使用URL来确立资源之间的关系——通常,在微服务中会存在一种父子关系,在这些资源中,子项不会存在于父项的上下文之外(因此可能没有针对该子项的单独的微服务)。使用URL来表达这些关系。但是,如果发现URL嵌套过长,可能意味着微服务尝试做的事情太多了。

(3)尽早建立URL的版本控制方案——URL 及其对应的端点代表了服务的所有者和服务的消费者之间的契约。一种常见的模式是使用版本号作为前缀添加到所有端点上。尽早建立版本控制方案,并坚持下去。在几个消费者使用它们之后,对URL进行版本更新是非常困难的。

现在,可以将我们刚刚创建的东西称为服务。在命令行窗口中,转到下载示例代码的项目目录,然后执行以下Maven命令:

mvn spring-boot:run

一旦按下回车键,应该会看到Spring Boot启动一个嵌入式Tomcat服务器,并开始监听8080端口。

图2-4 许可证服务成功运行

服务启动后就可以直接访问公开的端点了。因为公开的第一个方法是GET调用,可以使用多种方法来调用这一服务。我的首选方法是使用基于Chrome的工具,如POSTMAN或CURL来调用该服务。图2-5展示了在http://localhost:8080/v1/organizations/
e254f8c-c442-4ebe-a82a-e2fc1d1ff78a/licenses/f3831f8c-c338-4ebe-a82a-e2fc1d1ff78a端点上完成的一个GET请求。

图2-5 使用POSTMAN调用许可证服务

现在我们已经有了一个服务的运行骨架。但从开发的角度来看,这服务还不完整。良好的微服务设计不可避免地将服务分成定义明确的业务逻辑和数据访问层。在后面几章中,我们将继续对此服务进行迭代,并进一步深入了解如何构建该服务。

我们来看看最后一个视角——探索DevOps工程师如何实施服务并将其打包以部署到云中。

对于DevOps工程师来说,微服务的设计关乎在投入生产后如何管理服务。编写代码通常是很简单的,而保持代码运行却是困难的。

虽然DevOps是一个丰富而新兴的IT领域,在本书后面,读者将基于4条原则开始微服务开发工作并根据这些原则去构建。这些原则具体如下。

(1)微服务应该是独立的可独立部署的,多个服务实例可以使用单个软件制品进行启动和拆卸。

(2)微服务应该是可配置的。当服务实例启动时,它应该从中央位置读取需要配置其自身的数据,或者让它的配置信息作为环境变量传递。配置服务无须人为干预。

(3)微服务实例需要对客户端是透明的。客户端不应该知道服务的确切位置。相反,微服务客户端应该与服务发现代理通信,该代理将允许应用程序定位微服务的实例,而不必知道微服务的物理位置。

(4)微服务应该传达它的健康信息,这是云架构的关键部分。一旦微服务实例无法正常运行,客户端需要绕过不良服务实例。

这4条原则揭示了存在于微服务开发中的悖论。微服务在规模和范围上更小,但使用微服务会在应用程序中引入了更多的活动部件,特别是因为微服务在它自己的分布式容器中彼此独立地分布和运行,引入了高度协调性的同时也更容易为应用程序带来故障点。

从DevOps的角度来看,必须解决微服务的运维需求,并将这4条原则转化为每次构建和部署微服务到环境中时发生的一系列生命周期事件。这4条原则可以映射到以下运维生命周期步骤。

图2-6展示了这4个步骤是如何配合在一起的。

图2-6 当微服务启动时,它将在其生命周期中经历多个步骤

构建12-Factor微服务应用程序

本书最大的希望之一就是读者能意识到成功的微服务架构需要强大的应用程序开发和DevOps实践。这些做法中最简明扼要的摘要可以在Heroku的12-Factor应用程序宣言中找到。此文档提供了12种最佳实践,在构建微服务的时候应该始终将它们记在脑海中。在阅读本书时,读者将看到这些实践相互交织成例子。我将其总结如下。

代码库——所有应用程序代码和服务器供应信息都应该处于版本控制中。每个微服务都应在源代码控制系统内有自己独立的代码存储库。

依赖——通过构建工具,如Maven(Java),明确地声明应用程序使用的依赖项。应该使用其特定版本号声明第三方JAR依赖项,这样能够保证微服务始终使用相同版本的库来构建。

配置——将应用程序配置(特别是特定于环境的配置)与代码分开存储。应用程序配置不应与源代码在同一个存储库中。

后端服务——微服务通常通过网络与数据库或消息系统进行通信。如果这样做,应该确保随时可以将数据库的实施从内部管理的服务换成第三方服务。第10章将演示如何将服务从本地管理的Postgres数据库移动到由亚马逊管理的数据库。

构建、发布和运行——保持部署的应用程序的构建、发布和运行完全分开。一旦代码被构建,开发人员就不应该在运行时对代码进行更改。任何更改都需要回退到构建过程并重新部署。一个已构建服务是不可变的并且是不能被改变的。

进程——微服务应该始终是无状态的。它们可以在任何超时时被杀死和替换,而不用担心一个服务实例的丢失会导致数据丢失。

端口绑定——微服务在打包的时候应该是完全独立的,可运行的微服务中要包含一个运行时引擎。运行服务时不需要单独的Web或应用程序服务器。服务应该在命令行上自行启动,并通过公开的HTTP端口立即访问。

并发——需要扩大时,不要依赖单个服务中的线程模型。相反,要启动更多的微服务实例并水平伸缩。这并不妨碍在微服务中使用线程,但不要将其作为伸缩的唯一机制。横向扩展而不是纵向扩展。

可任意处置——微服务是可任意处置的,可以根据需要启动和停止。应该最小化启动时间,当从操作系统收到kill信号时,进程应该正常关闭。

开发环境与生产环境等同——最小化服务运行的所有环境(包括开发人员的台式机)之间存在的差距。开发人员应该在本地开发时使用与微服务运行相同的基础设施。这也意味着服务在环境之间部署的时间应该是数小时,而不是数周。代码被提交后,应该被测试,然后尽快从测试环境一直提升到生产环境。

日志——日志是一个事件流。当日志被写出时,它们应该可以流式传输到诸如Splunk或Fluentd这样的工具,这些工具将整理日志并将它们写入中央位置。微服务不应该关心这种情况发生的机制,开发人员应该在它们被写出来的时候通过标准输出直观地查看日志。

管理进程——开发人员通常不得不针对他们的服务执行管理任务(数据移植或转换)。这些任务不应该是临时指定的,而应该通过源代码存储库管理和维护的脚本来完成。这些脚本应该是可重复的,并且在每个运行的环境中都是不可变的(脚本代码不会针对每个环境进行修改)。

从DevOps的角度来看,微服务架构背后的一个关键概念是可以快速部署微服务的多个实例,以应对变化的应用程序环境(如用户请求的突然涌入、基础设施内部的问题等)。

为了实现这一点,微服务需要作为带有所有依赖项的单个制品进行打包和安装,然后可以将这个制品部署到安装了Java JDK的任何服务器上。这些依赖项还包括承载微服务的运行时引擎(如HTTP服务器或应用程序容器)。

这种持续构建、打包和部署的过程就是服务装配(图2-6中的步骤1)。图2-7展示了有关服务装配步骤的其他详细信息。

图2-7 在服务装配步骤中,源代码与其运行时引擎一起被编译和打包

幸运的是,几乎所有的Java微服务框架都包含可以使用代码进行打包和部署的运行时引擎。例如,在图2-7中的Spring Boot示例中,可以使用Maven和Spring Boot构建一个可执行的Java JAR文件,该文件具有嵌入式的Tomcat引擎内置于其中。以下命令行示例将构建许可证服务作为可执行JAR,然后从命令行启动JAR文件:

mvn clean package && java -Cjar target/licensing-service-0.0.1-SNAPSHOT.jar

对某些运维团队来说,将运行时环境嵌入JAR文件中的理念是他们在部署应用程序时的重大转变。在传统的J2EE企业组织中,应用程序是被部署到应用程序服务器的。该模型意味着应用程序服务器本身是一个实体,并且通常由一个系统管理员团队进行管理,这些管理员管理服务器的配置,而与被部署的应用程序无关。

在部署过程中,应用程序服务器的配置与应用程序之间的分离可能会引入故障点,因为在许多组织中,应用程序服务器的配置不受源控制,并且通过用户界面和本地管理脚本组合的方式进行管理。这非常容易在应用程序服务器环境中发生配置漂移,并突然导致表面上看起来是随机中断的情况。

将运行时引擎嵌入可部署制品中的做法消除了许多配置漂移的可能性。它还允许将整个制品置于源代码控制之下,并允许应用程序团队更好地思考他们的应用程序是如何构建和部署的。

服务引导(图2-6中的步骤2)发生在微服务首次启动并需要加载其应用程序配置信息的时候。图2-8为引导处理提供了更多的上下文。

图2-8 服务启动(引导)时,它会从中央存储库读取其配置

任何应用程序开发人员都知道,有时需要使应用程序的运行时行为可配置。通常这涉及从应用程序部署的属性文件读取应用程序的配置数据,或从数据存储区(如关系数据库)读取数据。

微服务通常会遇到相同类型的配置需求。不同之处在于,在云上运行的微服务应用程序中,可能会运行数百甚至数千个微服务实例。更为复杂的是,这些服务可能分散在全球。由于存在大量的地理位置分散的服务,重新部署服务以获取新的配置数据变得难以实施。

将数据存储在服务器外部的数据存储中解决了这个问题,但云上的微服务提出了一系列独特的挑战。

(1)配置数据的结构往往是简单的,通常读取频繁但不经常写入。在这种情况下,使用关系数据库就是“杀鸡用牛刀”,因为关系数据库旨在管理比一组简单的键值对更复杂的数据模型。

(2)因为数据是定期访问的,但是很少更改,所以数据必须具有低延迟的可读性。

(3)数据存储必须具有高可用性,并且靠近读取数据的服务。配置数据存储不能完全关闭,否则它将成为应用程序的单点故障。

在第3章中,将介绍如何使用简单的键值数据存储之类的工具来管理微服务应用程序配置数据。

从微服务消费者的角度来看,微服务应该是位置透明的,因为在基于云的环境中,服务器是短暂的。短暂意味着承载服务的服务器通常比在企业数据中心运行的服务的寿命更短。可以通过分配给运行服务的服务器的全新IP地址来快速启动和拆除基于云的服务。

通过坚持将服务视为短暂的可自由处理的对象,微服务架构可以通过运行多个服务实例来实现高度的可伸缩性和可用性。服务需求和弹性可以在需要的情况下尽快进行管理。每个服务都有一个分配给它的唯一和非永久的IP地址。短暂服务的缺点是,随着服务的不断出现和消失,手动或手工管理大量的短暂服务容易造成运行中断。

微服务实例需要向第三方代理注册。此注册过程称为服务发现(见图2-6中的步骤3,以及图2-9中有关此过程的详细信息)。当微服务实例使用服务发现代理进行注册时,微服务实例将告诉发现代理两件事情:服务实例的物理IP地址或域名地址,以及应用程序可以用来查找服务的逻辑名称。某些服务发现代理还要求能访问到注册服务的URL,服务发现代理可以使用此URL来执行健康检查。

图2-9 服务发现代理抽象出服务的物理位置

然后,服务客户端与发现代理进行通信以查找服务的位置。

服务发现代理不只是扮演了一名引导客户端到服务位置的交通警察的角色。在基于云的微服务应用程序中,通常会有多个服务实例运行,其中某些服务实例迟早会出现一些问题。服务发现代理监视其注册的每个服务实例的健康状况,并从其路由表中移除有问题的服务实例,以确保客户端不会访问已经发生故障的服务实例。

在发现微服务后,服务发现代理将继续监视和ping健康检查接口,以确保该服务可用。这是图2-6中的步骤4。图2-10提供了此步骤的上下文。

图2-10 服务发现代理使用公开的健康状况URL来检查微服务的“健康状况”

通过构建一致的健康检查接口,我们可以使用基于云的监控工具来检测问题并对其进行适当的响应。

如果服务发现代理发现服务实例存在问题,则可以采取纠正措施,如关闭出现故障的实例或启动另外的服务实例。

在使用REST的微服务环境中,构建健康检查接口的最简单的方法是公开可返回JSON净荷和HTTP状态码的HTTP端点。在基于非Spring Boot的微服务中,开发人员通常需要编写一个返回服务健康状况的端点。

在Spring Boot中,公开一个端点是很简单的,只涉及修改Maven构建文件以包含Spring Actuator模块。Spring Actuator提供了开箱即用的运维端点,可帮助用户了解和管理服务的健康状况。要使用Spring Actuator,需要确保在Maven构建文件中包含以下依赖项:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

如果访问许可证服务上的http://localhost:8080/health端点,则应该会看到返回的健康状况数据。图2-11提供了返回数据的示例。

图2-11 服务实例的健康状况检查使监视工具能确定服务实例是否正在运行

如图2-11所示,健康状况检查不仅仅是微服务是否在运行的指示器,它还可以提供有关运行微服务实例的服务器状态的信息,这样可以获得更丰富的监控体验。[2]

云中的微服务看起来很简单,但要想成功,却需要有一个综合的视角,将架构师、开发人员和DevOps工程师的视角融到一起,形成一个紧密结合的视角。每个视角的关键结论概括如下。

(1)架构师——专注于业务问题的自然轮廓。描述业务问题域,并听取别人所讲述的故事,按照这种方式,筛选出目标备选微服务。还要记住,最好从“粗粒度”的微服务开始,并重构到较小的服务,而不是从一大批小型服务开始。微服务架构像大多数优秀的架构一样,是按需调整的,而不是预先计划好的。

(2)软件工程师——尽管服务很小,但并不意味着就应该把良好的设计原则抛于脑后。专注于构建分层服务,服务中的每一层都有离散的职责。避免在代码中构建框架的诱惑,并尝试使每个微服务完全独立。过早的框架设计和采用框架可能会在应用程序生命周期的后期产生巨大的维护成本。

(3)DevOps工程师——服务不存在于真空中。尽早建立服务的生命周期。DevOps视角不仅要关注如何自动化服务的构建和部署,还要关注如何监控服务的健康状况,并在出现问题时做出反应。实施服务通常需要比编写业务逻辑更多的工作,也更需要深谋远虑。

[1] 最全面的REST服务设计方面的著作可能是Ian Robinson等人的REST in Practice

[2] Spring Boot提供了大量用于自定义健康检查的选项。有关这方面的更多细节,请查看Spring Boot in Action这本优秀的书。作者Craig Walls详细介绍了配置Spring Boot Actuator的所有不同机制。


相关图书

微服务之道
微服务之道
微服务实战
微服务实战
Istio实战指南
Istio实战指南
微服务实践
微服务实践
Git高手之路
Git高手之路
深入理解Spring Cloud与微服务构建
深入理解Spring Cloud与微服务构建

相关文章

相关课程