「AWSLambdaがなんかよいらしい」、ということをこの前勉強会で聞いたので試そうと思ったのがきっかけで、ついでにQiita初投稿
AWS自体初めてで、サーバーレスアーキテクチャだとかマイクロサービスだとかも全然理解できていないので変なところがあったら突っ込み歓迎です。
#やりたかったこと
- AWS Lambdaの使い方を覚える
- 汎用的に使えそうななにかを作ってみる
#やったこと
WebAPIと聞いて最初の思いついたのがデータフォーマットのコンバーターだったのでXMLからJSON変換をやってみようと思った。
↓
データ作るのも面倒なのでその辺にころがってるHTMLをXMLに変換してそれをJSONに変えてみようと思い立つ
↓
Lambdaという名前から安直に高階関数をイメージしたので
HTMLtoXML = (text/htmlを返すURL) ー> application/xml
XMLtoJson = (application/xmlを返すURL) ー> application/json
XMLtoJson(HTMLtoXML(HTMLを返すURL)) → JSon化されたHTML
といった仕様のラムダAPIを作ってみた
#まずは
AWSの契約
1年無料らしいのでとりあえず登録
サービスいっぱいあってすごい
#AWS Lambda
2016年8月時点、function実装に使える言語はNode.js、Python、Javaとのこと
Nodeだと簡単そうだったけどせっかくなので普段使ってるScalaでやってみることにした
functionの実装
たいしたことしてないので全ソースのせる
(https://github.com/bakenezumi/aws-converter)
AWSLambdaは1jar(fat jar)でデプロイしないといけないのでactivator assembly
でfat jarが作れるプラグインを追加
resolvers += "sbt-assembly-resolver-0" at "http://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-releases"
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.13.0")
build.sbtにAWS関係やパース関係のライブラリを追加
(実際はいろいろ試したけど最終形)
name := """aws-converter"""
version := "1.0"
scalaVersion := "2.11.7"
libraryDependencies += "org.scalatest" %% "scalatest" % "2.2.4" % "test"
libraryDependencies += "com.amazonaws" % "aws-lambda-java-core" % "1.0.0"
libraryDependencies += "org.scala-lang.modules" %% "scala-xml" % "1.0.1"
libraryDependencies += "nu.validator.htmlparser" % "htmlparser" % "1.4"
libraryDependencies += "net.liftweb" %% "lift-json" % "2.6+"
functionはこんなかんじ
ほぼLiftからもってきた
package converter
trait ConverterBase {
import com.amazonaws.services.lambda.runtime.Context
import io.Source
// テスト時はオーバーライドできるように(ファイルからとかプロキシ使うときとか)
def fromURL(url: String, encode: String = "UTF-8") = Source.fromURL(url, encode)
def htmlToXml(url: String, context: Context) = {
import java.io.StringReader
import scala.collection.JavaConverters._
import scala.xml.Node
import scala.xml.parsing.NoBindingFactoryAdapter
import nu.validator.htmlparser.sax.HtmlParser
import nu.validator.htmlparser.common.XmlViolationPolicy
import org.xml.sax.InputSource
val hp = new HtmlParser
hp.setNamePolicy(XmlViolationPolicy.ALLOW)
val saxer = new NoBindingFactoryAdapter
hp.setContentHandler(saxer)
hp.parse(new InputSource(new StringReader(fromURL(url).getLines.mkString)))
saxer.rootElem.toString
}
def xmlToJson(url: String, context: Context) = {
import scala.xml.XML
import net.liftweb.json.{Xml, pretty, render}
val source = fromURL(url).getLines.mkString
val xml = XML.loadString(source)
pretty(render(Xml.toJson(xml)))
}
}
class Converter extends ConverterBase
やる気のないテストコード
import org.scalatest._
import converter._
class ConverterSpec extends FlatSpec with Matchers with ConverterBase{
import io.Source
override def fromURL(url: String, encode: String = "UTF-8") = Source.fromFile(url, encode)
"Converter.htmlToXml" should "ok" in {
val url = "src/test/scala/test.html"
val ret = htmlToXml(url, null)
println(ret)
ret != null
}
"Converter.xmlToJson" should "ok" in {
val url = "src/test/scala/test.xml"
val ret = xmlToJson(url, null)
println(ret)
ret != null
}
}
おもむろにactivator assembly
target/scala-2.11/の下にaws-converter-assembly-1.0.jarができた!
###functionののデプロイ
コマンドでもできるらしいけどコンソールでやった
やり方は割愛
#Web公開(AWS API Gateway)
ここからが本題
AWSすごい
TriggersからAddtriggerを選ぶとかっこいい画面がでてくる
Lambda名と同一のResource名が初期表示される
SecurityだけOpenにしてSubmitするとひとまずOK
ただこのままだと動かない(LambdaにURLをGETで渡せない)ので、ごにょごにょする必要がある
API Gatewayの管理画面に飛んで
まずhtmlToXMLの「GETのメソッドリクエスト」の「URL クエリ文字列パラメータ」を追加
こうすることでGETで渡したパラメータをラムダに渡すようになる
で、テストしてみると
成功!のようで実は失敗
AWS Lambdaでは返りがJSONなので、文字列を返した場合""で囲まれてしまう
なのでもう一個編集「統合レスポンス」
レスポンスのContent-Type
をapplication/xml
にして、$input.path('$')
というテンプレートに流すと生データがレスポンスされるようになる
できた!
んでprodにデプロイするとサービス公開完了!
https://hogehoge.execute-api.ap-northeast-1.amazonaws.com/prod/htmlToXml?url=http://yahoo.co.jp
(hogehogeは内緒)にアクセスするとyahooがXMLになった!
同様にxmlToJsonを公開すると、、、
https://hogehoge.execute-api.ap-northeast-1.amazonaws.com/prod/xmlToJson?url=https://hogehoge.execute-api.ap-northeast-1.amazonaws.com/prod/htmlToXml?url=http://yahoo.co.jp
できた!!
#APIの統合(2016/8/8追記)
HTML→XML→JSONを一発でやってくれるAPIはいままで作ったAPIの組み合わせで実装できた
-
API-Gatewayに/htmlToJsonのGETメソッドを作成(リンク先はxmlToJsonのラムダ)
-
「メソッドリクエスト」の「URLクエリ文字列パラメータ」に"url"を追加
-
「統合リクエスト」の「マッピングテンプレート」を
"https://hogehoge.execute-api.ap-northe1.ast-amazonaws.com/prod/htmlToXml?url=$input.params('url')"
に
- 「統合レスポンス」は今までと同様生データを返すようにする
prodにデプロイするとhttps://hogehoge.execute-api.ap-northeast-1.amazonaws.com/prod/htmlToJson?url=http://yahoo.co.jp
で期待した結果が得られた
#所感
基本的な使い方は理解できた
サーバー立てずにサービス公開できるって素敵
あとScalaは重かった(約20MByte)