こんにちは、小野寺です。
これは、 TECHSCORE Advent Calendar 2016 の8日目の記事です。
先日のAWS re:Invent 2016の発表で AWS Lambda で使用できる言語に、これまでのJava、Python、Node.jsに加えてC#が追加されましたね。
Apex というLambdaFunctionを簡単に構築・展開・管理できるツールを使うと
正式にはサポートされていないGoをNode.jsを介してLambdaで使うことができます。
今回はApexがどのようにGoをLambdaで実行しているのか見てみました。
ApexでGoをLambdaにデプロイ
まず、ApexにあるGoのサンプルコードを参考にLambdaにデプロイします。
サンプルコードはこちらからご覧ください。(Apexの設定は割愛します)
apex deploy すると、Apexの内部パッケージであるshimによって
Lambdaに以下のようなファイルを展開します。
1 2 3 |
byline.js index.js main |
Apex で生成されたコードを読む
展開されたコードを読んでみましょう。
index.js は以下のような内容です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
var child = require('child_process') var byline = require('./byline') /** * Context for the request. */ var ctx /** * Child process for binary I/O. */ var proc = child.spawn('./main', { stdio: ['pipe', 'pipe', process.stderr] }) proc.on('error', function(err){ console.error('error: %s', err) process.exit(1) }) proc.on('exit', function(code){ console.error('exit: %s', code) process.exit(1) }) /** * Newline-delimited JSON stdout. */ var out = byline(proc.stdout) out.on('data', function(line){ if (process.env.DEBUG_SHIM) console.log('[shim] parsing: %j', line) var msg = JSON.parse(line) ctx.done(msg.error, msg.value) }) /** * Handle events. */ exports.handle = function(event, context) { ctx = context proc.stdin.write(JSON.stringify({ "event": event, "context": context })+'\n'); } |
index.js を細かく見ていきます。
1 2 |
var child = require('child_process') var byline = require('./byline') |
index.jsでは2つのモジュールが使われています。
1 |
var proc = child.spawn('./main', { stdio: ['pipe', 'pipe', process.stderr] }) |
child_process.spawn(command, [args], [options]) で子プロセスを作成しています。
command
実行するコマンドを指定します。
./main は Go で作成した main.go のビルド済みバイナリを実行します。
args
コマンドの引数を指定できますが、今回は省略しています。
options
stdioのオプションで子プロセスの入出力設定をしています。それぞれ、
1 |
[ChildProcess.stdin、ChildProcess.stdout、 ChildProcess.stderr] |
に対応していて、標準入力、標準出力はパイプに、標準エラー出力は親プロセスと共有しています。
1 2 3 4 5 6 7 8 9 |
proc.on('error', function(err){ console.error('error: %s', err) process.exit(1) }) proc.on('exit', function(code){ console.error('exit: %s', code) process.exit(1) }) |
また、子プロセスが終了した場合、もしくはエラーが発生した場合に標準エラー出力をして終了します。
1 2 3 4 5 6 7 |
var out = byline(proc.stdout) out.on('data', function(line){ if (process.env.DEBUG_SHIM) console.log('[shim] parsing: %j', line) var msg = JSON.parse(line) ctx.done(msg.error, msg.value) }) |
bylineモジュールによって、子プロセスからの標準出力を1行ずつ処理します。
ctx.done()で、レスポンス(エラーがあればエラーレスポンス)を返して処理を終了します。
1 2 3 4 5 6 7 8 |
exports.handle = function(event, context) { ctx = context proc.stdin.write(JSON.stringify({ "event": event, "context": context })+'\n'); } |
このハンドラがLambdaで実行される関数です。
event と context を子プロセスの標準入力に渡しています。
おわりに
Node.jsとGoが標準入出力パイプでやり取りしていることがわかりました。
今回、Go言語の記事を書こうと思っていたのですがNode.jsのコードリーディングになりました。
まだLambdaもApexも使い始めたばかりなので、これから色々試していこうと思います。