65.9K
CodeProject 正在变化。 阅读更多。
Home

Azure 上的 Java:完整的无服务器云原生应用程序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.64/5 (3投票s)

2021年4月22日

CPOL

6分钟阅读

viewsIcon

4759

在本文中,我们将向您展示如何使用 Azure Functions 来实现一个假设的温度监控应用程序。

Azure Functions 是一项用于事件驱动型体系结构的无服务器计算服务。使用 Azure Functions,您无需预配虚拟机、负载均衡器和自动伸缩器等底层云资源。该服务会自动为您预配所有这些资源,以便您可以专注于您的代码。

微软一直以来都为 .NET 和包括 C# 在内的 .NET 语言提供出色的工具支持。随着公司现在的愿景是“任何开发者,任何应用,任何平台”,微软现在也为 Java 开发者提供了强大的工具支持。由于云原生和微服务方法提倡使用任何编程语言,具体取决于要解决的问题,因此 Azure 是运行您的云原生、主要是容器化应用程序的绝佳场所,无论使用何种语言。

在我们 之前的 Java on Azure 系列中,我们介绍了云原生概念,并使用它们创建了自己的轮询应用程序,使用了多个函数。在本系列中,我们将基于我们的 Azure Functions 技能,创建一个完整的无服务器应用程序,构建一个 Kubernetes 集群,并 添加容器

在本文中,我们将向您展示如何使用 Azure Functions 来实现一个假设的温度监控应用程序。此应用程序将与其他 Azure 服务配合使用,包括表存储和 SendGrid。更具体地说,表存储将包含来自物联网 (IoT) 设备 của 传感器数据。第一个 Azure Function 将由 HTTP 请求触发,并返回 Azure 表中存储的平均温度。第二个 Azure Function 将定期调用,以检查温度是否超过预设阈值。如果超过,将通过 SendGrid 发送电子邮件通知。

设置项目

本文包含的所有代码示例均在 macOS 上的 Visual Studio Code 中编写。请安装 Visual Studio Code 的 Azure Functions 扩展和 Azure Functions Core Tools,如 Core Tools 文档中所述。我们将在整个文章中使用 Maven 构建工具。欢迎您访问和下载完整的 项目源代码

表存储

在实现 Azure Functions 之前,请使用 Azure 门户设置存储帐户(StorageV2)。然后,在表服务下创建 SensorReadings 表。使用 Storage Explorer,向表中添加几条记录,如下图所示。

最后,转到“访问密钥”以获取密钥1下的连接字符串。稍后连接函数与 Azure 表时将需要此字符串。

SendGrid

要从函数发送电子邮件通知,请使用 SendGrid 服务。首先,设置 SendGrid 帐户。然后,配置默认的发件人身份,并获取 SendGrid API 密钥。稍后配置函数以发送电子邮件时将需要此信息。

实现温度监控

设置好依赖服务后,回到 Visual Studio Code 并使用 Azure Functions 扩展创建一个新的函数应用。将应用名称设置为“TemperatureMonitoring”,选择 Java 8,并将程序包名称设置为“com.temperaturemonitoring”。将所有其他参数保留为默认值。

SensorReading 类添加到项目中(main/java/temperaturemonitoring/SensorReading.java

public class SensorReading {
    private String PartitionKey;
    private String RowKey;
    private double Temperature;    
 
    // PartitionKey
    public String getPartitionKey() { return this.PartitionKey; }
    public void setPartitionKey(String key) { this.PartitionKey = key; }
 
    // RowKey
    public String getRowKey() { return this.RowKey; }
    public void setRowKey(String key) { this.RowKey = key; }
 
    // Temperature
    public double getTemperature() { return this.Temperature; }
    public void setTemperature(double temperature) { this.Temperature = temperature; }
}

上面的类表示 Azure StorageSensorReadings 表中的条目。因此,它只有三个属性:PartitionKeyRowKeyTemperature

现在,我们可以实现第一个函数,该函数将从表中提取条目,计算它们的平均值,并通过 HTTP 响应消息返回。该函数将由匿名 HTTP GET 请求触发。保留默认的 HttpTrigger 注释(请参阅源代码中的 main/java/temperaturemonitoring/Function.java)。

@FunctionName("GetMeanTemperature")
public HttpResponseMessage get(
    @HttpTrigger(
        name = "get",
        methods = {HttpMethod.GET},
        authLevel = AuthorizationLevel.ANONYMOUS)
        HttpRequestMessage<Optional<String>> request, 
 
    @TableInput(name="inputTable",
        tableName="SensorReadings",
        connection="AzureWebJobsStorage",
        take="20") SensorReading[] sensorReadings,
 
    final ExecutionContext context) {
 
    // Function code
 
}

使用 TableInput 接口即可访问存储表,无需编写集成代码。让我们配置 TableInput 以使用 local.settings.json 文件中由 AzureWebJobsStorage 定义的连接字符串。您需要在此处粘贴连接字符串和 SendGrid API 密钥。

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "<YOUR_CONNECTION_STRING>",
    "AzureWebJobsSendGridApiKey": "<YOUR_SENDGRID_API_KEY>",
    "FUNCTIONS_WORKER_RUNTIME": "java"
  }
}

现在使用 take 方法将条目数限制为 20。这些条目将通过 sensorReadings 集合提供。

要继续操作,请检查此集合是否包含任何条目。如果包含,则计算平均值并将其返回给调用者。

if(sensorReadings.length == 0){
    return request.createResponseBuilder(HttpStatus.OK).
        body("Not enough sensor data").build();
}       

double meanTemperature = Arrays.stream(sensorReadings)
    .mapToDouble((s) -> s.getTemperature())
    .average()
    .orElse(-1);
 
String responseString = String.format("Mean temperature = %.2f", meanTemperature);

return request.createResponseBuilder(HttpStatus.OK).body(responseString).build();

现在,我们可以本地构建和测试该函数。

mvn clean install
mvn azure-functions:run

构建应输出函数终结点,如下所示:https://:7071/api/GetMeanTemperature

向上述 URL 发送 GET 请求后,您将获得平均温度。请参阅下面的屏幕截图中的“终端”选项卡。

配置电子邮件通知

现在,我们继续实现第二个函数,该函数将在温度超过预设阈值时发送电子邮件。此函数将定期调用——每分钟一次,使用 TimerTrigger

接下来,配置 TableInput 接口以从 SensorReadings 表中获取一个条目。最后,配置 SendGridOutput 接口以将函数与我们的 SendGrid 帐户集成。

@FunctionName("CheckTemperature")
public void checkTemperature(
    @TableInput(name="inputTable",
        tableName="SensorReadings", 
        connection="AzureWebJobsStorage", 
        take="1") SensorReading[] sensorReadings,

    @TimerTrigger(name="keepAliveTrigger", schedule = "0 */1 * * * *") String timerInfo,

    @SendGridOutput(
        name = "message",
        dataType = "String", 
        apiKey = "AzureWebJobsSendGridApiKey",
        from = "dawid@borycki.com.pl",
        to = "dawid@borycki.com.pl", 
        subject = "Temperature alert",
        text = "")
        OutputBinding<String> message,
    final ExecutionContext context) {
    
    // Function code 
}

检查 sensorReadings 集合是否包含任何条目。如果包含,则将第一个条目与阈值进行比较。如果温度大于阈值,我们需要发送电子邮件。使用 PrepareMessageBody 帮助方法配置电子邮件消息正文。

private static String PrepareMessageBody(double temperature, double temperatureThreshold) {
    final String value = String.format(
        "The temperature of %.1f is above the threshold of %.1f", 
            temperature, temperatureThreshold);
 
    StringBuilder builder = new StringBuilder()
        .append("{")
        .append("\"content\": [{\"type\": \"text/plain\", \"value\": \"%s\"}]") 
        .append("}");

    return String.format(builder.toString(), value);
}

请注意,我们通过 OutputBinding 访问传出的消息,该消息在函数声明中进行了配置。

要测试解决方案,我们需要重新构建代码并运行函数应用。

mvn install
mvn azure-functions:run

新的构建输出将显示两个函数:GetMeanTemperatureCheckTemperature

请注意,CheckTemperature 函数每分钟自动调用一次。如果 SensorReadings 中第一个条目的 Temperature 属性大于 35,它将发送电子邮件通知。您可以使用 Storage Explorer 编辑此条目以模拟高温度读数并触发警报。然后,您应该会收到如下所示的电子邮件通知。

后续步骤

在本文中,我们创建了一个由两个 Azure Functions 组成的无服务器解决方案,它们协调 Azure Storage 来存储假设的 IoT 传感器数据,并协调 SendGrid 来发送电子邮件。我们无需设置任何底层基础架构,这大大缩短了实现时间。

请注意,我们的解决方案有几个局限性。首先,当温度高于阈值时,TableInput 将始终从 Azure 表中获取第一个条目。实际上,我们只想在(根据最新条目计算的)平均温度超过阈值时发送电子邮件警报。我们可以通过筛选表来实现这一点。我们还可以对 TableInput 使用 filter 方法。筛选器必须是已编译的常量,例如(其中 ge 表示“大于或等于”)

filter="Timestamp ge datetime'2021-03-08T09:47:18.356Z'"

这意味着我们无法动态更改筛选器以适应当前日期和时间。我们可以通过两种方式克服此限制。一种直接的解决方案是使用代理函数。此函数将计算筛选器,然后通过 HTTP 调用下一个函数。另一种选择是使用 Azure 表客户端并在代码中执行查询。但是,上述两种解决方案都会增加代码的复杂性。更重要的是,自定义查询将延迟函数执行,从而增加解决方案的成本。

一种更好的方法是使用在 Kubernetes 集群中运行的自定义 API。敬请期待:我们将在本系列的最后一篇文章中实现这一点。但首先,我们需要一个可以部署到的 Kubernetes 集群。在下一篇文章中,我们将学习如何以云原生的方式设置 Azure Kubernetes Service 并创建我们的 Kubernetes 集群

© . All rights reserved.