빠른 시작: 명령줄에서 Azure에 Java 함수 만들기

이 문서에서는 명령줄 도구를 사용하여 HTTP 요청에 응답하는 Java 함수를 만듭니다. 코드를 로컬로 테스트한 후 서버리스 Azure Functions 환경에 배포합니다.

Maven이 선호하는 개발 도구가 아닌 경우 Java 개발자를 위한 유사한 자습서를 확인하세요.

이 빠른 시작을 완료하면 Azure 계정에서 약간의 비용(몇 USD 센트)이 발생합니다.

로컬 환경 구성

시작하기 전에 다음이 있어야 합니다.

필수 구성 요소 확인

  • 터미널 또는 명령 창에서 func --version을 실행하여 Azure Functions Core Tools가 4.x인지 확인합니다.

  • az --version을 실행하여 Azure CLI 버전이 2.4 이상인지 확인합니다.

  • az login을 실행하여 Azure에 로그인하고 활성 구독을 확인합니다.

로컬 함수 프로젝트 만들기

Azure Functions에서 함수 프로젝트는 각각 특정 트리거에 응답하는 하나 이상의 개별 함수에 대한 컨테이너입니다. 프로젝트의 모든 함수는 동일한 로컬 및 호스팅 구성을 공유합니다. 이 섹션에서는 단일 함수가 포함된 함수 프로젝트를 만듭니다.

  1. 빈 폴더에서 다음 명령을 실행하여 Maven archetype으로부터 Functions 프로젝트를 생성합니다.

    mvn archetype:generate -DarchetypeGroupId=com.microsoft.azure -DarchetypeArtifactId=azure-functions-archetype -DjavaVersion=8
    

    중요

    • Java 11에서 함수를 실행하려면 -DjavaVersion=11을 사용합니다. 자세히 알아보려면 Java 버전을 참조하세요.
    • 이 문서를 완료하려면 JAVA_HOME 환경 변수를 올바른 버전의 JDK 설치 위치로 설정해야 합니다.
  2. Maven은 배포 시 프로젝트 생성 완료를 위해 필요한 값을 요청합니다.
    메시지가 표시되면 다음 값을 제공합니다.

    prompt Description
    groupId com.fabrikam Java에 대한 패키지 명명 규칙에 따라 모든 프로젝트에서 프로젝트를 고유하게 식별하는 값입니다.
    artifactId fabrikam-functions 버전 번호가 없는 jar의 이름인 값입니다.
    version 1.0-SNAPSHOT 기본값을 선택합니다.
    package com.fabrikam 생성된 함수 코드에 대한 Java 패키지인 값입니다. 기본값을 사용하세요.
  3. Y를 입력하거나 Enter 키를 눌러 확인합니다.

    Maven은 이름이 artifactId인 새 폴더에 프로젝트 파일을 만드는데, 이 예제에서는 fabrikam-functions입니다.

  4. 프로젝트 폴더로 이동합니다.

    cd fabrikam-functions
    

    이 폴더에는 local.settings.jsonhost.json이라는 구성 파일을 포함하여 프로젝트의 다양한 파일이 있습니다. local.settings.json은 Azure에서 다운로드한 비밀을 포함할 수 있으므로 이 파일은 기본적으로 .gitignore 파일의 원본 제어에서 제외됩니다.

(선택 사항) 파일 내용 검사

원하는 경우 로컬에서 함수 실행으로 건너뛰고, 나중에 파일 내용을 검사할 수 있습니다.

Function.java

Function.java에는 request 변수에서 요청 데이터를 수신하는 run 메서드가 포함되며, 트리거 동작을 정의하는 HttpTrigger 주석으로 데코레이트된 HttpRequestMessage입니다.

/**
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License. See License.txt in the project root for
 * license information.
 */

package com.functions;

import com.microsoft.azure.functions.ExecutionContext;
import com.microsoft.azure.functions.HttpMethod;
import com.microsoft.azure.functions.HttpRequestMessage;
import com.microsoft.azure.functions.HttpResponseMessage;
import com.microsoft.azure.functions.HttpStatus;
import com.microsoft.azure.functions.annotation.AuthorizationLevel;
import com.microsoft.azure.functions.annotation.FixedDelayRetry;
import com.microsoft.azure.functions.annotation.FunctionName;
import com.microsoft.azure.functions.annotation.HttpTrigger;

import java.util.Optional;

/**
 * Azure Functions with HTTP Trigger.
 */
public class Function {
    /**
     * This function listens at endpoint "/api/HttpExample". Two ways to invoke it using "curl" command in bash:
     * 1. curl -d "HTTP Body" {your host}/api/HttpExample
     * 2. curl "{your host}/api/HttpExample?name=HTTP%20Query"
     */
    @FunctionName("HttpExample")
    public HttpResponseMessage run(
            @HttpTrigger(
                name = "req",
                methods = {HttpMethod.GET, HttpMethod.POST},
                authLevel = AuthorizationLevel.ANONYMOUS)
                HttpRequestMessage<Optional<String>> request,
            final ExecutionContext context) {
        context.getLogger().info("Java HTTP trigger processed a request.");

        // Parse query parameter
        final String query = request.getQueryParameters().get("name");
        final String name = request.getBody().orElse(query);

        if (name == null) {
            return request.createResponseBuilder(HttpStatus.BAD_REQUEST).body("Please pass a name on the query string or in the request body").build();
        } else {
            return request.createResponseBuilder(HttpStatus.OK).body("Hello, " + name).build();
        }
    }

    public static int count = 1;

    /**
     * This function listens at endpoint "/api/HttpExampleRetry". The function is re-executed in case of errors until the maximum number of retries occur.
     * Retry policies: https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-error-pages?tabs=java
     */
    @FunctionName("HttpExampleRetry")
    @FixedDelayRetry(maxRetryCount = 3, delayInterval = "00:00:05")
    public HttpResponseMessage HttpExampleRetry(
        @HttpTrigger(
            name = "req",
            methods = {HttpMethod.GET, HttpMethod.POST},
            authLevel = AuthorizationLevel.ANONYMOUS)
            HttpRequestMessage<Optional<String>> request,
        final ExecutionContext context) throws Exception {
        context.getLogger().info("Java HTTP trigger processed a request.");

        if(count<3) {
            count ++;
            throw new Exception("error");
        }

        // Parse query parameter
        final String query = request.getQueryParameters().get("name");
        final String name = request.getBody().orElse(query);

        if (name == null) {
            return request.createResponseBuilder(HttpStatus.BAD_REQUEST).body("Please pass a name on the query string or in the request body").build();
        } else {
            return request.createResponseBuilder(HttpStatus.OK).body(name).build();
        }
    }

    /**
     * This function listens at endpoint "/api/HttpTriggerJavaVersion".
     * It can be used to verify the Java home and java version currently in use in your Azure function
     */
    @FunctionName("HttpTriggerJavaVersion")
    public static HttpResponseMessage HttpTriggerJavaVersion(
        @HttpTrigger(
            name = "req",
            methods = {HttpMethod.GET, HttpMethod.POST},
            authLevel = AuthorizationLevel.ANONYMOUS)
            HttpRequestMessage<Optional<String>> request,
        final ExecutionContext context
    ) {
        context.getLogger().info("Java HTTP trigger processed a request.");
        final String javaVersion = getJavaVersion();
        context.getLogger().info("Function - HttpTriggerJavaVersion" + javaVersion);
        return request.createResponseBuilder(HttpStatus.OK).body("HttpTriggerJavaVersion").build();
    }

    public static String getJavaVersion() {
        return String.join(" - ", System.getProperty("java.home"), System.getProperty("java.version"));
    }
}

응답 메시지는 HttpResponseMessage API에서 생성됩니다.

pom.xml

앱을 호스트하기 위해 만든 Azure 리소스의 설정은 생성된 pom.xml 파일에 있는 com.microsoft.azuregroupId를 사용하여 플러그 인의 configuration 요소에 정의됩니다. 예를 들어 아래 구성 요소는 westus 지역의 java-functions-group 리소스 그룹에 함수 앱을 만들도록 Maven 기반 배포에 지시합니다. 함수 앱 자체는 java-functions-app-service-plan 플랜에서 호스트되는 Windows에서 실행되며, 이 플랜은 기본적으로 서버리스 사용 플랜입니다.

<plugin>
    <groupId>com.microsoft.azure</groupId>
    <artifactId>azure-functions-maven-plugin</artifactId>
    <version>${azure.functions.maven.plugin.version}</version>
    <configuration>
        <!-- function app name -->
        <appName>${functionAppName}</appName>
        <!-- function app resource group -->
        <resourceGroup>${functionResourceGroup}</resourceGroup>
        <!-- function app service plan name -->
        <appServicePlanName>java-functions-app-service-plan</appServicePlanName>
        <!-- function app region-->
        <!-- refers https://github.com/microsoft/azure-maven-plugins/wiki/Azure-Functions:-Configuration-Details#supported-regions for all valid values -->
        <region>${functionAppRegion}</region>
        <!-- function pricingTier, default to be consumption if not specified -->
        <!-- refers https://github.com/microsoft/azure-maven-plugins/wiki/Azure-Functions:-Configuration-Details#supported-pricing-tiers for all valid values -->
        <!-- <pricingTier></pricingTier> -->

        <!-- Whether to disable application insights, default is false -->
        <!-- refers https://github.com/microsoft/azure-maven-plugins/wiki/Azure-Functions:-Configuration-Details for all valid configurations for application insights-->
        <!-- <disableAppInsights></disableAppInsights> -->
        <runtime>
            <!-- runtime os, could be windows, linux or docker-->
            <os>windows</os>
            <javaVersion>8</javaVersion>
            <!-- for docker function, please set the following parameters -->
            <!-- <image>[hub-user/]repo-name[:tag]</image> -->
            <!-- <serverId></serverId> -->
            <!-- <registryUrl></registryUrl>  -->
        </runtime>
        <appSettings>
            <property>
                <name>FUNCTIONS_EXTENSION_VERSION</name>
                <value>~3</value>
            </property>
        </appSettings>
    </configuration>
    <executions>
        <execution>
            <id>package-functions</id>
            <goals>
                <goal>package</goal>
            </goals>
        </execution>
    </executions>
</plugin>

초기 배포 전에 runtime.oswindows에서 linux로 변경하는 등의 방법으로 이러한 설정을 변경하면 Azure에서 리소스를 만드는 방식을 제어할 수 있습니다. Maven 플러그 인에서 지원하는 전체 설정 목록은 구성 세부 정보를 참조하세요.

FunctionTest.java

원형 역시 함수의 단위 테스트를 생성합니다. 바인딩을 추가하거나 프로젝트에 새 함수를 추가하도록 함수를 변경하는 경우에는 FunctionTest.java 파일에서 테스트도 수정해야 합니다.

로컬에서 함수 실행

  1. LocalFunctionProj 폴더에서 로컬 Azure Functions 런타임 호스트를 시작하여 함수를 실행합니다.

    mvn clean package
    mvn azure-functions:run
    

    출력의 끝 부분에 다음 줄이 표시됩니다.

     ...
    
     Now listening on: http://0.0.0.0:7071
     Application started. Press Ctrl+C to shut down.
    
     Http Functions:
    
             HttpExample: [GET,POST] http://localhost:7071/api/HttpExample
     ...
    
     

    참고

    HttpExample이 위와 같이 표시되지 않으면 프로젝트의 루트 폴더 외부에서 호스트를 시작했을 가능성이 높습니다. 이 경우 Ctrl+C를 사용하여 호스트를 중지하고, 프로젝트의 루트 폴더로 이동하여 이전 명령을 다시 실행합니다.

  2. 이 출력에서 HttpExample 함수의 URL을 브라우저로 복사하고 ?name=<YOUR_NAME> 쿼리 문자열을 추가하여 전체 URL을 http://localhost:7071/api/HttpExample?name=Functions와 같이 만듭니다. 브라우저는 쿼리 문자열 값을 다시 에코하는 메시지를 표시해야 합니다. 프로젝트를 시작한 터미널에도 요청 시 로그 출력이 표시됩니다.

  3. 완료되면 Ctrl+C를 사용하고 y를 선택하여 함수 호스트를 중지합니다.

Azure에 함수 프로젝트 배포

함수 앱 및 관련 리소스는 함수 프로젝트를 처음 배포할 때 Azure에 생성됩니다. 앱을 호스트하기 위해 만든 Azure 리소스의 설정은 pom.xml 파일에 정의됩니다. 이 문서에서는 기본값을 적용합니다.

Windows 대신 Linux에서 실행되는 함수 앱을 만들려면 pom.xml 파일의 runtime.os 요소를 windows에서 linux로 변경합니다. 사용 플랜에서 Linux를 실행할 수 있는 지역은 여기서 확인할 수 있습니다. Linux에서 실행되는 앱과 Windows에서 실행되는 앱이 동일한 리소스 그룹에 있으면 안 됩니다.

  1. 배포하기 전에 Azure CLI 또는 Azure PowerShell을 사용하여 Azure 구독에 로그인합니다.

    az login
    

    az login 명령은 Azure 계정에 로그인합니다.

  2. 다음 명령을 사용하여 프로젝트를 새 함수 앱에 배포합니다.

    mvn azure-functions:deploy
    

    그러면 Azure에서 다음 리소스가 생성됩니다.

    • 리소스 그룹 이름은 java-functions-group입니다.
    • Storage 계정 함수에 필요합니다. 이름은 스토리지 계정 이름 요구 사항에 따라 임의로 생성됩니다.
    • 호스팅 계획. westus 지역의 함수 앱에 대한 서버리스 호스팅입니다. 이름은 java-functions-app-service-plan입니다.
    • 함수 앱 함수 앱은 함수에 대한 배포 및 실행 단위입니다. 이름은 artifactId에 따라 임의로 생성된 이름에 임의로 생성된 번호를 붙여서 지정됩니다.

    배포에서는 프로젝트 파일을 패키징한 후 zip 배포를 사용하여 새 함수 앱에 배포합니다. 코드는 Azure의 배포 패키지에서 실행됩니다.

Azure에서 함수 호출

함수에서 HTTP 트리거를 사용하므로 브라우저 또는 도구(예: curl)를 사용하여 URL에 대한 HTTP 요청을 수행하여 호출합니다.

publish 명령의 출력에 표시된 호출 URL 전체를 브라우저 주소 표시줄에 복사하여 ?name=Functions 쿼리 매개 변수를 추가합니다. 브라우저에서 함수를 로컬로 실행했을 때와 비슷한 출력이 표시됩니다.

The output of the function run on Azure in a browser

다음 명령을 실행하여 거의 실시간으로 스트리밍 로그를 확인합니다.

func azure functionapp logstream <APP_NAME> 

별도의 터미널 창 또는 브라우저에서 원격 함수를 다시 호출합니다. Azure의 함수 실행에 대한 자세한 로그가 터미널에 표시됩니다.

리소스 정리

다음 단계를 계속 진행하고 Azure Storage 큐 출력 바인딩을 추가하는 경우 이미 수행한 작업을 기반으로 하여 빌드되는 모든 리소스를 그대로 유지합니다.

그렇지 않으면 추가 비용이 발생하지 않도록 다음 명령을 사용하여 리소스 그룹 및 포함된 모든 리소스를 삭제합니다.

az group delete --name java-functions-group

다음 단계