首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Spring Boot 2.0的新特性解读

Spring Boot 2.0在2018年3月份发布了2.0的版本,在这个版本当中,加入了非常多的新特性,既集成了Spring Framework5.0,也集成了响应式的Spring,针对Metrics以及configuration进行了一些改进和升级。

本文中,Pivotal云计算资深架构师刘凡将针对下列内容为您详尽的介绍这些新特性。旨在帮助大家更快地在项目当中运用到Spring Boot2.0。我们也在文中提供了视频和PPT供您阅览。

响应式编程和响应式Spring

Actuator/Actuator Endpoint

Metrics/Micrometer

Configuration Properties Binding

首先,我们先来看一组数据:

2017年,Spring Boot1.X下载量增长300%

通过Initializer创建的项目增长了256%

在Github上Java项目的forks,Spring Boot1.X排名第1

Java项目的Stars排名第四

超过四百名的社区贡献者贡献到Spring Boot这个项目。

Spring Boot对Framework以及Infrastructure的升级

讲响应式编程之前,先看一下Spring Boot对Framework以及Infrastructure的升级。Spring Boot 2.0支持JAVA8+,如果想用JAVA7或者JAVA6的版本的话,可能只能用Spring Boot1.X。Spring Boot支持Tomcat8.5,支持Hibernate5.2,支持Thymeleaf3

一、响应式编程和响应式Spring

Spring Boot最重要的一个Feature,就是响应式编程。去年年底,Pivotal发布了Spring5.0版本,在5.0里面有一个很重要的Feature——响应式编程。在Spring Boot的发布当中,也集成了Spring5.0的版本。响应式编程主要是讲效率的问题,如何通过并行计算来提高整个应用程序的效率。

响应式编程的由来

最初在单核CPU时代,各个芯片厂商都在增加芯片的制造工艺,增加更高的组频。中央处理器到了一定阶段后,厂商达到了制造的瓶颈。于是就有一个很好的创意和发明,他们创造了多核CPU:一个处理器当中放入了多个核心,以达到更好的并行计算的性能。

阿姆达定律

在固定负载下,通过增强系统并行计算能力所能带来的性能加速S为:

所以在并行计算的领域,有一个非常重要的理论支撑——阿姆达尔定律。阿姆达尔定律主要内容指:在固定的负载下,通过增强系统并行计算的能力,所能达到的性能加速的加速S是这样的一个公式。这个公式里,P是并行计算所占比例;s为并行计算的节点数;S为并行计算处理效果加速比。当CPU的核数或者节点数趋于无穷的时候,加速比可以达到1/(1-P)。

通俗地讲,系统的性能取决于最慢的那个可以做并行计算的能力。换而言之,只能做串型的,最慢的工作任务,会决定了整个系统能够调优,包括改善它的性能达到最好的加速比的瓶颈。

在上面的曲线图里可以看到,横轴是CPU的核数,纵轴是加速比。举一个例子,假如说整个工作负载当中有95%是可以通过并行计算来进行加速的。那么它在CPU的核数趋于无穷的时候,它最大的加速比是达到20倍。

阻塞+线程池(Servlet)

最初的时候使用Servlet做应用服务器,它其实是一种阻塞+线程池的模式。简单举一个生活当中的例子来说明一下阻塞和非阻塞的区别。大家可能在生活当中有这样的经历:就是在周末的时候要烧水、要洗衣服。这两件事情有两种做法,一种就是先烧水,等水烧开了再去洗衣服。这种方式实际上就是阻塞的,每一个任务都需要前一个任务的完成才能进行自己的任务。那么什么是非阻塞呢?非阻塞的情况是:假设有一种水壶是响水壶,当水烧开了之后会发出声音提醒我。洗衣机也有一种功能,它有一个提示音,如果洗衣服洗好了,就会发出提示音提示我。这时候我们就可以这样做:把水放在燃气炉上开始烧,同时也把衣服放到洗衣机里开始洗,这时候就可以不管了,坐在客厅里看电视。当我的水烧开了和洗衣机提示我了,我再去处理后面的事情。这种方式在编程模式来讲就是非阻塞的。

Servlet模型就是这样的,每一个request来了之后,在后台的Server都需要有一个线程去处理这个request。这样带来一个很大的问题:当有很多request的时候,并发量很大,很有可能container里面的线程池就爆满,从而导致应用服务器崩溃。

非阻塞+事件循环(Netty)

所以如果使用非阻塞+事件循环,比较典型的应用服务器是Netty,这实际上是非阻塞的模式,在中间有一个工作的线程池,这个工作线程池是用事件驱动的方式来处理客户端发过来的request。对于应用服务器来讲,它是异步的IO。在后台,当工作完成了这个任务之后,返回到中间的工作线程,然后把结果再返回到request当中去。这样的话,极大的提高了并行计算的性能。

响应式编程的模型

举个例子,如果想要完成捕捉鼠标点击大于等于2的事件,没有用响应式编程,会发发现需要写很多程序逻辑来实现这样的一个大于等于2的点击事件。如果我们用流的方式来处理的话,首先在时间序列上有很多个点击事件,第一个操作取在250毫秒之间点击的流,完成第一个操作之后会生成一个新的流,在这个流里边可以看见这个点击操作按照250毫秒节流阀生成了一个新的流 。

然后再针对这个新形成的流,进行一次映射的操作。取到这个点击流的长度后,又生成一个新的流。 最后我对这个流做一个过滤的操作,过滤操作就是我点击次数大于等于2的,最终作为结果。

整个响应式编程都是基于流的操作。所以在响应式编程里面有两个非常重要的对象,一个是Subscriber作为观察者,另一个是Publisher作为被观察者。它们两个是怎么交互的呢?首先是观察者需要去Subscribe被观察者的动作,一旦被观察者处理完了需要处理的工作任务之后,通过onNext把返回的结果返回到观察者。这个地方onNext会带上一个星, 代表它可以从0到N,从0到数据的整个传输完成。

在传输完成之后,会触发一个事件叫onCompleted,即成功地把数据传输给被观察者之后,如果说在传输数据当中有发生错误,就会触发另外一个事件叫onError。在流当中如果一旦有onError的话,后续的操作都会中断。所以,对于响应式编程,一共是两个对象、四个事件

响应式的Spring

响应式Spring作为Spring 5.0一个非常重要的Feature,去年发布出来之后,大家也做了很多研究。这里简单介绍一下响应式编程的Reactive Stream的关系。

Reactive Stream是2013年由Pivotal和Netflix、Lightbend三家公司提出来的响应式的Stream的标准。 看过Reactive的源码的人知道,Reactive Stream只是定义了Publish和Subscriber的一个接口。

对于这个标准,不同的厂商有一个实现。比如说JDK9用flow去实现Publisher。而RxJAVA有observe和observable去实现订阅和被订阅者的关系。

今天主要介绍Pivotal的Reactive。这也是Spring5通过这个library来实现的响应式编程。除了这三个响应式库之外,还有像市面上Akka等等,都是对Reactive Stream这个流实现的类库。

接下来主要介绍一下Reactor当中响应式编程的模型。在Reactor里有两个非常重要的对象,一个是Flux,一个是Mono。这两个对象实际上都是前面讲的Publisher的对象。

Flux

0…N个元素的集合

Flux代表的是0到N的元素的集合。如图,在时间序列里面一共放有六个元素,通过Flux发射出来,在最后第六元素有一个小竖线,代表这个流发射已经成功完成了。中间每一个流都可以对它做一些操作,在这个Operator里面做的一些操作,生成一个新的流,可以发现在第三个传输完了之后,有一个叉。也就是说发生了一些异常,这个异常导致后面的这些数据就不能再生成到新的流里边了。

Flux这个模型非常适合于后台有数据库的操作,会产生一些集合类,可以通过Flux这种模型去分装list集合类

Flux示例代码

在Flux当中可以先去创建一个stream1的流,在这个流里面加入三个整型的数据。然后对这个流做映射操作,把每一个数据都乘以2。最后再对这个流进行一个过滤的操作,就是只剩下大于2的元素。这个流其实最终输出是4和6。

第二个创建一个字符串的流,在里边放了ABC三个字符串。接下来对stream1和stream2做一个zip操作,zip操作实际上就生成了一个元组的流。通过doOnNext,把原组里面的数据一一打印出来,最终通过subscribe结束这个流。因为我们之前已经把1这个数据过滤掉了,最后的输出应该是4a和6b。

除了zip的操作之外,还可以做两个流的合并。通过merge的操作,最终可以得到的是4、6、a、b、c这样的流。

接下来对比一下,如果使用传统的编程模式,不使用响应式编程的模式,大概是什么样呢?

不使用响应式

假设需要做一个后端的http的操作,我们需要调用后台的一个rest的接口,通过这种阻塞式的,假如需要调用一千次http的接口,在后续的工作当中需要等待一千次调用结束了,才能够做接下来的工作。

如果转变成用响应式编程的模式来做,就会变成这样:

使用响应式

range操作实际上就是做一个循环。通过flatMap去调用后端Reactive的方法,在后端也通过Reactive的方式去实现调用。把这个调用的结果发送到handlebody里面去,这次调用一共只有一次调用,也不用等每一次调用完成就可以得到最后的结果。

大家会问,一千次的话,是不是后台真的是在Reactor里面是同时去执行的?这里面有一个默认值,因为Reactor是基于事件驱动的,也是在后台实现的时候是用了binding queue这种方式,所以它其实是有一个队列在后面的。默认值实际上是有一个queue size 256。如果想去调整这个数值的话,可以多加一个新的参数。

Mono

0…1个元素

在Reactor里面第二个对象是Mono。Mono表明的是0到1个元素。Mono的对象适用于什么样的场景呢?比如说需要通过一个ID去取到一个数据对象,获取一个user的ID,通过ID取掉user本身。这个时候就比较适合用Mono这个对象。Mono也同样是我们放入一个元素,正常的话是complete,出错的话就是一个叉,onError的事件。

Mono示例代码

Mono的示例代码跟Flux非常像,需要注意的地方是,在just后面只能放入一个元素,假如放入多个元素的话,这个地方编译就报错了。

Spring MVC 还是 WebFlux?

在Spring Boot1.0的时候,很多开发人员多在用Spring MVC。Spring Boot2.0以后,可能会有一个问题:我还可不可以用MVC?是不是要把原有的代码都用WebFlux去改写它?用这种响应式编程的模型去改写它?

Spring MVC 还是 WebFlux?

其实在Spring Boot2.0里面,不需要改写原有的Spring MVC,因为像@Controller等等这些注解,在Spring WebFlux,同样可以支持这些注解。所以不用担心要把原有的这些Controller或者这些注解都统统改写成WebFlux。

在WebFlux里新增了一些内容,比如说Functional endpoint。 WebFlux的应用服务器默认使用的是Netty。当然如果大家有想用TomCat等其他的响应式的服务器的话,也是可以通过配置把它设置到用TomCat或者其他的服务器。

WebFlux用这种非阻塞的模式,给我们带来的不光是性能的提升,更多的是可以通过这种方式得到更好的扩展性。因为它是基于事件驱动,事件驱动把不同的系统之间可以更好的解耦,后期进行扩展会更加容易,同时还可以继续使用原来熟悉的这些注解。

Spring WebFlux示例代码

上图是一段Spring WebFlux的示例代码。可以看到,在这个示例代码当中使用了刚才提到的这两个对象,一个是Flux,一个是Mono。对于Mono来讲,是通过ID的方式,因为ID一般都是数据库的主键,所以它应该是要么返回0个元素,要么返回1个元素,也就是说,要么找到了这个元素,要么没有找到,所以它返回的是一个Mono的对象。

有一个比较特殊的,如果调用数据库的save的方法,其实save没有任何的返回池,可以用Mono void的方式去替换这种空的返回值。这是在应用服务器端的。

如果是对于数据的响应式的API的话,现在Reactive data的这种API,目前能支持的数据库是四个,包括MongoDB、Redis等等。有一些关系型数据库目前还不能支持Reactive的方式

Spring WebFlux.fn,fn指的是function。在WebFlux里面可以支持函数式的接口。函数式的接口是一个定义的路由,在这个路由实现的时候,这段代码可以完全等价于RequestMapping。前期写了很多的怎么去getMapping、postMapping等等,可以通过这么几行代码就可以完全等价的实现,它的代码量是非常少的。而且如果熟悉这种方式的话,它的可读性也是非常强的。

Router里面有两个参数,一个参数是RequestPredicate,另外一个是HandleFunction。HandleFunction需要提供一个Server的Request,作为它的输入。它的输出是一个Mono的ServerResponse对象。执行完了之后,会返回到客户端。

定义了多个访问的路径,可以通过它来查询整个list数据库,也可以去推送、创建一个新的person。

WebClient

上文提到的WebFlux实际上是在服务器端的,在客户端Spring也提供了响应式的客户端,就是WebClient。相对比RestTemplate,在Spring MVC可能会用RestTemplate来作为http去调用Server端的服务。

相比RestTemplate, WebClient有以下的不同:

非阻塞、响应式,使用更少的硬件资源提供更高的并发量

基于Java 8 lambdas提供了functional API

同时支持同步和异步的场景

支持流操作

首先它是非阻塞的、响应式的,而且使用了比较少的硬件资源,来提供更高的并发量。它是基于JAVA8的Lambda的表达式,提供了Functional API。所以当使用Functional API去完成一大段代码的时候,还是很精炼的,看到的代码也会比较优雅。同时它可以支持同步和异步的场景,支持流的这些操作。

WebClient的示例代码

这是一段WebClient的示例代码,通过WebClient最终可以通过ID的方式去取到一个Mono的person,通过一个/Quotes的方法,去得到一个Flux这样一个对象。

Spring Boot 2.0 on Pivotal Cloud Foundry

在开发运维的时候,很需要一个运维平台,所以PCF(Pivotal Cloud Foundry)作为一个PaaS平台,很好地支持了Spring Boot 2.0运行在这个平台上面。

这个平台让你可以很容易去部署云原生的Spring Boot应用。并且是一键式的,只要敲一个命令,不用进行任何的配置,就可以把应用推到这个平台上去。推上去了之后,可以去重启应用,可以去停止,也可以在这个平台上看到这个应用的一些启动或者一些事件,还有使用的一些服务、路由以及日志。

响应式编程会给我们编码带来哪些好处呢?

代码可读性高

生产者消费者之间控制流的最佳实践,有效避免内存溢出

较少的线程,使IO任务可以运行在异步/非阻塞的模式

交互性强、实时性高

良好的扩展性,利用动作/事件触发notification给下游系统

响应式编程会主要是用在哪些场景里面呢?

适合高并发消息处理的场景

有延迟的远程调用

大量慢客户端连接

往客户端推送消息

实时数据库查询

UI事件实时处理

大数据

实时数据分析

HTTP/2

比如说有高并发的,客户端是慢客户端,有比较大延迟的远程调用。如果有实时的数据库查询,还有大数据的场景,都可能比较适合用响应式编程去完成大家的应用程序的开发。

二、Actuator和Actuator Endpoint

Actuator是Spring Boot提供给开发人员去做监控、运维,以及可以通过Actuator Endpoint去跟Spring Boot应用做一些交互的组件。在Actuator里面,Spring Boot2.0也支持了三种模式,一个是Spring MVC,一个是Service WebFlux,一个是Jersey。

用Spring Boot2.0 Actuator也很简单,只要Maven里面加入这样一个依赖,Gradle的话用这样的dependencies。

在Spring Boot2.0Actuator,Endpoint做了一些更新和升级,有一些是新增的,像普罗米修斯这样的Endpoint以及Scheduledtasks、Sessions,这三个是新增的Endpoint。也有一些进行了一些更名。

Health Endpoints

在Actuator经常用到的几个端点,一个是Health为这个端点,去判断应用程序的健康状况。这个端点如果有定制化的需要,可以去实现Health Indicator这样的接口,去返回一个Health对象,在这儿做具体的逻辑、去判断这个服务或者Component是不是健康的。

如果说要用响应式的方式来返回,可以去继承ReactiveHealthIndicator这个接口,它返回的就是一个Mono的Health的对象。

在Spring Boot2.0,HealthIndicator,我们提供的主要是支持Spring MVC的。对于Actuator主要有两个,一个是Mongo,一个是Redis

三、Metrics/Micrometer

Spring Boot2.0用了一个新的组件Micrometer替换了原来的Metrics来进行实现。Micrometer是一个外置的监控的Metrics组件,主要是给Metrics提供一个统一的接口,来为其监控系统作为一个门面。这个非常类似SLF4J,SLF4J是给日志提供的统一接口。而Micrometer是给Metrics(应用的度量指标)提供了一个统一的接口。通过Micrometer可以外接很多监控系统,比如说现在支持的Prometheus、Netflix Atlas、Datadog、InfluxDB。

支持的监控系统

Micrometer也支持多维度的应用度量指标,从上图我们可以看到在Micrometer里可以支持Dimensional的监控系统,这是多维度的,通过定义你的一些标签、名称,去定义一些新的维度。假设可以对CPU或者内存使用率取最大值,通过一个表达式来生成一个新的Metrics,在监控系统里面可以自定义这些维度。不同的监控系统也有自己的取数据的方式,可以有poll的方式,可以有push的方式。大家有兴趣的话,或者在项目当中有需要的话,可以去集成这些外置的监控系统。

Prometheus的集成

对于Prometheus的集成也非常简单,只要在文件里面加入一个dependency,这个dependency加进去了之后,在Actuator-Prometheus,就可以看到Prometheus需要的Spring Boot应用的数据了。

四、Configuration Properties Binding

最后我们介绍一下对于Configuration Properties Binding的改进。

但是这里面也有一定的规则,首先两个元素之间需要用点去分割,只能用字母或者是数字。单词可以用横线或者下划线去分开。如果需要取一些系统的环境变量,其实在系统环境变量是不能用点的,可以用下划线的方式去替换这个点。如果系统环境变量里面想放一些数组,也可以通过下划线加上它的下标去放数组。这样的话可以保证Configuration Binding的行为和集合类的行为保持一致。

对于Security,现在默认的是会打开我们的Security,当然也可以通过你的配置去进行Security的配置。

本文作者:刘凡,Pivotal大中华区云计算资深架构师,长期从事软件研发和技术创新工作,曾先后就职于石化盈科、Adobe中国研发中心、IBM等大型国内外IT企业,从事软件产品研发,系统架构设计,研发管理等工作。在十多年的软件行业从业经历中,积累了丰富的分布式系统架构设计、自动化平台运维和系统稳定性调优等相关经验。近期主要专注于微服务云原生应用的开发和设计,支持多个知名客户企业进行数字化转型,对敏捷开发和传统巨石应用拆分以及往云上迁移拥有丰富的实战经验。

↓↓↓

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180507B0601V00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券