[AWS]LambdaVPCでScalaからAWS SNSを使ってiOSにJSON形式で通知してみた。
前提
SNSのTopicが設定済み
VPCのセキュリティグループが設定済み
NATGatewayを設定したsubnetが2つある! ←重要
ソースコードの作成
この辺参考に作成してください。
Amazon LambdaでScala
build.sbt
今回イベント駆動にはしていないので、依存関係から
aws-lambda-java-events
は除きました。また、SNSを使うので
"com.amazonaws" % "aws-java-sdk-sns" % "1.10.54",
を追加しました。
また、SNS通知メッセージをJSON形式にしたいので
"com.typesafe.play" %% "play-json" % "2.3.10",
を追加しています。
sample
コードはこんな感じです。(ただしサンプル用に作ったので動作未確認)
実行する関数のinputパラメータはjavaのMap型にする必要がある。
CredentialsProviderはEnvironmentVariableCredentialsProviderを指定すればいい感じに権限を取得してくれる。
package qr.test
import com.amazonaws.auth._
import com.amazonaws.regions.{ Region, Regions }
import com.amazonaws.services.lambda.runtime.Context
import com.amazonaws.services.sns._
import com.amazonaws.services.sns.model._
import play.api.libs.json._
case class PublishMsg(
default: String,
APNS: String,
APNS_SANDBOX: String)
case class APNSMsgBody(
aps: APS,
title2: String,
options: String)
case class APS(
`content-available`: Int,
sound: String,
alert: Alert)
case class Alert(
title: String,
body: String)
object Notify {
def notify(input: java.util.Map[String, Object], context: Context): String = {
val credential: AWSCredentialsProvider = getCredentials
val topicArn = "arn:aws:sns:ap-northeast-1:xxxxxxxxxxx:test"
val m = APNSMsgBody(APS(1, "default", Alert("title", "body")), "title2", "options")
val APNSStr = Json.toJson(m).toString
val msg = Json.toJson(PublishMsg("title", APNSStr, APNSStr)).toString
println("msg:" + msg)
val publishRequest = new PublishRequest(topicArn, msg)
publishRequest.setMessageStructure("json")
getSNSClient.publish(publishRequest).getMessageId
}
implicit def convertMsgBody: Writes[APNSMsgBody] = Json.writes[APNSMsgBody]
implicit def convertAlert: Writes[Alert] = Json.writes[Alert]
implicit def convertAPS: Writes[APS] = Json.writes[APS]
implicit def convertPublishMsg: Writes[PublishMsg] = Json.writes[PublishMsg]
def getSNSClient = {
val cli = new AmazonSNSClient(getCredentials)
cli.setRegion(Region.getRegion(Regions.AP_NORTHEAST_1))
cli
}
def getCredentials: AWSCredentialsProvider = {
new EnvironmentVariableCredentialsProvider
}
}
messageの内容はこんな感じになります。
{
"default": "title",
"APNS": "{\"aps\":{\"content-available\":1,\"sound\":\"default\",\"alert\":{\"title\":\"title\",\"body\":\"body\"}},\"title2\":\"title2\",\"options\":\"options\"}",
"APNS_SANDBOX": "{\"aps\":{\"content-available\":1,\"sound\":\"default\",\"alert\":{\"title\":\"title\",\"body\":\"body\"}},\"title2\":\"title2\",\"options\":\"options\"}"
}
jar作成
activator assemblyでjarファイル作成
Lambdaの設定
「Select blueprint」「Configure triggers」は無視
Configure Function
Nameは適宜
RuntimeにJava8を選択
Code entry typeはzip or jar fileを選択
Function packageからファイルを選択し、upload
Handlerは上記の場合であれば qr.test.Notify::notify
roleはVPCのデフォルトにSNS Publishのみ許可したものを作成して選択。
メモリは大きめにしておいた方がコンパイルが早い。1024以上はあまり恩恵がなさそうに感じた。
コンパイルに10~30秒ほどかかるのでTimeoutはそのくらいで設定
VPCを選び、NATGateway設定済みのサブネットを二つ選択する。
NATGatewayが設定されていないと、SNSのAPIを叩けないのでtimeoutエラーとなるので注意!
http://stackoverflow.com/questions/36167836/java-myhandler-s3-put-client-timeout-in-lambda
セキュリティグループは443か80が全オープンであれば問題ないはず…。(未確認)
確認
確認して問題なさそうなら作成。
テストを実行したら通知される。
SNS Java SDK利用時のエラー
TopicArn (Service: AmazonSNS; Status Code: 400; Error Code: InvalidParameter; Request ID: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx) (AmazonHttpClient.java:1369)
snsClientに対してはRegionの設定が必要なようです。
def getSNSClient = {
val cli = new AmazonSNSClient(getCredentials)
cli.setRegion(Region.getRegion(Regions.AP_NORTHEAST_1))
cli
}