Java에서 App Lifecycle Manager 기능 플래그 사용

이 가이드에서는 Java 애플리케이션을 App Lifecycle Manager 기능 플래그와 통합하는 방법을 설명합니다. App Lifecycle Manager에서 관리하는 플래그를 평가하기 위해 flagd 프로바이더와 함께 OpenFeature SDK를 사용하는 방법을 알아봅니다. 이를 통해 Java 서비스에서 기능 가용성 및 동작을 동적으로 제어할 수 있습니다.

이 가이드에서는 다음 빠른 시작 중 하나를 완료하여 필요한 App Lifecycle Manager 리소스 (예: SaaS 제공, 단위 종류, 단위, 플래그, 출시)를 이미 설정했다고 가정합니다.

기본 요건

시작하기 전에 다음 사항을 확인하세요.

  1. Java 설치됨: JDK 17 이상
  2. App Lifecycle Manager 기능 플래그 빠른 시작 완료:
  3. 애플리케이션 기본 사용자 인증 정보 (ADC)에 대해 인증된 gcloud: Java 애플리케이션은 ADC를 사용하여 Google Cloud 서비스를 인증합니다. 환경에서 인증했는지 확인합니다.

    gcloud auth application-default login
    
  4. IAM 권한: Java 애플리케이션을 실행하는 ID에는 플래그 구성을 읽기 위해프로젝트에 대한 roles/saasconfig.viewer Identity and Access Management 역할이 필요합니다. Google Cloud

    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의 전체 리소스 이름이어야 합니다.
    • 형식: 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라는 파일을 추가합니다. 이 클래스는 ADC 및 리전 라우팅 헤더를 연결하기 위해 gRPC 인터셉터를 설정하고 인프로세스 해결을 사용하여 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에서 플래그 업데이트:콘솔 또는 gcloud를 사용하여 플래그 페이로드 또는 타겟팅 규칙을 수정합니다. Google Cloud
  3. 업데이트된 값 확인: 로컬 실행을 다시 실행하거나 컨테이너 로그를 관찰하여 새 평가 상태를 확인합니다.

다음 단계