JavaのシリアライザであるKryoで圧縮設定を追加する

エキサイト株式会社メディア事業部エンジニアの佐々木です。以前Kryoを用いてJava内のデータをシリアライズする記事を書きました。

tech.excite.co.jp

その後、弊社内でもポツポツKryoを使っていますが、Kryo公式にシリアライズデータをさらに圧縮する方法があるのでご紹介します。

前提

前回のものと同じ条件にします。

$ java --version
openjdk 17.0.2 2022-01-18
OpenJDK Runtime Environment Temurin-17.0.2+8 (build 17.0.2+8)
OpenJDK 64-Bit Server VM Temurin-17.0.2+8 (build 17.0.2+8, mixed mode)

$ ./gradlew --version

------------------------------------------------------------
Gradle 7.6.1
------------------------------------------------------------

Build time:   2023-02-24 13:54:42 UTC
Revision:     3905fe8ac072bbd925c70ddbddddf4463341f4b4

Kotlin:       1.7.10
Groovy:       3.0.13
Ant:          Apache Ant(TM) version 1.10.11 compiled on July 10 2021
JVM:          17.0.2 (Eclipse Adoptium 17.0.2+8)
OS:           Mac OS X 12.5 aarch64

前回のコード

前回のコードは下記になります。(多少差分が見やすいように変更を加えています。)

サンプルで用意したデータモデルをKryoを通してシリアライズとデシリアライズするというものになります。

public class KryoSample {

    public void main(){

        Kryo kryo = new Kryo();
        kryo.setRegistrationRequired(false);
        kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        DataModel dataModel = new DataModel();
        byte[] bytes = serialize(kryo, outputStream, dataModel);

        DataModel deserializeDataModel = deserialize(kryo, bytes);
        System.out.println("Kryo Serialize and Deserialize consistency: " + dataModel.equals(deserializeDataModel));

    }

    private DataModel deserialize(Kryo kryo, byte[] bytes) {
        try (Input input = new Input(new ByteArrayInputStream(bytes))) {
            DataModel dataModel = kryo.readObject(input, DataModel.class);
            return dataModel;
        }
    }

    private byte[] serialize(Kryo kryo, ByteArrayOutputStream outputStream, DataModel dataModel) {
        try (Output output = new Output(outputStream)) {
            kryo.writeObject(output, dataModel);
            output.flush();
        }
        byte[] bytes = outputStream.toByteArray();
        return bytes;
    }

}

圧縮処理を追加したコード

圧縮処理を追加する場合は下記のようなコードになります。

public class KryoSample {

    public void main(){

        Kryo kryo = new Kryo();
        kryo.setRegistrationRequired(false);
        kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        DataModel dataModel = new DataModel();
        byte[] bytes = serialize(kryo, outputStream, dataModel);

        DataModel deserializeDataModel = deserialize(kryo, bytes);
        System.out.println("Kryo deflate Serialize and Deserialize consistency: " + dataModel.equals(deserializeDataModel));


    }

    private DataModel deserialize(Kryo kryo, byte[] bytes) {
        try (InflaterInputStream inflaterInputStream = new InflaterInputStream(new ByteArrayInputStream(bytes));  // 圧縮データを解凍するためのInflaterInputStreamを通す
             Input input = new Input(inflaterInputStream)) {
            DataModel dataModel = kryo.readObject(input, DataModel.class);
            return dataModel;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private byte[] serialize(Kryo kryo, ByteArrayOutputStream outputStream, DataModel dataModel) {
        try (
                DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(outputStream);  // データを圧縮するためのDeflaterInputStreamを通す
                Output output = new Output(deflaterOutputStream)) {
            kryo.writeObject(output, dataModel);
            output.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        byte[] bytes = outputStream.toByteArray();
        return bytes;
    }

}

Kryoが用意してくれている圧縮・解凍用のInputStream/OutputStreamを通すだけになります。チェック例外が投げられるので、try-catchをする必要があります。

テスト実行

上記を実行します。

Kryo Serialize and Deserialize consistency: true
Kryo deflate Serialize and Deserialize consistency: true

上記の通り、圧縮・解凍処理の方も正常に実行されています。

サイズの比較

圧縮率等を比較するために約1000文字のテキストを使用して比較します。

original size:2974
deflate size:1207

約60%サイズダウンになっています。データにもよりますけど、ネットワーク転送量などがこのくらい下がるとお財布にも優しそうです。

まとめ

今回は、Kryoで簡単にデータ圧縮を行う方法をご紹介させていただきました。弊社でよく利用しているSpringBootですとJacksonでシリアライズすることが多いですが、JSONデータにして保存するのでサイズはKryoの通常とあまり変わらない印象です。Kryoだと少ない手間で圧縮・解凍ができ、CPU使用率もほとんど増えないので、弊社での導入も継続的に行おうと思います。

最後に

エキサイトではフロントエンジニア、バックエンドエンジニア、アプリエンジニアを随時募集しております。長期インターンも歓迎していますので、興味があれば連絡いただければと思います。

募集職種一覧はこちらになります!(カジュアルからもOK) www.wantedly.com