quarkus實戰之八:profile

2023-07-28 09:03:57

歡迎存取我的GitHub

這裡分類和彙總了欣宸的全部原創(含配套原始碼):https://github.com/zq2599/blog_demos

本篇概覽

  • 本文是《quarkus實戰》系列的第八篇,經過前面的學習,咱們對設定有了足夠了解,但問題也隨之而來:如何讓應用以最小的改動同時執行在不同環境(如本地、測試、生產等)
  • 舉個例子,下面是個簡化版組態檔,有兩個設定項,第一個固定不變,第二個隨環境變化各不相同:
# 這個設定資訊在各個環境中都是相同的
greeting.message=hello
# 這個設定資訊在各個環境中都不一樣
quarkus.http.port=9090
  • 在實際部署的時候,如何達到要求呢?quarkus.http.port的值隨著環境變化
  • 不同環境用不同組態檔是一種方法,但會導致組態檔數量上升,並且greeting.message在各環境都是一樣的,這就出現了冗餘,除了維護成本增加,在管理過程中容易出錯
  • 除了多個組態檔,還有種方法可以滿足要求,並且不需要多個組態檔,這就是今天要聊的profile

演示程式碼

  • 建立一個demo工程,參考下面的命令,這樣的工程會自帶一個web服務類HobbyResource.java
mvn "io.quarkus:quarkus-maven-plugin:create" \
  -DprojectGroupId="com.bolingcavalry" \
  -DprojectArtifactId="hello-quarkus" \
  -DprojectVersion="1.0-SNAPSHOT" \
  -DclassName="HobbyResource" \
  -Dpath="actions"
  • 用下面這段程式碼來演示設定是否生效,可見用了一個設定項greeting.message,所以我們需要設定它的值才行
package com.bolingcavalry;

import org.eclipse.microprofile.config.inject.ConfigProperty;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.time.LocalDateTime;

@Path("/actions")
public class HobbyResource {

    @ConfigProperty(name = "greeting.message")
    String message;

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return "Hello RESTEasy, " + LocalDateTime.now() + " [" + message + "]";
    }
}
  • 組態檔是hello-quarkus/src/main/resources/application.properties
# 這個設定資訊在各個環境中都是相同的
greeting.message=hello

設定profile

  • profile自己是個普通的設定項,例如在application.properties檔案中,是這樣設定profile的
# 這個設定資訊在各個環境中都是相同的
quarkus.profile=dev

# 如果不指定profile,就使用此設定
quarkus.http.port=8080
  • 也可以在System properties中設定,如下所示,如此以來,不同環境只有啟動命令不同,組態檔可以完全不用修改:
java -Dquarkus.profile="dev" -jar hello-quarkus-1.0-SNAPSHOT-runner.jar

同一個設定項在不同profile時的值

  • profile的格式是%{profile-name}.config.name
  • 以剛才的設定為例,quarkus.http.port設定項共出現三次,前兩次帶有字首,格式是百分號+profile名稱+點號,如下所示
# 指定當前profile
quarkus.profile=dev

# 這個設定資訊在各個環境中都是相同的
greeting.message=hello

# 如果profile為dev,就是用此設定
%dev.quarkus.http.port=8081
# 如果profile為production,就是用此設定
%production.quarkus.http.port=8082
# 如果不指定profile,或者profile既不是dev也不是production,就使用此設定
quarkus.http.port=8080
  • 使用上述設定後,因為profile等於dev,會使用不同設定項%dev.quarkus.http.port,也就是說伺服器埠是8081,另外兩個設定%production.quarkus.http.portquarkus.http.port都無效
  • 啟動應用驗證,我這是用mvn quarkus:dev命令啟動的,如下圖紅框:
  • 瀏覽器存取地址http://localhost:8081/actions,服務正常,設定項greeting.message的值也符合預期:
  • 再試試另一種設定,先在application.properties檔案中刪除設定項quarkus.profile=dev,再改用mvn quarkus:dev -Dquarkus.profile=production啟動應用,這次生效的設定項是%production.quarkus.http.port,如下圖:
  • 存取地址也變成了http://localhost:8082/actions

需要大寫的場景

  • 《quarkus實戰之六:設定》一文中,曾提到過設定方式有六種,有幾種要求設定項大寫,例如在.env中的設定,此時格式變成了_{PROFILE}_CONFIG_KEY=value,舉例如下
# 這個設定資訊在各個環境中都是相同的
GREETING_MESSAGE=hello

# 如果profile為dev,就是用此設定
_DEV_QUARKUS_HTTP_PORT=8081

# 如果profile為production,就是用此設定
_PRODUCTION_QUARKUS_HTTP_PORT=8082

# 如果不指定profile,就使用此設定
QUARKUS_HTTP_PORT=8080
  • 注意,實測發現在.env中設定QUARKUS_PROFILE=dev無效,也就是說不能在.env中指定profile,此時應該在啟動命令中指定profile,例如:
java -Dquarkus.profile=dev -jar hello-quarkus-1.0-SNAPSHOT-runner.jar

不指定profile時的預設值

  • 不指定profile的時候,quarkus會給profile設定預設值,有三種可能:dev、test、prod,具體邏輯如下:
  1. 如果啟動命令是mvn quarkus:dev,profile等於dev,如下圖,大家應該見過多次了:
  2. 單元測試期間,例如執行命令mvn test,profile等於test
  3. 以上兩種場景之外,profile等於prod,例如用命令java -jar hello-quarkus-1.0-SNAPSHOT-runner.jar啟動應用

每個profile對應一個組態檔

  • 如果您希望每個profile都有自己的組態檔,quarkus也支援,如下所示,src/main/resources/目錄下同時存在兩個組態檔:application.propertiesapplication-staging.properties
resources
├── META-INF
│   └── resources
│       └── index.html
├── application-staging.properties
└── application.properties
  • application.properties內容如下
greeting.message=hello
quarkus.http.port=8080
  • application-staging.properties內容如下
greeting.message=hello
quarkus.http.port=8081
  • 如果啟動命令指定了profile,如mvn quarkus:dev -Dquarkus.profile=staging,此時只有application-staging.properties檔案生效,如下圖

  • 還要注意一點:此時如果指定一個不存在的profile,例如mvn quarkus:dev -Dquarkus.profile=xxxxxxx,此時生效的是application.properties檔案生效,如下圖

Parent Profile

  • parent profile解決的問題是:假設當前profile是aaa,那麼設定項xxx對應的設定名應該是%dev.aaa,如果找不到%dev.aaa,就去找它的parent profile對應的設定項,來看個例子就清楚了,假設設定資訊如下:
# 指定profile的名字
quarkus.profile=dev
# 指定parent的名字
quarkus.config.profile.parent=common

%common.quarkus.http.port=9090
%dev.quarkus.http.ssl-port=9443

quarkus.http.port=8080
quarkus.http.ssl-port=8443
  1. 當前profile已經指定為dev
  2. parent profile已經指定為common
  3. 對於設定項quarkus.http.port,由於沒找到%dev.quarkus.http.port,就去找parent profile的設定,於是找到了%common.quarkus.http.port,所以值為9090
  4. 對於設定項quarkus.http.ssl-port,由於找到了%dev.quarkus.http.ssl-port,所以值為9443
  5. 對於設定項quarkus.http.port,如果%dev.quarkus.http.port%common.quarkus.http.port都不存在,會用quarkus.http.port,值為8080

修改預設profile

  • 前面曾說到,啟動的時候如果不指定profile,quarkus會指定預設的profile:將應用製作成jar,以java -jar命令啟動時,profile會被設定為prod
  • 如果您想讓預設值從prod變為其他值,可以在構建的時候用-Dquarkus.profile去改變它,例如下面這個命令,jar包生成後,啟動的時候預設profile是prod-aws
mvn clean package -U -Dquarkus.package.type=uber-jar -Dquarkus.profile=prod-aws
  • 啟動jar的時候不指定profile,如下圖,profile已被設定為prod-aws

三個關鍵注意事項(重要)

  • quarkus官方給出了三個重點注意事項
  1. 應用在執行時,只會有一種profile生效
  2. 如果想在程式碼獲取當前的profile,可以用此API
io.quarkus.runtime.configuration.ProfileManager#getActiveProfile
  1. 用註解的方式獲取profile是無效的,下面這段程式碼無法得到當前的profile
 @ConfigProperty("quarkus.profile")
 String profile;

歡迎關注部落格園:程式設計師欣宸

學習路上,你不孤單,欣宸原創一路相伴...