使用 Java 搭配 App Lifecycle Manager 功能旗標

本指南說明如何將 Java 應用程式與 App Lifecycle Manager 功能旗標整合。您將瞭解如何搭配使用 OpenFeature SDK 和 flagd 提供者,評估由 App Lifecycle Manager 管理的旗標,以便動態控管 Java 服務中的功能可用性和行為。

本指南假設您已完成下列其中一個快速入門導覽課程,設定必要的 App Lifecycle Manager 資源 (例如 SaaS 產品、單元種類、單元、旗標和推出作業):

必要條件

開始之前,請確認您具備以下項目:

  1. 已安裝 Java:JDK 17 以上版本。
  2. 已完成 App Lifecycle Manager 功能旗標快速入門:
    • 已成功完成整合式獨立式功能旗標快速入門導覽課程,以佈建目標單元。
    • 您應該會有該快速入門中的環境變數或值,例如 PROJECT_IDLOCATION_1 (您的裝置區域)、UNIT_IDFLAG_KEY
  3. 已驗證 gcloud 應用程式預設憑證 (ADC):Java 應用程式使用 ADC 向 Google Cloud 服務進行驗證。請確認您已在環境中完成驗證:

    gcloud auth application-default login
    
  4. IAM 權限:執行 Java 應用程式的身分必須在roles/saasconfig.viewer專案中具備 Google Cloud Identity and Access Management 角色,才能讀取旗標設定:

    gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
        --member="user:YOUR_EMAIL_ADDRESS" \
        --role="roles/saasconfig.viewer"
    

    請視情況替換 YOUR_PROJECT_IDYOUR_EMAIL_ADDRESS

瞭解重要環境變數

Java 應用程式會依據環境變數連線至正確的旗標設定。

  • FLAGD_SOURCE_PROVIDER_ID:指定要從 App Lifecycle Manager 服務擷取的功能旗標設定。必須是與裝置相關聯的 FeatureFlagConfig 完整資源名稱。
    • 「Format」(形式)projects/PROJECT_ID/locations/LOCATION/featureFlagsConfigs/UNIT_ID
    • 示例projects/my-gcp-project/locations/us-central1/featureFlagsConfigs/my-app-instance-01

設定 Java 專案

您必須先初始化專案並設定必要依附元件,才能執行應用程式。

  1. 建立專案目錄:

    mkdir java-featureflag-app
    cd java-featureflag-app
    
  2. 新增依附元件:設定建構系統 (Maven 或 Gradle),加入下列程式庫。請使用 OpenFeature 1.14.1 以上版本、flagd 0.11.5 以上版本和 gRPC 1.71.0 以上版本,確保相容性。

    • dev.openfeature:javasdk:1.14.1+
    • dev.openfeature.contrib.providers:flagd:0.11.5+
    • io.grpc:grpc-netty-shaded:1.71.0+
    • io.grpc:grpc-auth:1.71.0+
    • com.google.auth:google-auth-library-oauth2-http
  3. 建立初始化程式碼:新增名為 FeatureManagement.java 的檔案,並加入下列內容。這個類別會設定 gRPC 攔截器,附加 ADC 和區域轉送標頭,並使用程序內解析初始化 FlagdProvider

    import com.google.auth.oauth2.GoogleCredentials;
    import dev.openfeature.contrib.providers.flagd.Config;
    import dev.openfeature.contrib.providers.flagd.FlagdOptions;
    import dev.openfeature.contrib.providers.flagd.FlagdProvider;
    import dev.openfeature.sdk.OpenFeatureAPI;
    import io.grpc.*;
    import io.grpc.auth.MoreCallCredentials;
    import java.util.ArrayList;
    import java.util.List;
    
    public class FeatureManagement {
        public static void initialize() throws Exception {
            // Read the full Unit resource name
            String flagConfigId = System.getenv("FLAGD_SOURCE_PROVIDER_ID");
            if (flagConfigId == null || flagConfigId.isEmpty()) {
                throw new IllegalStateException("FLAGD_SOURCE_PROVIDER_ID environment variable is not set.");
            }
    
            // Configure gRPC Security and Routing
            GoogleCredentials credentials = GoogleCredentials.getApplicationDefault();
            CallCredentials callCredentials = MoreCallCredentials.from(credentials);
    
            List<ClientInterceptor> interceptors = new ArrayList<>();
    
            // Interceptor to inject the Unit name into headers for regional routing
            interceptors.add(new ClientInterceptor() {
                @Override
                public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
                    return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(next.newCall(method, callOptions)) {
                        @Override
                        public void start(Listener<RespT> responseListener, Metadata headers) {
                            headers.put(Metadata.Key.of("x-goog-request-params", Metadata.ASCII_STRING_MARSHALLER), "name=" + flagConfigId);
                            super.start(responseListener, headers);
                        }
                    };
                }
            });
    
            // Interceptor to attach ADC to the gRPC calls
            interceptors.add(new ClientInterceptor() {
                @Override
                public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
                    return next.newCall(method, callOptions.withCallCredentials(callCredentials));
                }
            });
    
            // Initialize the flagd provider with In-Process resolution
            FlagdProvider provider = new FlagdProvider(
                FlagdOptions.builder()
                    .resolverType(Config.Resolver.IN_PROCESS)
                    .host("saasconfig.googleapis.com").port(443)
                    .tls(true)
                    .providerId(flagConfigId)
                    .clientInterceptors(interceptors)
                    .syncMetadataDisabled(true)
                    .deadline(10000)
                    .build()
            );
    
            // Set the global provider
            OpenFeatureAPI.getInstance().setProviderAndWait(provider);
        }
    }
    
  4. 建立應用程式邏輯:新增名為 Main.java 的檔案,並加入下列內容來執行旗標評估。

    import dev.openfeature.sdk.OpenFeatureAPI;
    import dev.openfeature.sdk.MutableContext;
    
    public class Main {
        public static void main(String[] args) throws Exception {
    
        // 1. Get Client
        var client = OpenFeatureAPI.getInstance().getClient("simple-api");
    
        // 2. Create Evaluation Context 
        var context = new dev.openfeature.sdk.MutableContext();
    
        // 3. Evaluate Flag
        // Default to false if evaluation fails or service is unreachable
        boolean isEnhanced = client.getBooleanValue("enhanced-search", false, context);
    
        // 4. Execute business logic based on result
        if (isEnhanced) {
            System.out.println("Executing enhanced search algorithm...");
        } else {
            System.out.println("Standard search algorithm...");
        }
    
        }
    }
    

執行 Java 應用程式

您可以透過獨立設定在本機執行應用程式,也可以部署為容器。

獨立執行 (在本機)

  1. 設定環境變數:

    export PROJECT_ID="your-gcp-project-id"
    export LOCATION_1="us-central1"
    export UNIT_ID="my-app-instance-01"
    export FLAGD_SOURCE_PROVIDER_ID="projects/${PROJECT_ID}/locations/${LOCATION_1}/featureFlagsConfigs/${UNIT_ID}"
    
  2. 執行應用程式:使用偏好的建構工具指令 (例如 mvn compile exec:java./gradlew run) 執行專案。

執行整合式 (容器化)

  1. 建立 Dockerfile:定義容器建構作業,將 Java 二進位檔封裝。

    FROM maven:3.9-eclipse-temurin-17 AS builder
    WORKDIR /app
    COPY pom.xml .
    COPY src ./src
    RUN mvn clean package
    
    FROM eclipse-temurin:17-jre
    WORKDIR /app
    COPY --from=builder /app/target/java-featureflag-app.jar app.jar
    ENTRYPOINT ["java", "-jar", "app.jar"]
    
  2. 建構及推送映像檔:

    export PROJECT_ID="your-gcp-project-id"
    docker build -t gcr.io/${PROJECT_ID}/my-java-app:latest .
    docker push gcr.io/${PROJECT_ID}/my-java-app:latest
    
  3. 部署服務:更新單元藍圖以參照 gcr.io/${PROJECT_ID}/my-java-app:latest,並使用 App Lifecycle Manager 推出作業部署新版本。

驗證旗標變更

應用程式執行後,請確認應用程式是否正確評估旗標。

  1. 觀察初始值:確認控制台輸出內容記錄了與初始設定狀態對應的評估值。
  2. 在 App Lifecycle Manager 中更新旗標:使用 Google Cloud 控制台或 gcloud 修改旗標酬載或指定目標規則。
  3. 驗證更新的值:重新執行本機執行作業,或觀察容器記錄,確認新的評估狀態。

後續步驟