Share via


Esercitazione: Creare una funzione in Java con un trigger di Hub eventi e un'associazione di output di Azure Cosmos DB

Questa esercitazione illustra come usare Funzioni di Azure per creare una funzione Java che analizza un flusso continuo di dati su temperatura e pressione. La funzione viene attivata da eventi dell'hub eventi che rappresentano le letture dei sensori. La funzione elabora i dati dell'evento, quindi aggiunge voci di stato a un'istanza di Azure Cosmos DB.

In questa esercitazione si apprenderà come:

  • Creare e configurare risorse di Azure con l'interfaccia della riga di comando di Azure.
  • Creare e testare funzioni Java che interagiscono con queste risorse.
  • Distribuire le funzioni in Azure e monitorarle con Application Insights.

Se non si ha una sottoscrizione di Azure, creare un account Azure gratuito prima di iniziare.

Prerequisiti

Per completare l'esercitazione, è necessario avere installato:

Importante

Per completare questa esercitazione, è necessario che la variabile di ambiente JAVA_HOME sia impostata sulla posizione in cui è installato JDK.

Se si preferisce usare direttamente il codice per questa esercitazione, vedere il repository di esempi java-functions-eventhub-cosmosdb.

Creare risorse Azure

In questa esercitazione sono necessarie le risorse seguenti:

  • Un gruppo di risorse che contenga le altre risorse
  • Uno spazio dei nomi, un hub eventi e una regola di autorizzazione di Hub eventi
  • Un account, un database e una raccolta di Azure Cosmos DB
  • Un'app per le funzioni e un account di archiviazione in cui ospitarla

Le sezioni seguenti illustrano come creare queste risorse con l'interfaccia della riga di comando di Azure.

Impostare le variabili di ambiente

Creare quindi alcune variabili di ambiente per i nomi e la posizione delle risorse che verranno create. Usare i comandi seguenti, sostituendo i segnaposto <value> con valori a scelta. I valori devono essere conformi alle regole di denominazione e alle restrizioni per le risorse di Azure. Per la variabile LOCATION, usare uno dei valori prodotti dal comando az functionapp list-consumption-locations.

RESOURCE_GROUP=<value>
EVENT_HUB_NAMESPACE=<value>
EVENT_HUB_NAME=<value>
EVENT_HUB_AUTHORIZATION_RULE=<value>
COSMOS_DB_ACCOUNT=<value>
STORAGE_ACCOUNT=<value>
FUNCTION_APP=<value>
LOCATION=<value>

Queste variabili vengono usate nella parte restante di questa esercitazione. Tenere presente che queste variabili persistono esclusivamente per la durata della sessione corrente dell'interfaccia della riga di comando di Azure o di Cloud Shell. Se si usa una finestra del terminale locale diversa, sarà necessario eseguire di nuovo questi comandi, altrimenti la sessione di Cloud Shell raggiungerà il timeout.

Creare un gruppo di risorse

Azure usa i gruppi di risorse per raccogliere tutte le risorse correlate dell'account. In questo modo, è possibile visualizzarle come un'unità ed eliminarle con un singolo comando quando non sono più necessarie.

Usare il comando seguente per creare un gruppo di risorse:

az group create \
    --name $RESOURCE_GROUP \
    --location $LOCATION

Creare un hub eventi

Creare quindi uno spazio dei nomi, un hub eventi e una regola di autorizzazione di Hub eventi di Azure con i comandi seguenti:

az eventhubs namespace create \
    --resource-group $RESOURCE_GROUP \
    --name $EVENT_HUB_NAMESPACE
az eventhubs eventhub create \
    --resource-group $RESOURCE_GROUP \
    --name $EVENT_HUB_NAME \
    --namespace-name $EVENT_HUB_NAMESPACE \
    --message-retention 1
az eventhubs eventhub authorization-rule create \
    --resource-group $RESOURCE_GROUP \
    --name $EVENT_HUB_AUTHORIZATION_RULE \
    --eventhub-name $EVENT_HUB_NAME \
    --namespace-name $EVENT_HUB_NAMESPACE \
    --rights Listen Send

Lo spazio dei nomi di Hub eventi contiene l'hub eventi effettivo e la relativa regola di autorizzazione. La regola di autorizzazione consente alle funzioni di inviare messaggi all'hub e di restare in ascolto degli eventi corrispondenti. Una funzione invia messaggi che rappresentano dati di telemetria. Un'altra funzione resta in ascolto degli eventi, analizza i relativi dati e archivia i risultati in Azure Cosmos DB.

Creare un'istanza di Azure Cosmos DB

Successivamente, creare un account, un database e una raccolta di Azure Cosmos DB usando i comandi seguenti:

az cosmosdb create \
    --resource-group $RESOURCE_GROUP \
    --name $COSMOS_DB_ACCOUNT
az cosmosdb sql database create \
    --resource-group $RESOURCE_GROUP \
    --account-name $COSMOS_DB_ACCOUNT \
    --name TelemetryDb
az cosmosdb sql container create \
    --resource-group $RESOURCE_GROUP \
    --account-name $COSMOS_DB_ACCOUNT \
    --database-name TelemetryDb \
    --name TelemetryInfo \
    --partition-key-path '/temperatureStatus'

Il valore partition-key-path partiziona i dati in base al valore temperatureStatus di ogni elemento. La chiave di partizione consente ad Azure Cosmos DB di migliorare le prestazioni dividendo i dati in subset distinti a cui può accedere in modo indipendente.

Creare un account di archiviazione e un'app per le funzioni

Successivamente, creare un account di archiviazione di Azure, richiesto da Funzioni di Azure, quindi creare l'app per le funzioni. Usare i comandi seguenti:

az storage account create \
    --resource-group $RESOURCE_GROUP \
    --name $STORAGE_ACCOUNT \
    --sku Standard_LRS
az functionapp create \
    --resource-group $RESOURCE_GROUP \
    --name $FUNCTION_APP \
    --storage-account $STORAGE_ACCOUNT \
    --consumption-plan-location $LOCATION \
    --runtime java \
    --functions-version 3

Quando il comando az functionapp create crea l'app per le funzioni, crea anche una risorsa di Application Insights con lo stesso nome. L'app per le funzioni viene configurata automaticamente con un'impostazione denominata APPINSIGHTS_INSTRUMENTATIONKEY che la connette ad Application Insights. È possibile visualizzare i dati di telemetria dell'app dopo aver distribuito le funzioni in Azure, come descritto più avanti in questa esercitazione.

Configurare l'app per le funzioni

Per il corretto funzionamento, l'app per le funzioni dovrà accedere alle altre risorse. Le sezioni seguenti illustrano come configurare l'app per le funzioni in modo che possa essere eseguita nel computer locale.

Recuperare le stringe di connessione delle risorse

Usare i comandi seguenti per recuperare l'archiviazione, l'hub eventi e i stringa di connessione di Azure Cosmos DB e salvarli nelle variabili di ambiente:

AZURE_WEB_JOBS_STORAGE=$( \
    az storage account show-connection-string \
        --name $STORAGE_ACCOUNT \
        --query connectionString \
        --output tsv)
echo $AZURE_WEB_JOBS_STORAGE
EVENT_HUB_CONNECTION_STRING=$( \
    az eventhubs eventhub authorization-rule keys list \
        --resource-group $RESOURCE_GROUP \
        --name $EVENT_HUB_AUTHORIZATION_RULE \
        --eventhub-name $EVENT_HUB_NAME \
        --namespace-name $EVENT_HUB_NAMESPACE \
        --query primaryConnectionString \
        --output tsv)
echo $EVENT_HUB_CONNECTION_STRING
COSMOS_DB_CONNECTION_STRING=$( \
    az cosmosdb keys list \
        --resource-group $RESOURCE_GROUP \
        --name $COSMOS_DB_ACCOUNT \
        --type connection-strings \
        --query 'connectionStrings[0].connectionString' \
        --output tsv)
echo $COSMOS_DB_CONNECTION_STRING

Queste variabili sono impostate sui valori recuperati dai comandi dell'interfaccia della riga di comando di Azure. Ogni comando usa una query JMESPath per estrarre la stringa di connessione dal payload JSON restituito. Le stringhe di connessione vengono visualizzate anche usando echo in modo da verificare che siano state recuperate correttamente.

Aggiornare le impostazioni dell'app per le funzioni

Successivamente, usare il comando seguente per trasferire i valori delle stringhe di connessione nelle impostazioni dell'app nell'account di Funzioni di Azure:

az functionapp config appsettings set \
    --resource-group $RESOURCE_GROUP \
    --name $FUNCTION_APP \
    --settings \
        AzureWebJobsStorage=$AZURE_WEB_JOBS_STORAGE \
        EventHubConnectionString=$EVENT_HUB_CONNECTION_STRING \
        CosmosDBConnectionSetting=$COSMOS_DB_CONNECTION_STRING

Le risorse di Azure sono ora state create e configurate per funzionare correttamente insieme.

Creare e testare le funzioni

Successivamente, creare un progetto nel computer locale, aggiungere codice Java e testarlo. Usare comandi compatibili con il plug-in Funzioni di Azure per Maven e con Azure Functions Core Tools. Le funzioni verranno eseguite in locale, ma useranno le risorse basate sul cloud create. Dopo aver configurato le funzioni per l'esecuzione in locale, è possibile usare Maven per distribuirle nel cloud e osservare l'accumulo di dati e analisi.

Se le risorse sono state create con Cloud Shell, non verranno connesse ad Azure in locale. In questo caso, usare il comando az login per avviare il processo di accesso basato su browser. Quindi, se necessario, impostare la sottoscrizione predefinita con az account set --subscription seguito dall'ID sottoscrizione. Infine, eseguire i comandi seguenti per ricreare alcune variabili di ambiente nel computer locale. Sostituire i segnaposto <value> con gli stessi valori usati in precedenza.

RESOURCE_GROUP=<value>
FUNCTION_APP=<value>

Creare un progetto per le funzioni locale

Usare il comando Maven seguente per creare un progetto di funzioni e aggiungere le dipendenze necessarie.

mvn archetype:generate --batch-mode \
    -DarchetypeGroupId=com.microsoft.azure \
    -DarchetypeArtifactId=azure-functions-archetype \
    -DappName=$FUNCTION_APP \
    -DresourceGroup=$RESOURCE_GROUP \
    -DappRegion=$LOCATION \
    -DgroupId=com.example \
    -DartifactId=telemetry-functions

Questo comando genera diversi file all'interno di una cartella telemetry-functions:

  • Un file pom.xml da usare con Maven
  • Un file local.settings.json per mantenere le impostazioni dell'app per i test locali
  • File host.json che abilita il bundle di estensione Funzioni di Azure, necessario per l'associazione di output di Azure Cosmos DB nella funzione di analisi dei dati
  • Un file Function.java che include un'implementazione predefinita delle funzioni
  • Alcuni file di test non necessari per questa esercitazione

Per evitare errori di compilazione, sarà necessario eliminare i file di test. Eseguire i comandi seguenti per passare alla nuova cartella del progetto ed eliminare la cartella di test:

cd telemetry-functions
rm -r src/test

Recuperare le impostazioni dell'app per le funzioni per l'uso locale

Per i test locali, sarà necessario fornire al progetto di funzioni le stringhe di connessioni aggiunte in precedenza all'app per le funzioni in Azure in questa esercitazione. Usare il comando seguente di Azure Functions Core Tools, che recupera tutte le impostazioni dell'app per le funzioni archiviate nel cloud e le aggiunge al file local.settings.json:

func azure functionapp fetch-app-settings $FUNCTION_APP

Aggiungere il codice Java

Successivamente, aprire il file Function.java e sostituirne il contenuto con il codice seguente.

package com.example;

import com.example.TelemetryItem.status;
import com.microsoft.azure.functions.annotation.Cardinality;
import com.microsoft.azure.functions.annotation.CosmosDBOutput;
import com.microsoft.azure.functions.annotation.EventHubOutput;
import com.microsoft.azure.functions.annotation.EventHubTrigger;
import com.microsoft.azure.functions.annotation.FunctionName;
import com.microsoft.azure.functions.annotation.TimerTrigger;
import com.microsoft.azure.functions.ExecutionContext;
import com.microsoft.azure.functions.OutputBinding;

public class Function {

    @FunctionName("generateSensorData")
    @EventHubOutput(
        name = "event",
        eventHubName = "", // blank because the value is included in the connection string
        connection = "EventHubConnectionString")
    public TelemetryItem generateSensorData(
        @TimerTrigger(
            name = "timerInfo",
            schedule = "*/10 * * * * *") // every 10 seconds
            String timerInfo,
        final ExecutionContext context) {

        context.getLogger().info("Java Timer trigger function executed at: "
            + java.time.LocalDateTime.now());
        double temperature = Math.random() * 100;
        double pressure = Math.random() * 50;
        return new TelemetryItem(temperature, pressure);
    }

    @FunctionName("processSensorData")
    public void processSensorData(
        @EventHubTrigger(
            name = "msg",
            eventHubName = "", // blank because the value is included in the connection string
            cardinality = Cardinality.ONE,
            connection = "EventHubConnectionString")
            TelemetryItem item,
        @CosmosDBOutput(
            name = "databaseOutput",
            databaseName = "TelemetryDb",
            containerName = "TelemetryInfo",
            connection = "CosmosDBConnectionSetting")
            OutputBinding<TelemetryItem> document,
        final ExecutionContext context) {

        context.getLogger().info("Event hub message received: " + item.toString());

        if (item.getPressure() > 30) {
            item.setNormalPressure(false);
        } else {
            item.setNormalPressure(true);
        }

        if (item.getTemperature() < 40) {
            item.setTemperatureStatus(status.COOL);
        } else if (item.getTemperature() > 90) {
            item.setTemperatureStatus(status.HOT);
        } else {
            item.setTemperatureStatus(status.WARM);
        }

        document.setValue(item);
    }
}

Come si può notare, questo file contiene due funzioni, generateSensorData e processSensorData. La funzione generateSensorData simula un sensore che invia le letture di temperatura e pressione all'hub eventi. Un trigger timer esegue la funzione ogni 10 secondi e un binding di output dell'hub eventi invia il valore restituito all'hub eventi.

Quando l'hub eventi riceve il messaggio, genera un evento. La funzione processSensorData viene eseguita quando riceve l'evento. Elabora quindi i dati dell'evento e usa un binding di output di Azure Cosmos DB per inviare i risultati ad Azure Cosmos DB.

I dati usati da queste funzioni vengono archiviati usando una classe denominata TelemetryItem, che sarà necessario implementare. Creare un nuovo file denominato TelemetryItem.java nella stessa posizione di Function.java e aggiungere il codice seguente:

package com.example;

public class TelemetryItem {

    private String id;
    private double temperature;
    private double pressure;
    private boolean isNormalPressure;
    private status temperatureStatus;
    static enum status {
        COOL,
        WARM,
        HOT
    }

    public TelemetryItem(double temperature, double pressure) {
        this.temperature = temperature;
        this.pressure = pressure;
    }

    public String getId() {
        return id;
    }

    public double getTemperature() {
        return temperature;
    }

    public double getPressure() {
        return pressure;
    }

    @Override
    public String toString() {
        return "TelemetryItem={id=" + id + ",temperature="
            + temperature + ",pressure=" + pressure + "}";
    }

    public boolean isNormalPressure() {
        return isNormalPressure;
    }

    public void setNormalPressure(boolean isNormal) {
        this.isNormalPressure = isNormal;
    }

    public status getTemperatureStatus() {
        return temperatureStatus;
    }

    public void setTemperatureStatus(status temperatureStatus) {
        this.temperatureStatus = temperatureStatus;
    }
}

Eseguire localmente

È ora possibile creare ed eseguire le funzioni in locale e visualizzare i dati in Azure Cosmos DB.

Eseguire i comandi Maven seguenti per creare ed eseguire le funzioni:

mvn clean package
mvn azure-functions:run

Dopo alcuni messaggi di creazione e avvio, verrà visualizzato un output simile all'esempio seguente per ogni esecuzione delle funzioni:

[10/22/19 4:01:30 AM] Executing 'Functions.generateSensorData' (Reason='Timer fired at 2019-10-21T21:01:30.0016769-07:00', Id=c1927c7f-4f70-4a78-83eb-bc077d838410)
[10/22/19 4:01:30 AM] Java Timer trigger function executed at: 2019-10-21T21:01:30.015
[10/22/19 4:01:30 AM] Function "generateSensorData" (Id: c1927c7f-4f70-4a78-83eb-bc077d838410) invoked by Java Worker
[10/22/19 4:01:30 AM] Executed 'Functions.generateSensorData' (Succeeded, Id=c1927c7f-4f70-4a78-83eb-bc077d838410)
[10/22/19 4:01:30 AM] Executing 'Functions.processSensorData' (Reason='', Id=f4c3b4d7-9576-45d0-9c6e-85646bb52122)
[10/22/19 4:01:30 AM] Event hub message received: TelemetryItem={id=null,temperature=32.728691307527015,pressure=10.122563042388165}
[10/22/19 4:01:30 AM] Function "processSensorData" (Id: f4c3b4d7-9576-45d0-9c6e-85646bb52122) invoked by Java Worker
[10/22/19 4:01:38 AM] Executed 'Functions.processSensorData' (Succeeded, Id=1cf0382b-0c98-4cc8-9240-ee2a2f71800d)

È quindi possibile aprire il portale di Azure e passare all'account Azure Cosmos DB. Selezionare Esplora dati, espandere TelemetryInfo, quindi selezionare Elementi per visualizzare i dati all'arrivo.

Azure Cosmos DB Data Explorer

Distribuire Azure e visualizzare i dati di telemetria dell'app

Infine, è possibile distribuire l'app in Azure e verificare se continua a funzionare allo stesso modo che in locale.

Distribuire il progetto in Azure con il comando seguente:

mvn azure-functions:deploy

Le funzioni vengono ora eseguite in Azure e continuano ad accumulare i dati in Azure Cosmos DB. È possibile visualizzare l'app per le funzioni distribuita nel portale di Azure e visualizzare i dati di telemetria dell'app tramite la risorsa di Application Insights connessa, come illustrato negli screenshot seguenti:

Live Metrics Stream:

Application Insights Live Metrics Stream

Prestazioni:

Application Insights Performance blade

Pulire le risorse

Dopo aver terminato le operazioni relative alle risorse di Azure create in questa esercitazione, è possibile eliminarle usando il comando seguente:

az group delete --name $RESOURCE_GROUP

Passaggi successivi

In questa esercitazione si è appreso come creare una funzione di Azure che gestisce gli eventi dell'hub eventi e aggiorna un'istanza di Azure Cosmos DB. Per altre informazioni, vedere la guida per sviluppatori Java per funzioni di Azure. Per informazioni sulle annotazioni usate, vedere le informazioni di riferimento su com.microsoft.azure.functions.annotation.

In questa esercitazione sono state usate variabili di ambiente e impostazioni di applicazioni per archiviare segreti come le stringhe di connessione. Per informazioni sull'archiviazione di questi segreti in Azure Key Vault, vedere Usare i riferimenti a Key Vault per il Servizio app e Funzioni di Azure.

A questo punto, vedere come usare il processo CI/CD di Azure Pipelines per la distribuzione automatizzata: