AWS LambdaのNode.jsランタイムでTCP接続を使いまわそう!(AWS_NODEJS_CONNECTION_REUSE_ENABLED)

AWS LambdaのNode.jsランタイムで環境変数をAWS_NODEJS_CONNECTION_REUSE_ENABLED=1にすると、keepaliveが有効になり、TCP接続を再利用します。
2020.04.20

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Node.js のHTTP/HTTPS エージェントはデフォルトで keepalive が無効化されているため、新しいリクエストがあるたびに新しい TCP 接続を作成します。

AWS Lambda の Node.js ランタイムを利用している場合、環境変数で AWS_NODEJS_CONNECTION_REUSE_ENABLED=1 を設定することでkeepaliveが有効になり、TCP 接続を再利用してより効率的に通信するようになります。

この動作を確認してみます。

背景

Node.js のHTTP/HTTPS エージェントはデフォルトで keepalive が無効化されています。

keepAlive Keep sockets around even when there are no outstanding requests, so they can be used for future requests without having to reestablish a TCP connection... Default: false.

https://nodejs.org/api/http.html#http_new_agent_options

DynamoDB クエリなどの短期間のオペレーションでは、TCP 接続を設定する際のレイテンシーのオーバーヘッドが、オペレーション自体よりも大きくなる可能性があります。さらに、DynamoDB の保管時の暗号化は AWS KMS と統合されているため、データベースからのレイテンシーが発生する可能性があります。データベースはオペレーションごとに新しい AWS KMS キャッシュエントリを再確立する必要があります。

Node.js で Keep-alive を使用して接続を再利用する - AWS SDK for JavaScript

軽微なHTTP/HTTPS通信を高頻度で行っている場合、keepalive を有効にすることでスループットの向上を期待できます。

keepalive を有効化するには?

TCP接続の keepalive を有効にするには2種類の方法があります。

AWS LambdaのNode.jsランタイム

実行環境が AWS Lambda の Node.js ランタイムの場合、環境変数で AWS_NODEJS_CONNECTION_REUSE_ENABLED=1 を設定するだけです。

このオプションは AWS SDK for JavaScript のバージョン 2.463.0 以降で対応しています。

Node.js全般

Node.jsのHTTP/HTTPSクライアントに対して汎用的に使える方法です。

HTTP/HTTPS エージェントの keepAlive プロパティを true にします。

AWS SDKの場合は、以下の通りです。(ソースコードは公式ドキュメントから)

const AWS = require('aws-sdk');
// http or https
const http = require('http');
const agent = new http.Agent({
  keepAlive: true
});

AWS.config.update({
  httpOptions: {
    agent
  }
});

計測してみる

AWS Lambda から DynamoDB に接続し、 keepalive の効果を計測します。

DynamoDB に Get-Item する Lambda 関数を用意し、 DynamoDB の操作に要した時間を計測します。

// based on https://theburningmonk.com/2019/02/lambda-optimization-tip-enable-http-keep-alive/
const AWS = require('aws-sdk')
const ddb = new AWS.DynamoDB.DocumentClient();

exports.handler = async (event) => {
    
    const start = new Date().getTime()
    await ddb.get({TableName:'test', Key:{'id':1}}).promise()
    const end = new Date().getTime()
    console.log(`Log:${end-start}`)
    
    const response = {
        statusCode: 200,
        body: JSON.stringify('Hello from Lambda!'),
    };
    return response;
};

この関数を keepalive 設定の 有・無それぞれのケースで100回シーケンシャルに同期呼び出ししました。

計測結果

DynamoDB の処理時間

HTTPS 接続を伴う DynamoDB の操作に限定すると、keepalive を有効にすることで多くのケースで10倍近く速くなりました。

keepalive p50 p90 p95
false 42 62 73
true 5 27 64

※単位はミリ秒

遅い5%を除いてヒストグラムにしてみましょう。

keepalive を有効にすると、安定して劇的に速く処理できていることがわかります。

keepalive 無効

keepalive 有効

Lambda の応答時間

AWS X-Ray で Lambda 関数の応答時間の分布を確認しましょう。(responsetime <= 0.1 に限定)

DynamoDB の処理時間の違いが Lambda 関数の応答時間にも大きく影響していることがわかります。

keepalive 無効

keepalive 有効

まとめ

TCP の keepalive を有効にすると、接続を再利用することで、接続コストの軽減を期待できます。

Node.jsのHTTP/HTTPSエージェントはkeepalive接続がデフォルトで無効になっているため、設定を見直しましょう。

AWS環境でNode.js LambdaからDynamoDBと高頻度で通信しているような場合は keepalive の有効を試してみる価値が大いにあります。 Lambda関数の環境変数でAWS_NODEJS_CONNECTION_REUSE_ENABLED=1と設定するけで、ソースコードを変更せずに keepalive を有効にできます。

参考