函数计算目前支持Java OpenJDK 1.8.0(runtime=java8)运行环境。本文介绍了Java运行环境的打印日志、错误处理和自定义模块。
打印日志
函数通过context.getLogger()
打印的内容会被收集到创建服务时指定的Logstore中。
package example;
import com.aliyun.fc.runtime.Context;
import com.aliyun.fc.runtime.StreamRequestHandler;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class HelloFC implements StreamRequestHandler {
@Override
public void handleRequest(
InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
context.getLogger().info("hello world");
outputStream.write(new String("hello world").getBytes());
}
}
执行上面的代码输出的日志内容如下所示。
message:2017-07-05T05:13:35.920Z a72df088-f738-cee3-e0fe-323ad89118e5 [INFO] hello world
使用context.getLogger().warn
和context.getLogger().error
分别可以打印WARN、ERROR级别的日志。
错误处理
您的函数如果在执行过程中抛出异常,函数计算会把异常捕获并将异常信息返回。
package example;
import com.aliyun.fc.runtime.Context;
import com.aliyun.fc.runtime.StreamRequestHandler;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class HelloFC implements StreamRequestHandler {
@Override
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
throw new IOException("oops");
}
}
调用时收到的响应如下所示。
>>> invk hello-java -f /tmp/a.json
{
"errorMessage" : "oops",
"errorType" : "java.io.IOException",
"errorCause" : "oops",
"stackTrace" : [ "example.HelloFC.handleRequest(HelloFC.java:15)" ]
}
Error: Request id: 45dd8d90-6b78-cce3-087c-8bf4ebc6c9af. Error type: UnhandledInvocationError
发生异常时,函数调用的响应的HTTP header中会包含X-Fc-Error-Type: UnhandledInvocationError
。
更多信息,请参见基本概念。
使用Java自定义模块
如果您需要使用自定义的模块,则需要在打JAR包时,将他们与代码一起打包。下文演示如何将OSS Java SDK打包到项目中。
- 安装Java和Maven。
- 创建一个Java项目,目录结构如下。
test/src/main/java/example/App.java
- 在App.java文件内输入以下内容。
package example;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import com.aliyun.fc.runtime.Context;
import com.aliyun.fc.runtime.StreamRequestHandler;
import com.aliyun.fc.runtime.FunctionInitializer;
/**
* Hello world!
*
*/
public class App implements StreamRequestHandler, FunctionInitializer {
public void initialize(Context context) throws IOException {
}
public void handleRequest(
InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
outputStream.write(new String("hello world\n").getBytes());
}
}
- 在项目文件夹根目录下创建pom.xml文件,在pom.xml中添加maven-assembly-plugin插件,内容如下。
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>example</groupId>
<artifactId>Java-example</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>Java-example</name>
<properties>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.test.skip>true</maven.test.skip>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<appendAssemblyId>false</appendAssemblyId> <!-- this is used for not append id to the jar name -->
</configuration>
<executions>
<execution>
<id>make-assembly</id> <!-- this is used for inheritance merges -->
<phase>package</phase> <!-- bind to the packaging phase -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<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>
</project>
- 项目需要引用Maven Central的外部包,可以根据需要添加依赖,下文以OSS Java SDK为例,pom.xml文件示例内容如下。
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>example</groupId>
<artifactId>Java-example</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>Java-example</name>
<dependencies>
<dependency>
<groupId>com.aliyun.fc.runtime</groupId>
<artifactId>fc-java-core</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>2.6.1</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.test.skip>true</maven.test.skip>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<appendAssemblyId>false</appendAssemblyId> <!-- this is used for not append id to the jar name -->
</configuration>
<executions>
<execution>
<id>make-assembly</id> <!-- this is used for inheritance merges -->
<phase>package</phase> <!-- bind to the packaging phase -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<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>
</project>
- 在项目的根目录下执行
mvn package
命令打包,编译输出如下。$mvn package
[INFO] Scanning for projects...
... .... ....
[INFO] --------------------------< example:example >---------------------------
[INFO] Building Java-example 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
... .... ....
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 11.681 s
[INFO] Finished at: 2020-03-26T15:55:05+08:00
[INFO] ------------------------------------------------------------------------
如果显示编译失败,请根据输出的编译错误信息调整代码。
编译后的JAR包位于项目文件夹内的target目录内,并根据pom.xml内的artifactId、version字段命名为Java-example、1.0-SNAPSHOT.jar。
- 在函数计算控制台上传JAR包。
- 登录函数计算控制台。
- 在顶部菜单栏,选择地域。
- 找到目标服务下的目标函数,单击函数名称。
- 在代码执行页面,选择OSS上传或者代码包上传的方式上传打包好的JAR包。
您也可以使用maven-shade-plugin插件进行打包。 通常上述的打包能够满足大多数的使用场景。但是如果您想把某些没有被Maven管理的JAR包打入到最终的JAR包中,例如您在resources/lib下引入的其他非Maven仓库中的JAR包,此时可以使用maven-jar-plugin和maven-dependency-plugin插件将其打入最终的JAR包中。
使用IDEA安装依赖并打包上传
- 本地创建Java项目,pom.xml文件内容如下。
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>example</groupId>
<artifactId>Java-example</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>Java-example</name>
<dependencies>
<dependency>
<groupId>com.aliyun.fc.runtime</groupId>
<artifactId>fc-java-core</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>2.6.1</version>
</dependency>
</dependencies>
</project>
- 使用IDEA导出JAR包相关配置。
- 选择。
- 在弹出的窗口中选择。
- 在配置窗口中使用默认配置,单击OK。
- 回到IDEA的主菜单,选择下的Build或者Rebuild即可生成JAR包。
- 您可以在项目根目录下out/artifacts/example_jar目录中将导出的JAR包上传函数计算。 您通过的代码执行页面,选择OSS上传或者代码包上传方式上传导出的JAR包。
使用Funcraft工具打包部署
如果您使用Funcraft部署应用,可以使用fun build命令来构建。
- 在工程的根目录下创建template.yml文件,内容示例如下。
ROSTemplateFormatVersion: '2015-09-01'
Transform: 'Aliyun::Serverless-2018-04-03'
Resources:
JavaDemo:
Type: 'Aliyun::Serverless::Service'
Properties:
Description: 'helloworld'
test:
Type: 'Aliyun::Serverless::Function'
Properties:
Handler: example.App::handleRequest
Initializer: example.App::initialize
Runtime: java8
CodeUri: './'
- 执行以下命令将工程打包。
fun build
返回结果如下。
using template: template.yml
start building function dependencies without docker
building JavaDemo/test
running task flow MavenTaskFlow
running task: MavenCompileTask
running task: MavenCopyDependencies
running task: CopyMavenArtifacts
Build Success
Built artifacts: .fun/build/artifacts
Built template: .fun/build/artifacts/template.yml
Tips for next step
======================
* Invoke Event Function: fun local invoke
* Invoke Http Function: fun local start
* Deploy Resources: fun deploy
- 执行以下命令部署函数。
fun deploy -y
返回结果如下。
using template: .fun/build/artifacts/template.yml
using region: cn-qingdao
using accountId: ***********3743
using accessKeyId: ***********Ptgk
using timeout: 60
Collecting your services information, in order to caculate devlopment changes...
Resources Changes(Beta version! Only FC resources changes will be displayed):
***********
Waiting for service JavaDemo to be deployed...
Waiting for function test to be deployed...
Waiting for packaging function test code...
The function test has been packaged. A total of 3 files were compressed and the final size was 330.35 KB
function test deploy success
service JavaDemo deploy success
执行结果
登录,即可看到相关的服务、函数被创建成功,且触发执行可以返回正确的结果。