用于 Swift 的 Azure Functions ⚡️

ver cliver Swift Package Manager compatible docs-status Swift version License: MIT

Chat

Swift 编写 Azure Functions

除了传统的自定义工作进程之外,此框架还支持新的 Azure Functions 自定义处理程序 (从 0.6.0 开始)。

免责声明:这是一个社区开源项目,而非官方的 Azure 项目。

文档

将示例项目部署到 Azure!

经典工作进程示例

Deploy to Azure

自定义处理程序示例

Deploy to Azure

示例

一个定时器函数(自定义处理程序)

import Foundation
import AzureFunctions
import Vapor

class TimerFunction: Function {
    
    required init() {
        super.init()
        self.name = "TimerFunction"
        self.functionJsonBindings =
            [
                [
                "type" : "timerTrigger",
                "name" : "myTimer",
                "direction" : "in",
                "schedule" : "*/5 * * * * *"
                ]
            ]
        //or
        //self.trigger = TimerTrigger(name: "myTimer", schedule: "*/5 * * * * *")

        app.post([PathComponent(stringLiteral: name)], use: run(req:))
    }

    func run(req: Request) -> InvocationResponse {
        var res = InvocationResponse()
        res.appendLog("Its is time!")
        return res
    }
}

一个 HTTP 函数(经典工作进程)

import Foundation
import AzureFunctions

class HttpFunction: Function {
    
    required init() {
        super.init()
        self.name = "HttpFunction"
        self.trigger = HttpRequest(name: "req")
    }
    
    override func exec(request: HttpRequest, context: inout Context, callback: @escaping callback) throws {
      
        let res = HttpResponse()
        var name: String?
        
        if let data = request.body, let bodyObj: [String: Any] = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
            name = bodyObj["name"] as? String
        } else {
            name = request.query["name"]
        }
        res.body  = "Hello \(name ?? "buddy")!".data(using: .utf8)
        
        return callback(res);
    } 
}

入门

安装和要求

macOS 上需要 Swift 5.2 或更高版本,或者 Xcode 11 或更高版本

Swift 安装:https://swiftlang.cn/getting-started/#installing-swift

Azure Functions Core Tools

安装最新的 Azure Functions Core Tools

Swift Functions Tools

与 Core Tools 类似,Swift Functions Tools 使 Swift 函数的开发更加容易和方便。

macOS 上,您可以从 Homebrew 🍺 安装它

brew install salehalbuga/formulae/swift-func

Linux 上,

克隆工具仓库

git clone https://github.com/SalehAlbuga/azure-functions-swift-tools

安装

make install

它安装一个名为 swiftfunc 的 CLI 工具,该工具可用于创建项目、函数并在本地运行它们。

创建一个新的项目/Azure Functions 应用程序

运行 init 命令以创建一个新的 Azure Functions 应用程序

swiftfunc init myApp [-hw]

它将在一个新文件夹中创建一个新应用程序,并在 Sources 目标内的名为 functions 的文件夹中创建函数应该放置的位置 (/myApp/Sources/myApp/functions)。 创建的项目是一个带有 Azure Functions 框架依赖项的 Swift 包项目。

传递 -hw--http-worker 选项以使用 自定义处理程序 模板创建项目。

创建一个简单的 HTTP 函数

在项目的新目录中,运行以下命令以创建一个名为 hello 的新 HTTP 函数

swiftfunc new http -n hello [-hw]

新的函数文件将在以下路径中创建 Sources/myApp/functions/hello.swift

init 命令类似,传递 -hw--http-worker 选项以使用自定义处理程序模板创建新函数。

运行新的 Functions 应用程序

在项目目录中运行 swiftfunc run 以在本地运行您的 Swift Functions 项目。 它将编译代码并为您启动主机 * (就好像您在运行 func host start)*。 主机输出应该显示您上面创建的 hello 函数的 URL。 单击它以运行该函数并查看输出!

部署到 Azure ☁️

有两种方法可以将 Swift Functions 部署到 Azure

容器函数

要将 Function 应用程序部署到容器中,您可以选择使用 Functions Core Tool func deploy 命令,它将构建镜像、将其推送到注册表并在目标 Function App 中设置它,或者您可以手动执行此操作,如下所示。

构建镜像 (创建项目时提供 Dockerfile)

docker build -t <imageTag> .

如果您使用 DockerHub,则标签将为 username/imageName:version。 如果您使用 ACR (Azure Container Registry) 或任何其他私有注册表,则标签将为 registryURL/imageName:version

然后推送它

docker push <imageTag>

Azure 门户 中,创建一个新的 Function App,并将 **Docker Container** 作为发布选项。 在 Hosting options 下,确保选择 Linux 作为操作系统。

创建应用程序后或在任何现有的容器函数应用程序中,在 Platform Features 下,选择 Container settings 并设置注册表并选择您推送的镜像。

您可以使用下面的按钮将预构建的示例项目部署到您的 Azure 订阅

Deploy to Azure

自定义处理程序示例

Deploy to Azure

在 Linux 消耗计划上托管

首先,您需要在 Azure 的 Function App 中设置以下应用程序设置。LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/site/wwwroot/workers/swift/lib/

然后取决于您是在 Linux 机器还是 Mac 上进行开发

Linux

从 Azure CLI 登录到您的 Azure 帐户

az login

当 Azure CLI 完成加载您的订阅信息后,运行

swiftfunc publish myswiftfunctions

Swift Function Tools 发布命令将编译、导出和发布您的 Swift Functions 项目。

macOS

从 macOS 发布到 Linux 消耗计划中的 Function App 需要首先在 Linux 容器中构建该应用程序,为此,您可以使用 VSCode Dev Containers。 需要使用 -dc--dev-container 选项创建项目,以便添加 Swift Function Dev Container(或者您可以创建一个新的容器并将 .devcontainer 文件夹复制到您的项目)。 swiftfunc init myFunctionApp -hw -dc

在开发容器中重新打开文件夹 (Command-Shift-P,搜索并选择 *Remote-Containers: Reopen in Container*)

一旦开发容器准备就绪,请按照上述相同的 Linux 步骤发布应用程序!

绑定

Azure Functions 提供了各种 绑定和触发器

函数的触发器、输入绑定和输出绑定在其初始化程序中设置。 Swift 中的 Azure Functions 必须继承框架中的 Function 类。

自定义处理程序 (HTTP Worker)

当使用自定义处理程序模式时,您可以通过将 functionJsonBindings 属性设置为 Azure Functions docs 中绑定/触发器的 JSON 配置,来使用所有 Azure Functions 绑定和触发器。 您还可以使用下面列出的框架支持的触发器/绑定类型。

传统工作进程(经典)

目前此模式支持以下内容。 未来将实现更多的绑定,并进行许多改进。

Swift 类型 Azure Functions 绑定 方向
HttpRequest HTTP 触发器 输入
HttpResponse 输出 HTTP 响应 输出
TimerTrigger 定时器触发器 输入
消息数据类型 String (构造函数中定义的 Table 绑定) 输入和输出表 输入,输出
消息数据类型 String (构造函数中定义的 Queue 绑定) 输出队列消息 输出
消息数据类型 String (构造函数中定义的 Queue 绑定) 队列触发器 输入
Blob (blob 数据 prob 是 String 或 Data) 输入 Blob 输入
String 或 Data 输出 Blob 输出
Blob Blob 触发器 输入
ServiceBusMessage Service Bus 输出消息 输出
ServiceBusMessage Service Bus 触发器 输入

自定义处理程序 (HTTP Worker)

import AzureFunctions
import Vapor

class QueueFunction: Function {

    required init() {
        super.init()
        self.name = "QueueFunction"
        self.functionJsonBindings = [
                [
                    "connection" : "AzureWebJobsStorage",
                    "type" : "queueTrigger",
                    "name" : "myQueueTrigger",
                    "queueName" : "myqueue",
                    "direction" : "in"
                ]
            ]
        // or
        //self.trigger = Queue(name: "myQueueTrigger", queueName: "myqueue", connection: "AzureWebJobsStorage")
        
        app.post([PathComponent(stringLiteral: name)], use: run(req:))
    }
    
    func run(req: Request) -> InvocationResponse {
        ...

传统工作进程(经典)

import AzureFunctions

class HttpFunction: Function {
    
    required init() {
        super.init()
        self.name = "HttpFunction"
        self.trigger = HttpRequest(name: "req")
        self.inputBindings = [Blob(name: "fileInput", path: "container/myBlob.json", connection: "AzureWebJobsStorage")]
        self.outputBindings = [Queue(name: "queueOutput", queueName: "myQueue", connection: "AzureWebJobsStorage")]
    }
    
    override func exec(request: HttpRequest, context: inout Context, callback: @escaping callback) throws {
   ...

编写 Swift Functions

传统工作进程(经典)

根据您的函数的触发器类型,工作进程将调用适当的 exec 重载。 例如,如果该函数是定时器触发的,则工作进程将调用

exec(timer:context:callback:)

如果它是一个 HTTP 触发的函数

exec(request:context:callback:)

您可以在 Xcode 中查看可用重载的列表。

输入和输出绑定在上下文中可用作字典,您可以在其中使用构造函数中指定的绑定名称访问/设置值。 例如

let tableVal = context.inputBindings["myTableInput"]
context.outputBindings["myQueueOutput"] = "new item!"

自定义处理程序 (HTTP Worker)

该框架使用 Vapor 4.0 HTTP 服务器。 Function 类具有 app 属性,它是您可以用来注册您的函数 HTTP 路由的 Vapor 应用程序实例。

class myFunction: Function {
    
    required init() {
        super.init()
        self.name = "myFunction"
        self.functionJsonBindings = [
            [
                "connection" : "AzureWebJobsStorage",
                "type" : "queueTrigger",
                "name" : "myQueueTrigger",
                "queueName" : "myqueue",
                "direction" : "in"
            ]
        ]
        
        app.post([PathComponent(stringLiteral: name)], use: run(req:))
    }
    
    func run(req: Request) -> InvocationResponse {
        var res = InvocationResponse()
        if let payload = try? req.content.decode(InvocationRequest.self) {
            res.appendLog("Got \\(payload.Data?["myQueueTrigger"] ?? "")")
        }
        return res
    }
}

该框架还提供了 Azure Function 主机所需的函数调用 Request 和 Response 模型,它们符合 Vapor 的 Content 协议,以及辅助方法。

调用请求

/// Trigger/Bindings data (values).
var data: [String:AnyCodable]?
/// Trigger/Bindings metadata.
var metadata: [String:AnyCodable]?

调用请求

/// Output bindings values dictionary
var outputs: [String:AnyCodable]?
/// Functions logs array. These will be logged when the Function is executed
var logs: [String] = []
/// The $return binding value
var returnValue: AnyCodable?

框架更新

由于框架正在积极更新,如果您遇到任何问题或想要获得最新的功能和改进,请更新框架和工具。

更新框架

swift package update

macOS 上更新工具

brew upgrade salehalbuga/formulae/swift-func

Linux

git clone https://github.com/SalehAlbuga/azure-functions-swift-tools
make install

存储连接和其他设置

在生成的 main.swift 中,您可以定义您的调试 AzureWebJobsStorage,并可选择定义任何其他连接/环境变量。 此外,您可以更改默认的 Extension Bundle id 和版本。

//
//  main.swift
//  
//
//  Auto Generated by SwiftFunctionsSDK
//
//  Only set env vars or register/remove Functions. Do Not modify/add other code
//

import AzureFunctions

let registry = FunctionRegistry()

registry.AzureWebJobsStorage = "yourConnection" //Remove before deploying. Do not commit or push any Storage Account keys
registry.EnvironmentVariables = ["queueStorageConnection": "otherConnection"]

// Optionally you can change the default ExtensionBundleId and version 
registry.ExtensionBundleId = "Microsoft.Azure.Functions.ExtensionBundle"
registry.ExtensionBundleVersion = "[1.*, 2.0.0)"

registry.register(hello.self)
...

请确保不要将任何调试存储帐户密钥提交到存储库

日志记录

传统工作进程(经典)

您可以使用 context 对象中的 log 方法进行记录

context.log(_)

自定义处理程序 (HTTP Worker)

日志在 InvocationResponse 对象中返回。 您可以附加日志

res.appendLog(_)

代码执行注意事项

传统工作进程(经典)

当您的函数完成执行逻辑时,您应该调用提供的回调,传递 $return 输出绑定值,如果没有,则传递 true

callback(res)