Руководство разработчика Java по Функциям Azure

В данном руководстве содержатся подробные сведения, полезные для разработчиков функций Azure с помощью Java.

В качестве разработчика Java, если вы не знакомы с Функции Azure, рассмотрите возможность первого чтения одной из следующих статей:

Начало работы Основные понятия Сценарии и примеры

Основные сведения о функции Java

Функцией в Java называется метод public, дополненный аннотацией @FunctionName. Такой метод определяет запись для функции Java и должен быть уникальным в пределах пакета. В пакете может быть несколько классов с несколькими открытыми методами, помеченными @FunctionName. Единый пакет развертывается в приложении-функции в Azure. В Azure приложение-функция предоставляет контекст развертывания, выполнения и управления для отдельных функций Java.

Модель программирования

Понятия триггеров и привязок играют решающую роль в Функциях Azure. Триггеры запускают выполнение вашего кода. Привязки предоставляют возможность передавать данные, а также возвращать их из функции без необходимости написания кода доступа к ним.

Создание функций на Java

Чтобы упростить создание функций на Java, созданы специальные средства и архетипы на основе Maven, которые предоставляют готовые шаблоны Java для создания проектов с определенным триггером функции.

Инструментарий на основе Maven

В следующих средах разработки есть инструментарий Функций Azure, который позволяет создавать проекты для функций Java:

В этих статьях показано, как создать первые функции с помощью выбранной интегрированной среды разработки.

Формирование шаблонов проекта

Если вы предпочитаете выполнять разработку из командной строки терминала, сформировать шаблоны для проектов функций на основе Java вам проще всего будет с использованием архетипов Apache Maven. Архетип Maven для Функций Azure на Java опубликован со следующими идентификаторами groupId:artifactId: com.microsoft.azure:azure-functions-archetype.

Следующая команда создает новый проект функции Java на основе этого архетипа:

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

Чтобы приступить к использованию архетипа, воспользуйтесь кратким руководством по Java.

Структура папок

Ниже приведена структура папок проекта java Функции Azure:

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

Вы можете использовать общий файл host.json для настройки приложения-функции. У каждой функции есть собственный файл кода (.java) и файл конфигурации привязки (function.json).

Вы можете добавить несколько функций в проект. Не добавляйте функции в отдельные JAR-файлы. Именно FunctionApp из целевого каталога будет развернут в виде приложения-функции в Azure.

Триггеры и заметки

Функции вызываются триггером, таким как HTTP-запрос, таймер или обновление данных. Функция должна обработать такой триггер и связанные с ним входные данные, а также создать на их основе одно или несколько выходных значений.

Используйте заметки Java, включенные в пакет com.microsoft.azure.functions.annotation.*, чтобы привязать входные и выходные данные к своим методам. Дополнительные сведения см. в справочной документации по Java.

Важно!

Чтобы локально обрабатывать триггеры хранилища BLOB-объектов, очередей или таблиц Azure, необходимо указать учетную запись хранения Azure в файле local.settings.json.

Пример:

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

Ниже приведены function.jsonсозданные 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"
    }
  ]
}

Версии Java

Версия Java, на которой работает приложение в Azure, указана в файле pom.xml. В настоящее время архетип Maven создает файл pom.xml для Java 8, который можно изменить перед публикацией. Версия Java в pom.xml должна соответствовать версии, в которой вы локально разработали и протестировали приложение.

Поддерживаемые версии

В следующей таблице приведены текущие поддерживаемые версии Java для каждой основной версии среды выполнения функций по операционным системам.

Версия службы "Функции" Версии Java (Windows) Версии Java (Linux)
4.x 17
11
8
21 (предварительная версия)
17
11
8
3.x 11
8
11
8
2.x 8 Н/Д

Если для развертывания не указана версия Java, по умолчанию во время развертывания в Azure архетипом Maven используется версия Java 8.

Определение версии развертывания

Для управления версией Java, используемой архетипом Maven, служит параметр -DjavaVersion. Значение этого параметра может быть либо 8, 1117 либо 21.

Архетип Maven создает файл pom.xml для заданной версии Java. Следующие элементы файла pom.xml указывают версию Java, необходимую для использования.

Элемент Значение Java 8 Значение Java 11 Значение Java 17 Значение Java 21 (предварительная версия, Linux) Description
Java.version 1.8 11 17 21 Версия Java для модуля компилятора Maven.
JavaVersion 8 11 17 21 Версия Java, размещенная в Azure приложением-функцией.

На следующих примерах показаны параметры для Java 8, расположенные в соответствующих разделах файла 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>

Важно!

Необходимо правильно установить переменную среды JAVA_HOME в каталог JDK, используемый во время компиляции кода с помощью Maven. Убедитесь, что версия JDK не ниже Java.version.

Определение операционной системы, используемой для развертывания

Maven также позволяет задать операционную систему, в которой выполняется приложение-функция в Azure. Используйте элемент os для выбора операционной системы.

Элемент Windows Linux Docker
os windows linux docker

На следующем примере показан параметр операционной системы в разделе runtime файла 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>

Обеспечения доступности и предоставления поддержки времени выполнения пакета JDK

Microsoft и Adoptium сборки OpenJDK предоставляются и поддерживаются в функциях Java 8 (Adoptium), Java 11, 17 и 21 (MSFT). Эти двоичные файлы предоставляются в виде бесплатных, кросс-платформенных дистрибутивов OpenJDK для Azure. Они содержат все компоненты для сборки и запуска приложений Java SE.

Для локальной разработки или тестирования можно бесплатно загрузить сборку Microsoft OpenJDK или двоичные файлы Adoptium Temurin. Поддержка Azure для устранения проблем с пакетами JDK и приложениями-функциями предоставляется, если у вас есть соответствующий план поддержки.

Если вы хотите продолжить использование Zulu для двоичных файлов Azure в приложении-функции, настройте приложение соответствующим образом. Вы можете продолжать использовать двоичные файлы Azul для вашего сайта. Однако все исправления безопасности или улучшения доступны только в новых версиях OpenJDK. Из-за этого вы должны в конечном итоге удалить эту конфигурацию, чтобы приложения использовали последнюю доступную версию Java.

Настройка виртуальной машины Java

Функции Azure позволяют настраивать виртуальную машину Java, которая используется для выполнения функций Java. По умолчанию применяются следующие параметры виртуальной машины Java.

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

Вы можете указать другие аргументы для JVM с помощью одного из следующих параметров приложения в зависимости от типа плана:

Тип плана Имя настройки Комментарий
План потребления languageWorkers__java__arguments Этот параметр увеличивает время холодного запуска для функций Java, работающих в плане потребления.
План категории "Премиум"
План ценовой категории "Выделенный"
JAVA_OPTS

В следующих разделах показано, как добавить эти параметры. Дополнительные сведения о работе с параметрами приложения см. в разделе "Работа с параметрами приложения".

Портал Azure

В портал Azure перейдите на вкладку "Приложение Параметры" для добавления languageWorkers__java__argumentsJAVA_OPTS или параметра.

Azure CLI

Команду az functionapp config appsettings set можно использовать для добавления этих параметров, как показано в следующем примере для -Djava.awt.headless=true параметра:

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

В этом примере показано, как включить режим без монитора. Замените в нем заполнитель <APP_NAME> именем приложения-функции, а <RESOURCE_GROUP> — именем группы ресурсов.

Сторонние библиотеки

Служба "Функции Azure" поддерживает использование сторонних библиотек. По умолчанию все зависимости, указанные в файле pom.xml для проекта, автоматически связываются при сборке с целью mvn package. Зависимости библиотек, не указанных как зависимости в файле pom.xml, поместите в каталог lib в корневой папке функции. Зависимости, размещенные в каталоге lib, добавляются в загрузчик системного класса во время выполнения.

Зависимость com.microsoft.azure.functions:azure-functions-java-library по умолчанию предоставляется в пути классов, и ее не нужно добавлять в каталог lib. Кроме того, azure-functions-java-worker добавляет в путь классов все перечисленные здесь зависимости.

Поддержка типов данных

Для входных и выходных привязок можно использовать объекты POJO, определенные в azure-functions-java-library типы, или примитивные типы данных, как например String или Integer.

Объекты POJO

Для преобразования входных данных в формат POJO azure-functions-java-worker использует библиотеку gson. Типам POJO, используемые как входные данные для функций, необходим такой же модификатор доступа public.

Двоичные данные

Чтобы создать привязку для ввода и вывода к byte[], в файле function.json задайте для поля dataType значение 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");
    }

Если допускаются значения NULL, добавьте Optional<T>.

Привязки

Входные и выходные привязки реализуют декларативный способ подключения к данным из кода. У функции может быть несколько входных и выходных привязок.

Пример привязки входа

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

Эта функция вызывается с помощью HTTP-запроса.

  • Полезные данные HTTP-запроса передаются в формате String через аргумент inputReq.
  • Из Хранилища таблиц извлекается одна запись, которая передается в формате TestInputData через аргумент inputData.

Чтобы получить пакет входных данных, выполните привязку к String[], POJO[], List<String> или 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;
}

Эта функция активируется всякий раз при наличии новых данных в настроенном концентраторе событий. Так как для cardinality установлено значение MANY, функция получает пакет сообщений от концентратора событий. Полученный от концентратора событий EventData преобразуется в TestEventData для выполнения функции.

Пример выходной привязки

Вы можете создать привязку для вывода к возвращаемому значению с помощью $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;
    }
}

При наличии нескольких выходных привязок используйте возвращаемое значение только для одной из них.

Чтобы отправить несколько выходных значений, используйте тип OutputBinding<T>, который определен в пакете 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;
    }

Эта функция вызывается в объекте HttpRequest . Она записывает несколько значений в Хранилище очередей.

HttpRequestMessage и HttpResponseMessage

Они определяются в azure-functions-java-library. Они вспомогательные типы для работы с функциями HttpTrigger.

Специализированные типы Назначение Типичное применение
HttpRequestMessage<T> Триггер HTTP Получение метода, заголовков или запросов
HttpResponseMessage Привязка к выходным данным HTTP Возврат кодов состояния, отличных от 200

Метаданные

Несколько триггеров отправляют метаданные триггеров вместе с входными данными. Вы можете использовать аннотацию @BindingName для привязки к метаданным триггера.

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

В приведенном выше примере значение queryValue привязано к параметру строки запроса name в URL-адресе HTTP-запроса http://{example.host}/api/metadata?name=test. Вот еще один пример привязки к Id из метаданных триггера очереди.

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

Примечание.

Предоставленное в заметке имя должно соответствовать свойству метаданных.

Контекст выполнения

Контекст ExecutionContext, определенный в azure-functions-java-library, содержит вспомогательные методы для взаимодействия со средой выполнения функций. Дополнительные сведения см. в справочной статье ExecutionContext.

Ведение журнала

Используйте средство getLogger, определенное в ExecutionContext, для записи журналов из кода функции.

Пример:


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

Просмотр журналов и трассировки

Azure CLI можно использовать для потоковой передачи журналов stdout и stderr Java и других приложений.

Ниже показано, как настроить для приложения-функции запись журнала приложения с использованием Azure CLI.

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

Чтобы выполнять потоковую передачу выходных данных журналов для приложения-функции через Azure CLI, откройте новое окно командной строки, сеанс Bash или терминала и введите следующую команду:

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

Команда az webapp log tail позволяет фильтровать выходные данные с помощью параметра --provider.

Чтобы скачать файлы журналов как единый ZIP-файл с помощью Azure CLI, откройте командную строку, Bash или сеанс терминала и введите следующую команду:

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

Перед выполнением этой команды необходимо включить ведение журнала файловой системы с помощью портала Azure или Azure CLI.

Переменные среды

В Функциях параметры приложения, такие как строки подключения службы, доступны в виде переменных среды во время выполнения. Доступ к этим параметрам можно получить с помощью System.getenv("AzureWebJobsStorage").

В следующем примере, показано как получить параметр приложения по ключу с именем 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);
    }
}

Использование внедрения зависимостей в Функциях Java

Функции Azure Java поддерживает шаблон разработки программного обеспечения для внедрения зависимостей (DI), который является способом достижения Инверсия элемента управления (IoC) между классами и их зависимостями. Java Функции Azure предоставляет перехватчик для интеграции с популярными платформами внедрения зависимостей в приложениях функций. Функции Azure Java SPI содержит интерфейс FunctionInstanceInjector. Реализуя этот интерфейс, вы можете вернуть экземпляр класса функции, и функции будут вызываться в этом экземпляре. Это дает платформам, таким как Spring, Quarkus, Google Guice, Dagger и т. д. возможность создавать экземпляр функции и зарегистрировать его в контейнере IOC. Это означает, что эти платформы внедрения зависимостей можно использовать для естественного управления функциями.

Примечание.

Microsoft Функции Azure Типы SPI Java (azure-function-java-spi) — это пакет, содержащий все интерфейсы SPI для сторонних производителей для взаимодействия со средой выполнения функций Microsoft Azure.

Инектор экземпляра функции для внедрения зависимостей

Azure-function-java-spi содержит интерфейс FunctionInstanceInjector

package com.microsoft.azure.functions.spi.inject; 

/** 

 * The instance factory used by DI framework to initialize function instance. 

 * 

 * @since 1.0.0 

 */ 

public interface FunctionInstanceInjector { 

    /** 

     * This method is used by DI framework to initialize the function instance. This method takes in the customer class and returns 

     * an instance create by the DI framework, later customer functions will be invoked on this instance. 

     * @param functionClass the class that contains customer functions 

     * @param <T> customer functions class type 

     * @return the instance that will be invoked on by azure functions java worker 

     * @throws Exception any exception that is thrown by the DI framework during instance creation 

     */ 

    <T> T getInstance(Class<T> functionClass) throws Exception; 

} 

Дополнительные примеры, использующие FunctionInstanceInjector для интеграции с платформами внедрения зависимостей, относятся к этому репозиторию.

Следующие шаги

Дополнительные сведения о разработке в службе "Функции Azure" на Java см. в следующих статьях.