作者:廖明(歡迎交流liaoming.lxm@gmail.com)
隨著 Spring Boot 生態社群的日益強大,貝聊服務端團隊在開發部分新系統時,也逐步採用了 Spring Boot 框架,並且積累了一定的經驗,在此和大家做個分享。
Spring Boot 的優點
專案採用 Spring Boot ,和直接使用 Spring MVC ,有什麼優點了?這裡我們分析了下,大概有以下幾點:
- 使用 Java Config 的配置方式替代 xml檔案 配置,實現配置類複用。避免每次構建新的 Spring MVC 建專案,都得往舊的專案中拷貝 xml 配置檔案。
- 社群提供的豐富的 Starter pom 依賴包,極大方便了我們整合第三方框架。
- 提供嵌入式 Servlet 容器,支援 jar 和 war 部署,給運維和開發都帶來了極大的便利。
- 自動管理依賴,勉去版本衝突煩惱。
- 提供了基本的應用監控的功能。
總結來說,相比於直接使用 Spring MVC 構建專案,Spring Boot 使我們在開發、配置、監控、部署的過程中,都變得更加簡單。
Spring Boot 在貝聊的應用情況
在講Spring Boot 之前,有必要講下我們貝聊服務端現在有用到的技術棧:
- 基礎框架 Spring MVC。
- 服務治理方面,使用了 Dubbo。
- 資料庫層面,使用了 Mybatis,Druid,並配置了多資料來源。
- 許可權控制用到 Shiro。
- 分散式配置,使用了 Disconf。
- 分散式定時任務,使用 Elastic-Job。
- 全鏈路監控方面使用了 Cat。
- 部署方面,我們用自己的釋出系統。
引入Spring Boot ,則必須要支援我們現有的技術棧,並且要能夠讓新同事能夠很快熟悉。依託於Spring Boot 約定優於配置的理念,及官方和社群提供的各種豐富的 Starter pom 配置,在各同事的支援下,各方面的整合,都相對順利。在接下來的文字裡,我會和大家講解貝聊在使用 Spring Boot 的一些情況。
在 Spring Boot 中使用 Dubbo
貝聊服務端使用了 Dubbo 框架來實現服務治理,Dubbo 目前在Apache 孵化器進行孵化,官方也提供了 incubator-dubbo-spring-boot-project,不過官方的 0.1.0 版本在4月份才釋出,在此之前,我們編寫了自己的 spring-boot-starter-dubbo,並滿足已有需求。
新專案在使用了 spring-boot-starter-dubbo 後,我們在暴露和引入 Dubbo 服務時,均使用了註解的形式,拋棄了原本的 xml 配置方式,開發的時候不用在關注是否在 dubbo.xml 中是否有引入服務,編碼時相對輕鬆了不少。
和 Elastic-Job-Lite 整合
貝聊在很早的時候,就使用了 Elastic-Job 來進行分散式作業管理,且一直沿用了較低版本,沒有做升級。之前已有的專案,Elastic-Job 的配置和作業都是放在一個 spring-job.xml 檔案裡面。在整合 Elastic-Job 和 Spring Boot 的時候,發現社群有開源的 spring-boot-starer-elastic-job,不過採用了最新版的 Elastic-Job-Lite。 在此背景下,我準備了兩種方案:
- 採用 @ImportResource({"classpath:spring-job.xml"}) 方式來相容舊版本的 Elastic-Job 的任務,自己在寫 demo 時執行正常;
- 升級 Elastic-Job 版本為 Elastic-Job-Lite,並安裝最新版的 Elastic-Console ,參考社群的 Starter ,編寫使用註解方式配置定時任務,去 xml;
後面和同事討論下,採用了後面的方案,升級 Elastic-Job 版本為 Elastic-Job-Lite,新專案使用 spring-boot-starter-elastic-job 來配置,並且使用註解來宣告作業。至此,又一個 xml 檔案被移除。
和 Disconf 整合
Discof 是貝聊在早期引進的分散式配置管理工具,使用起來也很簡單,雖然原作者已經停止維護,但還是有熱心的網友提供了 disconf-spring-boot-starter ,基於此 Starter ,也極大簡化了我們在新專案中的配置成本,新入職的同事在配置 Discof 的時候,也不需要再去拷貝舊專案 Diconf 的 xml 配置。
和 Mybatis、Druid 以及多資料來源整合
這部分整合是相對容易的,Mybatis 和 Druid 官方,均有提供自動化配置的 Starter pom,拿來開箱即用。
需要特殊處理的是多資料來源配置,之前系統用的公司統一的配置類,不過是 xml 配置。為了為複用程式碼,我們將其轉為了 Java Config 的方式,並計劃在未來也抽成一個自動化配置的 Starter 包。同樣,我們在整合 Shiro 的時候,也是採用了Java Config的形式,並整合了 JWT 作為介面訪問令牌。
和 Cat 監控整合
APM 監控方面,我們公司使用了 Cat,平臺組的同事搭建了 Cat,並且提供了對應的整合方式,其中一些配置 Bean 注入的時候,還是使用 xml 的方式,後面筆者將其改為了 Java Config 的方式,總個整合過程如下:
- 1、引入Cat 相關 jar 包;
- 2、如果是 web 專案,要注入 CatServletFilter ,實現 MVC層Cat 配置;
- 3、如果是專案中用到了 Dubbo ,新增 CatDubboFilter ;
- 4、在 META-INF 中新增 app.properties,宣告專案名字;
- 5、在 logback-spring.xml 中新增 CatLogbackAppender 配置;
上面的5個步驟中,前面3個步驟是可以合併為一個,2和3兩個步驟,可以通過將xml配置變成Java Config 的方式,並生成一個 spring-boot-starter-cat-monitor 包,實現配置類的重用,簡化配置步驟。下面也介紹下步驟2和3實現自動化配置的原理:
- 注入CatServletFilter,主要是利用 @ConditionalOnWebApplication 註解,如果當前環境是web環境,則該配置類會生效,核心程式碼如下:
/**
* 功能:web 環境下,整合cat監控功能
*/
@Configuration
@ConditionalOnWebApplication
public class CatWebFilterConfigure {
@Bean
public FilterRegistrationBean catFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new CatServletFilter());
registration.addUrlPatterns("/*");
registration.setDispatcherTypes(DispatcherType.REQUEST,DispatcherType.FORWARD);
registration.setName("cat-filter");
return registration;
}
}
複製程式碼
- 注入CatDubboFilter,主要是利用Dubbo的 @Activate 來無條件啟用配置。因為我們所有的專案,都整合了Dubbo,所以不需要根據環境來注入配置類。
/**
* 功能:dubbo cat 監控,使用Activate註解, 無條件自動啟用,不需要在yml 配置中宣告該filter 了
*/
@Activate(group = {Constants.PROVIDER, Constants.CONSUMER})
public class CatBootDubboConfigure extends Filter {
//此處省略具體的埋點操作
}
複製程式碼
使用 spring-boot-start-web-log 統一攔截請求和響應日誌
上面說了好些 Starter ,都是用了社群已有的輪子,當然,我們團部內部,也有封裝一些簡單的 Starter ,以實現程式碼複用,簡化配置。
我們在日常介面開發的除錯階段,經常需要檢視介面請求和響應日誌。使用 Spring 來攔截介面請求和響應日誌,有多種實現方式,可以自己定義 Filter,Intercepter,或者使用 Aspectj,如果每一個 Web 專案,都來寫一套實現,或者從別的專案中拷貝一份,這肯定不是一個好的方式。基於此情況,踐行 Spring Boot 約定優於配置的原則,我們自己內部封裝了 spring-boot-start-web-log,並提供了一些常見的配置項。新的 Web 專案需要時,只需要引入依賴,並在 yml 配置檔案中加入如下配置就好:
web.log:
## 攔截路徑
mapping-path: "/*"
## 排除路徑
exclude-mapping-path: "/files/*;/favicon.ico;/login;/captcha/*
## 列印header,多個按照';'分隔
print-header: "Authorization"
## 是否允許列印日誌,預設true,建議生成關閉
enable: true
複製程式碼
日誌效果如下:
在已有釋出系統上部署 Spring Boot 應用
剛開始在我們已有的釋出系統上部署 Spring Boot 應用時,遇到了一些問題。
在部署應用方面,我們嘗試了 war 包部署和 jar 部署兩種方式。其中採用 war 包部署時,發現會和現有的 Resin 有衝突,需要移除 Resion 的部分 jar ,比較麻煩,所以我們採用 jar 包部署的方式。
使用 jar 包部署,我們也遇到了一些問題。我們已有的 Dubbo 應用部署,就是採用了 jar 包部署。去除一些 JVM 引數是可配置的,最終都是使用到 java -cp jar包路徑 類名 的命令來執行應用。注意,這種方式,是需要指定jar路徑和類名的,和 java -jar spring-boot-app.jar 是不一樣的,因此,為了避免指令碼改動,我們得適配現有的釋出指令碼。
Dubbo 打成可獨立執行的 jar 包,使用了 maven-shade-plugin 外掛,指定的類名是 com.alibaba.dubbo.container.Main 。而在打包 Spring Boot 應用時,我們採用了 spring-boot-maven-plugin 外掛 。在選擇 java -cp 命令所需要引數類名時,我們剛開始使用了Spring Boot預設的 Application 啟動類,畢竟我們開發的時候,也是直接執行該類的 main 方法來啟動應用。然而實際執行的時候,發現應用根本起不來,提示找不到或無法載入主類。後面經過一番資料查詢與學習,才解決了該問題。
解決問題的關鍵,在於要了解 jar 執行的原理,也就是 MANIFEST.MF 檔案。 解壓Spring Boot 打包後的jar檔案,其中 MANIFEST.MF 檔案如下:
Manifest-Version: 1.0
Implementation-Title: demo
Implementation-Version: 0.0.1-SNAPSHOT
Archiver-Version: Plexus Archiver
Built-By: Ming
Implementation-Vendor-Id: com.beiliao.app
Spring-Boot-Version: 1.5.4.RELEASE
Implementation-Vendor: Pivotal Software, Inc.
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: com.beiliao.app.DemoApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Created-By: Apache Maven 3.3.1
Build-Jdk: 1.8.0_73
Implementation-URL: http://projects.spring.io/spring-boot/demo/
複製程式碼
注意到這裡面 Main-Class: org.springframework.boot.loader.JarLauncher 才是主方法,使用java -jar spring-boot-app.jar 可以執行Spring Boot 應用,是因為 spring-boot-maven-plugin 外掛在打包的時候已經生成該檔案 ,java -jar 命令會讀取到該檔案 。因此,使用 java -cp 命令啟動 Spring Boot 應用時,完整的命令應該是:
windows:
java -cp .;c:\\Project\\boot-demo\\target\\demo-0.0.1-SNAPSHOT.jar org.springframework.boot.loader.JarLauncher
linux:
java -cp .:/data/project/demo/* org.springframework.boot.loader.JarLauncher
複製程式碼
總結
以上就是Spring Boot 在貝聊的一些應用情況。目前,貝聊已經有幾個新專案使用到 Spring Boot,並在組內反響不錯,用過的開發同事多說比之前直接用 Spring MVC 要方便不少。公司 Gitlab 倉庫的 Starter 依賴包也有6個了,未來還會增加。