読者です 読者をやめる 読者になる 読者になる

y_uti のブログ

統計、機械学習、自然言語処理などに興味を持つエンジニアの技術ブログです

Proxygen を CentOS 7 でビルドする

Proxygen という C++ 向けの HTTP ライブラリが Facebook から公開されました。公式のブログ記事と GitHubリポジトリは以下にあります。

現在のところ Ubuntu 14.04 でテストされているとのことですが、CentOS 7 でも大きな問題なくビルドできましたので、手順を紹介します。

ソースコードの取得

まず、GitHubリポジトリを clone してソースコードを取得します。

$ git clone https://github.com/facebook/proxygen.git

proxygen/deps.sh が Ubuntu 用のインストールスクリプトになっています。これを読むと、以下の手順でビルドできるようです。apt-get でパッケージを取得する部分は CentOS 7 と構成の違いがあると思いますので、そこは飛ばしてビルドを進め、./configure しながら必要なものをその都度導入する方針にします。

  1. 必要なパッケージを apt-get で取得する
  2. Proxygen が依存する FBThrift, Folly を取得してビルドする*1
  3. Proxygen 本体をビルドする

FBThrift と Folly をそれぞれ取得します。deps.sh に書かれているように、Proxygen の配下に FBThrift を、FBThrift の配下に Folly を取得します。

$ cd proxygen/proxygen
$ git clone https://github.com/facebook/fbthrift.git
$ cd fbthrift/thrift
$ git clone https://github.com/facebook/folly.git
Folly のビルド

私の環境では、Folly の依存関係のうち google-gflags と double-conversion を新たにインストールする必要がありました。それぞれソースコードを取得してインストールしておきます。google-gflags のインストール手順は以下のとおりです。Folly のビルド時には .so ファイルを要求されるので、cmake のオプションを指定して共有ライブラリを作成します。

$ cd ~/
$ wget https://github.com/schuhschuh/gflags/archive/v2.1.1.tar.gz -O gflags-2.1.1.tar.gz
$ tar zxf gflags-2.1.1.tar.gz
$ cd gflags-2.1.1
$ cmake . -DBUILD_SHARED_LIBS=ON
$ make
$ sudo make install

double-conversion のインストール手順は以下のとおりです。

$ cd ~/
$ git clone https://github.com/floitsch/double-conversion.git
$ cd double-conversion
$ cmake . -DBUILD_SHARED_LIBS=ON
$ make
$ sudo make install

Folly をビルドします。Proxygen や FBThrift の deps.sh を読む限りでは make install しないままになっているので、ここでも make するだけにします*2

$ cd ~/proxygen/proxygen/fbthrift/thrift/folly/folly
$ ./configure
$ make
FBThrift のビルド

私の環境では、FBThrift の依存関係のうち以下のパッケージを新たにインストールする必要がありました。いずれも yum でインストールできます。

  • snappy-devel
  • numactl-devel
  • python-devel

FBThrift をビルドします。./configure を実行する際には、FBThrift の deps.sh に書かれている内容に加えて、-Xlinker を指定して -rpath を設定する必要がありました*3

$ cd ~/proxygen/proxygen/fbthrift/thrift
$ autoreconf --install
$ CPPFLAGS=" -I`pwd`/folly/" LDFLAGS="-L`pwd`/folly/folly/.libs/ -Xlinker -rpath -Xlinker `pwd`/folly/folly/.libs/" ./configure
$ make
Proxygen のビルド

Proxygen をインストールします。まず Proxygen のディレクトリに移動しておきます。

$ cd ~/proxygen/proxygen

Proxygen の configure.ac では AX_PREFIX_CONFIG_H マクロが使われているのですが、GitHub リポジトリには m4/ax_prefix_config_h.m4 が含まれていないため、./configure が動作しませんでした。以下のように取得しておきます。

$ wget 'http://git.savannah.gnu.org/gitweb/?p=autoconf-archive.git;a=blob_plain;f=m4/ax_prefix_config_h.m4' -O m4/ax_prefix_config_h.m4

Autoconf Archive: ax_prefix_config_h

また、google-gflags は version 2.1 から名前空間google から gflags に変更されているのですが、Proxygen のコードでは google になっているため、これを gflags に変更します。以下の diff のように変更します。
[2014-11-11 追記] Proxygen が gflags 2.1 にも対応したので、以下のコード修正は不要になりました。

$ diff httpserver/samples/echo/EchoServer.cpp{-,}
54c54
<   google::ParseCommandLineFlags(&argc, &argv, true);
---
>   gflags::ParseCommandLineFlags(&argc, &argv, true);


deps.sh に書かれているとおりにビルドします。なお、make の際に wget で gmock-1.6.0 を取得するようになっていますので、ネットワークに接続された状態でないと make できないと思います。

$ autoreconf -ivf
$ CPPFLAGS=" -I`pwd`/fbthrift/thrift/folly/ -I`pwd`/fbthrift/" \
    LDFLAGS="-L`pwd`/fbthrift/thrift/lib/cpp/.libs/ -L`pwd`/fbthrift/thrift/lib/cpp2/.libs/ -L`pwd`/fbthrift/thrift/folly/folly/.libs/" ./configure
$ make

make check を実行するには、以下の 2 ファイルについて、先ほどの gflags の名前空間の問題を修正する必要があります。
[2014-11-11 追記] Proxygen が gflags 2.1 にも対応したので、以下のコード修正は不要になりました。

$ diff lib/test/TestMain.cpp{-,}
17c17
<   google::ParseCommandLineFlags(&argc, &argv, true);
---
>   gflags::ParseCommandLineFlags(&argc, &argv, true);

$ diff lib/ssl/test/SSLCacheTest.cpp{-,}
99c99
<   google::SetUsageMessage(std::string("\n\n"
---
>   gflags::SetUsageMessage(std::string("\n\n"
102c102
<   google::ParseCommandLineFlags(&argc, &argv, true);
---
>   gflags::ParseCommandLineFlags(&argc, &argv, true);
サンプルプログラムの実行

Proxygen をビルドすると、サンプルの HTTP サーバとして echo_server というプログラムが生成されます。以下のようにサーバを起動します。

$ ./httpserver/samples/echo/echo_server

サーバは 11000 番ポートで起動するようです。別のターミナルを開いて、ここにリクエストを投げてみます。

$ curl -v http://localhost:11000
* About to connect() to localhost port 11000 (#0)
*   Trying ::1...
* Connected to localhost (::1) port 11000 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:11000
> Accept: */*
>
< HTTP/1.1 200 OK
< Request-Number: 1
< Date: Sun, 09 Nov 2014 07:41:21 GMT
< Connection: keep-alive
< Content-Length: 0
<
* Connection #0 to host localhost left intact

レスポンスに Request-Number というヘッダがあります。echo_server では、リクエストを処理するたびに、これがインクリメントされるようになっています。続けてリクエストを投げてみると、以下のようになります。

$ curl -v http://localhost:11000 2>&1 | grep Request-Number
< Request-Number: 2
$ curl -v http://localhost:11000 2>&1 | grep Request-Number
< Request-Number: 3
$ curl -v http://localhost:11000 2>&1 | grep Request-Number
< Request-Number: 4
$ curl -v http://localhost:11000 2>&1 | grep Request-Number
< Request-Number: 5

少しだけソースコードを眺めてみます。httpserver/samples/echo/EchoHandler.cpp にリクエストハンドラの実装があります。主要な部分は以下のような実装になっています。

$ cat -n httpserver/samples/echo/EchoHandler.cpp
        ...
    23  void EchoHandler::onRequest(std::unique_ptr<HTTPMessage> headers) noexcept {
    24    stats_->recordRequest();
    25  }
        ...
    35  void EchoHandler::onEOM() noexcept {
    36    ResponseBuilder(downstream_)
    37      .status(200, "OK")
    38      .header("Request-Number",
    39              folly::to<std::string>(stats_->getRequestCount()))
    40      .body(std::move(body_))
    41      .sendWithEOM();
    42  }
        ...

onRequest と onEOM 関数のそれぞれで、stats_ の関数が呼び出されています。これらの関数は次のように定義されており、先ほどの実行例のようにリクエストの回数がカウントアップされていくことが分かります。

$ cat -n httpserver/samples/echo/EchoStats.h
        ...
    29    virtual void recordRequest() {
    30      ++reqCount_;
    31    }
    32
    33    virtual uint64_t getRequestCount() {
    34      return reqCount_;
    35    }
        ...

*1:FBThrift を取得すると、そこにも deps.sh があります。Folly を取得してビルドする手順は FBThrift の deps.sh に書かれています。

*2:Proxygen を利用するプログラムのビルドには libfolly をリンクする必要がありそうなので、make install しておかなければ結局は不便なのではないかという気もしています。まだ自分でプログラムを書いてはいないので、このあたりは未確認です。

*3:./configure では libevent が見つからないというエラーが表示されるのですが、libevent の存在をチェックするための conftest で libfolly.so を見つけられないことが原因です。