Avvio rapido: Creare una funzione Java in Azure dalla riga di comando

In questo articolo vengono usati gli strumenti da riga di comando per creare una funzione Java che risponde alle richieste HTTP. Dopo aver testato il codice in locale, verrà distribuito nell'ambiente serverless di Funzioni di Azure.

Se Maven non è lo strumento di sviluppo preferito, vedere le esercitazioni simili per sviluppatori Java:

Le procedure illustrate in questa guida di avvio rapido comportano l'addebito di qualche centesimo (USD) o meno nell'account Azure.

Configurare l'ambiente locale

Per eseguire le procedure descritte è necessario:

Controllo dei prerequisiti

  • In una finestra terminale o comando eseguire func --version per verificare che gli strumenti di base Funzioni di Azure siano versione 4.x.

  • Eseguire az --version per verificare che la versione dell'interfaccia della riga di comando di Azure sia 2.4 o successiva.

  • Eseguire az login per accedere ad Azure e verificare la presenza di una sottoscrizione attiva.

Creare un progetto di funzione locale

In Funzioni di Azure un progetto di funzione è un contenitore per una o più funzioni singole che rispondono ognuna a un trigger specifico. Tutte le funzioni di un progetto condividono le stesse configurazioni locali e di hosting. In questa sezione viene creato un progetto di funzione che contiene una singola funzione.

  1. In una cartella vuota eseguire il comando seguente per generare il progetto di Funzioni da un archetipo Maven.

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

    Importante

    • Usare -DjavaVersion=11 per eseguire le funzioni in Java 11. Per altre informazioni, vedere Versioni di Java.
    • Per completare questo articolo, è necessario impostare la variabile di ambiente JAVA_HOME sul percorso di installazione della versione corretta di JDK.
  2. Maven chiede i valori necessari per completare la generazione del progetto nella distribuzione.
    Quando richiesto, specificare i valori seguenti:

    Prompt Valore Descrizione
    groupId com.fabrikam Un valore che identifica in modo univoco il progetto tra tutti gli altri, seguendo le regole di denominazione dei pacchetti per Java.
    artifactId fabrikam-functions Un valore che corrisponde al nome del jar, senza un numero di versione.
    version 1.0-SNAPSHOT Scegliere il valore predefinito.
    package com.fabrikam Un valore che corrisponde al pacchetto Java per il codice della funzione generato. Usare il valore predefinito.
  3. Digitare Y o premere INVIO per confermare.

    Maven crea i file di progetto in una nuova cartella denominata artifactId, che in questo esempio è fabrikam-functions.

  4. Passare alla cartella del progetto:

    cd fabrikam-functions
    

    Questa cartella contiene vari file per il progetto, inclusi i file di configurazione denominati local.settings.json e host.json. Poiché local.settings.json può contenere segreti scaricati da Azure, per impostazione predefinita il file viene escluso dal controllo del codice sorgente nel file con estensione gitignore.

(Facoltativo) Esaminare il contenuto del file

Se si vuole, è possibile passare a Eseguire la funzione localmente ed esaminare il contenuto del file in un secondo momento.

Function.java

Function.java contiene un metodo run che riceve i dati della richiesta nella variabile request come oggetto HttpRequestMessage decorato con l'annotazione HttpTrigger, che definisce il comportamento del trigger.

/**
 * 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"));
    }
}

Il messaggio di risposta viene generato dall'API HttpResponseMessage.Builder.

pom.xml

Le impostazioni per le risorse di Azure create per ospitare l'app sono definite nell'elemento configuration del plug-in con un elemento groupId di com.microsoft.azure nel file pom.xml generato. Ad esempio, l'elemento configuration seguente indica a una distribuzione basata su Maven di creare un'app per le funzioni nel gruppo di risorse java-functions-group nell'area westus. L'app per le funzioni viene eseguita in Windows ospitata nel piano java-functions-app-service-plan, che per impostazione predefinita è un piano a consumo serverless.

<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>

È possibile modificare queste impostazioni per controllare la modalità di creazione delle risorse in Azure, ad esempio cambiando runtime.os da windows a linux prima della distribuzione iniziale. Per un elenco completo delle impostazioni supportate dal plug-in Maven, vedere i dettagli di configurazione.

FunctionTest.java

L'archetipo genera anche uno unit test per la funzione. Quando si modifica la funzione per aggiungere i binding o si aggiungono nuove funzioni al progetto, sarà necessario modificare anche i test nel file FunctionTest.java.

Eseguire la funzione in locale

  1. Eseguire la funzione avviando l'host di runtime locale di Funzioni di Azure nella cartella LocalFunctionProj:

    mvn clean package
    mvn azure-functions:run
    

    Verso la fine dell'output, verranno visualizzate le righe seguenti:

     ...
    
     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
     ...
    
     

    Nota

    Se HttpExample non viene visualizzato come illustrato sopra, è probabile che l'host non sia stato avviato dalla cartella radice del progetto. In tal caso, premere CTRL+C per arrestare l'host, passare alla cartella radice del progetto ed eseguire di nuovo il comando precedente.

  2. Copiare l'URL della funzione HttpExample da questo output in un browser e aggiungere la stringa di query ?name=<YOUR_NAME>, rendendo l'URL completo come http://localhost:7071/api/HttpExample?name=Functions. Il browser deve visualizzare un messaggio che restituisce il valore della stringa di query. Il terminale in cui è stato avviato il progetto mostra anche l'output del log quando si effettuano le richieste.

  3. Al termine, premere CTRL+C e scegliere y per arrestare l'host di Funzioni.

Distribuire il progetto di funzione in Azure

La prima volta che si distribuisce il progetto di Funzioni, in Azure vengono create un'app per le funzioni e le risorse correlate. Le impostazioni per le risorse di Azure create per ospitare l'app sono definite nel file pom.xml. In questo articolo verranno accettate le impostazioni predefinite.

Suggerimento

Per creare un'app per le funzioni in esecuzione in Linux anziché in Windows, modificare l'elemento runtime.os nel file pom.xml da windows a linux. L'esecuzione di Linux in un piano a consumo è supportata in queste aree. Non è possibile avere nello stesso gruppo di risorse app eseguite in Linux e app eseguite in Windows.

  1. Prima della distribuzione, accedere alla sottoscrizione di Azure usando l'interfaccia della riga di comando di Azure o Azure PowerShell.

    az login
    

    Il comando az login consente di accedere all'account Azure.

  2. Usare il comando seguente per distribuire il progetto in una nuova app per le funzioni.

    mvn azure-functions:deploy
    

    In Azure verranno create le risorse seguenti:

    • Gruppo di risorse. Il nome è java-functions-group.
    • Account di archiviazione. Richiesto da Funzioni. Il nome viene generato in modo casuale in base ai requisiti di denominazione degli account di archiviazione.
    • Piano di hosting. Hosting serverless per l'app per le funzioni nell'area westus. Il nome è java-functions-app-service-plan.
    • App per le funzioni. Un'app per le funzioni è l'unità di distribuzione ed esecuzione per le funzioni. Il nome viene generato in modo casuale in base al valore di artifactId, a cui viene aggiunto un numero generato in modo casuale.

    La distribuzione inserisce i file di progetto in un pacchetto e li distribuisce nella nuova app per le funzioni tramite ZipDeploy. Il codice viene eseguito dal pacchetto di distribuzione in Azure.

Richiamare la funzione in Azure

Poiché la funzione usa un trigger HTTP, è possibile richiamarla eseguendo una richiesta HTTP al relativo URL nel browser o con uno strumento come curl.

Copiare l'URL di richiamo completo visualizzato nell'output del comando publish nella barra degli indirizzi di un browser, aggiungendo il parametro di query ?name=Functions. Nel browser dovrebbe essere visualizzato un output simile a quello visualizzato quando è stata eseguita la funzione in locale.

The output of the function run on Azure in a browser

Eseguire il comando seguente per visualizzare i log in streaming quasi in tempo reale:

func azure functionapp logstream <APP_NAME> 

In una finestra del terminale separata o nel browser chiamare di nuovo la funzione remota. Nel terminale viene visualizzato un log dettagliato dell'esecuzione della funzione in Azure.

Pulire le risorse

Se si continua con il passaggio successivo e si aggiunge un binding di output della coda di Archiviazione di Azure, mantenere tutte le risorse esistenti per poterle riutilizzare.

In caso contrario, usare il comando seguente per eliminare il gruppo di risorse e tutte le relative risorse contenute per evitare di incorrere in costi aggiuntivi.

az group delete --name java-functions-group

Passaggi successivi