全部产品
云市场

Java 事件函数

更新时间:2020-03-31 18:20:19

在函数计算服务使用 Java 编程,需要定义一个 Java 函数作为入口。本文介绍了 Java 事件函数的结构和特点。

背景信息

函数计算支持 Java8 运行环境。Java 语言由于需要编译后才可以在 JVM 虚拟机中运行。和 Python、Node.js 这类脚本型语言不同,有以下限制:

  • 不支持上传代码:使用 Java 语言,仅支持上传已经开发完成,编译打包后的 zip/jar 包。函数计算不提供 Java 的编译能力。

  • 不支持在线编辑:不能上传代码,所以不支持在线编辑代码。Java 运行时的函数,在代码页面仅能看到再次通过页面上传OSS 上传提交代码的方法。

事件函数接口

您在使用 Java 编程时,必须要实现函数计算提供的接口类,对于事件入口函数目前有 2 个预定义接口可以选择。这 2 个预定义接口分别是:

  • StreamRequestHandler

    以流的方式接受调用输入 event 和返回执行结果,您需要从输入流中读取调用函数时的输入,处理完成后把函数执行结果写入到输出流中来返回。

  • PojoRequestHandler

    通过泛型的方式,您可以自定义输入和输出的类型,但是输入和输出的类型必须是 POJO 类型。

StreamRequestHandler

一个最简单的事件函数定义如下所示。

  1. package example;
  2. import com.aliyun.fc.runtime.Context;
  3. import com.aliyun.fc.runtime.StreamRequestHandler;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.io.OutputStream;
  7. public class HelloFC implements StreamRequestHandler {
  8. @Override
  9. public void handleRequest(
  10. InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
  11. outputStream.write(new String("hello world").getBytes());
  12. }
  13. }
  • 包名和类名

    由于 Java 包含有包的概念,因此执行方法和其他语言有所不同,需要带有包信息。代码例子中对应的执行方法为 example.Hello::mainHandler,此处 example 标识为 Java package,Hello 标识为类,mainHandler 标识为类方法。

    包名和类名可以是任意的,但是需要与创建函数时的函数入口(handler)字段相对应。上面的例子包名是 example,类名是 HelloFC,那么创建函数时指定的 handlerexample.HelloFC::handleRequesthandler 的格式为 {package}.{class}::{method}

  • 实现的接口

    您的代码中必须要实现函数计算预定义的接口。上面的代码示例中实现了 StreamRequestHandler,其中的 inputStream 参数是调用函数时传入的数据,outputStream 参数用于返回函数的执行结果。

  • context 参数

    context 参数中包含一些函数的运行时信息(例如 requestId、临时 AccesKey 等),其类型是 com.aliyun.fc.runtime.Context

  • 返回值

    实现 StreamRequestHandler 接口的函数通过 outputStream 参数返回执行结果。

  • 引入接口库

    其中用到的 com.aliyun.fc.runtime 这个包的依赖可以通过下文的 pom.xml 引用。

  1. <dependency>
  2. <groupId>com.aliyun.fc.runtime</groupId>
  3. <artifactId>fc-java-core</artifactId>
  4. <version>1.3.0</version>
  5. </dependency>

通过 maven 仓库可以获取 fc-java-core 最新的版本号。

在创建函数之前,您需要将代码及其依赖的 fc-java-core 打包成 .jar。打包的方式,请参见 Java 代码打包

示例代码包是示例中的 hello world 代码打包成的 .jar ,您可以直接使用示例代码包进行测试。

使用 Fun 命令进行部署。

  1. fun deploy

执行成功时,会看到相关日志。

  1. using region: cn-hangzhou
  2. using accountId: ***********3557
  3. using accessKeyId: ***********r3Ra
  4. using timeout: 300
  5. Waiting for service FunDemo to be deployed...
  6. Waiting for function javademo to be deployed...
  7. Waiting for packaging function javademo code...
  8. package function javademo code done
  9. function javademo deploy success
  10. service FunDemo deploy success

登录函数计算控制台,您就可以查看函数的状态和调用函数了。

PojoRequestHandler

一个最简单的处理函数定义如下所示。

  1. // HelloFC.java
  2. package example;
  3. import com.aliyun.fc.runtime.Context;
  4. import com.aliyun.fc.runtime.PojoRequestHandler;
  5. public class HelloFC implements PojoRequestHandler<SimpleRequest, SimpleResponse> {
  6. @Override
  7. public SimpleResponse handleRequest(SimpleRequest request, Context context) {
  8. String message = "Hello, " + request.getFirstName() + " " + request.getLastName();
  9. return new SimpleResponse(message);
  10. }
  11. }
  1. // SimpleRequest.java
  2. package example;
  3. public class SimpleRequest {
  4. String firstName;
  5. String lastName;
  6. public String getFirstName() {
  7. return firstName;
  8. }
  9. public void setFirstName(String firstName) {
  10. this.firstName = firstName;
  11. }
  12. public String getLastName() {
  13. return lastName;
  14. }
  15. public void setLastName(String lastName) {
  16. this.lastName = lastName;
  17. }
  18. public SimpleRequest() {}
  19. public SimpleRequest(String firstName, String lastName) {
  20. this.firstName = firstName;
  21. this.lastName = lastName;
  22. }
  23. }
  1. // SimpleResponse.java
  2. package example;
  3. public class SimpleResponse {
  4. String message;
  5. public String getMessage() {
  6. return message;
  7. }
  8. public void setMessage(String message) {
  9. this.message = message;
  10. }
  11. public SimpleResponse() {}
  12. public SimpleResponse(String message) {
  13. this.message = message;
  14. }
  15. }

准备调用的输入文件

  1. {
  2. "firstName": "FC",
  3. "lastName": "aliyun"
  4. }

使用 context

context 是函数计算在运行时生成的一个对象,包含一些运行时的信息,您可以在代码中可以使用这些信息。context 的具体实现请参见 fc-java-libs ,其定义如下所示。

  1. package com.aliyun.fc.runtime;
  2. public interface Context {
  3. public String getRequestId();
  4. public Credentials getExecutionCredentials();
  5. public FunctionParam getFunctionParam();
  6. public FunctionComputeLogger getLogger();
  7. }

您可以看到 context 中包含了以下信息。

信息类型信息类型说明
requestId本次调用请求的唯一 ID,您可以把它记录下来在出现问题的时候方便查询。
function当前调用的函数的一些基本信息,例如函数名、函数入口、函数内存和超时时间。
credentials函数计算服务通过扮演您提供的服务角色获得的一组临时密钥,其有效时间是 5 分钟。您可以在代码中使用 credentials 去访问相应的服务( 例如 OSS ),这就避免了您把自己的 AccessKey 信息写死在函数代码里。
Logger函数计算封装过的 logger。

下文的代码演示了如何使用临时密钥向 OSS 中上传一个文件。

  1. package example;
  2. import com.aliyun.fc.runtime.Context;
  3. import com.aliyun.fc.runtime.Credentials;
  4. import com.aliyun.fc.runtime.StreamRequestHandler;
  5. import com.aliyun.oss.OSSClient;
  6. import java.io.ByteArrayInputStream;
  7. import java.io.IOException;
  8. import java.io.InputStream;
  9. import java.io.OutputStream;
  10. public class HelloFC implements StreamRequestHandler {
  11. @Override
  12. public void handleRequest(
  13. InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
  14. String endpoint = "oss-cn-shanghai.aliyuncs.com";
  15. String bucketName = "my-bucket";
  16. Credentials creds = context.getExecutionCredentials();
  17. OSSClient client = new OSSClient(
  18. endpoint, creds.getAccessKeyId(), creds.getAccessKeySecret(), creds.getSecurityToken());
  19. client.putObject(bucketName, "my-object", new ByteArrayInputStream(new String("hello").getBytes()));
  20. outputStream.write(new String("done").getBytes());
  21. }
  22. }