Gizzard
http://www.publickey1.jp/blog/10/twittergizzard_scalasharding.html
Gizzardてのはtwitterで使われている技術でシャーディングを行うものだそうです。
シャーディングってのはパーティショニングとレプリケーションを使ってDBを分散して管理できるようにするものらしく
githubで公開されているっと。
でも、使ってみないとよくわかりません。
ということでビルドするにも、git使ってant使ってivyだの、thriftだのいろいろ最新すぎる。
で、途中までid:nigakyさんやってくれたみたいなのでリンク。
http://d.hatena.ne.jp/nigaky/20100411
id:nigakyさん様様です。
ありがとうございます。
自分もthriftがってところでビルドできず止まってたので。
ただ、gitにあるfacebookのthriftをダウンロードした
でもapacheのthriftを使うのがいいみたいなので
でapache thriftでググるとapache版のthriftが見つかりました。
http://incubator.apache.org/thrift/download/
Thrift(スリフト)は、「スケーラブルな言語間サービス開発」のためにFacebookにて開発されたRPCフレームワークでオープンソース化されて
Apacheプロジェクトで開発されているっぽい。
http://codezine.jp/article/detail/4781
Apache Thriftチームは2009年12月11日、オープンソースのRPCフレームワーク「Apache Thrift 0.2.0」をリリースした。 Apache Thriftは異なるプログラミング言語をシームレスに接続するサービスを構築するフレームワークで、C++、Java、Python、PHP、Ruby、Erlang、Perl、Haskell、C#、Cocoa、Smalltalk、OCamlの各プログラミング言語をサポートする。 Thriftプロジェクトは2007年4月にFacebookでスタートし、2008年5月からApacheのインキュベーションとして開発されている。2009年8月に最初のバージョン0.1.0がリリースされていた。国内では、はてなブックマークがプリファードインストラクチャー製レコメンドエンジンとの連携にThriftを利用している。
ということで、けっこう前から開発されてるんですね。
こちらを使えばよいのだと思います。
サンプルアプリケーションの Rowz (http://github.com/nkallen/Rowz)のビルドはこんなかんじで出来るらしいです。
To build project: ant dist -DDB_USERNAME=fixme -DDB_PASSWORD=fixmetoo To run tests: mysql> CREATE DATABASE rowz_nameserver; % ant test -DDB_USERNAME=fixme -DDB_PASSWORD=fixmetoo To run a simple development server: % ./bin/rowz To sample the networking service: % cd src/scripts % ./gzr.rb
rowz_nameserverDBを作ってant testでそこにつないで
./bin/rowzでシンプル開発サーバを実行して
gzr.rbでネットワークサービスサンプルを試せるらしい。
なにが出来るんじゃ?ってところが問題なのでソースを読むしかない。
まず、./bin/rowzのソース
http://github.com/nkallen/Rowz/blob/master/bin/rowz
git clone git://github.com/nkallen/Rowz.git
でダウンロード
shだ。
これは実験に適した、フォアグランドにおけるRowzサーバを実行します。 使い方: rows MYSQL_USER [MYSQL_PASSWORD]
で、最初にmysqlのチェックをしたあと以下のjavaプログラムを実行するものらしい
java -DDB_USERNAME="$1" -DDB_PASSWORD="$2" -jar dist/rowz/rowz-1.0.jar -f config/development.conf
rowz-1.0.jarを実行するってことか。。。
たぶん、com.twitter.rowz.Mainが起動される。
com.twitter.rowz.Main
で、startThriftでrowzServer,jobServer,shardServerを実行するようだ。
おそらく、jobsディレクトリがjobServerの内容
thriftディレクトリがthrift周り
ということで、、、<どーいうことだ
gizzardのライブラリを使ってscalaでシャーディングの設定をゴリゴリ書いてサービスとして実行する。
クライアントからはthriftを使ってリモート呼び出しがかかる。
で、それを使うクライアントはgzr.rbでthriftを使ってリモート呼び出しでサービスを利用するっと。
gzr.rbは設定が長々続いて最後に簡単な例を入れておしまい。
とりあえず、コメントを入れただけのソースgzr.rbをここに張ります。
#!/usr/bin/env ruby # drop database rowz_nameserver; create database rowz_nameserver; $:.push(File.dirname($0)) require 'socket' require 'simple_thrift' # localhostのrowzポートは7919 shard_portは7920 HOST = "localhost" ROWZ_PORT = 7919 SHARD_PORT = 7920 # 設定 SHARD_CLASS = { :read_only => "ReadOnlyShard", # 読み込みオンリーシャード :blocked => "BlockedShard", # ブロックドシャード :write_only => "WriteOnlyShard", # 書き込みオンリーシャード :replica => "ReplicatingShard" # レプリケーションシャード } # シャード名作成 SHARD_NAME = Hash[*SHARD_CLASS.map { |a, b| [ b, a ] }.flatten] # Row の構造を作成 Row = SimpleThrift.make_struct(:Row, SimpleThrift::Field.new(:id, SimpleThrift::I64, 1), SimpleThrift::Field.new(:name, SimpleThrift::STRING, 2), SimpleThrift::Field.new(:created_at, SimpleThrift::I32, 3), SimpleThrift::Field.new(:updated_at, SimpleThrift::I32, 4), SimpleThrift::Field.new(:state, SimpleThrift::I32, 5)) # シャード情報構造作成 ShardInfo = SimpleThrift.make_struct(:ShardInfo, SimpleThrift::Field.new(:class_name, SimpleThrift::STRING, 1), SimpleThrift::Field.new(:table_prefix, SimpleThrift::STRING, 2), SimpleThrift::Field.new(:hostname, SimpleThrift::STRING, 3), SimpleThrift::Field.new(:source_type, SimpleThrift::STRING, 4), SimpleThrift::Field.new(:destination_type, SimpleThrift::STRING, 5), SimpleThrift::Field.new(:busy, SimpleThrift::I32, 6), SimpleThrift::Field.new(:shard_id, SimpleThrift::I32, 7)) # シャード子構造作成 ShardChild = SimpleThrift.make_struct(:ShardChild, SimpleThrift::Field.new(:shard_id, SimpleThrift::I32, 1), SimpleThrift::Field.new(:weight, SimpleThrift::I32, 2)) # フォワーディング構造作成 Forwarding = SimpleThrift.make_struct(:Forwarding, SimpleThrift::Field.new(:table_id, SimpleThrift::ListType.new(SimpleThrift::I32), 1), SimpleThrift::Field.new(:base_id, SimpleThrift::I64, 2), SimpleThrift::Field.new(:shard_id, SimpleThrift::I32, 3)) # シャードマイグレーション構造作成 ShardMigration = SimpleThrift.make_struct(:ShardMigration, SimpleThrift::Field.new(:source_shard_id, SimpleThrift::I32, 1), SimpleThrift::Field.new(:destination_shard_id, SimpleThrift::I32, 2), SimpleThrift::Field.new(:replicating_shard_id, SimpleThrift::I32, 3), SimpleThrift::Field.new(:write_only_shard_id, SimpleThrift::I32, 4)) # シャードマネージャーをサービス継承して作成 class ShardManager < SimpleThrift::ThriftService thrift_method :create_shard, i32, field(:shard, struct(ShardInfo), 1) thrift_method :find_shard, i32, field(:shard, struct(ShardInfo), 1) thrift_method :get_shard, struct(ShardInfo), field(:shard_id, i32, 1) thrift_method :update_shard, void, field(:shard, struct(ShardInfo), 1) thrift_method :delete_shard, void, field(:shard_id, i32, 1) thrift_method :add_child_shard, void, field(:parent_shard_id, i32, 1), field(:child_shard_id, i32, 2), field(:weight, i32, 3) thrift_method :remove_child_shard, void, field(:parent_shard_id, i32, 1), field(:child_shard_id, i32, 2) thrift_method :replace_child_shard, void, field(:old_child_shard_id, i32, 1), field(:new_child_shard_id, i32, 2) thrift_method :list_shard_children, list(struct(ShardChild)), field(:shard_id, i32, 1) thrift_method :mark_shard_busy, void, field(:shard_id, i32, 1), field(:busy, i32, 2) thrift_method :copy_shard, void, field(:source_shard_id, i32, 1), field(:destination_shard_id, i32, 2) thrift_method :setup_migration, struct(ShardMigration), field(:source_shard_info, struct(ShardInfo), 1), field(:destination_shard_info, struct(ShardInfo), 2) thrift_method :migrate_shard, void, field(:migration, struct(ShardMigration), 1) thrift_method :set_forwarding, void, field(:forwarding, struct(Forwarding), 1) thrift_method :replace_forwarding, void, field(:old_shard_id, i32, 1), field(:new_shard_id, i32, 2) thrift_method :get_forwarding, struct(ShardInfo), field(:table_id, list(i32), 1), field(:base_id, i64, 2) thrift_method :get_forwarding_for_shard, struct(Forwarding), field(:shard_id, i32, 1) thrift_method :get_forwardings, list(struct(Forwarding)) thrift_method :reload_forwardings, void thrift_method :find_current_forwarding, struct(ShardInfo), field(:table_id, list(i32), 1), field(:id, i64, 2) thrift_method :shard_ids_for_hostname, list(i32), field(:hostname, string, 1), field(:class_name, string, 2) thrift_method :shards_for_hostname, list(struct(ShardInfo)), field(:hostname, string, 1), field(:class_name, string, 2) thrift_method :get_busy_shards, list(struct(ShardInfo)) thrift_method :get_parent_shard, struct(ShardInfo), field(:shard_id, i32, 1) thrift_method :get_root_shard, struct(ShardInfo), field(:shard_id, i32, 1) thrift_method :get_child_shards_of_class, list(struct(ShardInfo)), field(:parent_shard_id, i32, 1), field(:class_name, string, 2) thrift_method :rebuild_schema, void end # ジョブマネージャーをサービス継承して作成 class JobManager < SimpleThrift::ThriftService thrift_method :retry_errors, void thrift_method :stop_writes, void thrift_method :resume_writes, void thrift_method :retry_errors_for, void, field(:priority, i32, 1) thrift_method :stop_writes_for, void, field(:priority, i32, 1) thrift_method :resume_writes_for, void, field(:priority, i32, 1) thrift_method :is_writing, bool, field(:priority, i32, 1) thrift_method :inject_job, void, field(:priority, i32, 1), field(:job, string, 2) end # Rowzをサービス継承して作成 class Rowz < SimpleThrift::ThriftService thrift_method :create, i64, field(:name, string, 1), field(:at, i32, 2) thrift_method :read, struct(Row), field(:id, i64, 1) thrift_method :destroy, void, field(:id, i64, 1) end # シャードマネージャーを作成 $service = ShardManager.new(TCPSocket.new(HOST, SHARD_PORT)) # Rowsを作成 $rowz = Rowz.new(TCPSocket.new(HOST, ROWZ_PORT)) # シャードマネージャのスキーマを再ビルド $service.rebuild_schema #パーティションは10個 partitions = 10 #キースペースは64bitくらい keyspace = 2**64 #パーティション分ループして partitions.times do |i| # data_aのシャード情報を作成 shard_info_a = ShardInfo.new("com.twitter.rowz.SqlShard", "data_a" + i.to_s, HOST, "", "", 0, 0) # data_aのシャードを作成 shard_id_a = $service.create_shard(shard_info_a) # data_bのシャード情報を作成 shard_info_b = ShardInfo.new("com.twitter.rowz.SqlShard", "data_b" + i.to_s, HOST, "", "", 0, 0) # data_bのシャードを作成 shard_id_b = $service.create_shard(shard_info_b) # レプリケーティングのシャード情報を作成 replicating_shard_info = ShardInfo.new("com.twitter.gizzard.shards.ReplicatingShard", "replicating_" + i.to_s, HOST, "", "", 0, 0) # レプリケーティングのシャードを作成 replicating_shard_id = $service.create_shard(replicating_shard_info) # レプリケーティングシャードの子にdata_aシャードを追加 $service.add_child_shard(replicating_shard_id, shard_id_a, 1) # レプリケーティングシャードの子にdata_bシャードを追加 $service.add_child_shard(replicating_shard_id, shard_id_b, 1) # キースペースをパーティションで分割してそのオフセットを取得 lower_bound = keyspace / partitions * i # フォワーディングを設定 $service.set_forwarding(Forwarding.new([], lower_bound, replicating_shard_id)) end # フォワーディングをリロード $service.reload_forwardings # 10回ループ 10.times do |i| # rowを作成 id = $rowz.create("row #{i}", Time.now.to_i) # id出力 p id # 0.1秒待ち sleep 0.1 # idをのrowzを読み込み出力 p $rowz.read(id) end
一番最後はrowzサービスの使い方だけど、その手前が毎回行うものなのか、1回行えばあとはやらなくてもいいものなのか?
使ってみないとよく分からないところだと思います。