前回の記事で、Apache Thriftを使ってRPCコードを自動生成する方法を解説しました。
今回は実際に生成されたコードを動かしてみたいと思います。Thriftを使えばいろんな言語のコードが生成できるのですが、まずは僕のメイン言語であるJavaのコードを生成して、サーバとして動かしてみたいと思います。
ちなみにThriftを解説したページを探すと、JavaサーバとしてTomcatなどのサーブレットコンテナを使わず、Thriftが用意しているTThreadPoolServerクラスなどを使ってスタンドアロンアプリとして動かす方法が多く見受けられます。
ちょっとしたテストには良いと思うのですが、本番サービスで使うには運用面などで不安があります。SSLにしたい場合はどうすればいいんでしょう?証明書は?できるのかもしれませんが、蓄積した運用ノウハウが行かせません。
それに、専用のポートをThriftのサーバで占有されるのも困ります。ロードバランサなども個別に設定する必要が出てきます。
本記事では、ThriftのサーバサイドコードをServletとして動かす方法を解説します。
Javaのコードを生成する
前回の記事の最後の部分ですが、おさらいです。
まずは、Thriftの定義ファイルを書きます。前回同様、木村俊也氏のブログ記事「Thriftが便利すぎる」からお借りします。
TinyCalc.thrift
#!/usr/bin/thrift
service TinyCalc
{
double sum(1: double param1, 2: double param2)
double subtract(1:double param1, 2:double param2)
}
メソッド名を見てお分かりの通り、2つの実数の「加算」と「減算」をするサービスを定義しています。
ではThriftでJavaのコードを生成します。
# thrift --gen java TinyCalc.thrift
上記コマンドを実行すると、gen-javaというサブディレクトリが生成され、中にTinyCalc.javaというファイルが生成されます。
Eclipseのプロジェクトを作る
たぶんみなさんEclipseを使っていると思うので、Eclipse上で動かしてみましょう。まずは、Dynamic Web Projectを生成します。プロジェクト名は thrift-test-serverとしました。
僕はEclipseのプロジェクトもMavenで管理するのが好きになってしまったので、次のようなpom.xmlをプロジェクトのルートに置き、Dependency ManagementをONにします。
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>jp.ohnaka.thrift</groupId>
<version>1.0.0-SNAPSHOT</version>
<artifactId>thrift-test-server</artifactId>
<packaging>war</packaging>
<name>Apache Thrift test Server</name>
<dependencies>
<dependency>
<groupId>org.apache.thrift</groupId>
<artifactId>libthrift</artifactId>
<version>0.6.1</version>
</dependency>
<!-- Servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>0.9.16</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>0.9.16</version>
</dependency>
<!-- Testing -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.1</version>
<configuration>
<encoding>UTF-8</encoding>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
ようは Thrift本体のjarと、Thriftによって生成されたコードが使用している slf4jのライブラリを追加しているだけです。Mavenを使っていない人はそれらの jarを手動でプロジェクトに追加してください。
プロジェクトの設定が終わったら、生成されたTinyCalc.javaをソースフォルダにいれます。生成されたコードはディフォルトパッケージに所属しているので、必要があればリファクタリングしましょう。今回はそのままにしておきます。
サーバサイドの実装を記述する
Apache Thriftがコードを自動生成してくれるといっても、あくまでインターフェースや通信制御の部分であって、実際の処理は自分で書く必要があります。
Thriftが生成してくれたTinyCalcというJavaクラスの中には、TinyCalc.Ifaceという内部インターフェースが定義されています。サーバサイドの処理は、このインターフェースを実装したクラスに書きます。
ここでは、TinyCalcHandlerというクラスにします。
TinyCalcHandler.java
import org.apache.thrift.TException;
/**
* TinyCalcのサーバサイド実装
*/
public class TinyCalcHandler implements TinyCalc.Iface {
/**
* 2つの引数を足してその値を返します。
*/
@Override
public double sum(double param1, double param2) throws TException {
return param1 + param2;
}
/**
* 1つ目の引数から2つ目の引数を引いてその値を返します。
*/
@Override
public double subtract(double param1, double param2) throws TException {
return param1 - param2;
}
}
TinyCalcServlet.java
次に、このサービスへの呼び出しを受け付けるサーブレットを用意します。最新のThriftにはTServletというクラスが用意されていて、それを継承すると簡単にサーブレットを作る事ができます。
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.server.TServlet;
/**
* TinyCalcサービスへの呼び出しを受け付けるサーブレット
*/
public class TinyCalcServlet extends TServlet {
private static final long serialVersionUID = 1L;
public TinyCalcServlet() {
// Binary Protocolを扱うサーブレットとして初期化する
super(new TinyCalc.Processor(new TinyCalcHandler()), new TBinaryProtocol.Factory());
}
}
TServletを継承したら、上記のようにデフォルトコンストラクタを実装し、TServletのコンストラクタを呼び出します。
super()の第一引数には、サービスの実処理を渡します。つまり先ほど記述したTinyCalcHanderなのですが、直接 TinyCalcHandlerを渡すのではなく、Processorでラップして渡します。ProcessorクラスもThriftによって生成された TinyCalc.Processorクラスを用います。
super()の第二引数には、プロトコルファクトリを渡します。これによってThriftのクライアントとの通信をどういうプロトコルで行なうかが決まります。プロトコルと言ってもHTTPとかFTPとかではなく、HTTPの上にどういうデータを流すか、という意味です。
Thriftが用意しているプロトコルには以下のようなものがあります。(Wikipediaより引用)
- TBinaryProtocol
- A straight-forward binary format encoding numeric values as binary. It is faster than the text protocol but more difficult to debug.
- 数値をバイナリフォーマットで直接的に表現するプロトコル。数値をテキストで表現するプロトコルよりも高速ですが、デバッグが難しくなります。
- TCompactProtocol
- Very efficient, dense encoding of data.
- とても効率的に、濃密にデータをエンコードするプロトコル。
- TDebugProtocol
- Uses a human-readable text format to aid in debugging.
- デバッグのしやすさのために、ヒューマンリーダブル(人間が読みやすい)なテキストフォーマットを用いるプロトコル。
- TDenseProtocol
- Similar to TCompactProtocol, striping off the meta information from what is transmitted.
- TCompactプロトコルにしていますが、転送データからメタ情報を除去したものです。
- TJSONProtocol
- Uses JSON for encoding of data.
- データのエンコーディングにJSONを用いるプロトコル
- TSimpleJSONProtocol
- A write-only protocol using JSON. Suitable for parsing by scripting languages.
- スクリプト言語でパースするのに適した、JSONを用いた書き込み専用のプロトコル。
今回は最初のTBinaryProtocolを用いましたが、別のものを用いても大丈夫です。ただし、クライアント側とプロトコルを合わせるのを忘れずに。
web.xml
サーブレットの 準備ができたら、実際に配備してみましょう。今回は /calcというパスに配備してみました。
web.xmlはこんな感じになります。
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>thrift-test</display-name> <servlet> <servlet-name>TinyCalcServlet</servlet-name> <servlet-class>TinyCalcServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>TinyCalcServlet</servlet-name> <url-pattern>/calc</url-pattern> </servlet-mapping> </web-app>
配備して実行する
念のため、EclipseのProject Explorerのスクリーンショットを載せておきます。
すべてのファイルが準備できたら、Tomcatなどのサーブレットコンテナに配備して起動してみて下さい。エラーが出なければひとまずOKです。
次のエントリで、実際にクライアントから繋いでみましょう。


Twitter