はじめに
LambdaとDynamoDBを使用すればIOT用のグラフページを作成できるのではと思ったのでその備忘録。
この記事では図中の「グラフ画像生成」部分を実施。
前回:https://qiita.com/aikawa_YO/items/0fb84aec97e28ea33136
関数URLとLambdaのロール設定は前回と同様。
(今回は読み込みだけなのでLambdaからDynamoDBへの権限はフルアクセスいらなかった。)
DynamoDBからデータを取得する
データを取得するためにいくつかの方法がありました。
get_item
条件に当てはまるデータ単体を取得する
query
条件に当てはまるデータをすべて取得する
PartitionKeyとSortKeyがある
上記例ではPartitionKeyがidでSortKeyがtime
PartitionKeyのみ、またはPartitionKeyとSortKey2つを指定する事が可能。
Primary Key = Partition Key
Primary Key = Partition Key + Sort Key
今回のIOT機器の場合、idが機器固有の番号、timeがデータを計測した際の時間となりこの2つの値を合わせてプライマリーキーとなる。
queryではPartitionKeyにより絞り込みを行いSortKeyで整列させ出力する。
scan
すべてのデータを取得
query
下記サンプルプログラムではidが1番のデータをtime降順にて取得している。
response = table.query(
KeyConditionExpression=Key("id").eq(1),
ScanIndexForward=False
)
BytesIO(Python)
バイナリデータをメモリ(RAM)上で扱うための機能。
Lambdaではエフェメラルストレージが用意されているのでそちらを使用したほうがよかったかもしれない。
matplotlib(Python)
グラフ画像の作成に使用する。
Lambdaにてmatplotlibを使用する方法は下記記事を参考にさせていただきました。
Lambdaの一般設定
グラフの作図や画像データ処理に処理にメモリが必要であるためある程度増やしておく。
処理に時間がかかるとタイムアウトするためタイムアウト時間も調整する必要がある。
プログラム全体
import base64
import boto3
import matplotlib.pyplot as plt
from io import BytesIO
from datetime import datetime as dt
from boto3.dynamodb.conditions import Key
def lambda_handler(event, context):
dynamodb = boto3.resource("dynamodb", region_name='ap-northeast-1')
table = dynamodb.Table("iot_data")
response = table.query(
KeyConditionExpression=Key("id").eq(1),
ScanIndexForward=False
)
items = response["Items"]
temps = []
times = []
for item in items:
temps.append(float(item['temperature']))
times.append(dt.strptime(item['time'], '%Y-%m-%d %H:%M:%S %z'))
fig = plt.figure()
plt.plot(times, temps)
byte_file = BytesIO()
fig.savefig(byte_file, format="png")
png_data = byte_file.getvalue()
plt.close()
return {
"statusCode": 200,
"headers": {"Content-Type": "image/png"},
"body": base64.b64encode(png_data).decode('utf-8'),
"isBase64Encoded": True
}
実行結果
BytesIOとエフェメラルストレージどちらが早い?
今回は画像の保存部分にBytesIOを使用しました。(通常であれば画像はS3に保存したりすると思います)
byte_file = BytesIO()
fig.savefig(byte_file, format="png")
png_data = byte_file.getvalue()
よくよく考えるとBytesIOを使用した場合メモリ(RAM)上に保存すると思うのでLambdaの場合はふさわしくないのでは?
エフェメラルストレージに保存したほうがいいかもしれないと思いました。
fig.savefig("/tmp/img.png")
file = open('/tmp/img.png', 'rb')
png_data = file.read()
ただ、実行結果は下記のようにそんなに大差はなかったです。
ふつうにBytesIO使っておけば良さそう
BytesIO
1回目:Duration: 1125.19 ms Billed Duration: 1126 ms Memory Size: 512 MB Max Memory Used: 159 MB Init Duration: 2847.22 ms
2回目:Duration: 422.46 ms Billed Duration: 423 ms Memory Size: 512 MB Max Memory Used: 160 MB
エフェメラルストレージ
1回目:Duration: 1140.78 ms Billed Duration: 1141 ms Memory Size: 512 MB Max Memory Used: 159 MB Init Duration: 2433.66 ms
2回目:Duration: 443.96 ms Billed Duration: 444 ms Memory Size: 512 MB Max Memory Used: 161 MB