API网关触发函数计算处理示例教程(runtime=java8)

API网关触发java runtime的函数计算处理示例教程

背景信息

函数计算(Function Compute)是一种事件驱动的服务。函数的执行可以由事件驱动,即当某个事件发生时,触发函数的执行。目前函数计算支持API网关作为事件源,简单说来,当有请求到达已经设置函数计算为后端服务的API网关时,API网关会触发函数的执行,函数计算会将执行结果返回给API网关。

本示例对API网关触发函数计算的使用步骤进行详细介绍,并以运行环境为Java为例,对API网关传入的请求参数进行解析。

通过示例,您将了解

  • 如何使用API网关触发函数计算;
  • 如何在函数中获取API网关传入函数的参数,并将处理结果返回给API网关(以Java运行环境为例)。

本示例分为以下三个步骤

  • 明确API网关和函数计算对接的格式要求(一定要以这个格式,否则互相不认识);
  • 创建服务和需要被API网关触发的函数(已有服务和函数,可跳过此步骤);
  • API网关控制台配置函数计算作为API后端服务。

API网关和函数计算对接的格式要求

API网关调用函数服务时,会将API的相关数据包装为一个Map形式传给函数计算服务,函数计算服务处理后,需要按照返回参数的格式返回statusCodeheadersbody等相关数据,API网关再将函数计算返回的内容映射到statusCodeheaderbody等位置返回给客户端。

API网关的传入参数格式

当以函数计算作为API网关的后端服务时,API网关会把请求参数通过一个固定结构传给函数计算的入参event,函数计算通过如下结构去获取需要的参数,然后进行处理,该结构如下:

{
    "path":"api request path",
    "httpMethod":"request method name",
    "headers":{all headers,including system headers},
    "queryParameters":{query parameters},
    "pathParameters":{path parameters},
    "body":"string of request payload",
    "isBase64Encoded":"true|false, indicate if the body is Base64-encode"
}
函数计算的返回参数格式

函数计算需要将输出内容通过如下JSON格式返回给API网关,方便API网关解析。

{
    "isBase64Encoded":true|false,
    "statusCode":httpStatusCode,
    "headers":{response headers},
    "body":"..."
}
创建服务和函数

创建服务和函数部分分为两个步骤,本示例以Java Runtime为例

  • 首先需要编写Java代码对API网关传入的参数进行处理,并返回符合格式要求的结果给API网关
  • 创建服务和函数,函数计算提供给我们两种方式创建服务和函数,本示例对两种创建函数的方式分别进行介绍。(如果已有服务就无需重新创建服务了,新建函数就可以)

    • 一种是通过控制台上传代码包的方式创建函数;
    • 另一种是通过命令行工具fcli创建函数。

温馨提示:这里提供了两种创建函数的方法,您根据个人喜好任选其一即可。

编写函数代码

用户在使用Java编程时,必须要实现一个类,它要实现函数计算预定义的接口,目前有2个预定义的接口可以实现(您任选其一即可):

  • StreamRequestHandler以流的方式接受调用输入(event)和返回执行结果,用户需要从inputStream中读取调用函数时的输入,处理完成后把函数执行结果写入到outputStream中来返回
  • PojoRequestHandler<I, O>通过泛型的方式,用户可以自定义输入和输出的类型,但是它们必须是POJO类型。下面将举例如何使用这个接口
    API网关触发函数计算的场景更适合使用PojoRequestHandler<I, O>接口,但是本文也提供使用以StreamRequestHandler接口实现的方法
使用PojoRequestHandler<I, O>接口(推荐)

本示例演示了在Java Runtime中,通过实现函数计算预定义的接口PojoRequestHandler,对从API网关传入的参数进行处理,并将结果返回给API网关的过程。PojoRequestHandler<I, O>通过泛型的方式接受调用输入和返回执行结果,用户可以自定义输入和输出的类型,但是它们必须是POJO类型。下面将举例如何使用这个接口通过自定义类ApiRequest传入API网关的参数,函数计算获取参数,对参数进行处理,并通过ApiResponse返回函数计算处理结果的过程。
Java Runtime使用请参考Java Runtime

import com.aliyun.fc.runtime.Context;
import com.aliyun.fc.runtime.PojoRequestHandler;
import java.util.HashMap;
import java.util.Map;

public class ApiTriggerDemo implements PojoRequestHandler<ApiRequest, ApiResponse> {

    public ApiResponse handleRequest(ApiRequest request, Context context) {
        // Get ApiRequest info
        context.getLogger().info(request.toString());
        String path = request.getPath();
        String httpMethod = request.getHttpMethod();
        String body = request.getBody();
        context.getLogger().info("path:" + path);
        context.getLogger().info("httpMethod:" + httpMethod);
        context.getLogger().info("body:" + body);

        // Deal with your own logic here

        // ApiResponse example
        Map headers = new HashMap();
        boolean isBase64Encoded = false;
        int statusCode = 200;
        String returnBody = "";
        return new ApiResponse(headers,isBase64Encoded,statusCode,returnBody);
    }
}

两个pojo类,ApiRequest类和ApiResponse类如下。注意pojo类的set()get()方法注意要写全哈

import java.util.Map;

public class ApiRequest {
    private String path;
    private String httpMethod;
    private Map headers;
    private Map queryParameters;
    private Map pathParameters;
    private String body;
    private boolean isBase64Encoded;

    @Override
    public String toString() {
        return "Request{" +
                "path='" + path + '\'' +
                ", httpMethod='" + httpMethod + '\'' +
                ", headers=" + headers +
                ", queryParameters=" + queryParameters +
                ", pathParameters=" + pathParameters +
                ", body='" + body + '\'' +
                ", isBase64Encoded=" + isBase64Encoded +
                '}';
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public String getHttpMethod() {
        return httpMethod;
    }

    public void setHttpMethod(String httpMethod) {
        this.httpMethod = httpMethod;
    }

    public Map getHeaders() {
        return headers;
    }

    public void setHeaders(Map headers) {
        this.headers = headers;
    }

    public Map getQueryParameters() {
        return queryParameters;
    }

    public void setQueryParameters(Map queryParameters) {
        this.queryParameters = queryParameters;
    }

    public Map getPathParameters() {
        return pathParameters;
    }

    public void setPathParameters(Map pathParameters) {
        this.pathParameters = pathParameters;
    }

    public String getBody() {
        return body;
    }

    public void setBody(String body) {
        this.body = body;
    }

    public boolean getIsBase64Encoded() {
        return this.isBase64Encoded;
    }

    public void setIsBase64Encoded(boolean base64Encoded) {
        this.isBase64Encoded = base64Encoded;
    }
}
import java.util.Map;

public class ApiResponse {
    private Map headers;
    private boolean isBase64Encoded;
    private int statusCode;
    private String body;

    public ApiResponse(Map headers, boolean isBase64Encoded, int statusCode, String body) {
        this.headers = headers;
        this.isBase64Encoded = isBase64Encoded;
        this.statusCode = statusCode;
        this.body = body;
    }

    public Map getHeaders() {
        return headers;
    }

    public void setHeaders(Map headers) {
        this.headers = headers;
    }

    public boolean getIsBase64Encoded() {
        return isBase64Encoded;
    }

    public void setIsBase64Encoded(boolean base64Encoded) {
        this.isBase64Encoded = base64Encoded;
    }

    public int getStatusCode() {
        return statusCode;
    }

    public void setStatusCode(int statusCode) {
        this.statusCode = statusCode;
    }

    public String getBody() {
        return body;
    }

    public void setBody(String body) {
        this.body = body;
    }
}

pom.xml文件如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>apiTrigger</groupId>
    <artifactId>apiTrigger</artifactId>
    <version>1.0-SNAPSHOT</version>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>com.aliyun.fc.runtime</groupId>
            <artifactId>fc-java-core</artifactId>
            <version>1.0.0</version>
        </dependency>
    </dependencies>   
</project>
使用StreamRequestHandler接口

使用StreamRequestHandler接口示例如下,需要将输入的InputStream转换为对应的pojo类,pom文件配置与使用PojoRequestHandler<I, O>接口相同,代码如下

import com.aliyun.fc.runtime.Context;
import com.aliyun.fc.runtime.StreamRequestHandler;
import com.aliyun.fc.runtime.Context;
import com.google.gson.Gson;
import java.io.*;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class ApiTriggerDemo2 implements StreamRequestHandler {

    public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) {
        try {
            // Convert InputStream to string
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            StringBuffer stringBuffer = new StringBuffer();
            String string = "";
            while ((string = bufferedReader.readLine()) != null) {
                stringBuffer.append(string);
            }
            String input = stringBuffer.toString();
            context.getLogger().info("inputStream: " + input);
            Request req = new Gson().fromJson(input, Request.class);
            context.getLogger().info("input req: ");
            context.getLogger().info(req.toString());
            String bodyReq = req.getBody();
            Base64.Decoder decoder = Base64.getDecoder();
            context.getLogger().info("body: " + new String(decoder.decode(bodyReq)));

            // Deal with your own logic here

            // construct response
            Map headers = new HashMap();
            headers.put("x-custom-header", " ");
            boolean isBase64Encoded = false;
            int statusCode = 200;
            Map body = new HashMap();
            Response resp = new Response(headers, isBase64Encoded, statusCode, body);
            String respJson = new Gson().toJson(resp);
            context.getLogger().info("outputStream: " + respJson);
            outputStream.write(respJson.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                outputStream.close();
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    class Request {
        private String path;
        private String httpMethod;
        private Map headers;
        private Map queryParameters;
        private Map pathParameters;
        private String body;
        private boolean isBase64Encoded;

        @Override
        public String toString() {
            return "Request{" +
                    "path='" + path + '\'' +
                    ", httpMethod='" + httpMethod + '\'' +
                    ", headers=" + headers +
                    ", queryParameters=" + queryParameters +
                    ", pathParameters=" + pathParameters +
                    ", body='" + body + '\'' +
                    ", isBase64Encoded=" + isBase64Encoded +
                    '}';
        }

        public String getBody() {
            return body;
        }
    }

    // FC need to return the response to API gateway in the following JSON format
    class Response {
        private Map headers;
        private boolean isBase64Encoded;
        private int statusCode;
        private Map body;

        public Response(Map headers, boolean isBase64Encoded, int statusCode, Map body) {
            this.headers = headers;
            this.isBase64Encoded = isBase64Encoded;
            this.statusCode = statusCode;
            this.body = body;
        }
    }
}
创建服务和函数
打成jar包

Java代码需要打成jar包上传到函数计算。如果您使用IntelliJ IDEA集成开发环境,打包方式如下(可参考IDEA导出可执行jar包)

  • File -> Project Structure -> Artifacts -> + -> Jar -> From modules with dependencies -> 选择函数所在的类 -> Apply -> 确定
  • Build -> Build Artifacts -> 找到对应的Aritifacts -> Build
创建服务和函数

函数计算提供两种方式创建服务和函数,使用控制台创建服务和函数和使用命令行工具fcli创建服务和函数,下面对这两种方式分别进行介绍

1.使用控制台创建服务和函数
使用控制台创建服务和函数图文并茂版请参考如何使用控制台,本示例只给出基本操作步骤

  • 创建服务:左侧服务列表 -> + -> 创建服务 -> 填写服务相关内容 -> 确定
  • 创建函数:进入对应服务 -> 左侧函数列表 -> + -> 新建函数 -> 使用空白模板 -> 不创建触发器 -> 输入函数名称 -> 运行环境选择Java8 -> 代码上传方式选择代码包上传 -> 选择刚刚生成的jar包 -> 设置函数入口,入口形式为[package].[class]::[method],例如我打包后的函数入口为ApiTriggerDemo::handleRequest,其他值选择默认即可
    这样函数就创建好啦

2.使用命令行工具fcli创建服务和函数

  • 创建服务: mks fc-demo,即创建服务,服务名称为fc-demo (在创建服务时可以指定log project等信息,具体内容可参考mks
  • 创建函数:
    mkf apiTrigger -t java8 -h ApiTriggerDemo::handleRequest -d fcDemo/out/artifacts/apiTrigger_jar (其中apiTrigger为函数名称,-t为指定运行环境,-h指定函数入口,-d指定代码包,此路径为代码包路径相对于fcli所在位置的路径,具体内容可参考mkf)

创建API

  1. API网关控制台分组管理 -> 创建分组,新建一个API分组(已经有API分组可跳过这步)
  2. API列表 -> 新建API,步骤如图所示
    创建API

API P1
API P2
API P3
API P4

通过API网关触发函数计算可参考以函数计算作为 API 网关后端服务

测试触发器

测试
测试成功后发布API即可

参考文献

以函数计算作为 API 网关后端服务

That's all,enjoy it~
Any question,可留言,或加入函数计算官方客户群(钉钉群号:11721331)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值