WEB事業部の西村です。
唐突ですが皆さんはLambda関数を作成する際にどのような言語を用いていますでしょうか?
用途に合った言語や自分で書きやすいと思う言語などの要因で決めていると思われます
私はKotlinという言語が好きでKotlinで書こうと思ったのですが、Kotlinで書かれている記事はそのほとんどがJavaで実行することを前提とした元なっています
しかし、Javaではコールドスタートした際に実行時間がかかってしまうというデメリットがあります
KotlinはJavaだけでなくJavaScriptでも動作させることができるので今回はNode.jsで実行させる関数の作成を行いたいと思います
目次
この記事の目標
Kotlin/JSを用いたLambda関数の作成とそのレスポンス確認
この記事で取り扱わないこと
この記事では AWS Lambda
と Amazon API Gateway
については詳しく解説いたしません
あらかじめLambda関数とその関数を呼び出すためのAPIの作成を行っておいてください
開発環境
この記事では下記の環境で開発を行っています
- AdoptOpenJDK 1.8.0_222-b10
- IntelliJ IDEA Community 2019.2.2
- Kotlin 1.3.50
プロジェクトの作成
プロジェクトの作成を行います
任意のディレクトリにプロジェクト用のフォルダを作成します(この記事では KotlinLambda
としています)
その後そのディレクトリ内に build.gradle.kts
と settings.gradle.kts
の2つのファイルを作成します
KotlinLambda ├─build.gradle.kts └─settings.gradle.kts
作成したらIntelliJ IDEAでプロジェクトのフォルダを開きます
続いて、 build.gradle.kts
と settings.gradle.kts
を開き下記の内容を入力します
build.gradle.kts
plugins { kotlin("js") version "1.3.50" } repositories { mavenCentral() } kotlin { target { nodejs() useCommonJs() } sourceSets["main"].dependencies { implementation(kotlin("stdlib-js")) } }
settings.gradle.kts
rootProject.name = "kotlin_lambda"
その後画面右下に出ている Gradle projects need to be imported
の Import Changes
をクリックします
CONFIGURE SUCCESSFUL
と出てきたら準備完了です
ハンドラソースコードの追加
次にソースディレクトリを作成します
プロジェクトのディレクトリに src/main/kotlin
でディレクトリを作成します
またパッケージ名を追加する場合、パッケージを作成します(この記事では jp.co.seeds_std.lambda.kotlin
とします)
作成したディレクトリに handler.kt
というファイルを作成し下記の内容を記述します
handler.kt
package jp.co.seeds_std.lambda.kotlin @JsExport @JsName("handler") fun handler(event: dynamic, context: dynamic, callback: (Error?, dynamic) -> Unit) { val response: dynamic = object {} response["statusCode"] = 200 response.body = "Hello from Kotlin Lambda!" callback(null, response) }
@JsExport
JavaScriptのexportを行うアノテーションです@JsName(name: String)
記述したクラスや関数をトランスパイルした際name
に指定した名前となるよう変換してくれるようになりますdynamic
JavaScriptのオブジェクトをそのまま取り扱います(.
[]
でその中の関数や変数にアクセスできます)
デプロイ
デプロイパッケージの作成を行います
圧縮を自動化するためのタスクを追加します
build.gradle.kts
を再度開き下記の内容を追加します
build.gradle.kts
import com.google.gson.JsonParser import java.io.OutputStream import java.util.zip.ZipOutputStream import java.util.zip.ZipEntry plugins { ... } repositories { ... } kotlin { ... } open class CompressTask : DefaultTask() { lateinit var buildDirectory: File lateinit var projectName: String var rootProjectName: String? = null private val blackLists = setOf("kotlin-test-nodejs-runner", "kotlin-test") @TaskAction fun compress() { val projectPrefix = if(rootProjectName != null && rootProjectName != projectName) "$rootProjectName-" else "" val zipFile = File(buildDirectory, "../compress.zip") val outputStream = ZipOutputStream(zipFile.outputStream(), Charsets.UTF_8).apply { setLevel(9) } val jsDir = File(buildDirectory, "js") val projectDir = File(jsDir, "packages/$projectPrefix$projectName") val nodeModuleDir = File(jsDir, "node_modules") addDependencies(outputStream, File(projectDir, "package.json"), nodeModuleDir, mutableSetOf(*blackLists.toTypedArray())) addZipEntry(outputStream, File(projectDir, "kotlin/$projectPrefix$projectName.js")) outputStream.close() } private fun addZipEntry(zipOutputStream: ZipOutputStream, file: File, addDirectory: String = "") { val name = if(addDirectory.isEmpty()) file.name else "$addDirectory/${file.name}" if(file.isDirectory) { file.listFiles()?.forEach { addZipEntry(zipOutputStream, it, name) } } else { val zipEntry = ZipEntry(name) zipOutputStream.addZipEntry(zipEntry) { it.writeFile(file) } } } private fun addDependencies(zipOutputStream: ZipOutputStream, packageJsonFile: File, nodeModuleDir: File, addedDependencies: MutableSet<String> = mutableSetOf()) { val packageJson = JsonParser().parse(packageJsonFile.reader()).asJsonObject if(!packageJson.has("dependencies")) return val dependencies = packageJson.getAsJsonObject("dependencies") dependencies.keySet().forEach { if(!addedDependencies.contains(it)) { addedDependencies.add(it) val dependenciesDir = File(nodeModuleDir, it) addZipEntry(zipOutputStream, dependenciesDir, "node_modules") addDependencies(zipOutputStream, File(dependenciesDir, "package.json"), nodeModuleDir, addedDependencies) } } } private inline fun ZipOutputStream.addZipEntry(entry: ZipEntry, crossinline block: (OutputStream) -> Unit) { try { putNextEntry(entry) block(this) } catch (e: Exception) { e.printStackTrace() } finally { closeEntry() } } @Suppress("NOTHING_TO_INLINE") private inline fun OutputStream.writeFile(file: File) { file.inputStream().use { it.copyTo(this) } } } tasks.register("noMainCall") { doFirst { kotlin.target.compilations.all { compileKotlinTask.kotlinOptions.main = "noCall" } } } tasks.register<CompressTask>("compress") { dependsOn("compileKotlinJs", "noMainCall") this.buildDirectory = rootProject.buildDir this.projectName = project.name this.rootProjectName = rootProject.name } tasks["compileKotlinJs"].mustRunAfter("noMainCall")
追加後、画面右下に出ている Gradle projects need to be imported
の Import Changes
をクリックします
CONFIGURE SUCCESSFUL
と出てきたら画面右側の Gradle
タブを開きます
kotlin_lambda -> Tasks -> other
の順に開き、その中の compress
をダブルクリックして実行します
するとプロジェクトのディレクトリに compress.zip
というファイルが生成されます
この生成されたファイルをコンソールからアップロードします
その後の ハンドラ
を kotlin_lambda.{パッケージ名}.handler
に変更します (この記事では kotlin_lambda.jp.co.seeds_std.lambda.kotlin.handler
となります)
アップロードと変更が完了したら保存を行います
動作確認
あらかじめ作成しておいたAPI GatewayのURLにアクセスし Hello from Kotlin Lambda!
と表示されれば成功です
最後に
いかがでしたでしょうか
Javaの時より動作も軽くなったと感じるのではないかと思います
次回はもう少し使いやすくなるような記事も書きたいと思います
ここまで読んでいただきありございました