Azure Functions Java 开发人员指南

Note

用于 Azure Functions 的 Java 当前为预览版。 若要接收重要更新,包括中断性变更公告,请留意 Azure 应用服务公告存储库。

编程模型

触发器和绑定是 Azure Functions 的基本概念。 触发器启动代码的执行。 绑定可让你向函数传递数据以及从函数返回数据,而无需编写自定义的数据访问代码。

函数应是处理输入和生成输出的无状态方法。 函数不应依赖于类的任何实例字段。 所有函数方法的性质应该是 public,带有 @FunctionName 注释的方法必须是唯一的,因为方法名称定义函数的项。

文件夹结构

下面是某个 Azure Functions Java 项目的文件夹结构:

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 中的函数应用的内容。

触发器和注释

Azure Functions 由触发器(例如 HTTP 请求、计时器或数据更新)进行调用。 函数需要处理该触发器和任何其他输入以生成一个或多个输出。

使用 com.microsoft.azure.functions.annotation.* 包中附带的 Java 注释将输入和输出绑定到方法。 有关详细信息,请参阅 Java 参考文档

Important

必须在 local.settings.json 中配置 Azure 存储帐户,才能本地运行 Azure 存储 Blob、队列或表触发器。

示例:

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

下面是 azure-functions-maven-plugin 生成的相应 function.json

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

JDK 运行时可用性和支持

Azul Systems 下载和使用适用于 Azure 的 Azul Zulu Enterprise Java 8 JDK,以进行本地 Java 函数应用开发。 将函数应用部署到云时,Azure Functions 使用 Azul Java 8 JDK 运行时。

对于 JDK 和函数应用的问题,Azure 支持可通过限定的支持计划获得。

第三方库

Azure Functions 支持使用第三方库。 默认情况下,项目 pom.xml 文件中指定的所有依赖项将在 mvn package 目标期间自动进行绑定。 对于未在 pom.xml 文件中指定为依赖项的库,请将它们放在函数根目录的 lib 目录中。 放置在 lib 目录中的依赖项将在运行时添加到系统类加载器中。

默认情况下,类路径上提供了 com.microsoft.azure.functions:azure-functions-java-library 依赖项,不需要将其包含在 lib 目录中。 此外,azure-functions-java-worker 会将此处列出的依赖项添加到类路径。

数据类型支持

可以使用无格式普通 Java 对象 (POJO)、azure-functions-java-library 中定义的类型或字符串、整数等基元 dataType 绑定到输入/输出绑定。

普通旧 Java 对象 (POJO)

azure-functions-java-worker 使用 gson 库将输入数据转换为 POJO。 用作函数输入的 POJO 类型应是 public

二进制数据

通过将 function.json 中的 dataType 字段设置为 binary,将二进制输入或输出绑定到 byte[]

   @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 = { "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 请求调用的。

  • String 的形式为参数 inputReq 传递 HTTP 请求有效负载
  • 从 Azure 表存储中检索一个项,并将其作为 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;
    }
}

如果有多个输出绑定,请只使用其中一个绑定的返回值。

若要发送多个输出值,请使用 azure-functions-java-library 包中定义的 OutputBinding<T>

@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 调用上述函数,并将多个值写入 Azure 队列

HttpRequestMessage 和 HttpResponseMessage

azure-functions-java-library 中定义的 HttpRequestMessage 和 HttpResponseMessage 类型是用于处理 HttpTrigger 函数的帮助器类型

专用类型 目标 典型用途
HttpRequestMessage<T> HTTP 触发器 获取方法、标头或查询
HttpResponseMessage HTTP 输出绑定 返回除 200 之外的状态

Metadata

少量的触发器会连同输入数据一起发送触发器元数据。 可以使用注释 @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 = { "get", "post" }, authLevel = AuthorizationLevel.ANONYMOUS) Optional<String> body,
        @BindingName("name") String queryValue
    ) {
        return body.orElse(queryValue);
    }
}

在上述示例中,queryValue 绑定到 Http 请求 URL http://{example.host}/api/metadata?name=test 中的查询字符串参数 name。 下面是从队列触发器元数据绑定到 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 + " whith metadaId:" + metadataId );
        TestData testData = new TestData();
        testData.id = metadataId;
        output.setValue(testData);
    }

Note

在注释中提供的名称需与元数据属性相匹配

执行上下文

azure-functions-java-library 中定义的 ExecutionContext 包含用来与 Functions 运行时通信的帮助器方法。

记录器

使用 ExecutionContext 中定义的 getLogger 从函数代码写入日志。

示例:


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

public class Function {
    public String echo(@HttpTrigger(name = "req", methods = {"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 来流式传输 Java stdout 和 stderr 日志记录以及其他应用程序日志记录。

将函数应用程序配置为使用 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 选项筛选输出。

若要使用 Azure CLI 下载单个 ZIP 文件形式的日志文件,请打开新的命令提示符、Bash 或终端会话,并输入以下命令:

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

运行此命令之前,必须已在 Azure 门户或 Azure CLI 中启用了文件系统日志记录。

环境变量

在 Functions 中,服务连接字符串等应用设置在执行过程中将公开为环境变量。 可以使用 System.getenv("AzureWebJobsStorage") 访问这些设置

示例:

添加名为 testAppSetting、值为 testAppSettingValue 的 AppSetting


public class Function {
    public String echo(@HttpTrigger(name = "req", methods = {"post"}, authLevel = AuthorizationLevel.ANONYMOUS) String req, ExecutionContext context) {
        context.getLogger().info("testAppSetting "+ System.getenv("testAppSettingValue"));
        return String.format(req);
    }
}

后续步骤

有关 Azure 函数 Java 开发的详细信息,请参阅以下资源: