Guia do desenvolvedor de Java do Azure Functions

Este guia contém informações detalhadas para ajudar você a desenvolver com sucesso o Azure Functions usando o Java.

Como desenvolvedor de Java, se você for novo no Azure Functions, considere ler um dos seguintes artigos primeiro:

Introdução Conceitos

Noções básicas da função de linguagem Java

Uma função Java é um método public, decorado com a anotação @FunctionName. Esse método define a entrada para uma função Java e deve ser exclusivo em um pacote específico. O pacote pode ter várias classes com vários métodos públicos anotados com @FunctionName. Um único pacote é implantado em um aplicativo de funções no Azure. Ao executar no Azure, o aplicativo de funções fornece a implantação, execução e contexto de gerenciamento para suas funções Java individuais.

Modelo de programação

Os conceitos de gatilhos e ligações são fundamentais para as Funções do Azure. Os gatilhos iniciam a execução do seu código. As ligações fornecem uma maneira de transmitir dados e retornar dados de uma função, sem precisar escrever código de acesso a dados personalizado.

Criar funções Java

Para facilitar a criação de funções Java, há ferramentas baseadas no Maven e arquétipos que usam modelos Java predefinidos para ajudá-lo a criar projetos com um gatilho de função específico.

Ferramentas baseadas no Maven

Os seguintes ambientes de desenvolvedor têm ferramentas do Azure Functions que permitem criar projetos de funções Java:

Os links do artigo acima mostram como criar suas primeiras funções usando o IDE de sua escolha.

Scaffolding do projeto

Caso você prefira o desenvolvimento de linha de comando do Terminal, a maneira mais simples de fazer o scaffolding de projetos de funções baseados em Java é com arquétipos Apache Maven. O arquétipo Java Maven para o Azure Functions é publicado na seguinte groupId:artifactId: com.microsoft.azure:azure-functions-archetype.

O seguinte comando gera um novo projeto de função Java usando esse arquétipo:

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

Para começar a usar esse arquétipo, confira o Início rápido do Java.

Estrutura de pastas

Aqui está a estrutura de pastas de um projeto Java do Azure Functions:

FunctionsProject
 | - src
 | | - main
 | | | - java
 | | | | - FunctionApp
 | | | | | - MyFirstFunction.java
 | | | | | - MySecondFunction.java
 | - target
 | | - azure-functions
 | | | - FunctionApp
 | | | | - FunctionApp.jar
 | | | | - host.json
 | | | | - MyFirstFunction
 | | | | | - function.json
 | | | | - MySecondFunction
 | | | | | - function.json
 | | | | - bin
 | | | | - lib
 | - pom.xml

Você pode usar um arquivo host.json compartilhado para configurar o aplicativo de funções. Cada função possui seu próprio arquivo de código (.java) e arquivo de configuração de associação (function.json).

Você pode colocar mais de uma função em um projeto. Evite colocar suas funções no jars separados. O FunctionApp no diretório de destino é o que é implantado em seu aplicativo de funções no Azure.

Gatilhos e anotações

As funções são invocadas por um gatilho, como uma solicitação HTTP, um temporizador ou uma atualização para os dados. Sua função precisa processar esse gatilho e quaisquer outras entradas para produzir uma ou mais saídas.

Use as anotações Java incluídas no pacote com.microsoft.azure.functions.annotation. * para ligar entradas e saídas a seus métodos. Para obter mais informações, confira os documentos de referência Java.

Importante

Você precisa configurar uma conta de Armazenamento do Azure em local.settings.json para executar localmente os gatilhos de armazenamento de Blob do Azure, armazenamento de Filas do Azure ou Armazenamento de Tabelas do Azure.

Exemplo:

public class Function {
    public String echo(@HttpTrigger(name = "req", 
      methods = {HttpMethod.POST},  authLevel = AuthorizationLevel.ANONYMOUS) 
        String req, ExecutionContext context) {
        return String.format(req);
    }
}

Aqui está o function.json correspondente gerado pelo azure-functions-maven-plugin:

{
  "scriptFile": "azure-functions-example.jar",
  "entryPoint": "com.example.Function.echo",
  "bindings": [
    {
      "type": "httpTrigger",
      "name": "req",
      "direction": "in",
      "authLevel": "anonymous",
      "methods": [ "GET","POST" ]
    },
    {
      "type": "http",
      "name": "$return",
      "direction": "out"
    }
  ]
}

Versões do Java

A versão Java usada durante a criação do aplicativo de funções no qual as funções são executadas no Azure é especificada no arquivo pom.xml. O arquétipo Maven atualmente gera um pom.xml para Java 8, que você pode alterar antes da publicação. A versão Java no pom.xml deve corresponder à versão na qual você desenvolveu e testou localmente seu aplicativo.

Versões com suporte

A seguinte tabela mostra as versões Java com suporte atuais para cada versão principal do runtime do Functions, por sistema operacional:

Versão do Functions Versões Java (Windows) Versões Java (Linux)
3.x 11
8
11
8
2. x 8 N/D

A menos que você especifique uma versão Java para a sua implantação, o Maven define o Java 8 como padrão durante a implantação no Azure.

Especifique a versão de implantação

Você pode controlar a versão Java direcionada pelo arquétipo Maven usando o parâmetro -DjavaVersion. O valor desse parâmetro pode ser 8 ou 11.

O arquétipo Maven gera um pom.xml que tem como destino a versão Java especificada. Os seguintes elementos no pom.xml indicam a versão do Java a ser usada:

Elemento Valor do Java 8 Valor do Java 11 Descrição
Java.version 1.8 11 Versão do Java usada pelo maven-compiler-plugin.
JavaVersion 8 11 Versão do Java hospedada pelo aplicativo de funções no Azure.

Os exemplos a seguir mostram as configurações para Java 8 nas seções relevantes do arquivo pom.xml:

Java.version

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>1.8</java.version>
    <azure.functions.maven.plugin.version>1.6.0</azure.functions.maven.plugin.version>
    <azure.functions.java.library.version>1.3.1</azure.functions.java.library.version>
    <functionAppName>fabrikam-functions-20200718015742191</functionAppName>
    <stagingDirectory>${project.build.directory}/azure-functions/${functionAppName}</stagingDirectory>
</properties>

JavaVersion

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

Importante

Você deve ter a variável de ambiente JAVA_HOME definida corretamente para o diretório do JDK que é usado durante a compilação de código usando o Maven. Certifique-se de que a versão do JDK seja pelo menos tão recente quanto a configuração Java.version.

Especifique o SO de implantação

O Maven também permite especificar o sistema operacional no qual seu aplicativo de funções é executado no Azure. Use o elemento os para escolher o sistema operacional.

Elemento Windows Linux Docker
os windows linux docker

O exemplo a seguir mostra a configuração do sistema operacional na seção runtime do arquivo pom.xml:

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

Suporte e disponibilidade de runtime do JDK

Para o desenvolvimento local de aplicativos de funções Java, baixe e use os JDKs Java 8 Azul Zulu Enterprise para Azure doAzul Systems. O Azure Functions usa um runtime do JDK Java da Azul quando você implanta seu aplicativo de funções na nuvem.

O Suporte do Azure está disponível para solucionar problemas com os JDKs e os aplicativos de funções com um plano de suporte qualificado.

Personalizar JVM

O Functions permite que você personalize a JVM (máquina virtual Java) usada para executar suas funções Java. As seguintes opções de JVM são usadas por padrão:

  • -XX:+TieredCompilation
  • -XX:TieredStopAtLevel=1
  • -noverify
  • -Djava.net.preferIPv4Stack=true
  • -jar

Você pode fornecer argumentos adicionais em uma configuração de aplicativo chamada JAVA_OPTS. Você pode adicionar configurações ao seu aplicativo de funções implantado no Azure no portal do Azure ou na CLI do Azure.

Importante

No plano de consumo, você também deve adicionar a configuração de WEBSITE_USE_PLACEHOLDER com um valor 0 para que a personalização funcione. Essa configuração aumenta as horas de inicialização a frio para funções Java.

Portal do Azure

No portal do Azure, use a guia Configurações de Aplicativo para adicionar a configuração JAVA_OPTS.

CLI do Azure

Você pode usar o comando az functionapp config appsettings set para definir JAVA_OPTS, como no seguinte exemplo:

az functionapp config appsettings set \
    --settings "JAVA_OPTS=-Djava.awt.headless=true" \
    "WEBSITE_USE_PLACEHOLDER=0" \
    --name <APP_NAME> --resource-group <RESOURCE_GROUP>

Este exemplo habilita o modo sem periféricos. Substitua <APP_NAME> pelo nome do seu aplicativo de funções e <RESOURCE_GROUP> pelo grupo de recursos.

Bibliotecas de terceiros

O Azure Functions oferece suporte ao uso de bibliotecas de terceiros. Por padrão, todas as dependências especificadas no arquivo pom.xml do projeto são agrupadas automaticamente durante a meta mvn package. Para bibliotecas não especificadas como dependências no pom.xmlarquivo, coloque-as em um diretóriolib no diretório-raiz da função. As dependências colocadas no diretório lib são adicionadas ao carregador de classe do sistema em runtime.

A dependência com.microsoft.azure.functions:azure-functions-java-library é fornecida no classpath por padrão e não precisa ser incluída no diretório lib. Além disso,o azure-functions-java-worker adiciona as dependências listadas aqui ao classpath.

Suporte do tipo de dados

Você pode usar POJOs (objetos Java básicos), tipos definidos em azure-functions-java-library ou tipos de dados primitivos, como String e Integer, para vincular a associações de entrada ou saída.

POJOs

Para converter dados de entrada em POJO, o azure-functions-java-worker usa a biblioteca gson. Tipos de POJO usados como entradas para funções devem ser public.

Dados binários

Associe entradas ou saídas binárias a byte[], definindo o campo dataType em function.json como binary:

   @FunctionName("BlobTrigger")
    @StorageAccount("AzureWebJobsStorage")
     public void blobTrigger(
        @BlobTrigger(name = "content", path = "myblob/{fileName}", dataType = "binary") byte[] content,
        @BindingName("fileName") String fileName,
        final ExecutionContext context
    ) {
        context.getLogger().info("Java Blob trigger function processed a blob.\n Name: " + fileName + "\n Size: " + content.length + " Bytes");
    }

Se você espera valores nulos, use Optional<T>.

Associações

As ligações de entrada e saída fornecem uma maneira declarativa de se conectar aos dados de dentro do seu código. Uma função pode ter várias ligações de entrada e saída.

Exemplo de associação de entrada

package com.example;

import com.microsoft.azure.functions.annotation.*;

public class Function {
    @FunctionName("echo")
    public static String echo(
        @HttpTrigger(name = "req", methods = { HttpMethod.PUT }, authLevel = AuthorizationLevel.ANONYMOUS, route = "items/{id}") String inputReq,
        @TableInput(name = "item", tableName = "items", partitionKey = "Example", rowKey = "{id}", connection = "AzureWebJobsStorage") TestInputData inputData,
        @TableOutput(name = "myOutputTable", tableName = "Person", connection = "AzureWebJobsStorage") OutputBinding<Person> testOutputData
    ) {
        testOutputData.setValue(new Person(httpbody + "Partition", httpbody + "Row", httpbody + "Name"));
        return "Hello, " + inputReq + " and " + inputData.getKey() + ".";
    }

    public static class TestInputData {
        public String getKey() { return this.RowKey; }
        private String RowKey;
    }
    public static class Person {
        public String PartitionKey;
        public String RowKey;
        public String Name;

        public Person(String p, String r, String n) {
            this.PartitionKey = p;
            this.RowKey = r;
            this.Name = n;
        }
    }
}

Você invoca essa função com uma solicitação HTTP.

  • A carga de solicitação HTTP é passada como um String para o argumento inputReq.
  • Uma entrada é recuperada do armazenamento de tabela e é passada como TestInputData para o argumento inputData.

Para receber um lote de entradas, você pode associar a String[], POJO[], List<String> ou List<POJO>.

@FunctionName("ProcessIotMessages")
    public void processIotMessages(
        @EventHubTrigger(name = "message", eventHubName = "%AzureWebJobsEventHubPath%", connection = "AzureWebJobsEventHubSender", cardinality = Cardinality.MANY) List<TestEventData> messages,
        final ExecutionContext context)
    {
        context.getLogger().info("Java Event Hub trigger received messages. Batch size: " + messages.size());
    }
    
    public class TestEventData {
    public String id;
}

Essa função é acionada sempre que houver novos dados no hub de eventos configurado. Como q cardinality é definidq para MANY, a função recebe um lote de mensagens do hub de eventos. EventData do hub de eventos é convertido em TestEventData para a execução da função.

Exemplo de associação de saída

Você pode vincular uma associação de saída ao valor retornado usando $return.

package com.example;

import com.microsoft.azure.functions.annotation.*;

public class Function {
    @FunctionName("copy")
    @StorageAccount("AzureWebJobsStorage")
    @BlobOutput(name = "$return", path = "samples-output-java/{name}")
    public static String copy(@BlobTrigger(name = "blob", path = "samples-input-java/{name}") String content) {
        return content;
    }
}

Se houver várias associações de saída, use o valor de retorno de apenas um deles.

Para enviar vários valores de saída, use OutputBinding<T> definido no pacote azure-functions-java-library.

@FunctionName("QueueOutputPOJOList")
    public HttpResponseMessage QueueOutputPOJOList(@HttpTrigger(name = "req", methods = { HttpMethod.GET,
            HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
            @QueueOutput(name = "itemsOut", queueName = "test-output-java-pojo", connection = "AzureWebJobsStorage") OutputBinding<List<TestData>> itemsOut, 
            final ExecutionContext context) {
        context.getLogger().info("Java HTTP trigger processed a request.");
       
        String query = request.getQueryParameters().get("queueMessageId");
        String queueMessageId = request.getBody().orElse(query);
        itemsOut.setValue(new ArrayList<TestData>());
        if (queueMessageId != null) {
            TestData testData1 = new TestData();
            testData1.id = "msg1"+queueMessageId;
            TestData testData2 = new TestData();
            testData2.id = "msg2"+queueMessageId;

            itemsOut.getValue().add(testData1);
            itemsOut.getValue().add(testData2);

            return request.createResponseBuilder(HttpStatus.OK).body("Hello, " + queueMessageId).build();
        } else {
            return request.createResponseBuilder(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("Did not find expected items in CosmosDB input list").build();
        }
    }

     public static class TestData {
        public String id;
    }

Você invoca essa função em uma HttpRequest. Ela grava vários valores no armazenamento de Filas.

HttpRequestMessage e HttpResponseMessage

Eles são definidos em azure-functions-java-library. Eles são tipos auxiliares para trabalhar com as funções HttpTrigger.

Tipo especializado Destino Uso típico
HttpRequestMessage<T> Gatilho de HTTP Obtém método, cabeçalhos ou consultas
HttpResponseMessage Associação de saída HTTP Retorna um status diferente de 200

Metadados

Poucos gatilhos enviam gatilhos de metadados juntamente com dados de entrada. Você pode usar a anotação @BindingName para associar aos metadados do gatilho.

package com.example;

import java.util.Optional;
import com.microsoft.azure.functions.annotation.*;


public class Function {
    @FunctionName("metadata")
    public static String metadata(
        @HttpTrigger(name = "req", methods = { HttpMethod.GET, HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS) Optional<String> body,
        @BindingName("name") String queryValue
    ) {
        return body.orElse(queryValue);
    }
}

No exemplo anterior, o queryValue está associado ao parâmetro de cadeia de caracteres de consulta name na URL de solicitação HTTP http://{example.host}/api/metadata?name=test. Aqui está outro exemplo, mostrando como associar a Id de metadados de gatilho de fila.

 @FunctionName("QueueTriggerMetadata")
    public void QueueTriggerMetadata(
        @QueueTrigger(name = "message", queueName = "test-input-java-metadata", connection = "AzureWebJobsStorage") String message,@BindingName("Id") String metadataId,
        @QueueOutput(name = "output", queueName = "test-output-java-metadata", connection = "AzureWebJobsStorage") OutputBinding<TestData> output,
        final ExecutionContext context
    ) {
        context.getLogger().info("Java Queue trigger function processed a message: " + message + " with metadaId:" + metadataId );
        TestData testData = new TestData();
        testData.id = metadataId;
        output.setValue(testData);
    }

Observação

O nome fornecido na anotação precisa corresponder à propriedade de metadados.

Contexto de execução

ExecutionContext definido na azure-functions-java-library contém métodos auxiliares para se comunicar com o runtime das funções. Para obter mais informações, consulte o artigo Referência do ExecutionContext.

Agente

Use getLogger, definido em ExecutionContext, para gravar logs do código de função.

Exemplo:


import com.microsoft.azure.functions.*;
import com.microsoft.azure.functions.annotation.*;

public class Function {
    public String echo(@HttpTrigger(name = "req", methods = {HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) String req, ExecutionContext context) {
        if (req.isEmpty()) {
            context.getLogger().warning("Empty request body received by function " + context.getFunctionName() + " with invocation " + context.getInvocationId());
        }
        return String.format(req);
    }
}

Exibir logs e rastreamento

Você pode usar a CLI do Azure para transmitir o log Java stdout e stderr, bem como outros logs de aplicativo.

Confira como configurar seu aplicativo de funções para gravar o log do aplicativo usando a CLI do Azure:

az webapp log config --name functionname --resource-group myResourceGroup --application-logging true

Para transmitir a saída de log de seu aplicativo de funções usando a CLI do Azure, abra um novo prompt de comando, Bash ou sessão de terminal e digite o seguinte comando:

az webapp log tail --name webappname --resource-group myResourceGroup

O comando az webapp log tail tem opções para filtrar a saída usando a opção --provider.

Para baixar os arquivos de log como um único arquivo ZIP usando a CLI do Azure, abra um novo prompt de comando, Bash ou sessão de terminal e digite o seguinte comando:

az webapp log download --resource-group resourcegroupname --name functionappname

Você deve ter habilitado o log do sistema de arquivos no Portal do Azure ou na CLI do Azure antes de executar esse comando.

Variáveis de ambiente

Em funções, configurações do aplicativo, como conexão de serviço cadeias de caracteres, são expostas como variáveis de ambiente durante a execução. Você pode acessar essas configurações usando System.getenv("AzureWebJobsStorage").

O exemplo a seguir obtém a configuração de aplicativo, com a chave chamada myAppSetting:


public class Function {
    public String echo(@HttpTrigger(name = "req", methods = {HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) String req, ExecutionContext context) {
        context.getLogger().info("My app setting value: "+ System.getenv("myAppSetting"));
        return String.format(req);
    }
}

Observação

O valor de AppSetting FUNCTIONS_EXTENSION_VERSION deve ser ~ 2 ou ~ 3 para uma experiência de inicialização a frio otimizada.

Próximas etapas

Para obter mais informações sobre o desenvolvimento de Java do Azure Functions confira os seguintes recursos: