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

Concluído

Para nosso exemplo, usamos o fornecimento de eventos. Vamos criar uma função que simula dados telemétricos e os envia a um hub de eventos. Posteriormente, outra função poderá escutar esse evento, bem como 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.

Prepare o seu ambiente

Vamos definir algumas variáveis de ambiente e fazer o possível para manter os comandos a seguir curtos e compreensíveis. Defina os espaços reservados de <value>, depois cole e execute os seguintes comandos no terminal ou na 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>

Observação

Para definir a variável LOCATION, será possível verificar o comando az functionapp list-consumption-locations e usar a localização mais próxima.

Criar os componentes obrigatórios

O provisionamento de recursos no Azure levará algum tempo. Vamos começar com a criação de um componente o mais cedo possível para evitar longas esperas no futuro.

Criar um grupo de recursos

Recomendamos associar todos os recursos de treinamento, prova de conceito ou protótipo em um grupo de recursos. Desse modo, será possível limpar de maneira conveniente todos os serviços usados com um comando único. Para criar um grupo de recursos no local especificado, execute o seguinte comando no seu terminal:

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

Criar e configurar um hub de eventos

É necessário especificar o namespace que o hub de eventos deverá escutar. Além disso, será preciso 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 uma função do Azure

Para tornar este exemplo o mais realista possível, crie uma função do Azure e simule dados telemétricos. Também será possível associar um dispositivo IoT à função do Azure. Esse procedimento usará dados reais. Como essa função é a que produz 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

Observação

Use a versão 4 das funções, pois as versões 2 e 3 foram preteridas em dezembro de 2022.

Quando o comando az functionapp create cria seu aplicativo de funções, 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 da conta de armazenamento e do hub de eventos, use os comandos a seguir para salvá-las em variáveis de ambiente e, em seguida, exiba-as 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 Functions, execute o seguinte comando em seu 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, o hub de eventos de recursos do Azure e a função do Azure são criados e configurados para funcionarem corretamente juntos.

A seguir, 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

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

  • O arquivo 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 teste manual.
  • Um arquivo host.json que habilita o pacote de extensão do Azure Functions.
  • Um arquivo Function.java que inclui a função HTTP padrão do gatilho.
  • Alguns arquivos de teste que este módulo do Learn não usará.

Não usamos os arquivos de teste neste módulo do Learn, então, fique à vontade para excluí-los.

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

Para a 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"

Depois 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 as leituras de temperatura e de pressão para o hub de eventos. Um gatilho de temporizador executa a função a cada dez segundos e uma associação de saída do hub de eventos envia o valor retornado 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ê precisará implementar. Crie um 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

Ao executar as funções do Azure localmente, elas poderão ser transmitidas ao mundo todo! Além disso, você pode examiná-las em seu portal do Azure.

mvn clean package
mvn azure-functions:run

Após algumas mensagens de build e de 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 a função na nuvem do Azure, será possível enviar eventos ao mundo todo de seu computador local! Isso é muito útil para desenvolvimento, depuração e teste local.

Implantar no Azure

Dispare a implantação no Azure executando o comando mvn azure-functions:deploy, depois prossiga.

mvn azure-functions:deploy