javascript
跟我极速尝鲜 Spring Boot 2.3
Spring Boot 2.3 已經發布一個月了,這兩天才想起來嘗一嘗鮮兒。除了常規的升級外,很大部分的升級是針對 Docker 的,讓你不得不相信,Docker 容器化微服務已然大勢所趨。還沒有用過的同學,再不下手就晚了。
此次升級主要包括如下幾個方面,接下來就跟著我一起來嘗一嘗吧。
??
準備工作
為了說明 Spring Boot 2.3 的新特性,必須創建一個項目,以便試驗。
創建一個項目并啟動
1、創建一個 Spring Boot 項目,可以到 https://start.spring.io/ 上創建,也可以使用 IDEA 自帶的功能創建。選擇版本 2.3.1,JDK 還是選擇親愛的 Java 8,引入 Web 和 Actuator 兩個依賴包。
有一點要注意一下,在我寫本文的時候,Spring Boot 2.3.1 還不能從中央倉庫下載,需要添加 Spring Boot 官方的里程碑倉庫。
<repositories><repository><id>spring-milestone</id><name>Spring?Milestone?Repository</name><url>https://repo.spring.io/milestone</url></repository> </repositories>2、在 pom 文件中引入 Maven 插件
<build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>2.3.1.RELEASE</version></plugin></plugins> </build>3、添加一個 Controller,做測試用。
@RestController public?class?PlayController?{@GetMapping(value?=?"play")public?String?play(){return?"hey,?play?with?me!";} }4、啟動項目
mvn?spring-boot:run5、訪問 http://localhost:8080/play,說明項目啟動成功
更好的 Docker 支持
如果不使用 Docker 呢,那就直接打成 jar 包,使用如下命令
mvn?package?spring-boot:repackage然后就可以把這個 Jar包部署到服務器了,當然這個過程可能是用自動化部署工具實現的,不如 jenkins 或者自研系統。
之前 Docker 打包方式
拋開公司(尤其是大廠)里成熟的自動化部署流程不談,我這里說的是一般性小廠或者是個人項目。
如果你在之前的版本就已經用 Docker 方式,那基本上都是自己寫 Dockerfile ,然后自己寫腳本使用 Dockerfile 打鏡像包,或者使用 Maven 插件,比如 dockerfile-maven-plugin。
Cloud Native Buildpacks
如果你了解 Dockerfiles 的話,那你肯定了解用 Dockerfiles 構建鏡像的過程,需要你創建一個 Dockerfile 文件然后在里面寫上構建鏡像所需的一系列動作,而 Cloud Native Buildpacks 則無需配置類似的過程文件,很大程度上減輕了開發者的工作,提高了效率。這還不是最重要的,最重要的是它提供了更高層次的抽象能力,使鏡像的分層更加清晰,并且合理有效的利用層緩存,這樣一來,當我們對應用程序進行修改之后,再次構建鏡像時的速度飛快,比如我們的應用只改了幾行代碼,那當我們使用 Buildpacks 構建鏡像時,只需要在應用程序層進行重新構建,其他層使用緩存就可以,也就是只對變化了的層重新構建。
Spring Boot 2.3 Docker 方式
首先要確保你本地已經正常啟動了 Docker 服務。
Spring Boot 2.3 官方的 Docker Maven 插件,從此不用再借助第三方了。我們前面創建項目的時候已經引入了這個 Maven 插件。
此插件不僅提供了打鏡像包的功能,還有其他的常用功能,比如 run、repackage 等。
為什么前面要說 Cloud Native Buildpacks 呢,不是跑題啊,是因為 Spring Boot 2.3 生成 Docker 鏡像包的方式就是集成了 Cloud Native Buildpacks。
那我們就打個鏡像包試一下吧
mvn?spring-boot:build-image你以為馬上就能看到成果了嗎,還是太年輕。
大中華區開發者怎么了
對于中國的開發者來說,打包這一步不會太順利,原因大家都很清楚。不出意外的話,應該會出現這樣的錯誤,不出錯可能才是意外。
[ERROR]?Failed?to?execute?goal?org.springframework.boot:spring-boot-maven-plugin:2.3.1.RELEASE:build-image?(default-cli)?on?project?play:?Execution?default-cli?of?goal?org.springframework.boot:spring-boot-maven-plugin:2.3.1.RELEASE:build-image?failed:?Docker?API?call?to?'localhost/v1.24/images/create?fromImage=gcr.io%2Fpaketo-buildpacks%2Fbuilder%3Abase-platform-api-0.3'?failed?with?status?code?500?"Internal?Server?Error"?and?message?"Get?https://gcr.io/v2/:?net/http:?request?canceled?while?waiting?for?connection?(Client.Timeout?exceeded?while?awaiting?headers)"?->?[Help?1]出現這個問題的原因是因為 Buildpacks 調用 Docker API 創建鏡像的方法,要訪問 https://gcr.io ,從上面 pull 一些基礎鏡像下來,這是 Google 的 Google Cloud ,是 Google 的容器倉庫,然而對于中國的開發者來說,這個地址是 404 的。
所以我們要加個系統級別代理,或者專門為 Docker 配置代理。我是在 Docker 中配置的代理,系統代理的影響太大。我本機安裝的是 Docker Desktop,直接打開設置,在里面加上代理就可以了(別問我代理怎么搞,問我就是沒有代理)。
好了,通過上面一頓猛如虎的操作,再次運行命令
mvn?spring-boot:build-image根據你的網速,等上一段時間,就會出現下面的結果,說明鏡像創建成功了。
之后你可以使用 docker images命令查看。這時間也是醉了,40 years ago。
使用此鏡像啟動容器
使用命令直接啟動容器。
docker?run?-it?-p8080:8080?play:0.0.1-SNAPSHOT然后訪問 8080 端口,得到正確的返回結果,說明啟動成功了。
Docker Image 的一個特點是,每個層都是前一層變化的增量。有一個工具叫做 dive,可以清楚的查看分層結構里面包含的內容。具體安裝和使用請自行搜索。
使用 dive 查看的一個小技巧,因為鏡像層包含的指令很多,所以我們選擇只查看相對于上一層的增量內容,使用 Ctrl+L組合鍵。
然后按 Tab進入視圖,然后按 Ctrl+U,去掉沒有更改的選項,也就是只看變化的部分。
然后上下箭頭可以切換層查看,比如下面這個圖展示了一個 18 M 的層相對于上一層的變化內容,可以看出來這個層實際上就是應用程序層,包含了很多當前應用程序的類和第三方依賴包等。
??
分層 jar 包
分層打包配置很方便,最簡單的方式就是在 pom 文件中加上如下配置:
<plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>2.3.1.RELEASE</version><configuration><layers><enabled>true</enabled></layers></configuration> </plugin>加上分層配置之后,仍然使用常規的命令打包
mvn?package?spring-boot:repackage分層打包其實和以前的打包方式沒有什么不同,打出來的包幾乎和之前是完全一樣的,分層其實只是邏輯上的抽象而已。打出的 jar 包結構如下(jar包其實就是個壓縮包,可以解壓縮查看目錄結構)
在 jar 包的 BOOT-INF 目錄下可以看到 classpath.idx和layers.idx兩個文件,這兩個就是為了分層 jar 的關鍵。
默認情況下會分成如下四個層。
dependencies 對版本沒有要求的依賴包,也就是你的應用程序無論怎么改,都幾乎不會影響的依賴包。
spring-boot-loader Spring Boot 加載類。
snapshot-dependencies對應用版本有要求的依賴包,比如應用升級后,可能同時需要升級的依賴包。
application 應用程序編譯類和配置文件等。
在 layers.idx可以看出這個分層結構,用普通的文本編輯器就可以打開,比如 sublime。打開之后看到這樣一個類似于 yaml 的結構,四個層以及他們所指的目錄都清晰的列出來了。
-?"dependencies":-?"BOOT-INF/lib/" -?"spring-boot-loader":-?"org/" -?"snapshot-dependencies": -?"application":-?"BOOT-INF/classes/"-?"BOOT-INF/classpath.idx"-?"BOOT-INF/layers.idx"-?"META-INF/"classpath.idx文件列出了依賴的 jar 包列表,到時候會按照這個順序載入。
-?"spring-boot-starter-actuator-2.3.1.RELEASE.jar" -?"spring-boot-starter-2.3.1.RELEASE.jar" -?"spring-boot-2.3.1.RELEASE.jar" -?"spring-boot-autoconfigure-2.3.1.RELEASE.jar" -?"spring-boot-starter-logging-2.3.1.RELEASE.jar" -?"logback-classic-1.2.3.jar" -?"logback-core-1.2.3.jar" -?"log4j-to-slf4j-2.13.3.jar"自定義分層結構
如果我們想要在默認的 4 層上增加新的分層,Spring Boot 2.3 也提供了定制分層的功能。配置也很簡單,在 plugin配置如下,指定了 layers.xml作為自定義分層配置
<configuration><layers><enabled>true</enabled><configuration>${project.basedir}/src/layers.xml</configuration></layers> </configuration>layers.xml的配置像下面這樣
<layers?xmlns="http://www.springframework.org/schema/boot/layers"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/boot/layershttps://www.springframework.org/schema/boot/layers/layers-2.3.xsd"><application><into?layer="spring-boot-loader"><include>org/springframework/boot/loader/**</include></into><into?layer="application"?/></application><dependencies><into?layer="snapshot-dependencies"><include>*:*:*SNAPSHOT</include></into><into?layer="dependencies"?/></dependencies><layerOrder><layer>dependencies</layer><layer>spring-boot-loader</layer><layer>snapshot-dependencies</layer><layer>application</layer></layerOrder> </layers>當你開啟分層功能后,可以使用 -Djarmode查看分層情況。
java?-Djarmode=layertools?-jar?target/play-0.0.1-SNAPSHOT.jar?list顯示的結果就是分層情況,比如默認情況下就是這樣,列出了 4 個默認分層。
dependencies spring-boot-loader snapshot-dependencies application題外話:
Djarmode其實就是個 Java-Agent。
分層包的意義
說了半天分層包了,那分層包到底有啥用呢?
這么說吧,它其實是為了和 Docker 配合使用的,如果你不用 Docker 方式部署,還是用原始 jar 包的方式,可以說沒什么用,如果非得說有什么用,那就是讓你更加清楚項目的依賴情況。
分層包 和 Docker 結合
前面介紹 Docker 鏡像包的時候說了 Buildpacks 可以讓你的鏡像分層清晰,而 Spring Boot 2.3 提供的分層 jar 功能可以在鏡像分層的基礎上更上一層樓,使分層更加清晰。
那我們開啟分層配置,然后重新打個 Docker 鏡像出來看一看。
mvn?spring-boot:build-image然后再使用 dive 工具看一下啟用分層 jar 功能后的 Docker 鏡像分層情況,是不是變得更好了。前面的層都是一樣的,都是一些集成鏡像和配置,從 18 MB 的這個層開始的 4 個層就是啟用分層后的4個層,分別對應 dependencies、spring-boot-loader、snapshot-dependencies、application
比如這個 5.4K 的 application 層。
那這樣做有什么好處呢,前面不是說了嗎,Buildpacks 打鏡像包會使用緩存的,如果這一層沒變那就不用重新打這一層,只需要重新打包修改過的層,這樣一來,如果你只修改了 application 中的內容,比如新加了 Controller 或者配置文件等,那么只需要重新打包這一層,也就是幾 K,幾十K 不會很大,這樣一來打包速度就很快了,要不然一個上百兆的鏡像包也得需要一段時間。
優雅停機功能
什么叫優雅停機呢,假設這是一個分布式服務,其中一臺服務所在的實體機需要打安全補丁,需要關機重啟,那實體機關機之前要先把這個服務停掉。
關掉服務的方式,比如:
我不管,我就直接關實體機,至于服務,你命由我不由天。
也好辦,kill -9 ,一行命令解決,也挺省心。
額,還行吧,但是有點兒問題,比如當前服務實例正在處理請求,還沒處理完,你咔嚓一下就給它結束了,誰受得了,不要太刺激。
我們把前面的那個 Controller 中的 play方法改一下,加一個延時,等待 6 秒才返回,模擬一個比較慢的請求。
@GetMapping(value?=?"play") public?String?play()?throws?InterruptedException{Thread.sleep(6000);return?"hey,?play?with?me!"; }效果就是你訪問這個地址,然后等了 6 秒之后才顯示出 hey, play with me!。
如果在這 6 秒鐘之內我殺掉了進程,將會在瀏覽器中出現下面這個討厭的界面。
啟用優雅關機
只需要在配置文件中增加 server.shutdown的配置,一種是 immediate,也就是立即停止,另一種就是所謂的優雅關機 graceful。
server:port:?8081shutdown:?graceful#?緩沖10s,上面定義的那個方法延時?6秒,所以10秒肯定夠了 spring:lifecycle:timeout-per-shutdown-phase:?10s之后,再啟動服務,然后訪問這個頁面,這個過程中結束進程。然后會看到控制臺有輸出,提示優雅關機的過程,并提示說會等待活動狀態的請求處理完成。
請求也變得正常了。
活動狀態檢測
之前版本的 spring-boot-starter-actuator就已經有健康狀態檢測了,不開啟活性狀態檢測,當我們訪問 health 的時候,會看到下面的信息,說明服務是可用的。
通過在配置文件中配置如下信息,可開啟活動狀態檢測。
management:health:probes:enabled:?trueendpoint:health:show-details:?always開啟上述配置之后,重啟服務,在訪問 health 頁面,看到的內容如下
除了狀態標示外,還多了一個 groups節點。
Liveness:應用程序是否處于可用狀態
可通過 ?/actuator/health/liveness 路徑查看
Readiness:應用程序是否準備好接受客戶端請求了。
可通過 /actuator/health/readiness路徑查看
這個功能其實是針對部署在 Kubernetes 上的服務做的支持。Kubernetes 提供了 LivenessProbe 和 cProbe 兩類探針,活動狀態檢查便是對這兩類探針提供無縫支持。
在配置文件中增加配置即可,與 kubernetes 做無縫對接。
spring:main:cloud-platform:?kubernetes那應該怎么用呢
拿 Readiness 來說吧,假設我們要對外宣布次服務暫時不接受請求,那就改變 readiness 的狀態,當探針過來的時候發現不接受請求,那就去請求其他實例了。
具體怎么做呢,我在 Controller 中加了兩個方法,一個開啟接受請求,一個停止接收請求。
@RestController public?class?PlayController?{private?final?ApplicationEventPublisher?publisher;public?PlayController(ApplicationEventPublisher?publisher)?{this.publisher?=?publisher;}@GetMapping(value?=?"play")public?String?play()?throws?InterruptedException{Thread.sleep(6000);return?"hey,?play?with?me!";}@GetMapping(value?=?"up")public?String?up(){AvailabilityChangeEvent.publish(publisher,this,?ReadinessState.ACCEPTING_TRAFFIC);return?"up";}@GetMapping(value?=?"down")public?String?down(){AvailabilityChangeEvent.publish(publisher,this,?ReadinessState.REFUSING_TRAFFIC);return?"down";} }通過 AvailabilityChangeEvent這個類的 publish 方法,更改自身服務狀態。當我們訪問 down 接口之后,再次查看 health/readiness的狀態情況,會顯示如下內容:OUT_OF_SERVICE表示離線,不接受請求。
而當我們請求 up 接口后,服務狀態又變成了 up,這也就實現了服務下線和上線的功能。
支持 JDK 14
Spring Boot 2.3 支持 JDK 14了,但跟我有啥關系嗎,沒有。我依然用我的 Java 8。真香。
Spring Data Neumann
Spring Boot 2.3發布了 Spring Data Neumann,其中包含許多主要版本和驅動程序升級。此版本還增加了對 R2DBC(Reactive Relational Database Connectivity) 的穩定版本支持。R2DBC 提供了異步編程方式訪問數據庫的 API,主要是配合開發異步非阻塞式的應用程序使用的。
總結
從中可以看出很大部分內容都是與 Docker 容器技術有關的,比如分層打鏡像包、無縫支持 kubernetes 等,可見 docker 微服務已然成為很多開發者的選擇。但是仍然有待改進,比如默認的 docker hub 是 Google Cloud,就不能靈活配置,支持國內的鏡像倉庫不好嗎。
你們用的 Spring Boot 哪個版本,會來嘗個鮮兒嗎?
參考文檔:
https://docs.spring.io/spring-boot/docs/2.3.1.RELEASE/maven-plugin/reference/html/index.html#goals
https://medium.com/@TimvanBaarsen/whats-new-in-spring-boot-2-3-22d01d036f11
有道無術,術可成;有術無道,止于術
歡迎大家關注Java之道公眾號
好文章,我在看??
新人創作打卡挑戰賽發博客就能抽獎!定制產品紅包拿不停!總結
以上是生活随笔為你收集整理的跟我极速尝鲜 Spring Boot 2.3的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: NYOJ 518 取球游戏
- 下一篇: 看完这篇你就知道什么是无服务器架构了