[HHVM/Hack] @cosme PROアプリで使っている技術

昨日のaboyくんに続きアプリ関連の記事です。

@cosme PRO for Specialist(iOS, Android)というアプリのバックエンドを担当しました、kubotakです。
当初はこのアプリには専用のバックエンドは用意せず、既存のあらゆる社内のサービスのRESTAPIとアプリがデータをやりとりする想定でした。
しかし、それには以下の問題がありました。

  • イントラネットのみのAPIがある。
  • アプリが多数のAPIホストやエンドポイントを知る必要がある
    • それらにはそれぞれ共通していない認証方式を採用している

このままではアプリ開発者への負担増が目に見えていました。
また、アプリで出したい情報の要件としてそのまま取得するには実現できないものもありました。
例えばapi:Aの結果を元にapi:Bへのパラメータとして取得する場合など。。。

ということで各APIを束ねたProxy的なAPIを開発することになりました。
ここで弊社で導入実績のあるGo言語が候補にあがりましたが、並列処理でAPIをいい感じに取得して束ねて返すのはHHVM/Hackでも良いのではないかという考えからHHVM/Hackでの開発がスタートしました。

HHVM/Hack

HHVMはHipHop Virtual Machineの頭文字で、PHP, Hack言語が実行できる仮想環境です。HackはHHVM上で動作し、PHPより型指定が強化されていて並列処理の機能も取り込まれている言語です。
どちらもFacebook社が開発しOSSとして公開されています。

ZendExpressive

HHVM3.24(2018年2月現在)ではPHPコードが実行できます。
これを利用してPHPフレームワークを導入いたしました。
ZendExpressiveというpsr-7対応のマイクロフレームワークです。
残念ながらv2系を導入したところ内部の処理でHHVMが実行できないコードがあり、断念してv1系を採用しました。
このフレームワークでは導入時に対話式にRouterやDI Containerなどを選択できるユニークな仕組みを持っています。

せっかくなので一部実装コードを紹介

final class TestAction
{
    public function __construct(private ApiClient $apiClient) {}

    public function __invoke(
        ServerRequestInterface $request,
        ResponseInterface $response,
        ?callable $next = null
    ): JsonResponse
    {
        $attributes   = $request->getAttributes();
        $param1 = (int) $attributes['param1'];

        $res = \HH\Asio\join($this->apiClient->exec($param1));

        return new JsonResponse($res, 200);
    }
}

RouterがDispatchするActionClassです。

public function __construct(private ApiClient $apiClient) {}

これはHack特有の書き方ですね。PHPに置き換えると以下のようになります

private $apiClient;

public function __counstruct(ApiClient $apiClient) {
    $this->apiClient = $apiClient;
}

短縮で記述できて最高ですね!

\HH\Asio\join()としているところで並列処理を束ねています。
これはHHVMに用意されているHackの関数となります。
定義は以下のようになっています

namespace HH\Asio;

function join<T>(
  Awaitable<T> $awaitable,
): T;

引数はAwaitable型、返り値型はGenericsとなってますね。
ですので、この引数には以下のような関数が入る想定となります。

async function exec(int $id): Awaitable<array<int, string>> {
    return [0 => 'test'];
}

このasync functionが並列処理が行える宣言となります。
HHVMの場合は協調的マルチタスキングをこのように利用することができ、I/Oやデータ取得のレイテンシ間に別の処理を実行する機構を持っています。
ですのでMySQLやAPIからデータを取得する時間を短縮することが可能です。

今回作成したアプリケーションではActionClassで並列宣言の関数をまとめて、それ以下の階層では多くがasync functionで実装しています。

ハマりポイント

Hackは一見するとPHPに構文が似ています。ですのでPHPStormで開発できそうですが、実は一切補完が効かないんです。
AtomプラグインのNuclide(Facebook社製)かVisual Studio CodeのHackプラグインがおすすめです。

また、HHVMのバグを踏み抜いてしまいました。現在も早い勢いで更新を続けているだけに、バグへの遭遇も懸念しなくてはならないかもしれません。
ちなみに、踏み抜いたバグは一つだけでそれを改善以降は安定して動いています。
このバグについてはPHPerKaigi2018のLTでお話する予定ですのでこの記事では割愛とさせていただきます。

導入後

先述していますが、現在は安定に稼働しています。特に@cosme PROアプリは承認されたユーザーしか利用できない仕組みのためトラフィック負荷も現在は多くはありません。
処理速度はProxyしているRESTAPIに依存するため一概に評価しにくいですが、やはり内部でなんどもAPIコールをしなくてはいけない処理に対しては効果を発揮していると思います。

所感

PHP7がリリース後、「そういえばそんなのあったね」「まだあったの?」なんて思われがちなHHVM/Hackですが今後はPHPの互換をやめてHackを拡張させていく方針だそうです。
よく考えてみるとかなりのトラフィックを抱えるサイトで導入実績があるので今一度PHP7と比較してみるのも良いかもしれません。
マイクロサービスなどと言われる昨今ですが、まさにHHVM/HackはPHPのような開発速度が期待できデータベースやRESTAPIを並列でコールできる仕様は向いてるのではないかと思います。

弊社でもHHVM/Hackは新規アプリケーションで導入検討をしているところです。
一緒にHHVM/Hack、PHPを盛り上げてくれるメンバーも募集中です!

アイスタイルのヘアカラー担当 元デザイナーの中途4年目です PHP, JavaScript, Golang, Scala, etc