こんにちは、サーバーサイドエンジニアの佐々木です。
現在、私の所属しているITイノベーション3部では、私を含めた一部のメンバーでアットコスメでDBに保持しているデータの新たな持ち方や、データ間のひも付け方を検証・検討しており、それにともなって必要となりそうなシステムや、ミドルウェア、ストリーム処理の仕組みなどを、GoやScalaなどを用いて開発しています。
今回、そういった開発を繰り返している中で、タイトルの通り、snowflakeと呼ばれるID生成の仕組みをベースにしたユニークID採番機としてiceflakeというものを開発し、Githubの方に公開いたしましたのでご紹介しようと思います。
github.com/istyle-inc/iceflake
snowflakeとは
数年前からsnowflake方式のID生成については度々話題になることがあったかと思いますので、snowflake自体の説明は不要かとも思いますが、一応ご説明させていただきます。
snowflakeというのは、4年ほど前までTwitter社が公開していたユニークID生成機のことで、基準となる日時からのエポックタイムを利用したタイムスタンプ値と、採番機の動作するマシンID、マシンIDごとのシーケンスIDを元に生成した64ビットのUnsignedInt値をID値とする仕組みでした。
※ 現在では、リポジトリ自体からはソースコードは外されていますが、アーカイブされたものを入手することはできます。
iceflakeについて
そして今回開発したiceflakeですが、以下のような特徴があります。
- Go製
- UNIXドメインソケット経由でのみ採番機にアクセス
- 採番機ークライアント間の値の受け渡しにはProtocol Buffersを利用
以下それぞれ簡単に解説していきますと、
Go製
言語の選定という観点では、アイスタイルで最も広く利用されているサーバーサイドの言語はPHPとなりますので、OSSとして公開するアプリケーションとして本来であればPHPを選択するというのが自然な判断と言えるかもしれません。
しかしながら、私自身はアイスタイルに入社してからほぼGoで実装してきまして、同時に社内勉強会等、地道な布教活動も続けてきており、そのかいあってか最近では徐々にではありますが他部署にもGo導入の流れが生まれてきており、少数の人間だけしかメンテできないという状況にはないであろうと言える状況になってきました。
また採番機自体はシンプルに動作し、スケールが容易いものであるべきと考えており、そういった事情から、今回はバイナリへのビルドを行ってしまえばその後のデプロイなどの取り回しが容易なGoでの実装を行っています。
※ ちなみに、Goでsnowflakeを実装しているアプリケーションにはカヤックさんのkatsubushiもあり、こちらは非常に高速に動作するので素晴らしいアプリケーションなのですが、そこまで手間でもないのでどうせなら自分たちで作ったほうが面白いし技術的に学びがありますので新しく作ることにしました。
UNIXドメインソケットで動作
iceflake自身はワーカーIDを引数に渡すことで一つのサーバーでも複数プロセス動作させることが可能なので、iceflake自体にはTCPで通信する術を持たず、TCPポートを開いて他サーバーのWebアプリケーション等からの接続を受け付け、採番したデータを返却するためのアプリケーションは別途用意する形にし、分散しスケールさせたい場合はマシン自体を増やすかマシン内にプロセスを増やすことで対応する想定で開発しています。
Protocol Buffers
採番機とクライアントとの採番したIDの受け渡しにはProtocol Buffersを利用しています。
これは、単に私が使ってみたかっただけ採番IDだけの受け渡しにとどまらない可能性も当初あったという事と、ProtocolBuffersを利用した実装を一度やってみることで、同様の仕組みにすぐに転用できそうだったので、相談の上このアプリケーションで一度やってみることにしました。
利用側の実装コストも本来は発生するのですが、利用元については現状Goで開発されたアプリケーションからのみであったので、Goで取得するためのクライアントコードを同梱することでひとまず問題とはならないかなと考え、このようにしています。
本来的にはもうちょっと大きなデータ定義を受け渡す場合にはよりパフォーマンスが顕著になってくるとは思いますが、今回はIDのみ受け渡すのでパフォーマンス的にはそのまま送ってしまったりJSONに変換して送ってしまったりするのとそこまで大差ないかなという印象です。
利用方法
まずはマシン上で採番機を動作させる方法ですが
インストールは以下を実行するか、Githubのリポジトリからリリースの最新のものをダウンロードしてきてください。
go get -u github.com/istyle-inc/iceflake/...
iceflakeコマンドに以下の引数を渡すことで実行可能です
- ワーカーID(-w)
- Unixソケットのパス(-s)
すなわち、/var/run/iceflake.sockにてワーカーID:1のプロセスを起動するには
iceflake -w 1 -s /var/run/iceflake.sock
とします。
次に、クライアントを利用して上記の採番機からIDを取得するには以下のようなコードを書きます。
package main
import (
"fmt"
"github.com/istyle-inc/iceflake/client/iceflake"
)
func main() {
client := iceflake.NewClient("unix", "/var/run/iceflake.sock")
flake, err := client.Get()
if err != nil {
fmt.Println(err)
return
}
fmt.Println(flake.GetId())
}
これでユニークなIDが利用可能になるはずです
おわりに(+ちょっとだけ告知)
いかがでしたでしょうか。
今回ご紹介したiceflakeは、今後本番運用されていくものですので、本番環境での導入における知見や本番でのベンチマーク結果などについてはまた別途ご紹介したく思います。
アイスタイルでは、新しい技術、自身が経験したことのない技術であっても恐れずにアプリケーションを通して未来を作っていく仲間を随時募集中です。
ご興味のある方はこちらからお気軽にご応募ください、カジュアル面談等も私達エンジニアで柔軟に対応しております!
もう少し告知なのですが、
来る 4/17(火)に弊社アイスタイルの会議室を利用して、GoのLT大会を開催する予定です。
Go(Un)Conference(Goあんこ) LT大会 1kg
内容としては、Goに関するゆるふわLT大会にしようとしておりまして、Goやったことの無い方でも、ご興味さえあればどしどしご参加頂きたいなと思っております。
GoCon、GolangTokyoなど、Go関連のメジャーなイベントが続いたあとでの開催となり、皆さんお疲れのところかとは思いますが、両イベントに抽選漏れしてしまった方やもうちょっと軽い気持ちで発表してみたいなーなど思っておられる方、ビールと軽食をたしなみながらにGoの話をしたい方、お気軽にご参加ください!まだまだお席がございます!!