Rustと非同期ライブラリーTokioで作成した簡易版Redisを紹介

2021年7月12日(月)
松下 康之 - Yasuyuki Matsushita
メモリーセーフなRustと非同期処理ライブラリーTokioで作成した簡易版Redisのコードを紹介する。

メモリーセーフなプログラミング言語Rustは、その特性からシステムプログラミングに向いているとされている。Rustの非同期処理の実装はTokioというライブラリーによって行われている。今回は、AWSのイベントRe:InventからAWSのエンジニアによるRustとTokioに関するセッションを紹介する。

動画:AWS re:Invent 2020: Next-gen networking infrastructure with Rust and Tokio

セッションのタイトルは「Next-gen networking infrastructure with Rust and Tokio」というもので、AWSのエンジニアCarl Lerche氏とSean McArthur氏が登壇した。前半のRustに関する概略やメモリーセーフという特徴などの紹介をLerche氏が担当し、後半のキャッシュとして使われるRedisをmini-redisとしてRustで書き直すというチュートリアルは、McArthur氏が担当している。

あらためてRustについて解説

セッションの前半はLerche氏が担当

セッションの前半はLerche氏が担当

パブリッククラウドのトップであるAWSがRustを有望なプログラミング言語として見なしているのは、このセッションがRe:Inventで行われていることからも理解できる。AWSに限らずMicrosoftも積極的にRustを使っていこうという姿勢を見せているし、GoogleもRust Foundationに創立メンバーとして名前を連ねている。このことから、GoogleもGoに並んでRustを有望なシステムプログラミング言語として意識していることがわかる。ちなみにMicrosoftがRust Foundationに参加したことは、Microsoftの公式ブログでも紹介されている。

参考:Microsoft joins Rust Foundation

「性能を犠牲にせずに信頼できるネットワークアプリを構築する」のがRust

「性能を犠牲にせずに信頼できるネットワークアプリを構築する」のがRust

Lerche氏は、ネットワーク処理が必要なアプリケーションを開発する際に「信頼性と同時に高速に稼働する性能が必要である」としてJavaやGoなどを開発言語の候補として挙げたが、Rustをホビー用の開発言語として使ってみたところから、Rustを評価するようになったと解説した。

Java、Goなどを検討したがRustをメモリーセーフと高速性で評価

Java、Goなどを検討したがRustをメモリーセーフと高速性で評価

Rustの特徴を紹介するスライドでは、Rustのデータオーナーシップ(所有)にフォーカスして解説を行った。

Rustの特徴である所有権について解説するLerche氏

Rustの特徴である所有権について解説するLerche氏

変数によってデータが所有され、結果としてデータは明確に「誰(変数や関数)がそれを所有しているのか?」が常に確定されることで、メモリーアクセス違反が起こらない構造であることを強調した。

ソースコードを使ってデータの所有権について解説

ソースコードを使ってデータの所有権について解説

ここではtake_ownershipという関数を定義し、その引数としてfooを使うが、fooが関数内で利用された後は解放されるために、その後同じ関数内でfooを使おうとしても、すでに解放されているために使えないというエラーを、コンパイラーが返すということを解説している。関数が実行され、そのスコープが終了したと同時に変数は解放される仕様であるため、「データを誰がいつまで所有しているのか?」をRustのコンパイラーが厳密にチェックしているという例だ。

非同期ライブラリーであるTokioの紹介

非同期ライブラリーであるTokioの紹介

Lerche氏は、Tokioのコントリビューターの中では最も多くのコミットを行っているエンジニアである。元々はRustでデータベースを作ろうとしたが、Rustの機能には非同期処理を行うライブラリー//ランタイムがなかったため、いわゆるYak Shaving(「ヤクの毛を刈る」という言い回しはある目的を達成するために、その他の多くのことを先にやる必要がある状態を指す)として非同期通信機能であるTokioを開発したことを解説した。

この例ではループの中でプロセスがSpawnされ、非同期にテキストを書き出す処理を行っている。ソケットそのものを閉じる処理がなくても、関数の終了時にRustがデータを解放することでソケットを閉じる処理が行われることを示している。

Tokioの機能の概要

Tokioの機能の概要

TokioはTCP、UDP、UNIX Socketなど非同期通信を行うための必要な機能を提供していることを紹介。

さらにasync/awaitの解説として、hello_worldという関数のソースコードを使って解説。このシンプルなコードがTokioのライブラリーではどのような記述になるのか? を解説したのが次の2枚のスライドだ。

シンプルな非同期処理のコード

シンプルな非同期処理のコード

非同期処理の中身を解説

非同期処理の中身を解説

Tokioのランタイムの構造について解説したスライドでは、複数のWorkerがタスクを非同期に処理することを紹介。先ほどの例で言えば、SpawnされたタスクがこのWorkerの中で処理されているということになるのだろう。

Tokioの構造を解説

Tokioの構造を解説

またデータベース処理に必要なデータをタスク間で共有する処理についても、Spawnされたタスクからデータをmoveする処理ではRustのコンパイラーがエラーを返すという例を使って解説。ここでもRustが持つ厳密なデータ所有権の機能が効果を発揮していると言える。

Rustのコンパイラーのエラーメッセージはわかりやすい

Rustのコンパイラーのエラーメッセージはわかりやすい

Rustのエコシステムの中ではCargoというビルドツールが非常に使いやすいことは知られているが、Rustのコンパイラーもデベロッパーフレンドリーであることは特記すべきポイントだろう。

そしてこの部分のまとめとしてRustは安全で高速、コンパイル時にメモリー関連のチェックが行われるという特性を解説して、後半であるmini-redisを作るというパートを担当するMcArthur氏に交替した。

Rustのまとめ

Rustのまとめ

Redisの簡易版mini-redisをRustで作る

後半を担当するMcArthur氏も、Tokioのコントリビューターとして活動しているAWSのエンジニアだ。ここでは非同期処理の例としてRedisを挙げて、そのコアの機能をRustとTokioで書くとどういうコードになるのか? を検証し、それを教育として使うためのプロジェクトとしてmini-redisを開発したという。

参考:tokio-rs/mini-redis: Incomplete Redis client and server implementation using Tokio

mini-redisを解説するMcArthur氏

mini-redisを解説するMcArthur氏

最初に、インメモリーのキーバリューストアデータベースであるRedisの特性を解説した。キャッシュとして使われることの多いRedisだが、C言語で書かれていること、多くのプロセスから非同期並列に接続され、データの書き込み、検索、読み込みなどの機能を満たしていることを説明した。

mini-redisの解説。Redisのサブセットの機能をRustとTokioで実装

mini-redisの解説。Redisのサブセットの機能をRustとTokioで実装

その上で今回のプロジェクト、mini-redisの概要を紹介した。mini-redisはRedisを置き換えるものではなく、あくまでもRustによる非同期通信をデベロッパーに理解してもらうための教育が目的であると解説した。

実際のコードは、上記のGitHubのリポジトリーを参照してほしい。前半のLerche氏によるデータを複数のプロセスで共有するために必要な処理について、クライアントからのコネクションを処理するサーバー側のコードを使って紹介した。

インメモリーデータベース作成するコードの例

インメモリーデータベース作成するコードの例

データベースを構造体として定義する際にMutexを追加して排他制御の実装が必要であることを解説。他にも、Arcというスマートポインターを使ってデータを共有する仕組みを使っている。

db構造体にMutexを指定

db構造体にMutexを指定

RustにおけるArcについては以下のQiitaの記事が参考になるだろう。

参考:Rustの `Arc` を読む(1): Arc/Rcの基本

次のスライドでは標準のMutexについて解説を行っている。

Mutexについて解説

Mutexについて解説

ここからインメモリーデータベースへのデータ書き込み、読み込みのソースコードを解説し、Rustであれば簡潔に記述できることを説明した。

GETの例

GETの例

SETの例

SETの例

最後にRedisとmini-redisでSET、GETのベンチマークを実施し、C言語で書かれたRedisとほぼ同様の性能を出していることを解説した。ここではメモリーセーフでありながら高速に実行できるRustとTokioの組み合わせが、安全性と高速性を両立したソフトウェアを開発できるという例として強調された形になった。

Redisとmini-redisの性能比較。ほぼ同等と言える

Redisとmini-redisの性能比較。ほぼ同等と言える

McArthur氏は、RustとTokioの組み合わせによって安全で高速なソフトウェアが実現できることを再度紹介して、セッションを終えた。GitHubのmini-redisのリポジトリーのソースコードを見れば、多くのコメント行によって初めてコードを見るデベロッパーへの教育という目的を目指していることがわかる。

mini-redisのようなコードがあれば、RustとTokioによるリアルなアプリケーション開発に初めて取り組むエンジニアにとっても参考になるだろう。

安全で高速なソフトウェアを実現できるRustとTokio

安全で高速なソフトウェアを実現できるRustとTokio

著者
松下 康之 - Yasuyuki Matsushita
フリーランスライター&マーケティングスペシャリスト。DEC、マイクロソフト、アドビ、レノボなどでのマーケティング、ビジネス誌の編集委員などを経てICT関連のトピックを追うライターに。オープンソースとセキュリティが最近の興味の中心。

連載バックナンバー

サーバー技術解説

Tigeraのアドボケイトが、x86とARMのマルチアーキテクチャークラスターを解説

2022/4/7
ARMの優位性を解説しながら、ARMをx86クラスターに追加するマルチアーキテクチャークラスターを、デモを用いて解説。
システム開発イベント

KubeCon NA 2021からサービスメッシュの2セッションを紹介

2022/3/18
KubeCon NA 2021からサービスメッシュのLinkerdの最新情報とIstioを使ったユースケースのセッションを紹介する。
セキュリティイベント

KubeCon NA 2021、ソフトウェア開発工程のタンパリングを防ぐSLSAを解説

2022/3/9
KubeCon NA 2021のプレカンファレンスから、ソフトウェアサプライチェーンを実装するフレームワークSLSAを取り上げたセッションを紹介する。

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています