Spring Cloud微服务:入门、实战与进阶(txt+pdf+epub+mobi电子书下载)


发布时间:2020-06-15 05:17:24

点击下载

作者:尹吉欢

出版社:机械工业出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

Spring Cloud微服务:入门、实战与进阶

Spring Cloud微服务:入门、实战与进阶试读:

前言

为什么要写这本书

在互联网时代,互联网产品的最大特点就是需要快速发布新功能,支持高并发和大数据。传统的架构已经慢慢不能支撑互联网业务的发展,这时微服务架构便顺势而出。

最开始,国内很多公司都是基于阿里开源的Dubbo框架来构建微服务的,由于阿里内部的原因,Dubbo已经几年没进行维护了,不过在2018年,阿里宣布重新开始维护。反观Spring Cloud,其在国外发展得很好,但在国内,由于Dubbo的存在导致Spring Cloud鲜为人知。不过从2017年开始,Spring Cloud在国内的普及度逐渐变高,很多中小型互联网公司都开始拥抱Spring Cloud。

Spring Cloud提供一整套微服务的解决方案,基于Spring Boot可实现快速集成,且开发效率很高,堪称中小型互联网公司微服务开发的福音。而且Spring Cloud发布新功能的频率非常高,目前仅大版本就有很多个,同时还有庞大的社区支持,照这样的发展势头,我相信未来几年国内互联网公司的公布式系统开发一定是Spring Cloud的天下。

我一直在使用Spring Boot、Spring Data等一系列框架来进行开发,作为一名Spring Cloud的忠实粉丝,自然希望能够有更多开发者参与进来,于是自己坚持写Spring Cloud相关的文章,并且将文章涉及的代码整理好放在GitHub上面进行分享。在这个过程中我得到了很多开发者的关注,他们向我咨询一些微服务方面的问题,我也会在研究和解决了一些问题后,通过文章分享给各位开发者。在有幸结识了华章的杨老师后,我决定将这些文章整理成书,目的是想推广Spring Cloud在国内的使用和发展,并分享自己在微服务领域的一些小经验。

读者对象

·Java开发工程师

·Spring Cloud用户和爱好者

·微服务爱好者

本书的读者对象主要是Java开发人员,特别是工作1~3年的开发人员,这个阶段的开发人员资历尚浅,需要一些实用的技术和经验来提升自己,Spring Cloud正是一门符合提升要求的技术。因为它现在正处于快速发展的阶段,越来越多的企业也开始使用Spring Cloud。相信在不久的将来,熟练掌握Spring Cloud将会成为Java开发人员面试的门槛。

本书内容

本书主打的是与微服务相关的实战体系。第一部分是准备篇,可以帮助各位读者了解微服务以及Spring Cloud的概念。第二部分是基础篇,会对Spring Cloud中常用的模块进行详细讲解。第三部分是实战篇,开始实战性质的内容讲解,包括选择配置中心、自研发配置中心、分布式跟踪、微服务安全认证、Spring Boot Admin管理微服务、快速生成API文档等实用内容。

最后一部分是高级篇,也是难度比较大的一部分,主要内容如下:

·对Zuul进行扩展,即对认证、限流、降级、灰度发布等内容进行讲解。

·讲解缓存框架的使用,解决缓存穿透、缓存雪崩等问题。

·数据存储的选型,比如对MySQL、MongoDB、ElasticSearch的使用进行讲解。

·分布式事务的解决方案,重点是利用消息队列开发可靠性消息服务来实现数据的最终一致性。

·讲解分布式任务调度框架Elastic-Job。

·讲解分库分表的解决方案Sharding-JDBC。

勘误和支持

由于水平有限,书中难免会出现一些不准确的地方,恳请读者批评指正。为此,特贴出本书源码地址https://github.com/yinjihuan/spring-cloud。如果你遇到任何问题或者有其他宝贵意见,欢迎发送邮件至邮箱jihuan900@126.com,期待能够得到你们的真挚反馈。

致谢

首先要感谢Spring Cloud的各位开发人员,感谢你们开发出这样一个好用的框架。

感谢机械工业出版社华章公司的杨福川老师,是你在这半年多的时间中始终支持我的写作,正因为有你的鼓励和帮助,我才能顺利完成全部书稿。

感谢机械工业出版社华章公司的张锡鹏老师,是你在本书的审稿过程中给了我很多实用的建议,让我学习到了很多写作方面的技巧。

最后感谢家人的支持和理解,让我能够把全部精力投入到本书的写作中。谨以此书献给我最亲爱的家人,以及众多热爱Spring Cloud的朋友们!第一部分 准备篇

·第1章 Spring Cloud与微服务概述

·第2章 实战前的准备工作第1章 Spring Cloud与微服务概述

微服务架构是一种架构风格,而Spring Cloud是实现微服务架构的一系列框架的有序集合。本章将带你进入神秘的微服务世界,去探索微服务存在的价值及意义,并为阅读后面的章节打下扎实的理论基础。

本书涉及的源码均可在https://github.com/yinjihuan/spring-cloud中下载。如果下载失败,也可以发邮件给笔者jihuan900@126.com,或者关注微信公众号“猿天地”,直接与笔者交流。1.1 传统的单体应用

所谓单体应用程序,通俗来说就是把所有功能全部堆积在一起。这个应用大部分都是一个WAR包或者JAR包。以笔者自己搭建的技术网站“猿天地”为例,用户、文章、源码、课程都是在一个项目中的。随着业务的发展,功能的增加,多年以后这个单体项目将变得越来越臃肿。

这样的单体应用在公司创建初期是一种比较好的方案,要快速增加新功能或部署发布都比较简单。不过,随着时间的推移,危机也会慢慢显露出来。任何一个BUG都可能导致整个应用瘫痪,正所谓牵一发而动全身。1.1.1 改进单体应用的架构

架构总是通过演变而来的,既然传统的单体应用架构不能满足业务的发展,那么架构的改变必然会提上日程。在系统不能支撑当前的用户量后,我们将项目按照不同的业务来做拆分,分成多个子系统,系统之间通过Webservice或者HTTP接口来进行交互,这样做的好处是系统不再那么臃肿了。

随着用户量越来越多,系统的压力也随之增长。可能其中某一个模块使用的频率比较高,这个时候就需要对这个模块进行扩展,其实就是多部署几个节点。前面再加一个Nginx用于负载均衡,刚开始还没什么大问题,当子系统越来越多的时候,每个子系统前面都要加一层负载,对运维人员来说工作量就增加了,因为要维护的也增多了。1.1.2 向微服务靠拢

前面讲了这么多,还是不能满足互联网公司快速发展的需求,比如高并发、高可用、高扩展。于是基于之前的架构又改进了一番,引入了阿里巴巴开源的Dubbo框架,解决了服务之间的调用问题,服务调用方不再需要关注服务提供方的地址,只要从注册中心获取服务提供方的地址即可。

目前国内很多公司的微服务架构都是基于Dubbo构建的,为什么我们要转向Spring Cloud?可以从下面几个方面进行分析:

·社区的支持:

·首先Spring Cloud有强大的社区支持,在Java生态圈必定离不开Spring,且Spring Cloud的更新频率也越来越高。

·Dubbo虽然出自阿里巴巴,但是有很长一段时间没维护了,原因是内部有另一个RPC的框架HSF,所以Dubbo被抛弃了,不过去年Dubbo又重回大众视野,对使用开源框架的用户来说,社区对框架的持续维护非常重要,所以笔者认为Spring家族的产品更适合中小型公司。

·关注内容:

·Spring Cloud关注的是整个服务架构会涉及的方方面面,在Spring Cloud中各种组件应有尽有,从而使其具有可快速集成、方便、成本低等优势。

·Dubbo关注的更细一些,只针对服务治理,相当于Spring Cloud中的一个子集。能和Dubbo相互比较的应该是gRPC,Thrift之类的框架。

·性能问题:

·对于性能这块,Dubbo确实要比Spring Cloud好,原因大家也都清楚,Dubbo基于Netty的TCP及二进制的数据传输,Spring Cloud基于HTTP,HTTP每次都要创建连接,传输的也是文本内容,自然在性能上有些损耗。

·Spring Cloud带来的性能损耗对于大部分应用来说是可以接受的,而它具有的HTTP风格的API交互,在不同的语言中是通用的,且对每个微服务的测试来说是非常方便的,也就是说Spring Cloud用小的性能损耗换来了更多好处。当当网在Dubbo的基础上加上REST的支持扩展出目前的Dubbox也是这个道理。1.2 什么是微服务“微服务”一词来源于Martin Fowler的《Microservices》一文。微服务是一种架构风格,即将单体应用划分为小型的服务单元,微服务之间使用HTTP的API进行资源访问与操作。

在笔者看来,微服务架构的演变更像是一个公司的发展过程,从最开始的小公司,到后来的大集团。大集团可拆分出多个子公司,每个子公司的都有自己独立的业务、员工,各自发展,互不影响,合起来则是威力无穷。1.2.1 使用微服务架构的优势和劣势

臃肿的系统、重复的代码、超长的启动时间带给开发人员的只有无限的埋怨,丝毫没有那种很舒服的、很流畅的写代码的感觉。他们把大部分时间都花在解决问题和项目启动上面了。

1.优势

使用微服务架构能够为我们带来如下好处:

·服务的独立部署:每个服务都是一个独立的项目,可以独立部署,不依赖于其他服务,耦合性低。

·服务的快速启动:拆分之后服务启动的速度必然要比拆分之前快很多,因为依赖的库少了,代码量也少了。

·更加适合敏捷开发:敏捷开发以用户的需求进化为核心,采用迭代、循序渐进的方法进行。服务拆分可以快速发布新版本,修改哪个服务只需要发布对应的服务即可,不用整体重新发布。

·职责专一,由专门的团队负责专门的服务:业务发展迅速时,研发人员也会越来越多,每个团队可以负责对应的业务线,服务的拆分有利于团队之间的分工。

·服务可以动态按需扩容:当某个服务的访问量较大时,我们只需要将这个服务扩容即可。

·代码的复用:每个服务都提供REST API,所有的基础服务都必须抽出来,很多的底层实现都可以以接口方式提供。

2.劣势

微服务其实是一把双刃剑,既然有利必然也会有弊。下面我们来谈谈微服务有哪些弊端,以及能采取什么办法避免。

·分布式部署,调用的复杂性高:单体应用的时候,所有模块之前的调用都是在本地进行的,在微服务中,每个模块都是独立部署的,通过HTTP来进行通信,这当中会产生很多问题,比如网络问题、容错问题、调用关系等。

·独立的数据库,分布式事务的挑战:每个微服务都有自己的数据库,这就是所谓的去中心化的数据管理。这种模式的优点在于不同的服务,可以选择适合自身业务的数据,比如订单服务可以用MySQL、评论服务可以用Mongodb、商品搜索服务可以用Elasticsearch。缺点就是事务的问题了,目前最理想的解决方案就是柔性事务中的最终一致性,后面的章节会给大家做具体介绍。

·测试的难度提升:服务和服务之间通过接口来交互,当接口有改变的时候,对所有的调用方都是有影响的,这时自动化测试就显得非常重要了,如果要靠人工一个个接口去测试,那工作量就太大了。这里要强调一点,就是API文档的管理尤为重要。

·运维难度的提升:在采用传统的单体应用时,我们可能只需要关注一个Tomcat的集群、一个MySQL的集群就可以了,但这在微服务架构下是行不通的。当业务增加时,服务也将越来越多,服务的部署、监控将变得非常复杂,这个时候对于运维的要求就高了。1.2.2 重构前的准备工作

对于上不上微服务,关键在于公司的发展程度。系统是否真的到了必须做分解的地步?在上微服务之前一定要做好技术选型。用什么框架来构建微服务?公司是否支持重构?这些问题都很重要,没有公司的支持一切都是空谈。你要告诉你的上级为什么要重构,为什么要上微服务,上了之后能解决哪些问题,比如能否提高系统稳定性、能否节约机器资源等。有了明确的目标及计划,我相信这件事必成。

在重构之前,架构师一定要对公司所有的产品做一遍梳理,出一个重构方案,画一个架构图。还要对团队成员进行一次培训,讲讲重构的过程中会遇到哪些技术问题,可采用什么方式解决,在这个过程中大家能学到什么。我相信,对于有成长、有意义的事情,就算加班,大家也会开心的。这些你都不准备好,别人会觉得你没事找事,天天让他加班。

重构时最好采用循序渐进的模式,首先对一个产品进行重构规划,抽出业务服务,再抽出这个产品所依赖的基础服务,基础服务是最为重要的。等一个产品稳定之后,再重构其他产品,把核心业务放到最后面。不要想着一步登天,重构就像堆积木,堆着堆着就高了,一周抽一个微服务,慢慢就都变成微服务了。1.3 什么是Spring Cloud

Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性,巧妙地简化了分布式系统基础设施的开发,如服务注册、服务发现、配置中心、消息总线、负载均衡、断路器、数据监控等,这些都可以用Spring Boot的开发风格做到一键启动和部署。通俗地讲,Spring Cloud就是用于构建微服务开发和治理的框架集合(并不是具体的一个框架),主要贡献来自Netflix OSS。1.3.1 Spring Cloud模块介绍

Spring Cloud模块的相关介绍如下:

·Eureka:服务注册中心,用于服务管理。

·Ribbon:基于客户端的负载均衡组件。

·Hystrix:容错框架,能够防止服务的雪崩效应。

·Feign:Web服务客户端,能够简化HTTP接口的调用。

·Zuul:API网关,提供路由转发、请求过滤等功能。

·Config:分布式配置管理。

·Sleuth:服务跟踪。

·Stream:构建消息驱动的微服务应用程序的框架。

·Bus:消息代理的集群消息总线。

除了上述模块,还有Cli、Task……。本书只介绍一些常用的模块。

Spring Cloud是一个非常好的框架集合,它包含的功能模块非常多,这里不可能一一讲解到,凡是在本书中出现的模块都是真实开发中用得到的。对于那些没有在本书中进行讲解的模块,大家也可以自[1]行学习,当然有任何问题也可以咨询笔者。

[1] 联系邮箱:jihuan900@126.com。1.3.2 Spring Cloud版本介绍

相信大家跟笔者一样,在第一次访问Spring Cloud官网时一定会有一个疑惑那就是版本太多了,到底哪个是稳定版本?哪个才是自己需要的版本?接下来就给大家简单介绍一下版本的问题。

访问官网https://projects.spring.io/spring-cloud/#learn可以看到网页右侧的版本列表,如图1-1所示。

截至本书完稿时,最新的稳定版本是Finchley SR2。为什么其中还有Dalston、Edgware等这些版本?而不是像别的项目那样,版本号采用1.1、1.2、1.3这种的格式?因为Spring Cloud是一个拥有诸多子项目的大型综合项目,可以说是对微服务架构解决方案的综合套件组件,其中包含的各个子项目都独立进行着内容的迭代与更新,各自维护着自己的发布版本号。

至于怎么选择适合自己的版本,笔者认为,大家可以在接触的时候直接选最新的稳定版本。新版本中的Bug肯定要少,并且更稳定。写作本书的时候,官方发布的Spring Cloud最新稳定版本是Finchley SR2,所以本书的案例都是基于Finchley SR2进行讲解的。不同的版本有不同的功能,对应的每个子模块的版本也不一样,那么如何知道每个大版本下面具体的子模块是什么版本呢?答案就在官网的首页上面,在页面的最下方有一个表格(见表1-1),通过这个表格我们可以清楚地知道Finchley SR2对应的Spring Boot版本是2.0.6.RELEASE,Spring-Cloud-Bus是2.0.0.RELEASE。图1-1 Spring Cloud版本表1-1 Spring Cloud版本列表1.4 本章小结

Spring Cloud的诞生对于微服务架构来说简直是如鱼得水,本章主要是对微服务及Spring Cloud做了一些理论性的讲解,同时介绍了我们为什么要选择Spring Cloud、Spring Cloud有哪些内容、使用Spring Cloud能够为我们带来什么好处等。下一章我们将学习Spring Boot框架的使用方法。第2章 实战前的准备工作

工欲善其事,必先利其器。在开始学习之前,最重要的事情就是准备开发环境了,各位读者需要准备JDK1.8、Maven3.3.3、Spring Tools 4 for Eclipse。为了保证读者在实践的时候所用及所见跟本书介绍的一样,建议大家的环境跟本书所用的一致。本书适合有一定开发经验的朋友,故在环境配置这块不会讲得太细,都是一些非常基础的东西。当然,每个人的开发环境在某些方面肯定是不一样的,比如有的人用Windows,有的人用Mac,有什么问题大家可以直接联系笔者,或者上网查资料。2.1 开发环境的准备

开发环境的准备主要涉及三个方面:JDK、Maven、Spring Tools 4 for Eclipse。

1.JDK

JDK的版本用1.8即可,环境变量大家自行去配置。配置好环境变量,在命令行中输入“java–version”能够显示出版本信息即可。笔者这边用的是Mac的命令行,Windows上面用cmd。yinjihuandeMacBook-Pro:~ yinjihuan$ java -versionjava version "1.8.0_40" Java(TM) SE Runtime Environment (build 1.8.0_40-b27) Java HotSpot(TM) 64-Bit Server VM (build 25.40-b25, mixed mode)

2.Maven

Maven是用于项目构建的,本书所用的版本是3.3.3。安装完之后也需要配置环境变量,配置好后同样需要在命令行中输入“mvn–version”进行检测。yinjihuandeMacBook-Pro:~ yinjihuan$ mvn -versionApache Maven 3.3.3 (7994120775791599e205a5524ec3e0dfe41d4a06; 2015-04-22T19:57:37+08:00) Maven home: /Users/yinjihuan/Documents/java/apache-maven-3.3.3

3.Spring Tools 4 for Eclipse

大家可以选择自己熟悉的开发工具,不一定要用Spring Tools 4 for Eclipse,Spring Tools 4 for Eclipse下载的地址:http://spring.io/tools。

下载完成后,还需要安装Lombok插件,本书的示例代码会采用Lombok来简化get,set方法。安装方式可参考http://cxytiandi.com/blog/detail/6813。2.2 Spring Boot入门

Spring Cloud基于Spring Boot搭建,本节会简单介绍一下Spring Boot的使用方法,如需学习更多Spring Boot相关的内容,可以关注笔者的微信公众号“猿天地”,获取更多信息。2.2.1 Spring Boot简介

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式进行配置,从而使开发人员不再需要定义样板化的配置。Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。

在使用Spring Boot之前,我们需要搭建一个项目框架并配置各种第三方库的依赖,还需要在XML中配置很多内容。Spring Boot完全打破了我们之前的使用习惯,一分钟就可以创建一个Web开发的项目;通过Starter的方式轻松集成第三方的框架;去掉了XML的配置,全部用注解代替。

Spring Boot Starter是用来简化jar包依赖的,集成一个框架只需要引入一个Starter,然后在属性文件中配置一些值,整个集成的过程就结束了。不得不说,Spring Boot在内部做了很多的处理,让开发人员使用起来更加简单了。

下面笔者总结了一些使用Spring Boot开发的优点:

·基于Spring开发Web应用更加容易。

·采用基于注解方式的配置,避免了编写大量重复的XML配置。

·可以轻松集成Spring家族的其他框架,比如Spring JDBC、Spring Data等。

·提供嵌入式服务器,令开发和部署都变得非常方便。2.2.2 搭建Spring Boot项目

在Spring Tools 4 for Eclipse中依次选择File->New->Maven Project,然后在出现的界面中按图2-1所示增加相关信息。

完了上述操作之后,在pom.xml中添加Spring Boot的依赖,如代码清单2-1所示。编写启动类,如代码清单2-2所示。图2-1 创建maven项目

代码清单2-1 Spring Boot依赖配置 org.springframework.boot spring-boot-starter-parent 2.0.6.RELEASE org.springframework.boot spring-boot-starter-web

代码清单2-2 Spring Boot启动类@SpringBootApplicationpublic class App { public static void main(String[] args) { SpringApplication.run(App.class, args); }}

启动类使用了@SpringBootApplication注解,这个注解表示该类是一个Spring Boot应用。直接运行App类即可启动,启动成功后在控制台输出信息,默认端口是8080。Tomcat started on port(s): 8080 (http)

可以看到,我们只在pom.xml中引入了一个Web的Starter,然后创建一个普通的Java类,一个Main方法就可以启动一个Web项目。与之前的使用方式相比,这种方式简单很多。以前需要配置各种Spring相关的包,还需要配置web.xml文件,还需要将项目放入Tomcat中去执行,搭建项目的过程还特别容易出错,会出现各种jar包冲突。有了Spring Boot后这些问题都解决了。

我们之所以能够通过一个Main方法启动一个Web服务,是因为Sprig Boot中内嵌了Tomcat,然后通过内嵌的Tomcat来提供服务。当然,我们也可以使用别的容器来替换Tomcat,比如Undertow或Jetty。

Spring Tools 4 for Eclipse还为我们提供了更加便捷的项目创建方式,在File->New选项中有Spring Starter Project,可以直接选择Spring Boot的版本以及需要依赖的第三方包,直接生成Spring Boot项目,不用再去手动配置Maven依赖。这个功能和https://start.spring.io/提供的是同一个功能,方便快速搭建Spring Boot项目脚手架。2.2.3 编写第一个REST接口

本节将创建一个控制器,编写第一个REST接口,访问地址使用/hello,如代码清单2-3所示。

代码清单2-3 REST接口@RestControllerpublic class HelloController { @GetMapping("/hello") public String hello() { return "hello"; }}

@RestController是@Controller和@ResponseBody的组合注解,可以直接返回Json格式数据。@GetMapping其实就是@RequestMapping(method=RequestMethod.GET),通过访问http://localhost:8080/hello可以看到输出的结果“hello”。2.2.4 读取配置文件

在以前的项目中我们主要在XML文件中进行框架配置,业务的相关配置会放在属性文件中,然后通过一个属性读取的工具类来读取配置信息。在Spring Boot中我们不再需要使用这种方式去读取数据了。Spring Boot中的配置通常放在application.properties中,读取配置信息非常方便,总共分为3种方式。(1)Environment:可以通过Environment的getProperty方法来获取想要的配置信息,如代码清单2-4所示。

代码清单2-4 Environment读取配置@RestControllerpublic class HelloController { // 注入对象 @Autowired private Environment env; @GetMapping("/hello") public String hello() { // 读取配置 String port = env.getProperty("server.port"); return port; }}(2)@Value:可以注入具体的配置信息,如代码清单2-5所示。

代码清单2-5 @Value读取配置@RestControllerpublic class HelloController { // 注入配置 @Value("${server.port}") private String port; @GetMapping("/hello") public String hello() { return port; }}(3)自定义配置类:prefix定义配置的前缀,如代码清单2-6所示。

代码清单2-6 prefix定义配置前缀@ConfigurationProperties(prefix="com.cxytiandi")@Componentpublic class MyConfig { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; }}

读取配置的方法如代码清单2-7所示。

代码清单2-7 配置读取@RestControllerpublic class HelloController { @Autowired private MyConfig myConfig; @GetMapping("/hello") public String hello() { return myConfig.getName(); }}

定义配置application.properties的方法如下:com.cxytiandi.name=yinjihuan2.2.5 profiles多环境配置

在平时的开发中,项目会被部署到测试环境、生产环境,但是每个环境的数据库地址等配置信息都是不一样的。通过profile来激活不同环境下的配置文件就能解决配置信息不一样的问题。在Spring Boot中可以通过spring.profiles.active=dev来激活不同环境下的配置。

可以定义多个配置文件,每个配置文件对应一个环境,格式为application-环境.properties,如表2-1所示。表2-1 profile多环境配置

在开发环境中,可以通过修改application.properties中的spring.profiles.active的值来激活对应环境的配置,在部署的时候可以通过java–jar xxx.jar--spring.profiles.active=dev来指定使用对应的配置。2.2.6 热部署

开发过程中经常会改动代码,此时若想看下效果,就不得不停掉项目然后重启。对于Spring Boot项目来说,启动时间是非常快的,在微服务的架构下,每个服务只关注自己的业务,代码量也非常小,这个启动时间是可以容忍的。对于那些臃肿的单体老项目,启动时间简直是浪费生命。虽然Spring Boot启动很快,但是我们还是要自己去重启。能不能做到有改动,它就会悄无声息地自己把改动的地方重新加载一遍?答案是肯定的,通过spring-boot-devtools就可以实现。

只需要添加spring-boot-devtools的依赖即可实现热部署功能,如代码清单2-8所示。

代码清单2-8 热部署配置 org.springframework.boot artifactId>spring-boot-devtools

注意

在IDEA中就算加了插件也是没有效果的,需要开启自动编译功能,开启方法如下:首先,如图2-2所示进行操作。图2-2 开启IDEA自动编译(一)

然后,同时按下Shift+Ctrl+Alt+/,选择Registry,如图2-3所示。

最后,重启IDEA就可以了。

配置完热部署后,只要我们有保存操作,Spring Boot就会自动重新加载被修改的Class,我们再也不用手动停止、启动项目了。图2-3 开启IDEA自动编译(二)2.2.7 actuator监控

Spring Boot提供了一个用于监控和管理自身应用信息的模块,它就是spring-boot-starter-actuator。该模块使用起来非常简单,只需要加入依赖即可,如代码清单2-9所示。

代码清单2-9 Actuator配置 org.springframework.boot spring-boot-starter-actuator

启动项目我们会发现在控制台输出的内容中增加了图2-4所示的信息。

图2-4所示的这些信息是Actuator模块提供的端点信息,具体如表2-2所示,通过访问这些端点我们可以得到很多监控信息。

比如,我们访问/actuator/health可以得到下面的信息:{ "status": "UP"}图2-4 Spring Boot启动控制台输出表2-2 Actuator端点信息

UP表示当前应用处于健康状态,如果是DOWN就表示当前应用不健康。增加下面的配置可以让一些健康信息的详情也显示出来:management.endpoint.health.show-details=ALWAYS

再次访问/actuator/health,就可以得到健康状态的详细信息:{ "status": "UP", "diskSpace": { "status": "UP", "total": 491270434816, "free": 383870214144, "threshold": 10485760 }}

大部分端点默认都不暴露出来,我们可以手动配置需要暴露的端点。如果需要暴露多个端点,可以用逗号分隔,如下所示:management.endpoints.web.exposure.include=configprops,beans

如果想全部端点都暴露的话直接配置成下面的方式:management.endpoints.web.exposure.include=*

关于这些监控的信息不再赘述,大家可以自行了解。后面我们会介绍如何使用Spring Boot Admin在页面上更加直观地展示这些信息,目前都是Json格式的数据,不方便查看。2.2.8 自定义actuator端点

在很多场景下,我们需要自定义一些规则来判断应用的状态是否健康,可以采用自定义端点的方式来满足多样性的需求。如果我们只是需要对应用的健康状态增加一些其他维度的数据,可以通过继承AbstractHealthIndicator来实现自己的业务逻辑。如代码清单2-10所示。

代码清单2-10 扩展健康端点@Componentpublic class UserHealthIndicator extends AbstractHealthIndicator { @Override protected void doHealthCheck(Builder builder) throws Exception { builder.up().withDetail("status", true); //builder.down().withDetail("status", false); }}

通过up方法指定应用的状态为健康,down方法指定应用的状态为不健康。withDetail方法用于添加一些详细信息。

访问/actuator/health,可以得到我们自定义的健康状态的详细信息:{ "status": "UP", "details": { "user": { "status": "UP", "details": { "status": true } }, "diskSpace": { "status": "UP", "details": { "total": 249795969024, "free": 7575375872, "threshold": 10485760 } } }}

上面我们是在框架自带的health端点中进行扩展,还有一种需求是完全开发一个全新的端点,比如查看当前登录的用户信息的端点。自定义全新的端点很简单,通过@Endpoint注解就可以实现。如代码清单2-11所示。

代码清单2-11 自定义端点@Component@Endpoint(id = "user")public class UserEndpoint { @ReadOperation public List> health() { List> list = new ArrayList<>(); Map map = new HashMap<>(); map.put("userId", 1001); map.put("userName", "yinjihuan"); list.add(map); return list; }}

访问/actuator/user可以看到返回的用户信息如下:[ { "userName": "yinjihuan", "userId": 1001 }]2.2.9 统一异常处理

对于接口的定义,我们通常会有一个固定的格式,比如:{ "status": true, "code": 200, "message": null, "data": [ { "id": "101", "name": "jack" }, { "id": "102", "name": "jason" } ]}

但是,如果调用方在请求我们的API时把接口地址写错了,就会得到一个404错误:{ "timestamp": 1492063521109, "status": 404, "error": "Not Found", "message": "No message available", "path": "/rest11/auth"}

后端服务会告诉我们哪个地址没找到,其实也挺友好。但是因为我们上面自定义的数据格式跟下面的不一致,所以当用户拿到这个返回的时候是无法识别的,其中最明显的是status字段。我们自定义的是boolean类型,用来表示请求是否成功,这里返回的就是Http的状态码,所以我们需要在发生这种系统错误时也能返回我们自定义的那种格式,那就要定义一个异常处理类(见代码清单2-12),通过这个类既可以返回统一的格式,也可以统一记录异常日志。

代码清单2-12 统一异常处理@ControllerAdvicepublic class GlobalExceptionHandler { private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); @ExceptionHandler(value = Exception.class) @ResponseBody public ResponseData defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception { logger.error("", e); ResponseData r = new ResponseData(); r.setMessage(e.getMessage()); if (e instanceof org.springframework.web.servlet .NoHandlerFoundException){ r.setCode(404); } else { r.setCode(500); } r.setData(null); r.setStatus(false); return r; }}

ResponseData是我们返回格式的实体类,其发生错误时也会被捕获到,然后封装好返回格式并返回给调用方。最后关键的一步是,在Spring Boot的配置文件中加上代码清单2-13所示配置。

代码清单2-13 异常处理配置# 出现错误时,直接抛出异常spring.mvc.throw-exception-if-no-handler-found=true# 不要为我们工程中的资源文件建立映射spring.resources.add-mappings=false

然后当我们调用一个不存在的接口时,返回的错误信息就是我们自定义的那种格式了:{ "status": false, "code": 404, "message": "No handler found for GET /rest11/auth", "data": null}

最后贴上ResponseData的定义,如代码清单2-14所示。

代码清单2-14 统一返回结果定义public class ResponseData {private Boolean status = true;private int code = 200;private String message;private Object data; // get set ...}2.2.10 异步执行

异步调用就是不用等待结果的返回就执行后面的逻辑;同步调用则需要等待结果再执行后面的逻辑。

通常我们使用异步操作时都会创建一个线程执行一段逻辑,然后把这个线程丢到线程池中去执行,代码如代码清单2-15所示。

代码清单2-15 传统多线程使用方式ExecutorService executorService = Executors.newFixedThreadPool(10);executorService.execute(() -> { try { // 业务逻辑 } catch (Exception e) { e.printStackTrace(); } finally { }});

这种方式尽管使用了Java的Lambda,但看起来没那么优雅。在Spring中有一种更简单的方式来执行异步操作,只需要一个@Async注解即可,如代码清单2-16所示。

代码清单2-16 @Async使用方式@Asyncpublic void saveLog() { System.err.println(Thread.currentThread().getName());}

我们可以直接在Controller中调用这个业务方法,它就是异步执行的,会在默认的线程池中去执行。需要注意的是,一定要在外部的类中去调用这个方法,如果在本类调用则不起作用,比如this.saveLog()。最后在启动类上开启异步任务的执行,添加@EnableAsync即可。

另外,关于执行异步任务的线程池我们也可以自定义,首先我们定义一个线程池的配置类,用来配置一些参数,具体代码如代码清单2-17所示。

代码清单2-17 线程池参数配置类@Configuration@ConfigurationProperties(prefix = "spring.task.pool")public class TaskThreadPoolConfig { // 核心线程数 private int corePoolSize = 5; // 最大线程数 private int maxPoolSize = 50; // 线程池维护线程所允许的空闲时间 private int keepAliveSeconds = 60; // 队列长度 private int queueCapacity = 10000; // 线程名称前缀 private String threadNamePrefix = "FSH-AsyncTask-"; // get set ...}

然后我们重新定义线程池的配置,如代码清单2-18所示。

代码清单2-18 线程池配置类@Configurationpublic class AsyncTaskExecutePool implements AsyncConfigurer { private Logger logger = LoggerFactory.getLogger(AsyncTaskExecutePool.class); @Autowired private TaskThreadPoolConfig config; @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(config.getCorePoolSize()); executor.setMaxPoolSize(config.getMaxPoolSize()); executor.setQueueCapacity(config.getQueueCapacity()); executor.setKeepAliveSeconds(config.getKeepAliveSeconds()); executor.setThreadNamePrefix(config.getThreadNamePrefix()); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initia lize(); return executor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { // 异步任务中异常处理 return new AsyncUncaughtExceptionHandler() { @Override public void handleUncaughtException(Throwable arg0, Method arg1, Object... arg2) { logger.error("==========================" +arg0.getMessage()+"=======================", arg0); logger.error("exception method:" + arg1.getName()); } }; }}

配置完之后我们的异步任务执行的线程池就是我们自定义的了,我们可以在属性文件里面配置线程池的大小等信息,也可以使用默认的配置:spring.task.pool.maxPoolSize=100

最后讲一下线程池配置的拒绝策略。当我们的线程数量高于线程池的处理速度时,任务会被缓存到本地的队列中。队列也是有大小的,如果超过了这个大小,就需要有拒绝的策略,不然就会出现内存溢出。目前支持两种拒绝策略:

·AbortPolicy:直接抛出java.util.concurrent.RejectedExecutionException异常。

·CallerRunsPolicy:主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,这样可以有效降低向线程池内添加任务的速度。

建议大家用CallerRunsPolicy策略,因为当队列中的任务满了之后,如果直接抛异常,那么这个任务就会被丢弃。如果是CallerRunsPolicy策略,则会用主线程去执行,也就是同步执行,这样操作最起码任务不会被丢弃。2.2.11 随机端口

在实际的开发过程中,每个项目的端口都是定好的,通过server.port可以指定端口。当一个服务想要启动多个实例时,就需要

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载