跟我極速嚐鮮 Spring Boot 2.3

來自專輯
Spring 系列
跟我極速嚐鮮 Spring Boot 2.3
古時的風箏第 80 篇原創文章 html

做者 | 風箏
公衆號:古時的風箏(ID:gushidefengzheng)
轉載請聯繫受權,掃碼文末二維碼加微信java

Spring Boot 2.3 已經發佈一個月了,這兩天才想起來嘗一嚐鮮兒。除了常規的升級外,很大部分的升級是針對 Docker 的,讓你不得不相信,Docker 容器化微服務已然大勢所趨。尚未用過的同窗,再不下手就晚了。程序員

這次升級主要包括以下幾個方面,接下來就跟着我一塊兒來嘗一嘗吧。
跟我極速嚐鮮 Spring Boot 2.3spring

準備工做

爲了說明 Spring Boot 2.3 的新特性,必須建立一個項目,以便試驗。docker

建立一個項目並啓動數據庫

一、建立一個 Spring Boot 項目,能夠到 https://start.spring.io/ 上建立,也可使用 IDEA 自帶的功能建立。選擇版本 2.3.1,JDK 仍是選擇親愛的 Java 8,引入 Web 和 Actuator 兩個依賴包。編程

跟我極速嚐鮮 Spring Boot 2.3
image-20200623155810851
有一點要注意一下,在我寫本文的時候,Spring Boot 2.3.1 還不能從中央倉庫下載,須要添加 Spring Boot 官方的里程碑倉庫。api

<repositories>
  <repository>
    <id>spring-milestone</id>
    <name>Spring Milestone Repository</name>
    <url>https://repo.spring.io/milestone</url>
  </repository>
</repositories>

二、在 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>

三、添加一個 Controller,作測試用。緩存

@RestController
public class PlayController {

    @GetMapping(value = "play")
    public String play(){
        return "hey, play with me!";
    }
}

四、啓動項目

mvn spring-boot:run

五、訪問 http://localhost:8080/play,說明項目啓動成功

跟我極速嚐鮮 Spring Boot 2.3
image-20200623161822953

更好的 Docker 支持

若是不使用 Docker 呢,那就直接打成 jar 包,使用以下命令

mvn package spring-boot:repackage

跟我極速嚐鮮 Spring Boot 2.3
image-20200623162023503

而後就能夠把這個 Jar包部署到服務器了,固然這個過程多是用自動化部署工具實現的,不如 jenkins 或者自研系統。

以前 Docker 打包方式

拋開公司(尤爲是大廠)裏成熟的自動化部署流程不談,我這裏說的是通常性小廠或者是我的項目。

若是你在以前的版本就已經用 Docker 方式,那基本上都是本身寫 Dockerfile ,而後本身寫腳本使用 Dockerfile 打鏡像包,或者使用 Maven 插件,好比 dockerfile-maven-plugin,我以前寫過一篇 Spring Boot 和 Docker 實現微服務部署,就是用的這種方式,能夠對比着看一下。

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 等。

跟我極速嚐鮮 Spring Boot 2.3
image-20200623154127475

爲何前面要說 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,直接打開設置,在裏面加上代理就能夠了(別問我代理怎麼搞,問我就是沒有代理)。
跟我極速嚐鮮 Spring Boot 2.3

image-20200623174349112

好了,經過上面一頓猛如虎的操做,再次運行命令

mvn spring-boot:build-image

根據你的網速,等上一段時間,就會出現下面的結果,說明鏡像建立成功了。
跟我極速嚐鮮 Spring Boot 2.3

以後你可使用 docker images命令查看。這時間也是醉了,40 years ago。

跟我極速嚐鮮 Spring Boot 2.3
image-20200623222649235

使用此鏡像啓動容器

使用命令直接啓動容器。

docker run -it -p8080:8080 play:0.0.1-SNAPSHOT

而後訪問 8080 端口,獲得正確的返回結果,說明啓動成功了。
跟我極速嚐鮮 Spring Boot 2.3

image-20200623161822953

Docker Image 的一個特色是,每一個層都是前一層變化的增量。有一個工具叫作 dive,能夠清楚的查看分層結構裏面包含的內容。具體安裝和使用請自行搜索。

使用 dive 查看的一個小技巧,由於鏡像層包含的指令不少,因此咱們選擇只查看相對於上一層的增量內容,使用 Ctrl+L組合鍵。
跟我極速嚐鮮 Spring Boot 2.3

image-20200622231229994
而後按 Tab進入視圖,而後按 Ctrl+U,去掉沒有更改的選項,也就是隻看變化的部分。
跟我極速嚐鮮 Spring Boot 2.3

image-20200622225041292

而後上下箭頭能夠切換層查看,好比下面這個圖展現了一個 18 M 的層相對於上一層的變化內容,能夠看出來這個層實際上就是應用程序層,包含了不少當前應用程序的類和第三方依賴包等。

跟我極速嚐鮮 Spring Boot 2.3

image-20200623223512781

分層 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包其實就是個壓縮包,能夠解壓縮查看目錄結構)
跟我極速嚐鮮 Spring Boot 2.3

image-20200624073901950

在 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/layers
                      https://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,關於Java-Agent,能夠看我以前寫的一篇文章,Java 調試工具、熱部署、JVM 監控工具都用到了它,挺有意思的。

分層包的意義

說了半天分層包了,那分層包到底有啥用呢?

這麼說吧,它實際上是爲了和 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

跟我極速嚐鮮 Spring Boot 2.3
image-20200624090924915
好比這個 5.4K 的 application 層。

跟我極速嚐鮮 Spring Boot 2.3
image-20200624092311622

那這樣作有什麼好處呢,前面不是說了嗎,Buildpacks 打鏡像包會使用緩存的,若是這一層沒變那就不用從新打這一層,只須要從新打包修改過的層,這樣一來,若是你只修改了 application 中的內容,好比新加了 Controller 或者配置文件等,那麼只須要從新打包這一層,也就是幾 K,幾十K 不會很大,這樣一來打包速度就很快了,要否則一個上百兆的鏡像包也得須要一段時間。

優雅停機功能

什麼叫優雅停機呢,假設這是一個分佈式服務,其中一臺服務所在的實體機須要打安全補丁,須要關機重啓,那實體機關機以前要先把這個服務停掉。

關掉服務的方式,好比:

  1. 我無論,我就直接關實體機,至於服務,你命由我不禁天。

  2. 也好辦,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 秒鐘以內我殺掉了進程,將會在瀏覽器中出現下面這個討厭的界面。
跟我極速嚐鮮 Spring Boot 2.3

image-20200624095526291
啓用優雅關機
只須要在配置文件中增長 server.shutdown的配置,一種是 immediate,也就是當即中止,另外一種就是所謂的優雅關機 graceful。

跟我極速嚐鮮 Spring Boot 2.3
image-20200624095818220

server:
  port: 8081
  shutdown: graceful

# 緩衝10s,上面定義的那個方法延時 6秒,因此10秒確定夠了
spring:
  lifecycle:
    timeout-per-shutdown-phase: 10s

以後,再啓動服務,而後訪問這個頁面,這個過程當中結束進程。而後會看到控制檯有輸出,提示優雅關機的過程,並提示說會等待活動狀態的請求處理完成。
跟我極速嚐鮮 Spring Boot 2.3

image-20200624103229034
請求也變得正常了。
跟我極速嚐鮮 Spring Boot 2.3

image-20200624103359637

活動狀態檢測

以前版本的 spring-boot-starter-actuator就已經有健康狀態檢測了,不開啓活性狀態檢測,當咱們訪問 health 的時候,會看到下面的信息,說明服務是可用的。

跟我極速嚐鮮 Spring Boot 2.3
image-20200624113746766
經過在配置文件中配置以下信息,可開啓活動狀態檢測。

management:
  health:
    probes:
      enabled: true
  endpoint:
    health:
      show-details: always

開啓上述配置以後,重啓服務,在訪問 health 頁面,看到的內容以下
跟我極速嚐鮮 Spring Boot 2.3

image-20200624114306674
除了狀態標示外,還多了一個 groups節點。

Liveness:應用程序是否處於可用狀態

可經過 /actuator/health/liveness 路徑查看
跟我極速嚐鮮 Spring Boot 2.3

image-20200624114517046
Readiness:應用程序是否準備好接受客戶端請求了。

可經過 /actuator/health/readiness路徑查看

跟我極速嚐鮮 Spring Boot 2.3
image-20200624114631242

這個功能實際上是針對部署在 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表示離線,不接受請求。跟我極速嚐鮮 Spring Boot 2.3

image-20200624120041609
而當咱們請求 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


公衆號:古時的風箏

一個兼具深度與廣度的程序員鼓勵師,一個本打算寫詩卻寫起了代碼的田園碼農!你可選擇如今就關注我,或者看看歷史文章再關注也不遲。

技術交流還能夠加羣或者直接加我微信。

跟我極速嚐鮮 Spring Boot 2.3

相關文章
相關標籤/搜索