はじめに
lambdaハンズオンを参考にlambdaを触ってみました。
プログラム書きたくて社会人になったのに気がついたらインフラエンジニアになってた人がアプリケーションもやれるのがAWSの良いところ!
Lambda in JAVA!
いろんなjavaアプリケーションをイベント駆動で実行できるようになりますね!(知ったかぶり
AWS Lambda Update – Run Java Code in Response to Events | AWS Official Blog
Amazon Web Services ブログ: 【AWS発表】AWS Lambdaのアップデート、イベントに応じてJavaコードを実行
なにを試そう…
パッと思いついた kuromoji.jar をLambda Functionの中で使ってみよう。
外部からS3 Bucketにテキストファイルが置かれたら自動で形態素解析して結果を別のbucketに出力する。
(それ何に使えるの?とか、cloudsearch使ったら?とか言わせない。)
やってみた
構成を考える
※lambda:InvokeFunctionについては後述。特別設定は不要になったようです。
ここまで書いてみて必要そうな知識
- Java SDKで、S3からObjectをgetする
- Java SDKで、getしたObjectの文章を読込む
- Kuromojiで形態素解析する
- 結果をJava SDKでputする
- Lambda Functionの書き方
本当にkuromoji.jar動かせんの?(Lambdaにアップロードできんの?)を調べる
容量制限の話。
Input | Limit |
---|---|
Compressed function .zip file | 50 MB |
Uncompressed function .zip file | 250 MB |
Invoke request body JSON | 6 MB |
圧縮して50MBまで。
kuromoji-0.7.7.jarが11M。いけそう。
どうやって作るのかを調べる(開発環境)
- Creating a .jar Deployment Package Using Maven without any IDE (Java)
- Creating a .jar Deployment Package Using Maven and Eclipse IDE (Java)
- Creating a .zip Deployment Package (Java)
- Authoring Lambda Functions Using Eclipse IDE and AWS SDK Plugin (Java)
2番目のMaven and Eclipse IDEでやってみようかと思います。
開発環境を準備する
作業PC
OS X Yosemite 10.10.3
homebrew, brew-cask インストール済み
Eclipseインストール
brew cask install eclipse-platform
Mavenプロジェクトの作成
を参考。
Kuromojiを追加
http://www.atilika.org/
Maven artifact repository For ease of use with Maven or Ivy
を参照。
aws toolkitインストール
インストール
以下を参考にインストール
credential 設定
Toolkit for Eclipse に AWS アクセスキーを追加するにはを参照。
構成図に合わせてAWSの設定をする
S3 Bucket(ファイル受取用)
東京リージョンに作ります
~/.aws/configにregion設定済み。
$ aws s3 mb s3://imura81gt-text-bucket
S3 Bucket(処理結果出力用)
形態素解析した結果はこのバケットに入れることにします。
$ aws s3 mb s3://imura81gt-text-bucket-toknized
入力先bucketと出力先bucketを一緒にするとたぶん無限lambdaして大変なことになる。
-> EventSourceの設定でPrefix/Sufixの設定ができるようになったので、無限lambdaを防ぐ方法はある。
IAM role
exec Role
- Lambda exec Roleの作成
を行う。 lambda_s3_exec_role
とする。
Invoke Role
Event Sourceの設定の際にInvocation Roleの設定が必要でしたが、無くなってました。
coding
こんな感じでやりました。
(普段bashくらいしか書かないので、名前の付け方がおかしかったりしたら教えて下さい)
package net.outofbounds8.kuromoji.lambda;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.S3Event;
import com.amazonaws.services.lambda.runtime.LambdaLogger;
import org.atilika.kuromoji.Token;
import org.atilika.kuromoji.Tokenizer;
import com.amazonaws.auth.ClasspathPropertiesFileCredentialsProvider;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.UUID;
import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.regions.Region;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.Bucket;
import com.amazonaws.services.s3.model.GetObjectRequest;
import com.amazonaws.services.s3.model.ListObjectsRequest;
import com.amazonaws.services.s3.model.ObjectListing;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectSummary;
import org.atilika.kuromoji.Token;
import org.atilika.kuromoji.Tokenizer;
// sample http://docs.aws.amazon.com/lambda/latest/dg/java-wt-s3-log-event-data.html
import com.amazonaws.services.s3.event.S3EventNotification.S3EventNotificationRecord;
public class LambdaFunctionHandler implements RequestHandler<S3Event, Object> {
@Override
public Object handleRequest(S3Event input, Context context) {
context.getLogger().log("Input: " + input);
context.getLogger().log("Input: " + input);
LambdaLogger lambdaLogger = context.getLogger();
S3EventNotificationRecord record = input.getRecords().get(0);
lambdaLogger.log(record.getEventName()); //イベント名
lambdaLogger.log(record.getS3().getBucket().getName()); //バケット名
lambdaLogger.log(record.getS3().getObject().getKey()); //オブジェクトのキー(オブジェクト名)
String bucketName = record.getS3().getBucket().getName();
String key = record.getS3().getObject().getKey();
S3Object object = null;
String tld = "-toknized";
StringBuilder buf = new StringBuilder();
buf.append(bucketName);
buf.append(tld);
String outputBucketName = buf.toString();
//AmazonS3 s3 = new AmazonS3Client(new ClasspathPropertiesFileCredentialsProvider());
AmazonS3 s3 = new AmazonS3Client();
try {
object = s3.getObject(new GetObjectRequest(bucketName, key));
lambdaLogger.log("Content-Type: " + object.getObjectMetadata().getContentType());
lambdaLogger.log("object.getObjectContent: " + object.getObjectContent());
String text = getTextInputStream(object.getObjectContent());
lambdaLogger.log(text); //イベント名
File file = File.createTempFile("tokenize-", ".txt");
file.deleteOnExit();
Writer writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
Tokenizer tokenizer = Tokenizer.builder().build();
for (Token token : tokenizer.tokenize(text)) {
System.out.println(token.getSurfaceForm() + "\t" + token.getAllFeatures());
writer.write(token.getSurfaceForm() + "\t" + token.getAllFeatures() + "\n");
}
writer.close();
s3.putObject(new PutObjectRequest(outputBucketName, "toknized-" + key, file));
} catch (AmazonServiceException ase) {
System.out.println("Caught an AmazonServiceException, which means your request made it "
+ "to Amazon S3, but was rejected with an error response for some reason.");
System.out.println("Error Message: " + ase.getMessage());
System.out.println("HTTP Status Code: " + ase.getStatusCode());
System.out.println("AWS Error Code: " + ase.getErrorCode());
System.out.println("Error Type: " + ase.getErrorType());
System.out.println("Request ID: " + ase.getRequestId());
} catch (AmazonClientException ace) {
System.out.println("Caught an AmazonClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with S3, "
+ "such as not being able to access the network.");
System.out.println("Error Message: " + ace.getMessage());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return record.getS3().getObject().getKey();
//http://dev.classmethod.jp/cloud/aws/aws-sdk-for-java-s3-create-download-application/
// TODO: implement your handler
//return null;
}
private static String getTextInputStream(InputStream input) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));
String text = "";
String line = "";
while (true) {
line = reader.readLine();
if (line == null) break;
text = text + line;
}
return text;
}
}
Eclipseからlambda functionをアップロード
upload
※東京リージョンが出る前に試した人はEclipseの
[help] - [check for update] からupdateを行うこと。
(デプロイする際に、古いものをしようしていると、東京リージョンが表示されないため)
実行!
送るファイル
すもももももももものうち。
つまり、スモモは桃のうちであり
桃ももちろん桃のうちである、
という事である。
実際のところ、スモモはバラ科サクラ属であり、別名プラムという。
なので桃ではない。
このような文章のことを言葉遊びと言う。
s3になにもファイルがないのを確認
$ aws s3 ls s3://imura81gt-text-bucket/
$ aws s3 ls s3://imura81gt-text-bucket-toknized/
アップロード
$ aws s3 cp sumomo.txt s3://imura81gt-text-bucket/
upload: ./sumomo.txt to s3://imura81gt-text-bucket/sumomo.txt
$
$ aws s3 ls s3://imura81gt-text-bucket/
2015-08-01 00:58:21 334 sumomo.txt
$
形態素解析されたテキストが出力されていることを確認
$ aws s3 ls s3://imura81gt-text-bucket-toknized/
2015-08-01 00:58:33 3590 toknized-sumomo.txt
$
中身を表示してみる
$ aws s3 cp s3://imura81gt-text-bucket-toknized/toknized-sumomo.txt -
すもも 名詞,一般,*,*,*,*,すもも,スモモ,スモモ
も 助詞,係助詞,*,*,*,*,も,モ,モ
もも 名詞,一般,*,*,*,*,もも,モモ,モモ
も 助詞,係助詞,*,*,*,*,も,モ,モ
もも 名詞,一般,*,*,*,*,もも,モモ,モモ
の 助詞,連体化,*,*,*,*,の,ノ,ノ
うち 名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
。 記号,句点,*,*,*,*,。,。,。
つまり 接続詞,*,*,*,*,*,つまり,ツマリ,ツマリ
、 記号,読点,*,*,*,*,、,、,、
スモモ 名詞,一般,*,*,*,*,スモモ,スモモ,スモモ
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
桃 名詞,一般,*,*,*,*,桃,モモ,モモ
の 助詞,連体化,*,*,*,*,の,ノ,ノ
うち 名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
で 助動詞,*,*,*,特殊・ダ,連用形,だ,デ,デ
あり 助動詞,*,*,*,五段・ラ行アル,連用形,ある,アリ,アリ
桃 名詞,一般,*,*,*,*,桃,モモ,モモ
も 助詞,係助詞,*,*,*,*,も,モ,モ
もちろん 副詞,一般,*,*,*,*,もちろん,モチロン,モチロン
桃 名詞,一般,*,*,*,*,桃,モモ,モモ
の 助詞,連体化,*,*,*,*,の,ノ,ノ
うち 名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
で 助動詞,*,*,*,特殊・ダ,連用形,だ,デ,デ
ある 助動詞,*,*,*,五段・ラ行アル,基本形,ある,アル,アル
、 記号,読点,*,*,*,*,、,、,、
という 助詞,格助詞,連語,*,*,*,という,トイウ,トユウ
事 名詞,非自立,一般,*,*,*,事,コト,コト
で 助動詞,*,*,*,特殊・ダ,連用形,だ,デ,デ
ある 助動詞,*,*,*,五段・ラ行アル,基本形,ある,アル,アル
。 記号,句点,*,*,*,*,。,。,。
実際 副詞,助詞類接続,*,*,*,*,実際,ジッサイ,ジッサイ
の 助詞,連体化,*,*,*,*,の,ノ,ノ
ところ 名詞,非自立,副詞可能,*,*,*,ところ,トコロ,トコロ
、 記号,読点,*,*,*,*,、,、,、
スモモ 名詞,一般,*,*,*,*,スモモ,スモモ,スモモ
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
バラ 名詞,一般,*,*,*,*,バラ,バラ,バラ
科 名詞,接尾,一般,*,*,*,科,カ,カ
サクラ 名詞,一般,*,*,*,*,サクラ,サクラ,サクラ
属 名詞,サ変接続,*,*,*,*,属,ゾク,ゾク
で 助動詞,*,*,*,特殊・ダ,連用形,だ,デ,デ
あり 助動詞,*,*,*,五段・ラ行アル,連用形,ある,アリ,アリ
、 記号,読点,*,*,*,*,、,、,、
別名 名詞,一般,*,*,*,*,別名,ベツメイ,ベツメイ
プラム 名詞,一般,*,*,*,*,プラム,プラム,プラム
と 助詞,格助詞,引用,*,*,*,と,ト,ト
いう 動詞,自立,*,*,五段・ワ行促音便,基本形,いう,イウ,イウ
。 記号,句点,*,*,*,*,。,。,。
なので 接続詞,*,*,*,*,*,なので,ナノデ,ナノデ
桃 名詞,一般,*,*,*,*,桃,モモ,モモ
で 助詞,格助詞,一般,*,*,*,で,デ,デ
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
ない 助動詞,*,*,*,特殊・ナイ,基本形,ない,ナイ,ナイ
。 記号,句点,*,*,*,*,。,。,。
この 連体詞,*,*,*,*,*,この,コノ,コノ
よう 名詞,非自立,助動詞語幹,*,*,*,よう,ヨウ,ヨー
な 助動詞,*,*,*,特殊・ダ,体言接続,だ,ナ,ナ
文章 名詞,一般,*,*,*,*,文章,ブンショウ,ブンショー
の 助詞,連体化,*,*,*,*,の,ノ,ノ
こと 名詞,非自立,一般,*,*,*,こと,コト,コト
を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
言葉 名詞,一般,*,*,*,*,言葉,コトバ,コトバ
遊び 名詞,一般,*,*,*,*,遊び,アソビ,アソビ
と 助詞,格助詞,引用,*,*,*,と,ト,ト
言う 動詞,自立,*,*,五段・ワ行促音便,基本形,言う,イウ,イウ
。 記号,句点,*,*,*,*,。,。,。
$
まとめ
Amazon API Gateway と組み合わせれば
形態素解析APIが作れそう!