HHVM/Hack 運用時の設定について

# HHVMの設定について理解しよう！
HHVMを商用環境や、または導入にあたってただ使うだけでは障害発生時や、
パフォーマンスチューニング等の対応ができなくなってしまいます。
実際に導入するにあたって、設定の項目などをみていきます。

## HHVM / PHP information
利用する方、している方はご存知のコマンドですが、一応・・。
PHPの場合は、CLIで次のコマンドでPHPの情報が表示されるのはみなさんご存知だと思います。

```bash
$ php -i
```

HHVMの場合は、以下のコマンドです。

```bash
$ hhvm --info
```
これから紹介していく項目の設定内容はこのコマンドで確認しながら進めていくといいでしょう。

## PHP7互換モード
3.30以降のバージョンでは使うことがなくなると思いますが、
現在のHHVM環境でPHP7互換で動かしたい場合に指定します。
これを指定する場合は、HHVMのphp.iniに指定します。
PHP7のライブラリをどうしても使いたい、という時に利用するくらいです。
scalar型もPHP互換にできますが、Hackと同じではなく、あくまでPHPのものになります。
PHP7の互換を保ちつつ、scalar型はPHP7互換にしたくない場合などは下記の設定にするといいでしょう。

```ini
hhvm.php7.all = true
hhvm.php7.scalar_types = 0
```

## Server Mode
HHVMはProxygenかFastCGIが選択できます。
それぞれの違いをみていきましょう。
ちなみに結構違います。

### FastCGI
PHP環境に慣れていて、あまり環境を変えたくない場合はFastCGIの方が安全かもしれません。
FastCGIで動かしたい場合は、HHVMのphp.iniで下記の設定を記述します。

```ini
hhvm.server.port = 8080
hhvm.server.type = fastcgi
```

`hhvm.server.port`　は、デフォルトだと80になりますので注意が必要です。
`hhvm.server.type`　は、デフォルトはfastcgiではないため、こちらもPHP-FPM同様に利用したい場合は忘れずに指定してください。

nginxを利用する場合は、confは下記の様に記述しましょう。

```conf
    location ~ \.(hh|php)$ {
        fastcgi_pass   127.0.0.1:9001;
        fastcgi_index index.php;
        fastcgi_read_timeout 120; 
        fastcgi_buffers 16 16k;
        fastcgi_buffer_size 32k;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;
        include fastcgi_params;
    }
```

PHPとそんなに変わりませんね

### proxygen
コマンドでも簡単に利用できるサーバで、PHPのビルトインWebServerとは異なり、
Facebookが数年前にHTTPフレームワーク/サーバProxygenを[オープンソース化](https://jp.techcrunch.com/2014/11/06/20141105facebook-open-sources-its-proxygen-http-framework-and-server/)したものです。
[facebook/proxygen](https://github.com/facebook/proxygen)

Apache HTTP ServerなどのWebServerの代替というわけではありませんが、
アプリケーションサーバとして利用できるもの、と考えた方がいいでしょう。

例として、index.phpに次のコードを利用します。

```hack
<?hh // strict

<<__Entrypoint>>
function main(): void {
  echo 'HHVM/Hack';
}
```

`<<__EntryPoint>>`はこれまでも紹介の中でも登場してきたAttributeです。
(HHVM3.30で `<<__Entrypoint>>`から`<<__EntryPoint>>`に)
これを実行すると　`main()` が実行される、というものですね。
proxygenとしてコマンドで手っ取り早く動かしたい場合は、以下の様に実行できます。

```bash
$ hhvm -m server -p 8080 -d hhvm.server.source_root=/Path/To/HackProject/public -d hhvm.server.type=proxygen
```

ブラウザ等で `http://127.0.0.1:8080` にアクセスしてみましょう。
Web Serverなしで稼働しているのがわかると思います。

次にHHVMのphp.iniでコマンドで指定したオプションと同じものを指定してHHVMを再起動させてみましょう。

```hack
hhvm.server.port = 8080
hhvm.server.source_root=/Path/To/HackProject/public
```

ブラウザからアクセスできるのが確認できます。

### AdminServer
HHVMはproxygenで動作させると、HHVMのプロセス等を制御できるAdminServerが利用できます。(便利！)
これを利用するには、adminサーバ用のpasswordとportを指定しましょう。

```ini
hhvm.admin_server.port=9001
hhvm.admin_server.password=SomePassword
```

指定したportにアクセスすると、下記のものが利用できます。

|            |             |
|-------------------|-------------------|
| /stop |           stop the web server | 
|    instance-id |   optional, if specified, instance ID has to match |
| /free-mem: |       ask allocator to release unused memory to system |
| /prepare-to-stop: | ask the server to prepare for stopping |
| /flush-logs: |      trigger batching log-writers to flush all content | 
| /translate:   |    translate hex encoded stacktrace in 'stack' param | 
|   stack |         required, stack trace to translate | 
|     build-id |      optional, if specified, build ID has to match | 
|     bare  |         optional, whether to display frame ordinates | 
| /build-id:  |       returns build id that's passed in from command line | 
| /instance-id:  |    instance id that's passed in from command line | 
| /compiler-id:  |    returns the compiler id that built this app | 
| /repo-schema:  |    return the repo schema id used by this app | 
| /ini-get-all:  |    dump all settings as JSON | 
| /check-load:   |    how many threads are actively handling requests | 
| /check-queued: |    how many http requests are queued waiting to be handled | 
| /check-health: |     return json containing basic load/usage stats | 
| /check-ev: |        how many http requests are active by libevent | 
| /check-pl-load: |   how many pagelet threads are actively handling requests | 
| /check-pl-queued: |  how many pagelet requests are queued waiting to be handled | 
| /check-sql:  |      report SQL table statistics
| /check-sat   |      how many satellite threads are actively handling requests and queued waiting to be handled | 
| /status.xml:   |    show server status in XML | 
| /status.json:  |    show server status in JSON | 
| /status.html: |      show server status in HTML | 
| /memory.xml: |      show memory status in XML | 
| /memory.json:  |    show memory status in JSON | 
| /memory.html:  |    show memory status in HTML | 
| /stats-on:  |       main switch: enable server stats | 
| /stats-off:  |      main switch: disable server stats | 
| /stats-clear:  |    clear all server stats | 
| /stats-web:    |    turn on/off server page stats (CPU and gen time) | 
| /stats-mem:    |    turn on/off memory statistics | 
| /stats-sql:    |    turn on/off SQL statistics | 
| /stats-mutex:     turn on/off mutex statistics | 
|     sampling   |    optional, default 1000 | 
| /stats.keys:   |    list all available keys | 
|     from   |        optional, <timestamp>, or <-n> second ago | 
|     to     |        optional, <timestamp>, or <-n> second ago | 
| /stats.kvp:  |      show server stats in key-value pairs | 
|     from     |      optional, <timestamp>, or <-n> second ago | 
|     to       |      optional, <timestamp>, or <-n> second ago | 
|     agg      |      optional, aggragation: *, url, code | 
|     keys     |      optional, <key>,<key/hit>,<key/sec>,<:regex:> | 
|     url      |      optional, only stats of this page or URL | 
|     code     |      optional, only stats of pages returning this code | 
| /xenon-snap: |      generate a Xenon snapshot, which is logged later | 
| /hugepage:   |      show stats about hugepage usage | 
| /const-ss:   |      get const_map_size | 
| /static-strings: |  get number of static strings | 
| /static-strings-rds: |  ... that correspond to defined constants | 
| /dump-static-strings: |  dump static strings to /tmp/static_strings | 
| /random-static-strings: | return randomly selected static strings | 
|     count    |      number of strings to return, default 1 | 
| /dump-apc:    |     dump all current value in APC to /tmp/apc_dump | 
| /dump-apc-prefix: |  dump a key prefix contents from APC to /tmp/apc_dump_prefix | 
|     prefix      |   required, the prefix to dump | 
|     count       |   optional, the number of keys to dump, default 1 | 
| /dump-apc-info:  |  show basic APC stats | 
| /dump-apc-meta: |   dump meta information for all objects in APC to /tmp/apc_dump_meta | 
| /advise-out-apc: |  forcibly madvise out APC prime data | 
| /random-apc:     |  dump the key and size of a random APC entry | 
|     count     |     number of entries to return | 
| /treadmill:  |      dump treadmill information | 
| /pcre-cache-size: |  get pcre cache map size | 
| /dump-pcre-cache: |  dump cached pcre's to /tmp/pcre_cache | 
| /dump-array-info: |  dump array tracer info to /tmp/array_tracer_dump | 
| /start-stacktrace-profiler: |  set enable_stacktrace_profiler to true | 
| /relocate:   |      relocate translations | 
|     random   |      optional, default false, relocate random subset | 
|        all   |      optional, default false, relocate all translations | 
|       time   |      optional, default 20 (seconds) | 
| /vm-tcspace:   |    show space used by translator caches | 
| /vm-tcaddr:    |    show addresses of translation cache sections | 
| /vm-dump-tc:   |    dump translation cache to /tmp/tc_dump_a and /tmp/tc_dump_astub | 
| /vm-namedentities: | show size of the NamedEntityTable | 
| /thread-mem:   |    show memory usage per thread | 
| /proxy:        |    set up request proxy | 
|     origin     |    URL to proxy requests to | 
|     percentage |    percentage of requests to proxy | 
| /load-factor:  |    get or set load factor | 
|     set        |    optional, set new load factor (default 1.0,valid range [-1.0, 10.0]) | 
| /queue-discount:  get/set how much we discount the queue-length | 
|     set |           optional, set discount value (default 0, valid range [0, 10000]) | 
| /warmup-status:  |  Describes state of JIT warmup. Returns empty string if warmed up. | 
| /jemalloc-stats: |  get internal jemalloc stats | 
| /jemalloc-stats-print: | get comprehensive jemalloc stats in human-readable form | 
| /jemalloc-prof-activate: | activate heap profiling | 
| /jemalloc-prof-deactivate: | deactivate heap profiling | 
| /jemalloc-prof-dump: dump heap profile | 
|     file       |    optional, filesystem path | 
| /jemalloc-prof-request: |  dump thread-local heap profile in the next request that runs | 
|     file       |    optional, filesystem path | 

たくさんありますね
HHVMにはapcなども搭載していますので、これらがこのAdminServerから確認したり操作ができる様になります。
指定したパスワードを利用して値を確認してみてください。

`http://127.0.0.1:9002/dump-apc-info?auth=SomePassword` 

プロセス監視等に利用できますので商用環境に導入する場合は必ず利用しましょう。

## 商用ではTypecheckerをオフにしよう
商用ではTypecheckerをオフにするといいでしょう！

```ini
hhvm.hack.lang.look_for_typechecker = false
```

開発時にオフにしてはいけません！
*HHVMのphp.iniで設定します。

## JIT関連
JITそのものは `hhvm.jit` で設定できます。
稼働させるサーバのスペックに合わせてJIT関連の設定をすることをお勧めします。
[JIT Settings](https://docs.hhvm.com/hhvm/configuration/INI-settings#jit-settings)

## Repo Authoritative
いわゆるソースコードをコンパイルして動かすモードです。
JITだけで早くするよりは、併用して効果があると言われていますが・・！

最初に紹介した下記のコードを利用してみましょう。

```hack:index.php
<?hh // strict

<<__Entrypoint>>
function main(): void {
  echo 'HHVM/Hack';
}
```

このコードを どこか任意のディレクトリに配置し、そのディレクトリを対象にコンパイルします。

```bash
$ hhvm --hphp -t hhbc -v AllVolatile=true --input-dir /Path/To/HackProject/public
```

コンパイルすると、デフォルトで何も設定していない状態であれば /tmp配下のディレクトリに　`hhvm.hhbc`ファイルが出力されます。
このファイルをphp.iniで指定し、Repo Authoritativeを利用する様にします。

```
hhvm.server.source_root=/Path/To/HackProject/public
hhvm.repo.authoritative=true
hhvm.repo.central.path=/tmp/hphp_dWMuXm/hhvm.hhbc
```

この状態で再起動し、ブラウザからアクセスします。
画面上には `HHVM/Hack` が表示されているはずです。
さらにindex.phpの内容を変更し、アクセスし直してください。
コンパイルしたファイルから読んでいるため、index.phpを編集しても変更されません。

ちなみにHello Worldくらいであればそこまで大きく変わりません。
多少捌けてる様に見えますが、おそらく誤差です。

### hhvm.hhbc
```
Document Length:        9 bytes

Concurrency Level:      20
Time taken for tests:   0.010 seconds
Complete requests:      100
Failed requests:        0
Total transferred:      18600 bytes
HTML transferred:       1000 bytes
Requests per second:    9920.63 [#/sec] (mean)
Time per request:       2.016 [ms] (mean)
Time per request:       0.101 [ms] (mean, across all concurrent requests)
Transfer rate:          1801.99 [Kbytes/sec] received
```

### 通常の実行
```
Document Length:        9 bytes

Concurrency Level:      20
Time taken for tests:   0.011 seconds
Complete requests:      100
Failed requests:        0
Total transferred:      18400 bytes
HTML transferred:       900 bytes
Requests per second:    8906.31 [#/sec] (mean)
Time per request:       2.246 [ms] (mean)
Time per request:       0.112 [ms] (mean, across all concurrent requests)
Transfer rate:          1600.35 [Kbytes/sec] received
```

今回は導入時に利用できるものを中心に紹介しました。
色々いじり倒してみましょう