[高速キャッシュ] MomentoをGraalVMで動かす [高速起動VM]

2022.11.08

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

Introduction

以前、MicronautとGraalVMを使った記事を書きました。
また、最近はMomentoという高速サーバレスキャッシュについても
何度か紹介しています。

高速起動と最適化されたパフォーマンスのGraalVMと
高速キャッシュサービスであるMomentoを組み合わせて速くないはずがない、
ということで、GraalVM + Momentoを試してみました。

Momento?

Momentoはクラウドネイティブな高速キャッシュサービスです。
セットアップが簡単で、プロビジョニングの必要もなく(サービス側で管理)、
料金もデータの転送量($0.15/GB)のみとなっている素晴らしいサービスです。
※ Momentoの特徴など、概要はこちら

Momento with GraalVM

残念ながら、現状ではGraalVMのネイティブイメージでMomentoはそのままでは動きません。
Momento SDKが依存しているライブラリがリフレクションを使用しているため、
それに対応する必要があります。

素晴らしいことにMomentoのエンジニアがGraalVMで動くリポジトリを
公開してくれています。
これはAWS LambdaでGraalVMを動かすデモになってます。
READMEにあるとおりに手順を実行していけば、
カスタムランタイム(GraalVM)を設定したAWS LambdaでMomnentoが動きます。

デモではMavenを使っているのですが、
今回はここで作成したプロジェクトの構成(Gradle + Micronaut)で
Momentoをうごかしてみます。

Environment

  • MacBook Pro (13-inch, M1, 2020)
  • OS : MacOS 12.4
  • Gradle : 7.5.1

Momentoはセットアップ済みで、認証トークン取得済みとします。
セットアップ方法はここをご確認ください。

Try

では、ここのプロジェクトをベースに実装していきます。

gradleファイルの修正とFeatureクラスのコピー

まずはここにある2つのjavaファイルをローカルのsrc/mainにコピーします。  

  • src/main/java/com/momento/nativeimage/GrpcNettyFeature.java
  • src/main/java/com/momento/nativeimage/NativeImageUtils.java

これらのクラスをコピーしたら、build.gradleを下記のように修正します。

repositories {
    mavenCentral()

    // リポジトリを追加
    maven {url "https://momento.jfrog.io/artifactory/maven-public"}

}

・・・

dependencies {
    annotationProcessor("io.micronaut:micronaut-http-validation")
    annotationProcessor("io.micronaut.serde:micronaut-serde-processor")
    implementation("io.micronaut:micronaut-http-client")
    implementation("io.micronaut.serde:micronaut-serde-jackson")
    implementation("jakarta.annotation:jakarta.annotation-api")
    runtimeOnly("ch.qos.logback:logback-classic")
    compileOnly("org.graalvm.nativeimage:svm")
    implementation("io.micronaut:micronaut-validation")

    // ライブラリを追加
    implementation 'momento.sandbox:momento-sdk:0.21.0'
}

・・・

graalvmNative {
    binaries {
        main {
            //ビルドオプションの追加
            //buildArgs.add("--verbose")
            //buildArgs.add("--no-fallback")
            buildArgs.add("--initialize-at-build-time=org.slf4j")
            buildArgs.add("--features=com.momento.nativeimage.GrpcNettyFeature")
            buildArgs.add("--initialize-at-run-time=io.grpc.netty.shaded.io.netty.handler.ssl.OpenSsl")
            buildArgs.add("--initialize-at-run-time=io.grpc.netty.shaded.io.netty.internal.tcnative.SSL")
            buildArgs.add("--initialize-at-run-time=io.grpc.netty.shaded.io.netty.internal.tcnative.CertificateVerifier")
            buildArgs.add("--initialize-at-run-time=io.grpc.netty.shaded.io.netty.internal.tcnative.SSLPrivateKeyMethod")
            buildArgs.add("--initialize-at-run-time=io.grpc.netty.shaded.io.netty.internal.tcnative.AsyncSSLPrivateKeyMethod")
            buildArgs.add("--initialize-at-run-time=io.grpc.netty.shaded.io.grpc.netty")
            buildArgs.add("--initialize-at-run-time=io.grpc.netty.shaded.io.netty.channel.epoll")
            buildArgs.add("--initialize-at-run-time=io.grpc.netty.shaded.io.netty.channel.unix")
            buildArgs.add("--initialize-at-run-time=io.grpc.netty.shaded.io.netty.handler.ssl")
            buildArgs.add("--initialize-at-run-time=io.grpc.internal.RetriableStream")
            buildArgs.add("--enable-url-protocols=http")
            buildArgs.add("-H:+AllowIncompleteClasspath")
        }
    }
}

Momento用のリポジトリの設定と依存ライブラリの追加、
そして、reflect-config用にイニシャライザの指定をします。  

Controllerの修正

ControllerクラスでMomentoへのGetとSetを行います。
MOMENTO_AUTH_TOKENの値は取得した認証トークンを指定してください。

import momento.sdk.SimpleCacheClient;
import momento.sdk.messages.CacheGetResponse;

・・・

    final String MOMENTO_AUTH_TOKEN = "Your Auth Token";
    final String CACHE_NAME = "default";
    final String KEY_NAME = "key";

・・・

    @Get("/momento") 
    public String helloMoment() {

        // init client
        SimpleCacheClient client = SimpleCacheClient.builder(MOMENTO_AUTH_TOKEN, 3600).build();
        // set value
        client.set(CACHE_NAME,KEY_NAME,"my_value");
        // get value
        CacheGetResponse getResponse = client.get(CACHE_NAME, KEY_NAME);

        return getResponse.string().orElse("NOT FOUND");
    }

コンパイル&実行

ネイティブコンパイルして Micronautでhttpサーバを起動します。

% cd /path/your/local_gr
% ./gradlew nativeCompile

・・・

% ./build/native/nativeCompile/local_gr
 __  __ _                                  _
|  \/  (_) ___ _ __ ___  _ __   __ _ _   _| |_
| |\/| | |/ __| '__/ _ \| '_ \ / _` | | | | __|
| |  | | | (__| | | (_) | | | | (_| | |_| | |_
|_|  |_|_|\___|_|  \___/|_| |_|\__,_|\__,_|\__|
  Micronaut (v3.7.3)

20:12:29.227 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 456ms. Server Running: http://localhost:8080

curlでアクセスしてみます。
MomentoへのGet、Setアクセスできてますね。

% curl http://localhost:8080/hello/momento
my_value

Summary

本稿ではMicronaut Graal ApplicationでMomentoを動かしてみました。
多少設定が必要ですが、問題なく動作することが確認できたと思います。

今後、MomentoのJava SDKが正式にGraalVM対応するかは未定ですが、
今回ためした方法で使うことができるので、
ぜひ試してみてください。

Momentoセミナーのお知らせ

クラスメソッドでは定期的にMomentoのセミナーを開催しております。
興味があるかたはぜひご参加ください。

References