API网关触发java runtime的函数计算处理示例教程
背景信息
函数计算(Function Compute)是一种事件驱动的服务。函数的执行可以由事件驱动,即当某个事件发生时,触发函数的执行。目前函数计算支持API网关作为事件源,简单说来,当有请求到达已经设置函数计算为后端服务的API网关时,API网关会触发函数的执行,函数计算会将执行结果返回给API网关。
本示例对API网关触发函数计算的使用步骤进行详细介绍,并以运行环境为Java为例,对API网关传入的请求参数进行解析。
通过示例,您将了解
- 如何使用API网关触发函数计算;
- 如何在函数中获取API网关传入函数的参数,并将处理结果返回给API网关(以Java运行环境为例)。
本示例分为以下三个步骤
- 明确API网关和函数计算对接的格式要求(一定要以这个格式,否则互相不认识);
- 创建服务和需要被API网关触发的函数(已有服务和函数,可跳过此步骤);
- 在API网关控制台配置函数计算作为API后端服务。
API网关和函数计算对接的格式要求
API网关调用函数服务时,会将API的相关数据包装为一个Map形式传给函数计算服务,函数计算服务处理后,需要按照返回参数的格式返回statusCode
,headers
,body
等相关数据,API网关再将函数计算返回的内容映射到statusCode
,header
,body
等位置返回给客户端。
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
- 去API网关控制台,
分组管理
->创建分组
,新建一个API分组(已经有API分组可跳过这步) API列表
->新建API
,步骤如图所示
通过API网关触发函数计算可参考以函数计算作为 API 网关后端服务
测试触发器
测试成功后发布API即可
参考文献
That's all,enjoy it~
Any question,可留言,或加入函数计算官方客户群(钉钉群号:11721331)