Exercício - Criar uma função do Azure para simular dados telemétricos

Concluído

Para o nosso exemplo, usamos o fornecimento de eventos. Vamos criar uma função que simula dados telemétricos e enviá-los para um hub de eventos. Mais tarde, outra função pode ouvir esse evento e processá-lo e armazená-lo em um banco de dados criado com o Azure Cosmos DB.

Visualization of event sourcing for buying coffee at a coffee shop.

Preparar o ambiente

Vamos definir algumas variáveis de ambiente para manter os comandos a seguir tão curtos e compreensíveis quanto possível. Defina os espaços reservados, cole e execute os <value> seguintes comandos no seu terminal ou ferramenta de linha de comando:

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>

Nota

Para definir a variável LOCATION, você pode verificar o comando e usar o az functionapp list-consumption-locations local mais próximo.

Criar os componentes necessários

O provisionamento dos recursos no Azure leva algum tempo. Vamos começar com a criação do componente o mais cedo possível para evitar longas esperas mais tarde.

Criar um grupo de recursos

É sempre uma boa ideia vincular todos os recursos de um treinamento, prova de conceito ou um protótipo em um grupo de recursos. Dessa forma, você pode limpar convenientemente todos os serviços usados com um comando. Para criar um grupo de recursos no local especificado, execute o seguinte comando no terminal:

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

Criar e configurar um hub de eventos

Para o hub de eventos, é necessário especificar o namespace que ele deve ouvir. Além disso, você precisa configurar a regra de autorização para Listen e Send.

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

Criar, configurar e implantar a função do Azure

Para tornar este exemplo o mais realista possível, crie uma função do Azure e simule dados telemétricos. Você também pode vincular um dispositivo IoT à sua função do Azure, que levaria dados reais. Como essa função é a produtora de eventos, vamos adicionar um sinalizador p ou -p.

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

Nota

As funções de uso da versão 4 como 2 e 3 foram preteridas em dezembro de 2022.

Quando o comando cria seu aplicativo de função, ele também cria um recurso do Application Insights com o az functionapp create mesmo nome. Usamos esse recurso posteriormente para nosso monitoramento.

Para recuperar as cadeias de conexão da conta de armazenamento e do hub de eventos, use os comandos a seguir para salvá-las em variáveis de ambiente e exibi-las com o echo comando.

AZURE_WEB_JOBS_STORAGE=$( \
    az storage account show-connection-string \
        --resource-group $RESOURCE_GROUP \
        --name $STORAGE_ACCOUNT"p" \
        --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

Para armazenar as cadeias de conexão nas configurações do aplicativo da sua conta do Azure Function, execute o seguinte comando no terminal:

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

Agora, seu hub de eventos de recursos do Azure e a função do Azure são criados e configurados para funcionar corretamente juntos.

Em seguida, crie um projeto de funções locais com o Maven.

mvn archetype:generate --batch-mode \
    -DarchetypeGroupId=com.microsoft.azure \
    -DarchetypeArtifactId=azure-functions-archetype \
    -DappName=$FUNCTION_APP"-p" \
    -DresourceGroup=$RESOURCE_GROUP \
    -DappRegion=$LOCATION \
    -DappServicePlanName=$LOCATION"plan" \
    -DgroupId=com.learn \
    -DartifactId=telemetry-functions-producer

Este comando gera vários arquivos dentro de uma telemetry-functions-producer pasta:

  • O pom.xml arquivo de compilação com dependências predefinidas do Azure.
  • O local.settings.json arquivo para armazenar as configurações do aplicativo para implantação local e teste manual.
  • Um host.json arquivo que habilita o Pacote de Extensão do Azure Functions.
  • Um Function.java arquivo que inclui a função de gatilho HTTP padrão.
  • Alguns arquivos de teste que este módulo do Learn não usa.

Não tocamos nos arquivos de teste neste módulo do Learn, então sinta-se à vontade para excluí-los.

cd telemetry-functions-producer
rm -r src/test

Para execução local, as configurações do aplicativo precisam ser recuperadas local.settings.json e armazenadas no arquivo. Você pode fazer isso automaticamente executando o fetch-app-settings comando.

func azure functionapp fetch-app-settings $FUNCTION_APP"-p"

Em seguida, abra o arquivo e substitua o Function.java conteúdo pelo seguinte código:

package com.learn;

import com.microsoft.azure.functions.annotation.EventHubOutput;
import com.microsoft.azure.functions.annotation.FunctionName;
import com.microsoft.azure.functions.annotation.TimerTrigger;
import com.microsoft.azure.functions.ExecutionContext;
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);
    }
}

A generateSensorData função simula um sensor que envia leituras de temperatura e pressão para o hub de eventos. Um gatilho de temporizador executa a função a cada 10 segundos e uma ligação de saída do hub de eventos envia o valor de retorno para o hub de eventos.

Quando o hub de eventos recebe a mensagem, ele gera um evento.

Os dados usados por essa função são armazenados usando uma classe chamada TelemetryItem, que você precisa implementar. Crie um novo arquivo chamado TelemetryItem.java no mesmo local que Function.java e adicione o seguinte código:

package com.learn;

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

Executar localmente

Quando você executa funções do Azure localmente, elas já são transmitidas em todo o mundo! Além disso, você pode revisá-los em seu portal do Azure.

mvn clean package
mvn azure-functions:run

Após algumas mensagens de compilação e inicialização, você verá uma saída semelhante ao exemplo a seguir para cada vez que as funções forem executadas:

[2021-01-19T16:25:40.005Z] Executing 'Functions.generateSensorData' (Reason='Timer fired at 2021-01-19T17:25:40.0044630+01:00', Id=fcf567a3-03ec-4159-9714-aa4449861b30)
[2021-01-19T16:25:40.011Z] Java Timer trigger function executed at: 2021-01-19T17:25:40.009405
[2021-01-19T16:25:40.013Z] Function "generateSensorData" (Id: fcf567a3-03ec-4159-9714-aa4449861b30) invoked by Java Worker
[2021-01-19T16:25:40.048Z] Executed 'Functions.generateSensorData' (Succeeded, Id=fcf567a3-03ec-4159-9714-aa4449861b30, Duration=43ms)

Nota

Antes de implantar e executar sua função na nuvem do Azure, você pode enviar eventos de sua máquina local para todo o mundo! Isso é muito útil para desenvolver, depurar e testar localmente.

Implementar no Azure

Acione a implantação no Azure executando o mvn azure-functions:deploy comando e continue.

mvn azure-functions:deploy