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

Concluído

Para o nosso exemplo, usamos o event sourcing. 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.

Visualização de sourcing de eventos para compra de café em uma cafeteria.

Prepare seu 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 placeholders <value> e cole e execute os seguintes comandos no seu terminal ou 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>

Observação

Para definir a variável LOCATION, você pode verificar o comando az functionapp list-consumption-locations e usar o 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 escutar. 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 esta função é a que produz eventos, vamos adicionar uma flag 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

Observação

** Use a versão 4 das funções, já que as versões 2 e 3 foram descontinuadas em dezembro de 2022.

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

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

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 pasta telemetry-functions-producer:

  • O ficheiro de compilação pom.xml com dependências predefinidas do Azure.
  • O arquivo local.settings.json para manter as configurações do aplicativo para implantação local e testes manuais.
  • Um arquivo host.json que habilita o Azure Functions Extension Bundle.
  • Um arquivo Function.java 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 e armazenadas no arquivo local.settings.json. Você pode fazer isso automaticamente executando o comando fetch-app-settings.

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

Em seguida, abra o arquivo Function.java e substitua o 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 função generateSensorData 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)

Observação

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, fazer debugging, e testar localmente.

Implantar no Azure

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

mvn azure-functions:deploy