Finagleハッカソンへ行ってきたっ! #finagle_hack
Scalaを始めて数ヶ月、より深くScalaを学ぶためにTwitter製RPCフレームワークであるFinagleで遊び倒そうの会へ参加してきました。
まったくのFinagle初心者の私でしたが、水島さん(@kmizu)のわかりやすい資料や中村さん(@gakuzzzz)の解説のお陰でFinagleは触って遊ぶことができました!
ありがとうございました(´∀`)
また、会場を提供してくださったTwitter Japanの山本裕介さん(@yusukey)にも良くしていただいて、至れり尽くせりでした!
沢山のノベルティグッズも頂きました!
以下、ハッカソンを通じて学んだことのメモみたいなものφ(`д´)メモメモ...
Finagle(ふぃねーぐる)とは、Twitter製の非同期RPCフレームワークで、RPCのシステムを作るためのフレームワーク。
※ Apache Thriftのように、それ自体がRPCを提供するわけではない
- 様々なプロトコルのServerとClientを簡単に作れる。
- JSONのメッセージングとか簡単に作れちゃう。
- FinagleはだいたいScalaで書かれている。
- JVM上で動作する言語からも利用できるが基本的にScalaがオススメ
Twitter社が出しているEffective Scalaでは以下のように書かれている
Twitterにおいて、最も重要な標準ライブラリはUtilとFinagleだ。Utilは、ScalaやJavaの標準ライブラリの拡張という位置付けで、それらに欠けている機能やより適切な実装を提供する。Finagleは、TwitterのRPCシステムで、分散システムの構成要素の中核だ。
Finagleのアーキテクチャ
- 中ではNettyが動いている
- NettyはJava NIOをラップしている
- ノンブロッキングなI/O操作ができる
- FinagleはJBoss Nettyをラップして使いやすくしたもの
- Filter, Codec, Serviceという概念がある
Twitter Finagle server as an alternate HTTP ser... - Scala Resources - Quora
Finagleの要素
重要な要素はFuture, Service, Filter, Codecの4つ!
Futureクラス
class Future[+A]{ def apply():A }
Service
Finagleにおけるロジックを実装する部分
class Service[-Req,+Rep] extends (Req) => Future[Rep]{ def apply(request:Req):Future[Rep] }
- Req型を受け取ってFuture[Rep]型を非同期に返す関数
- Server
- Serverは任意のプロトコルに任意のServiceを結びつける
//EchoServerの例 val server:Server = ServerBuilder() .codec(Http()) .bindTo(new InetSocketAddress(8000)) .name("ServerName") .build(service)
- Client
- Clientは任意のプロトコルのServiceそのもの
//EchoClientの例 val client: Service[HttpRequest, HttpResponse] = ClientBuilder() .codec(Http()) .hosts(new InetSocketAddress("localhost",8000)) .build()
Filter
FilterはServiceに対して何か変換を行ったりする
class Filter[-ReqI, +RepO, +ReqO, -RepI] extends(ReqI, Service[ReqO, RepI])=>Future[RepO] { def apply (request: ReqI, service: Service[ReqO, RepI]): Future[RepO] }
Codec
- Codecは、NettyにPipelineと密接な関係がある
- Codecは、各プロトコルのencodeとdecodeを司るNettyとのつなぎ役
- Finagleに標準でサポートされているCodec
- HTTP
- Stream(Http chunked transfer encoding)
- Memcached
- protocol buffer
- Redis
- Thrift
- Kestrel
trait Codec[Req,Rep] //Httpの例 val client: Service[HttpRequest,HttpResponse] = ClientBuilder() .codec(Http()) .hosts(address) .build() //Memcachedの例 val client: Service[Command, Response] = ClientBuilder() .codec(Memcached()) .hosts(address) .build()
その他の機能
- Server Support
- Backpressure
- Service Registration
- Native OpenSSL bindings
- Client Support
- Connection Pooling
- Load Balancing
- Failure Detection
- Failover/Retry
- Distributed Tracing(a la Dapper)
- Service Discovery(e.g., via Zookeeper)
- Rich Statistics
- Native OpenSSL bindings
- Sharding
Hello World
- Echoサーバ/クライアントでHello Worldを出力
Server側:HelloServer.scala
import com.twitter.util.Future import com.twitter.finagle.Service import com.twitter.finagle.builder.Server import com.twitter.finagle.builder.ServerBuilder import java.net.InetSocketAddress object HelloServer { def main(atgs:Array[String]){ val service = new Service[String,String]{ def apply(request: String) = Future.value(request) } val server:Server = ServerBuilder() .codec(StringCodec) .bindTo(new InetSocketAddress(8080)) .name("TestServer") .build(service) } }
Client側:HelloClient.scala
import com.twitter.finagle.Service import com.twitter.finagle.builder.ClientBuilder import java.net.InetSocketAddress object HelloClient { def main(args:Array[String]){ val client:Service[String,String] = ClientBuilder() .codec(StringCodec) .hosts(new InetSocketAddress("localhost",8080)) .hostConnectionLimit(1) .build() client("Hello, Finagle!!\n") onSuccess{ result => println(result) }onFailure { error => error.printStackTrace() }ensure{ println("end") client.release() } } }
実行結果
Hello, Finagle!!