第二章 微服務構建:SpringBoot

框架簡介

在這裏介紹SpringBoot的目的除了它是SpringCloud的基礎之外,也由於其自身的各項優點,如自動化配置、快速開發、輕鬆部署等,非常適合作爲微服務架構中各項具體微服務的開發框架。所以我們強烈推薦使用SpringBoot來構建微服務,它不僅可以幫助我們快速地構建微服務,還可以輕鬆簡單地整合SpringCloud實現系統服務化,而如果使用了傳統的Spring構建方式的話,在整合過程中我們還需要做更多的依賴管理工作才能讓它們完好地運行起來。

SpringBoot的宗旨並非要重寫Spring或是替代Spring,而是希望通過設計大量的自動化配置等方式來簡化Spring原有樣板化的配置,使得開發者可以快速構建應用。除了解決配置問題之外,SpringBoot還通過一系列Stater POMs的定義,讓我們整合各項功能的時候,不需要在Maven的pom.xml中維護那些錯綜複雜的依賴關係,而是通過類似模塊化的Starter模塊定義來引用,使得依賴管理工作變得更爲簡單。
在如今容器化大行其道的時代,SpringBoot除了可以很好融入Docker之外,其自身就支持嵌入式的Tomcat、Jetty等容器。所以,通過SpringBoot構建的應用不再需要安裝Tomcat,將應用打包成war,再部署到Tomcat這樣複雜的構建與部署動作,只需將SpringBoot應用打成jar包,並通過java -jar命令直接運行就能啓動一個標準化的Web應用,這使得SpringBoot應用變得非常輕便。
SpringBoot對於構建、部署等做了這麼多的優化,自然不能少了對開發環節的優化。整個SpringBoot的生態系統都使用到了Groovy,很自然的,我們完全可以通過使用Gradle和Groovy來開發SpringBoot應用。

<!-- 如下依賴即爲Starter POMs -->
<dependency>
  <groupid>org.springfrarnework.boot</groupId> 
  <artifactid>spring-boot-starter-test</artifactid> 
  <scope>七est</scope>
</dependency> 

在使用SpringBoot構建應用的時候,各項功能模塊的整合不再像傳統Spring應用的開發方式那樣,需要在 pom.xml中做大量的依賴配置,而是通過使用Starter POMs定義的依賴包,使得功能模塊整合變得非常輕巧,易於理解與使用。SpringBoot針對常用的開發場景提供了一系列自動化配置來減少原本複雜而又幾乎很少改動的模板化配置內容

配置文件

src/rnain/resources目錄是SpringBoot的配置目錄,所以當要爲應用創建個性化配置時,應在該目錄下進行。SpringBoot的默認配置文件位置爲src/main/resources/application.properties。關於SpringBoot應用的配置內容都可以集中在該文件中,根據我們引入的不同Starter模塊,可以在這裏定義容器端口號、數據庫連接信息、日誌級別等各種配置信息。SpringBoot的配置文件除了可以使用傳統的properties文件之外,還支持現在被廣泛推薦使用的YAML文件

YAML中配置信息利用階梯化縮進的方式,其結構更爲清晰易讀,同時配置內容的字符量也得到顯著減少。除此之外,YAML還可以在一個單個文件中通過使用spring.profiles屬性來定義多個不同的環境配置。如下配置文件中在指定爲 test 環境時, server.port 將使用8081端口;而在 prod 環境中, server.port將使用8082端口;如果沒有指定環境, server.port將使用8080端口。

server:
  port: 8080
​
---
spring:
     profiles: test
server:
  port: 8081
​
---
spring:
  profiles: prod
server:
  port: 8082

YAML目前還有—些不足,它無法通過@PropertySource註解來加載配置。但是,YAML將屬性加載到內存中保存的時候是有序的,所以當配置文件中的信息需要具備順序含義時,YAML的配置方式比起properties配置文件更有優勢

自定義參數

除了可以在SpringBoot的配置文件中設置各個Starter模塊中預定義的配置屬性,也可以在配置文件中定義一些我們需要的自定義屬性。比如在application.properties中添加

book.name=SpringCloudinAction
book.author=sun

然後,在應用中可以通過@Value註解來加載這些自定義的參數

@Value("${book.name}")
private String name;

@Value註解加載屬性值的時候可以支持兩種表達式來進行配置,如下所示:

  • 一種是上面介紹的PlaceHolder方式,格式爲${...},大括號內爲PlaceHolder。
  • 另一種是使用SpEL表達式(Spring Expression Language),格式爲#{...},大括號內爲SpEL表達式。

在application.properties中的各個參數之間可以直接通過使用PlaceHolder的方式來來進行引用

book.name=SpringCloud
book.author=ZhaiYongchao
book.desc=${book.author} is writing ${book.name}

使用隨機數

在一些特殊情況下,我們希望有些參數每次被加載的時候不是一個固定的值,比如密鑰、服務端口等。在SpirngBoot的屬性配置文件中,可以通過使用${random}配置來產生隨機的int值、long值或者string字符串,這樣我們就可以容易地通過配置隨機生成屬性,而不是在程序中通過編碼來實現這些邏輯。

#隨機字符串
com.didispace.blog.value=${random.value}
#隨機int 
com.didispace.blog.number=${random.int} 
#隨機long 
com.didispace.blog.bignumber=${random.long}
# 10以內的隨機數
com.didispace.blog.tes七 1=${random.int(l0)}
# 10-20的隨機數 
com.didispace.blog.test2=${random.int[l0,20]}

命令行參數

java -jar該命令除了啓動應用之外,還可以在命令行中指定應用的參數,比如java -jar xxx.jar --server.port=8888,直接以命
令行的方式來設置server.port屬性,並將啓動應用的端口設爲8888。在用命令行方式啓動SpringBoot應用時,連續的兩個減號–就是對application.properties中的屬性值進行賦值的標識。所以java -jar xxx.jar --server.port=8888命令,等價與在application.properties中添加屬性server.port=8888。
通過命令行來修改屬性值是SpringBoot非常重要的一個特性。通過此特性,理論上已經使得應用的屬性在啓動前是可變的,所以其中的端口號也好、數據庫連接也好,都是可以在應用啓動時發生改變的,而不同於以往的Spring應用通過Maven的Profile在編譯器中進行不同環境的構建。SpringBoot的這種方式,可以讓應用程序的打包內容貫穿開發、測試以及線上部署,而Maven不同Profile的方案爲每個環境所構建的包其內容本質上是不同的。但是如果每個參數都需要通過命令行來指定,這顯然也不是一個好的方案,所以下面我們看看如何在SpringBoot中實現多環境的配置。

多環境配置

我們在開發應用的時候,通常同一套程序會被應用和安裝到幾個不同的環境中,比如開發、測試、生產等。其中每個環境的數據庫地址、服務器端口等配置都不同,如果在爲不同環境打包時都要頻繁修改配置文件的話,那必將是個非常煩瑣且容易發生錯誤的事。對於多環境的配置,各種項目構建工具或是框架的基本思路是一致的,通過配置多份不同環境的配置文件,再通過打包命令指定需要打包的內容之後進行區分打包,SpringBoot也不例外,或者說實現起來更加簡單
在SpringBoot中,多環境配置的文件名需要滿足application-{profile}.properties的格式,其中{profile}對應你的環境標識,如下所示。

application-dev.properties:開發環境。
application-test.properties:測試環境。
application-prod.properties:生產環境。

至於具體哪個配置文件會被加載,需要在application.properties文件中通過spring.profiles.active屬性來設置,其值對應配置文件中的{profile}值。如spring.profiles.active=test就會加載application-test.properties配置。
文件內容。

多環境的配置思路

  • application.properties中配置通用內容,並通過設置spring.profiles.active=xxx或通過命令行方式去激活不同環境的配置。
  • application-{profile}.properties中配置各個環境不同的內容。

加載順序

我們可以將SpringBoot應用需要的配置內容都放在項目工程中,然後通過spring.profiles.active或是通過Maven來實現多環境的支持。但是,當團隊逐漸壯大,分工越來越細緻之後,往往不需要讓開發人員知道測試或是生產環境的細節,而是希望由每個環境各自的負責人(QA或是運維)來集中維護這些信息。那麼如果還是以這樣的方式存儲配置內容,對於不同環境配置的修改就不得不去獲取工程內容然後來修改這些配置內容,當應用非常多的時候就變得非常不方便。同時,配置內容對開發人員都可見,這本身也是一種安全隱患。對此,出現了很多將配置內容外部化的框架和工具,後續將要介紹的SpringCloud Config就是其中之一,爲了後續能更好地理解SpringCloud Config的加載機制,我們需要對SpringBoot對數據文件的加載機制有一定的瞭解。

爲了能夠更合理地重寫各屬性的值,SpringBoot使用了下面這種較爲特別的屬性加載順序
1、在命令行中傳入的參數。
2、SPRING APPLICATION JSON中的屬性。SPRING APPLICATION JSON是以JSON格式配置在系統環境變量中的內容。
3、java:comp/env中的JNDI屬性。
4、Java的系統屬性,可以通過System.getProperties()獲得的內容。
5、操作系統的環境變量。
6、通過random.*配置的隨機屬性。
7、位於當前應用jar包之外,針對不同{profile}環境的配置文件內容,例如application-{profile}.properties或是YAML定義的配置文件。
8、位於當前應用jar包之內,針對不同{profile}環境的配置文件內容,例如application-{profile}.properties或是YAML定義的配置文件。
9、位於當前應用jar包之外的application.properties和YAML配置內容。
10、位於當前應用jar包之內的application.properties和YAML配置內容。
11、在@Configuration註解修改的類中,通過@PropertySource註解定義的屬性。
12、應用默認屬性,使用SpringApplication.setDefaultProperties定義的內容。
**優先級按上面的順序由高到低,數字越小優先級越高。**可以看到,其中第7項和第9項都是從應用jar包之外讀取配置文件,所以實現外部化配置的原理就是從此切入,爲其指定外部配置文件的加載位置來取代jar包之內的配置內容。通過這樣的實現,我們的工程在配置中就變得非常乾淨,只需在本地放置開發需要的配置即可,而不用關心其他環境的配置,由其對應環境的負責人去維護即可。

監控與管理

在微服務架構中,我們將原本龐大的單體系統拆分成多個提供不同服務的應用。雖然各個應用的內部邏輯因分解而得以簡化,但是由於部署應用的數量成倍增長,使得系統的維護複雜度大大提升。對於運維人員來說,隨着應用的不斷增多,系統集羣中出現故障的頻率也變得越來越高,雖然在高可用機制的保護下,個別故障不會影響系統的對外服務,但是這些頻繁出現的故障需要被及時發現和處理才能長期保證系統處於健康可用狀態。爲了能對這些成倍增長的應用做到高效運維,傳統的運維方式顯然是不合適的,所以我們需要實現一套自動化的監控運維機制,而這套機制的運行基礎就是不間斷地收集各個微服務應用的各項指標情況,並根據這些基礎指標信息來制定監控和預警規則,更進一步甚至做到一些自動化的運維操作等。爲了讓運維繫統能夠獲取各個微服務應用的相關指標以及實現一些常規操作控制,我們需要開發一套專門用於植入各個微服務應用的接口供監控系統採集信息。而這些接口往往有很大一部分指標都是類似的,比如環境變量、垃圾收集信息、內存信息、線程池信息等。既然這些信息那麼通用,難道就沒有一個標準化的實現框架嗎?當我們決定用SpringBoot來作爲微服務框架時,除了它強大的快速開發功能之外,還因爲它在Starter POMs中提供了一個特殊依賴模塊spring-boot-starter-actuator引入該模塊能夠自動爲SpringBoot構建的應用提供一系列用於監控的端點。同時,SpringCloud在實現各個微服務組件的時候,進一步爲該模塊做了不少擴展,比如,爲原生端點增加了更多的指標和度量信息(比如在整合Eureka的時候會爲/health端點增加相關的信息),並且根據不同的組件還提供了更多有空的端點(比如,爲API網關組件Zuul提供了/routes端點來返回路由信息)。
spring-boot-starter-actuator模塊的實現對於實施微服務的中小團隊來說,可以有效地省去或大大減少監控系統在採集應用指標時的開發量。當然它也並不是萬能的,有時候也需要對其做一些簡單的擴展來幫助我們實現自身系統個性化的監控需求。

原生端點

spring-boot-starter-actuator模塊中已經實現的一些原生端點。 根據端點的作用,可以將原生端點分爲以下三大類:

  • 應用配置類:獲取應用程序中加載的應用配置、環境變量、自動化配置報告等與SpringBoot應用密切相關的配置類信息。

    由於SpringBoot爲了改善傳統Spring應用繁雜的配置內容,採用了包掃描和自動化配置的機制來加載原本集中於XML文件中的各項內容。雖然這樣的做法讓我們的代碼變得非常簡潔,但是整個應用的實例創建和依賴關係等信息都被離散到了各個配置類的註解上,這使我們分析整個應用中資源和實例的各種關係變得非常困難。而這類端點可以幫助我們輕鬆獲取一系列關於Spring應用配置內容的詳細報告,比如自動化配置的報告、Bean創建的報告、環境屬性的報告等。

    • /conditions:該端點用來獲取應用的自動化配置報告,其中包括所有自動化配置的候選項。同時還列出了每個候選項是否滿足自動化配置的各個先決條件。所以,該端點可以幫助我們方便地找到一些自動化配置爲什麼沒有生效的具體原因。該報告內容將自動化配置內容分爲以下兩部分。

      • positiveMatches中返回的是條件匹配成功的自動化配置。
      • negativeMatches中返回的是條件匹配不成功的自動化配置。
    • beans:該端點用來獲取應用上下文中創建的所有Bean

    • /configprops:該端點用來描述配置屬性(包含默認值)如何注入Beanprefix屬性代表了屬性的配置前綴,properties代表了各個屬性的名稱和值。所以我們可以通過該報告來看到各個屬性的配置路徑,比如我們要關閉該端點,就可以通過使用management.endpoint.configprops.enabled=false來完成設置。

    • env:該端點與/configprops不同,它用來獲取應用所有可用的環境屬性報告。 包括環境變量、JVM屬性、應用的配置屬性、命令行中的參數。 它不僅返回了應用的配置屬性,還返回了系統屬性、環境變量等豐富的配置信息,其中還包括了應用還沒有使用的配置,所以它可以幫助我們方便地看到當前應用可以加載的配置信息。

    • /mappings: 該端點用來返回所有Spring MVC的控制器映射關係報告。

    • /info:該端點用來返回一些應用自定義的信息。默認清況下,該端點只會返回一個空的JSON內容。我們可以在application.properties配置文件中通過info前綴來設置一些屬性,例如:

      info.app.name=spring-boot
      info.app.version=v1.0.0
      
  • 度量指標類:獲取應用程序運行過程中用於監控的度量指標,比如內存信息、線程池信息、HTTP請求統計等。上面的應用配置類端點所提供的信息報告在應用啓動的時候就已經基本確定了其返回內容,可以說是一個靜態報告。而度量指標類端點提供的報告內容則是動態變化的,這些端點提供了應用程序在運行過程中的一些快照信息,比如內存使用情況、HTTP請求統計、外部資源指標等。這些端點對於我們構建微服務架構中的監控系統非常有幫助,由於SpringBoot應用自身實現了這些端點,所以我們可以很方便地利用它們來收集我們想要的信息,以制定出各種自動化策略。

    • /metrics:該端點用來返回當前應用的各類重要度量指標,比如內存信息、線程信息、垃圾回收信息等。/metrics端點可以提供應用運行狀態的完整度量指標報告,這項功能非常實用,但是對千監控系統中的各項監控功能,它們的監控內容、數據收集頻率都有所不同,如果每次都通過全量獲取報告的方式來收集,略顯粗暴。所以,我們還可以通過/metrics/{name}接口來更細粒度地獲取度量信息,比如可以通過訪問/metrics/jvm.memory.committed來獲取當前JVM可用內存數量。
      在這裏插入圖片描述
    • /health:它用來獲取應用的各類健康指標信息。在spring-boot-starter-actuator模塊中自帶實現了一些常用資源的健康指標檢測器。這些檢測器都通過HealthIndicator接口實現,並且會根據依賴關係的引入實現自動化裝配。
      在這裏插入圖片描述
    • /threadDump:該端點用來暴露程序運行中的線程信息。它使用java.lang.management.ThreadMXBean的AllThreads方法來返回所有含有同步信息的活動線程詳情。
    • httpTrace:該端點用來返回基本的HTTP跟蹤信息。默認情況下,跟蹤信息的存儲採用org.springfrarnework.boot.actuate.trace.InMernoryTraceRepository
      實現的內存方式,始終保留最近的100條請求記錄。
  • 操作控制類:提供了對應用的關閉等操作類功能。

    上面應用配置類、度量指標類裏介紹的所有端點都是用來反映應用自身的屬性或是運行中的狀態,相對於操作控制類端點沒有那麼敏感,所以它們都是默認啓用的。而操作控制類端點擁有更強大的控制能力,如果要使用它們的話,需要通過屬性來配置開啓操作。在原生端點中,只提供了一個用來關閉應用的端點:/shutdown(在後續我們引入了Eureka之後,會引入更多控制端點)。可以通過如下配置開啓它:management.endpoint.shutdown.enabled=true
    在配置了上述屬性之後,只需要訪問該應用的/shutdown端點就能實現關閉該應用的遠程操作。由於開放關閉應用的操作本身是一件非常危險的事,所以真正在線上使用的時候,需要對其加入一定的保護機制,比如定製actuator的端點路徑、整合SpringSecurity進行安全校驗等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章