我最近开始研究 Azure 并想实际运行一些东西,所以我开始使用 Azure Functions。

这篇文章的作用

  • 为 Azure Functions (Java) 构建本地开发环境使用
  • 命令部署到 Azure

微软文档我们将根据实际构建
这类文章还有很多,但我想留下我偶然发现的要点。

*此内容适用于有Java开发经验的人。

目录

操作环境

工作环境如下。

  • 操作系统
    • Windows 11 专业版 (21H2)
    • Ubuntu 20.04 (WLS)
      * 使用旧版本,因为最新版本不起作用
  • JDK

安装 Azure 核心工具

  • 视窗

    安装程序安装。

  • Linux

    1. 包完整性验证。

      curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
      sudo mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg
      
    2. 配置 apt 源列表。

      • 对于 Ubuntu

        sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-$(lsb_release -cs)-prod $(lsb_release -cs) main" > /etc/apt/sources.list.d/dotnetdev.list'
        
      • 对于 Debian

        sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/debian/$(lsb_release -rs | cut -d'.' -f 1)/prod $(lsb_release -cs) main" > /etc/apt/sources.list.d/dotnetdev.list'
        
    3. 安装

      sudo apt-get update
      sudo apt-get install azure-functions-core-tools-4
      

安装 Azure CLI

  • 视窗

    安装程序安装。

  • Linux

    sudo apt install azure-cli
    

    安装的版本在我的工作环境(Ubuntu20.04)好像比较旧,所以这里我安装它是指

    curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
    

如果执行以下命令显示版本,则安装完成。

az --version

安装 Maven

  • 视窗

    参考本文安装。

  • Linux

    sudo apt install maven
    

创建一个 Maven 项目

  1. 项目创建

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

    您可以在-DjavaVersion=11 部分中指定java 版本。

    *截至2022/10/12,811可以选择(17是预览功能)

  2. 出现提示时输入以下内容。

    环境 价值 解释
    组 ID com.azure_functions 唯一标识项目的值。
    工件 ID 天蓝色函数 没有版本号的 jar 名称。
    版本 1.0-快照 罐子版本。选择默认。
    包裹 com.azure_functions 包裹名字。默认值与 groupId 相同。
  3. 如果显示“BUILD SUCCESS”,请输入“Y”并确定。

    创造了什么
    azure-functions/
    ├── host.json
    ├── local.settings.json
    ├── pom.xml
    └── src
        ├── main
        │   └── java
        │       └── com
        │           └── azure_functions
        │               └── Function.java
        └── test
            └── java
                └── com
                    └── azure_functions
                        ├── FunctionTest.java
                        └── HttpResponseMessageMock.java
    
  4. 移动到创建的文件夹并使用以下命令进行构建。

    mvn clean package
    

    启动本地服务器

    mvn azure-functions:run
    

    如果正常启动,端点会显示如下。

    ・・・
    Functions:
    
            HttpExample: [GET,POST] http://localhost:7071/api/HttpExample
    ・・・
    

    函数调用

    您可以通过访问显示的端点来调用该函数。

    用命令检查

    curl http://localhost:7071/api/HttpExample
    
    结果
    Please pass a name on the query string or in the request body
    

    带参数调用

    curl http://localhost:7071/api/HttpExample?name=AzureLearn
    
    结果
    Hello, AzureLearn
    

    函数体中的代码

    分钟 c 土壤温度。爪哇
    public class Function {
        /**
         * This function listens at endpoint "/api/HttpExample". Two ways to invoke it
         * using "curl" command in bash:
         * 1. curl -d "HTTP Body" {your host}/api/HttpExample
         * 2. curl "{your host}/api/HttpExample?name=HTTP%20Query"
         */
        @FunctionName("HttpExample")
        public HttpResponseMessage run(
                @HttpTrigger(name = "req", methods = { HttpMethod.GET,
                        HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
                final ExecutionContext context) {
            context.getLogger().info("Java HTTP trigger processed a request.");
    
            // Parse query parameter
            final String query = request.getQueryParameters().get("name");
            final String name = request.getBody().orElse(query);
    
            if (name == null) {
                return request.createResponseBuilder(HttpStatus.BAD_REQUEST)
                        .body("Please pass a name on the query string or in the request body").build();
            } else {
                return request.createResponseBuilder(HttpStatus.OK).body("Hello, " + name).build();
            }
        }
    }
    

    正在做

    1. 从正文中获取请求的查询参数或name。如果无法获取
    2. name,则返回 Please pass a name on the query string or in the request body 作为响应。如果可以获取
    3. name参数,则返回Hello, <nameの値>作为响应。

      使用 @FunctionName 注释指定函数名称。 (部署到 Azure 的名称)
      如果route 未在下面描述的@HttpTrigger 注释中指定,/api/<関数名> 将成为端点。
      您可以通过指定route 来自定义端点。

      使用@HttpTrigger 注解设置触发器。

      在示例中,它看起来像这样:

      环境 设置 解释
      姓名 要求 请求或请求正文中函数代码中使用的变量名称。 (……?)
      方法 获取,发布 接受请求的 HTTP 方法。
      可以在数组中指定多个。
      授权级别 匿名的 指定调用函数时的授权级别。
      • 匿名 - 无需 API 密钥即可访问。
      • FUNCTION - 需要特定于函数的 API 密钥。
      • 管理员 - 需要主密钥。

      有关@HttpTrigger的更多信息
      Azure Functions 中的 HTTP 触发器

      代码更改

      这很无聊,所以让我们更改代码以返回正确的 JSON 数据。

      azure-functions/
      ├── host.json
      ├── local.settings.json
      ├── pom.xml ------------------------------------ 変更
      └── src
          ├── main
          │   └── java
          │       └── com
          │           └── azure_functions
          │               ├── Function.java ---------- 変更
          │               ├── models
          │               │   └── TodoItem.java ------ 追加
          │               ├── services
          │               │   └── TodoService.java --- 追加
          │               └── utils
          │                   └── JsonUtil.java ------ 追加
          └── test
              └── java
                  └── com
                      └── azure_functions
                          ├── FunctionTest.java
                          └── HttpResponseMessageMock.java
      
      pom.xml
          <dependencies>
              <dependency>
                  <groupId>com.microsoft.azure.functions</groupId>
                  <artifactId>azure-functions-java-library</artifactId>
                  <version>${azure.functions.java.library.version}</version>
              </dependency>
      
              <!-- ↓追加↓(Lombok使いたかった) -->
              <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
              <dependency>
                  <groupId>org.projectlombok</groupId>
                  <artifactId>lombok</artifactId>
                  <version>1.18.24</version>
                  <scope>provided</scope>
              </dependency>
      
              <!-- JSONシリアライズのカスタマイズ用 -->
              <dependency>
                  <groupId>com.azure</groupId>
                  <artifactId>azure-core-serializer-json-jackson</artifactId>
                  <version>1.2.22</version>
              </dependency>
              <!-- ↑追加↑ -->
      
      Function.java(函数体)
      public class Function {
      
         /**
          * TODO一覧を取得
          * 
          * @param request リクエスト
          * @param context 実行コンテキスト
          * @return レスポンス
          */
         @FunctionName("fetchTodoItems")
         public HttpResponseMessage fetchTodoList(
                     @HttpTrigger(name = "req", methods = { HttpMethod.GET,
                                     HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS,
                                     // エンドポイントを「/api/todo/list」に設定
                                     route = "todo/list") HttpRequestMessage<Optional<String>> request,
                     final ExecutionContext context) {
            context.getLogger().info("Java HTTP trigger processed a request.");
      
            // TODO一覧取得
            var todoService = new TodoService(context);
            List<TodoItem> todoItems = todoService.fetchTodoItems();
      
            // TODO一覧をJSONに変換
            var jsonUtil = new JsonUtil(context);
            String json = jsonUtil.serialize(todoItems);
      
            return request.createResponseBuilder(HttpStatus.OK)
                            .header("Content-Type", "application/json;")
                            .body(json).build();
      
         }
      
      }
      
      • 修改为返回 TODO 列表
      • 自定义端点

      绊脚点①

      在生成最后一个响应的部分,即使todoItems传递给body(),也会在未经许可的情况下被序列化,但是由于LocalDateTime类型的格式不整齐,所以后面描述的JsonUtil.java尝试传递连载一本。

      TodoService.java(获取 TODO 列表的类)
      /**
       * TODOサービス
       */
      @RequiredArgsConstructor
      public class TodoService {
      
         /** 実行コンテキスト */
         private final ExecutionContext context;
      
         /**
          * TODO一覧を取得
          * 
          * @return TODO一覧
          */
         public List<TodoItem> fetchTodoItems() {
            context.getLogger().info("fetchTodoItems");
            // ダミーデータ生成
            List<TodoItem> todoItems = List.of(
                            TodoItem.builder()
                                    .content("Azureの勉強")
                                    .done(false)
                                    .createdAt(LocalDateTime.now())
                                    .build(),
                            TodoItem.builder()
                                    .content("Javaの勉強")
                                    .done(true)
                                    .createdAt(LocalDateTime.now())
                                    .build(),
                            TodoItem.builder()
                                    .content("TypeScriptの勉強")
                                    .done(false)
                                    .createdAt(LocalDateTime.now())
                                    .build());
      
            return todoItems;
         }
      
      }
      
      TodoItem.java(TODO 容器)
      @Data
      @Builder
      public class TodoItem {
      
          /** 内容 */
          private String content;
      
          /** 完了フラグ */
          private boolean done;
      
          /** 作成日時 */
          private LocalDateTime createdAt;
      
      }
      
      JsonUtil.java(JSON 转换实用程序)
      /**
       * JSON関連のユーティリティ
       */
      @RequiredArgsConstructor
      public class JsonUtil {
      
          /** JSONシリアライズ用のマッパー */
          private static final ObjectMapper MAPPER = objectMapper();
      
          /** 実行コンテキスト */
          private final ExecutionContext context;
      
          private static ObjectMapper objectMapper() {
              var mapper = new ObjectMapper();
      
              // LocalDateTime用のシリアライザを追加
              var javaTimeModule = new JavaTimeModule();
              javaTimeModule.addSerializer(LocalDateTime.class,
                      new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss")));
              mapper.registerModule(javaTimeModule);
      
              return mapper;
          }
      
          /**
           * オブジェクトをJSONに変換
           * 
           * @param <T> オブジェクトの型
           * @param obj オブジェクト
           * @return JSON文字列
           */
          public <T> String serialize(T obj) {
              try {
                  return MAPPER.writeValueAsString(obj);
              } catch (JsonProcessingException e) {
                  context.getLogger().severe("オブジェクト→JSONの変換に失敗しました。エラーメッセージ:" + e.getMessage());
                  throw new IllegalArgumentException(e);
              }
          }
      
      }
      

      部署到 Azure

      登录到 Azure

      az login
      

      执行命令时,浏览器将启动并要求您登录,因此请按照它进行操作。
      *仅首次登录即可

      部署

      mvn azure-functions:deploy
      

      成功部署后,您将看到端点。
      这是/api/todo/list

      结果
      [INFO] HTTP Trigger Urls:
      [INFO]   fetchTodoItems : https://azure-learn-functions796.azurewebsites.net/api/todo/list
      [INFO] ------------------------------------------------------------------------
      [INFO] BUILD SUCCESS
      [INFO] ------------------------------------------------------------------------
      

      检查部署结果

      创建的资源

      在部署期间,会自动创建以下资源。

      • 资源组
        • 功能应用
        • 存储帐户
        • 应用服务计划
        • 应用程序洞察

      Azure FunctionsでAPIを作る~Java編~

      pom.xml 中的build.plugins.plugin.configuration 允许您自定义创建资源的配置。

      设置名称 默认值 解释
      应用名称 项目创建时指定的 artifactId + 项目创建的日期和时间 功能应用名称
      资源组 java函数组 函数应用所属的资源组名称。
      应用服务计划名称 java-functions-app-服务计划 应用服务计划名称
      地区 韦斯特斯 待创建资源所属区域

      用命令检查

      curl https://azure-learn-functions796.azurewebsites.net/api/todo/list
      
      结果(为便于阅读而格式化)
      [
        {
          "content": "Azureの勉強",
          "done": false,
          "createdAt": "2022-10-10 18:38:26"
        },
        {
          "content": "Javaの勉強",
          "done": true,
          "createdAt": "2022-10-10 18:38:26"
        },
        {
          "content": "TypeScriptの勉強",
          "done": false,
          "createdAt": "2022-10-10 18:38:26"
        }
      ]
      

      作为响应,返回了设置为 body() 的 JSON。

      检查 Azure 门户

      Azure上部署的功能是Azure 门户你也可以从上面做。
      関数アプリ<関数アプリ名>関数<関数名>コードとテストテストと実行
      您还可以指定参数、标头和 API 密钥。
      执行时,标准错误输出实时显示到屏幕上的控制台。
      如果它不起作用,我建议你在这里检查。

      Azure FunctionsでAPIを作る~Java編~

      这可能只是我的环境,但是即使函数本身成功,也会返回 response: 500 。
      向 URL https://functions.azure.com/api/passthrough 发出请求,该请求似乎调用了一个实际函数,该函数返回 500。

      绊脚点②

      Windows (PowerShell) 没有问题,但是在我的环境中,当我在 WSL 上执行部署命令时,出现错误,无法部署。

      错误
      Failed to execute goal com.microsoft.azure:azure-functions-maven-plugin:1.21.0:deploy (default-cli) on project azure-functions: login with (AUTO): device code consumer is not configured.
      

      login with (AUTO)身份验证方法好像是自动选择的,但是好像实际使用的是设备码认证。 (根据错误内容推测)
      是不是像“设置了设备认证,但没有设置设备代码”?
      因此,要将身份验证方法更改为“Azure CLI”,请将 pom.xml 更改如下。

      pom.xml
         <build>
            <plugins>
               ・・・
               <plugin>
                  <groupId>com.microsoft.azure</groupId>
                  <artifactId>azure-functions-maven-plugin</artifactId>
                  <version>${azure.functions.maven.plugin.version}</version>
                  <configuration>
                     ・・・
                     <!-- ↓追加↓ -->
                     <auth>
                        <type>azure_cli</type>
                     </auth>
                     <!-- ↑追加↑ -->
                  </configuration>
                  ・・・
               </plugin>
               ・・・
            </plugins>
         </build>
      

      当我部署时,我收到以下错误...

      错误
      Failed to execute goal com.microsoft.azure:azure-functions-maven-plugin:1.21.0:deploy (default-cli) on project azure-functions: login with (AZURE_CLI): execute Azure Cli command 'az account list --output json' failed due to error: Process exited with an error: 2 (Exit value: 2).
      

      az account list --output json 看来命令失败了,所以我尝试单独执行它,但它没有任何问题。

      最后查不出原因,只好放弃,将认证方式改为“设备码”。

      pom.xml
      <auth>
         <type>device_code</type>
      </auth>
      

      执行deploy命令时会显示URL和设备代码,在浏览器中打开指定的URL,在弹出的对话框中输入设备代码。
      由于这次我正确输入了设备代码,因此我通过了设备代码认证,没有任何问题。

      [INFO] Auth type: DEVICE_CODE
      To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code H6HNEDQCQ to authenticate.
      

      后记

      我偶然发现在 WSL 上进行部署,但我能够轻松地创建和部署一个函数应用程序。
      由于这次我们没有使用 API 密钥,任何人都可以访问它,但我们希望将来考虑到这一点。

      接下来,我想从我这次创建的函数连接到 Azure 托管数据库(MySQL)。

      感谢您阅读到最后!

      参考


原创声明:本文系作者授权爱码网发表,未经许可,不得转载;

原文地址:https://www.likecs.com/show-308629288.html

相关文章:

  • 2022-12-23
  • 2021-09-26
  • 2021-12-25
  • 2019-06-19
  • 2021-09-10
  • 2022-12-23
  • 2022-12-23
  • 2021-07-24
猜你喜欢
  • 2021-04-01
  • 2022-01-03
  • 2022-12-23
  • 2022-02-27
  • 2021-04-14
  • 2022-12-23
  • 2022-12-23
相关资源
相似解决方案