GoでThriftを試した。 Thrift自体が初めてだったがチュートリアル・サンプルが準備されていて簡単に利用できた。

目次

tl;dr

Thrift はRCPを提供するミドルウェアだ。 (複数言語間を繋げる目的があり複雑なデータ構造を渡す事は考えられていない。また参照渡しなどRPCの難しい問題は避けている感じがある。)

Thfiftで責任を持つのはISO参照モデルでのプレゼンテーション層とセッション層。 この2層はThrift Architecture ではTProtocol,TTransportに当てはまりコンポーネントを差し替えられる様になっている。

Thrift Architecture

気になった事

  • TTransport でフレーミング実装がありTCPハンドシェイクを減らせる
  • 公式Goライブラリでノンブロッキングサーバーがサポートされてない
    • 1コネクション内で処理を多重化するのは難しい
      • goroutine で対応できる可能性はある
  • 負荷分散してくれるクライアント実装を探す必要がある
    • VIPによる負荷分散はワークロードで分散できず、フレーミング利用時に注意が必要
  • 言語間の差異を吸収し辛い機能はサポートされていない
    • 構造体継承, ポリモーフィズム, オーバーロード, heterogeneous containers, Null return
    • 参照渡し, unsigned int

読んだ資料

チュートリアルを試す

インストール

チュートリアルみたいにwget などソースからやるのが面倒なのでlinuxbrewのお世話になる。

$ brew install thrift
$ go get git.apache.org/thrift.git/lib/go/thrift

コードジェネレート

GOPATH 配下では相対パスを利用できない事と、tutorial, shared 間で相対パスを一律で与える事が難しいため以下の様に生成するコード配置を意識して pacakge_prefix を与える必要がある。

$ thrift -o ./pkg/ --gen go:package_prefix=github.com/masu-mi/test/pkg/gen-go/ shared.thrift
$ thrift -o ./pkg/ --gen go:package_prefix=github.com/masu-mi/test/pkg/gen-go/ tutorial.thrift

チュートリアルのコードを取ってくる

go サンプルから取ってこれる。

実際に走らせる

$ go build .
$ nohup ./test -server &
$ ./test

チュートリアルコードの説明

Thrift network stackがプロトコルスタックがわかる。 4レイヤーで構成される。

レイヤー 役割
Server 下位コンポーネントの管理を行う
Processor 通信のInput/Outputのやりとりを担当する
Protocol データのシリアライズなどを行う
Transport 下位レイヤ通信(TCPなど) を隠蔽する

このうちProcessor部分がIDLにより生成され、実処理を行うハンドラをプログラマーが実装する。 他のレイヤーはThfirtライブラリに含まれているのを利用する。 コンポーネントは差し替えられるようになっている。 同様の図はチュートリアルにもあったが少しイメージが違うので気にかかる。

Thrift Architecture

チュートリアルとの対応

server定義箇所

server.goにある。 抜粋すると以下な感じのコードになっている。

func runServer(transportFactory thrift.TTransportFactory, protocolFactory thrift.TProtocolFactory, addr string, secure bool) error {
	transport, _ = thrift.NewTServerSocket(addr)
	handler := NewCalculatorHandler()
	processor := tutorial.NewCalculatorProcessor(handler)

	server := thrift.NewTSimpleServer4(processor, transport, transportFactory, protocolFactory)
	server.Serve()
}

アドレスを元にソケットを作成している(NewTServerSocket)。 次にコードジェネレートしたNewCalculatorProcessor()に自作したハンドラを渡してプロセッサを完成させる。

最後にNewTSimpleServer4()に上記2つとtransportFactory, protocolFactoryを渡しサーバーを完成させServeメソッドでホスティングを完了している。 transportFactory,protocolFactoryはアーキテクチャで出てきた2レイヤに対応している。 この部分は外部から与えているように差し替え可能。 チュートリアルのmain.goからどう使うかは判断できる。

Protocol, Transport の設定方法

チュートリアルではオプションによりProtocolが変えられるように書かれている同じくTransportも変えられる。 当然だけどClient,Serverの両方で同じProtocol,Transportを利用しないと上手く機能しない。

Transportにはフレーミングをサポートしたもの(TFramedTransport)が存在し別のTTransportFactoryを受け取り機能するようになっている。 フレーミングでコネクションを使い回す実装はハンドシェイクを避けられるため効率良さそうに思える。

しかし負荷分散は考えにくくなる。 L3DSRなどVIP構成を行うとコネクション単位で分散できるが実際のワークロードで分散が簡単には出来ない。 そのため複数サーバーに分散するクライアント実装があるかは調べたい。

またフレーミングをTransportで採用してもTNonblockingServerが公式(git.apache.org/thrift.git/lib/go/thrift)には見当たらない。 そのためgolanlg でサーバー実装するとフレーミングを活かせなさそう。 ただメンテされていない google code内のthrift-go には TNonblockingServerが存在する。 読んでみると以下の様にgoroutineを使うか否かの違いだけなので検証して問題がなければ公式にパッチを送るのを検討しても良さそう。