edit-icon download-icon

Java

最終更新日: Jan 31, 2019

Function Compute は現在、次の Java ランタイム環境をサポートしています。

  • OpenJDK 1.8.0 (runtime = java8)

Java で Function Compute を使用する場合は、クラスを定義し、あらかじめ定義された Function Compute インターフェイスを実装する必要があります。最もシンプルな関数は次のように定義されます。

  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. }
  • パッケージ名/クラス名

    パッケージとクラスは任意の名前を持つことができますが、その名前は作成された関数の “handler” フィールドに対応していなければなりません。上の例では、パッケージ名は “example” で、クラス名は “HelloFC” です。したがって、関数の作成時に指定された handler は example.HelloFC::handleRequest です。”handler” の形式は {package}.{class}::{method} です。

  • インターフェースを実装する

    定義済みの Function Compute インターフェイスは、コード内に実装する必要があります。上の例では、 StreamRequestHandler が実装されています。inputStream は関数が呼び出されたときにインポートされるデータで、 outputStream は関数の実行結果を返すために使われます。関数インタフェースの詳細については、「関数インタフェース 」をご参照ください。

  • context パラメーター

    context パラメーターには、関数の操作情報 (リクエスト ID や一時 AccessKey など) が含まれます。パラメーターの型は com.aliyun.fc.runtime.Context です。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.0.0</version>
  5. </dependency>

バージョン情報リストから、fc-java-core の最新バージョンを取得できます。

関数を作成する前に、依存関係を持つ fc-java-core を含むプロジェクトを JAR ファイルにパッケージ化する必要があります。JAR ファイルをパッケージ化する方法を カスタムモジュールの使用法 で学ぶことができます。JAR パッケージを作成したら、fcli またはコンソールを使用してパッケージをアップロードします。以下は、fcli を例として使用しています。

  1. rockuw-MBP:hello-java (master) $ ls -lrt
  2. total 16
  3. -rw-r--r-- 1 rockuw staff 7690 Aug 31 19:45 hellofc.jar
  4. >>> mkf hello-java -t java8 -h example.HelloFC::handleRequest -d ./functions/hello-java
  5. >>> invk hello-java
  6. hello world
  7. >>>

高度な使い方

context の使用法

context は、Function Compute 操作中に生成されるオブジェクトで、操作情報を含みます。コード内で情報を使用することができます。context は object 型で、定義は以下の通りです。詳細について知るには、ここ をクリックしてください。

  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 には 4 つの要素が含まれています。

  • RequestId: この実行リクエストの一意の ID です。将来何らかの例外が発生した場合の参考として記録することができます。
  • FunctionParam: FunctionParam:関数名、関数エントリ、関数メモリ、タイムアウト時間など、関数に関する基本情報です。
  • ExecutionCredentials:Function Compute が提供するサービスロールを実行するときに取得される一時キーです。有効な間隔は 5 分です。コード内で使用して、関連サービス (OSS など) にアクセスできます。これにより、ユーザーは認証情報を関数コードにハードコーディングするのを避けられます。
  • Logger: Function Compute でカプセル化されたロガーです。詳細は下記の logging の使用法 をご参照ください。

たとえば、次のコードでは一時キーを使用して 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. }

logging の使用法

context.getLogger() によって出力された関数についての情報は、サービスの作成時に割り当てられた Logstore に収集されます。

  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. context.getLogger().info("hello world");
  12. outputStream.write(new String("hello world").getBytes());
  13. }
  14. }

上記のコードで出力されたログは次のとおりです。

  1. message:2017-07-05T05:13:35.920Z a72df088-f738-cee3-e0fe-323ad89118e5 [INFO] hello world

context.getLogger().warncontext.getLogger().error は、 WARN と ERROR レベルでログをそれぞれパッケージ化するために使用できます。

関数インタフェース

Java プログラミングを使用する場合は、クラスを実装する必要があります。また、このクラスでは、事前定義された Function Compute インターフェイスを実装する必要があります。現在、2 つの事前定義されたインタフェースを実装できます。

このインタフェースでは、ストリームリクエストハンドラを使用して、関数を呼び出すときの情報 (イベント) の入力を受け取り、実行結果を返します。inputStream から入力情報を読み取り、読み取り操作が完了した後、 関数実行結果を outputStream に書き込む必要があります。このドキュメントの最初の例は、このインタフェースを使用しています。

このインターフェイスでは、一般的なメソッドを使用して入力と出力の型をカスタマイズできますが、その型は POJO である必要があります。次に、このインターフェイスの使用方法の例を示します。

  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. }

fcli を使用して結果を呼び出します。

  1. >>> invk hello-java -f /tmp/a.json
  2. {"message":"Hello, FC aliyun"}
  3. >>>

カスタムモジュールの使用法

カスタムモジュールを使用する場合は、JAR ファイルをパッケージ化するときに、モジュールをコードとともにパッケージ化する必要があります。ここでは Maven と IDEA を使用して、OSS Java SDK を jar ファイルにパッケージ化する方法を示します。

maven を使って jar ファイルをパッケージ化する

  1. pom.xml に OSS Java SDK の依存関係を追加します。

    1. <dependencies>
    2. <dependency>
    3. <groupId>com.aliyun.fc.runtime</groupId>
    4. <artifactId>fc-java-core</artifactId>
    5. <version>1.0.0</version>
    6. </dependency>
    7. <dependency>
    8. <groupId>com.aliyun.oss</groupId>
    9. <artifactId>aliyun-sdk-oss</artifactId>
    10. <version>2.6.1</version>
    11. </dependency>
    12. </dependencies>
  2. pom.xml に maven-assembly-plugin を追加します。

    1. <build>
    2. <plugins>
    3. <plugin>
    4. <artifactId>maven-assembly-plugin</artifactId>
    5. <version>3.1.0</version>
    6. <configuration>
    7. <descriptorRefs>
    8. <descriptorRef>jar-with-dependencies</descriptorRef>
    9. </descriptorRefs>
    10. <appendAssemblyId>false</appendAssemblyId> <!-- this is used for not append id to the jar name -->
    11. </configuration>
    12. <executions>
    13. <execution>
    14. <id>make-assembly</id> <!-- this is used for inheritance merges -->
    15. <phase>package</phase> <!-- bind to the packaging phase -->
    16. <goals>
    17. <goal>single</goal>
    18. </goals>
    19. </execution>
    20. </executions>
    21. </plugin>
    22. <plugin>
    23. <groupId>org.apache.maven.plugins</groupId>
    24. <artifactId>maven-compiler-plugin</artifactId>
    25. <configuration>
    26. <source>1.8</source>
    27. <target>1.8</target>
    28. </configuration>
    29. </plugin>
    30. </plugins>
    31. </build>
  3. Java ファイルをパッケージ化します。

    1. mvn package

上記の手順を完了すると、依存するサードパーティ製の jar も一緒にパッケージ化されます。生成された jar は、ターゲットディレクトリに格納されます。

IDEA を使用して jar ファイルをパッケージ化する

  1. pom.xml に OSS Java SDK の依存関係を追加します。

    1. <dependencies>
    2. <dependency>
    3. <groupId>com.aliyun.fc.runtime</groupId>
    4. <artifactId>fc-java-core</artifactId>
    5. <version>1.0.0</version>
    6. </dependency>
    7. <dependency>
    8. <groupId>com.aliyun.oss</groupId>
    9. <artifactId>aliyun-sdk-oss</artifactId>
    10. <version>2.6.1</version>
    11. </dependency>
    12. </dependencies>
  2. JAR パッケージのエクスポートオプションを設定します。

    java1

    java2

    java3

  3. jar ファイルを確認します。

    1. rockuw-MBP:hello-java (master) $ ls -lrth
    2. total 6520
    3. -rw-r--r-- 1 rockuw staff 3.2M Aug 31 21:03 hellofc.jar
    4. rockuw-MBP:hello-java (master) $ jar -tf hellofc.jar | head
    5. Picked up _JAVA_OPTIONS: -Duser.language=en
    6. META-INF/MANIFEST.MF
    7. example/
    8. example/HelloFC.class
    9. example/SimpleRequest.class
    10. example/SimpleResponse.class
    11. META-INF/
    12. META-INF//
    13. org/
    14. org//
    15. org/apache/

maven を使用して jar ファイルをパッケージ化し、依存関係の .jar ファイルを別の /lib ディレクトリに置く

プロジェクトの依存関係が増加すると、jar のサイズが大きくなります。ユーザーがアップロードした jar または zip コードは、実行前に解凍されます。したがって、上記 2 つの実装には、パッケージ化された jar に多数のクラスファイルが含まれているため、解凍時間が増え、関数の最初の起動時間が長くなるという問題があります。

より良い方法としては、依存関係の .jar ファイルを別々の /lib ディレクトリに置くことです。

次に、maven-dependency-plugin を使用した例を示します。

  1. <plugin>
  2. <groupId>org.apache.maven.plugins</groupId>
  3. <artifactId>maven-dependency-plugin</artifactId>
  4. <executions>
  5. <execution>
  6. <id>copy-dependencies</id>
  7. <phase>prepare-package</phase>
  8. <goals>
  9. <goal>copy-dependencies</goal>
  10. </goals>
  11. <configuration>
  12. <outputDirectory>${project.build.directory}/classes/lib</outputDirectory>
  13. <includeScope>runtime</includeScope>
  14. </configuration>
  15. </execution>
  16. </executions>
  17. </plugin>

mvn package の後、パッケージ化された jar のディレクトリ構造は次のようになります。

  1. */*.class
  2. lib/*.jar

例外処理

関数が実行されたときに例外が発生すると、Function Compute は例外をキャプチャしてその情報を返します。以下のコードを例として使用します。

  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(InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
  10. throw new IOException("oops");
  11. }
  12. }

呼び出し時に返されるレスポンスは次のとおりです。

  1. >>> invk hello-java -f /tmp/a.json
  2. {
  3. "errorMessage" : "oops",
  4. "errorType" : "java.io.IOException",
  5. "errorCause" : "oops",
  6. "stackTrace" : [ "example.HelloFC.handleRequest(HelloFC.java:15)" ]
  7. }
  8. Error: Request id: 45dd8d90-6b78-cce3-087c-8bf4ebc6c9af. Error type: UnhandledInvocationError

例外が発生すると、関数呼び出しレスポンスの HTTP ヘッダーに X-Fc-Error-Type: UnhandledInvocationError が含まれます。