nginxでhttps2設定

仕事の都合でローカルにhttp2を立てたのでその記録
ローカルで行う事を前提にしているので環境としてdockerを採用して証明書は自己証明書で進める。

nginxの用意

dockerでnginxのイメージからコンテナを用意

docker run -it -d --name http2 -p 80:80 -p 443:443 nginx

docker内で証明書作ったり設定変えたりするので-itを指定
httpsでの接続になるので443のポートフォワーディング設定をするが、最初はポート80番で受け付ける様になっているので確認のために80番も追加しておく

この状態でブラウザ等のhttp通信が行えるものでhttp://localhostに対してアクセスしてWelcome to nginx!が表示されればOK

f:id:YuukTsuchida:20180917234453p:plain

コンテナの立ち上げまで終わったらbashログインを設定を変更をしていく。

docker exec -it http2 bash

ただ、nginxのコンテナにはvi等普通はインストールされているものがいろいろないのでインストールしておく。

$ apt-get update
$ apt-get install vim
$ apt-get install less 
$ apt-get install openssl 

余計なパッケージインストールすることになるのであまりよくなさそうだが目的がhttp2設定なので気にしないでおく!!

nginxの設定ファイルを確認

nginx のグローバル設定は /etc/nginx/nginx.conf にある。
ひとまずこいつの中身を確認

$ less /etc/nginx/nginx.conf

# 以下 nginx.conf

user  nginx;            # nginxのプロセス起動ユーザー
worker_processes  1;    # 実行プロセス数の指定 auto とすると自動設定をしてくれる。 CPUのコア数と揃えるのが良さそう

# エラーログの指定 最初は出力先 その後にレベル
# レベルは debug, info, notice, warn, error, crit, alert, emerg がある
error_log  /var/log/nginx/error.log warn;   

# PIDが格納されるファイル 基本みることはなさそう
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;   # コネクション数
}

# httpモジュールの設定
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}

この中にルーティングの設定を書けばよいのだが、別の設定ファイルに逃がすことも可能になっている。(というかデフォルトでそうなっている)
include /etc/nginx/conf.d/*.conf; となっているのが該当の設定
/etc/nginx/conf.d/.conf をおいておけば読み込んで有効にしてくれるのでルート毎でファイルを作ってやるのが良いのかも

ルーティングの設定を確認

/etc/nginx/conf.d/ を見ると default.conf が存在している。(wllcome to nginx! を表示する設定がすでにある)
今度はこいつの中身を確認

less /etc/nginx/conf.d/default.conf

# 以下 default.conf
server {
    # ポートの指定
    # default_serverを指定すると他の全てにマッチしない場合に使われるサーバー
    listen       80;  
    server_name  localhost;     # ドメインの指定

    #charset koi8-r;
    #access_log  /var/log/nginx/host.access.log  main;

    # URI に対してどこをルートにするか
    # ただ、ディレクトリやファイルに対して適切なアクセス権を設定してやる必要がある
    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    // エラーページの設定
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    #    proxy_pass   http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    #    root           html;
    #    fastcgi_pass   127.0.0.1:9000;
    #    fastcgi_index  index.php;
    #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
    #    include        fastcgi_params;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny  all;
    #}
}

実際に設定をしてやる

中身の確認はしたので実際に設定をしていく

http2 の設定をするためには証明書が必要なので事故証明書で生成をする

$ openssl req -new -x509 -sha256 -newkey rsa:2048 -days 365 -nodes -out /etc/nginx/ssl/ssl.pem -keyout /etc/nginx/ssl/ssl.key

生成が終わったらsslの設定と同じ様な設定をする。
具体的には ssl_certificate ssl_certificate_key の追加を行い、listenにhttp2の設定をする。

server {
    listen       443 ssl http2 default_server;
    server_name  localhost;

    #charset koi8-r;
    #access_log  /var/log/nginx/host.access.log  main;

    ssl_certificate /etc/nginx/ssl/ssl.pem;
    ssl_certificate_key /etc/nginx/ssl/ssl.key;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    #    proxy_pass   http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    #    root           html;
    #    fastcgi_pass   127.0.0.1:9000;
    #    fastcgi_index  index.php;
    #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
    #    include        fastcgi_params;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny  all;
    #}
}

default.confを書き換えたらnginxの再起動を行う

$ nginx -s reload

再起動あとにブラウザでアクセスをして開発ツールでプロトコルがh2になっていればOK。

とりあえず設定完了。
Dockerログインしたりしていろいろ作業しているのであまりやりたくないな(これならvagrantと変わらん)
Dockerfile覚えたらそっちでの設定方法も調べるか

シーンからGameObjectを検索して自動アタッチ

Unityを使っているとよくやるのがクラスの変数にシーン上にあるGameObjectをアタッチするという行為
GameObjectの数が少なければ簡単なんだけれど
* 階層が深い
* デザイナーから納品されたもの

などといった場合にはどこに目的のものがあるのか探したり、間違えて選択してしまうとインスペクタが変わって即座にアタッチできなかったりと少し手間がかかってしまう。

その手間を多少減らすためにAttributeを追加してシーン上から検索して自動でアタッチしてくれるようにEditor用の拡張を追加

機能紹介

こんな風に参照の右にボタンを追加して押せばシーンから該当のオブジェクトを検索してアタッチしてくれる。

[SerializeField, UniUtil.Attribute.FindObjects("GameObject")]
private GameObject obj;

この機能を使う場合変数のAttributeを追加してやるだけ。
Attributeの引数に指定したオブジェクトが検索条件となる。

ただ、この方法で配列を行おうとするとすべて同じオブジェクトをアタッチしてしまってとても使えない。

なので配列のときに利用できる特殊な指定方法として

[SerializeField, UniUtil.Attribute.FindObjects("GameObject ({0})")]
private List<GameObject> bbb;

このように記述することが可能になっている。
FindObjectsAttributeは配列のときには指定した検索条件にstring.Format("条件", index);を実行するためオブジェクト名に連番をつけておけばインデックスを考慮したオブジェクトをアタッチしてくれる。

開発メモ

この機能は実際とても簡単なコードで実現している。
単にPropertyDrawerでGameObject.Findを実行するだけだ。

ただ配列の時だけ少し特殊なことをやってる。

Editor上でFindObject

PropertyDrawerの引数にあるSerializedPropertyは配列全体ではなく要素の1つ分になっている。
配列のプロパティパスを見ると【変数名】.Array.data[【インデックス】]となっている。 現在のプロパティの親のプロパティさえ取得できれば親なのか判別できるため

37行目以降でおこなっているように.でパスを切って3以上の個数になる場合は配列の可能性があるのでチェックを行う。
配列なのか判別できればdata[【index】]正規表現を利用してインデックスを取得してstring.Format("条件", index);で検索条件を利用するだけだ。
string.Formatを利用しているので
* 001のような形式
* プレフィックス
などある程度のパターンはどうにかなる。 今後はこの拡張をして少しずつ更新して便利にしていこうかな。

phoenixのjsライブラリ追加方法(brunchを利用)

nodeモジュールを追加してページ側のjsで利用する時にbrunchの使い方で詰まったので忘れないために

※ver1.2系で行った方法なのでver1.3だとまた少し違うかも

基本

nodeのモジュールをnpmで手に入れてweb/static/js/app.jsにimportを行いmix phoenix.digest をした時に該当のモジュールが利用できる様になる。

import "phoenix_html" # 最初からある
import "モジュール名"

importで該当のモジュールが利用できるのか確認したい場合priv/static/js/app.jsの中身を見てやる。 このファイルにはmix phoenix.digest実行をした時に該当のjsファイルをapp.jsに展開してくれるので文字列を検索してヒットすれば扱えるようになる。

ただ、jqueryをこの様にして利用しようとした時にUncaught ReferenceError: $ is not defined が発生 調べた所jqueryはapp.jsのimportではなくbrunch-config.jsに設定をしてやる必要があるみたい。

デフォルトだとbrunch-config.jsの最後に

.
.
.
  npm: {
    enabled: true
  }
};

npmキーの項目があるのでここを

.
.
.
  npm: {
    enabled: true,
    globals: {
      jQuery: "jquery",
      $: "jquery"
    }
  }
};

この様に変更してやる必要がある。 やってる内容をざっくり説明するとjQueryというオブジェクトにjqueryの内容を参照させてそれを\$オブジェクトが参照するような意味になる。 $の記載がないと$(function(){})こういったよくあるjqueryの記法が使えないので注意。

jsをモジュールを追加する方法として2つ記載したが、このどちらも読み込むjsファイルに関してはモジュール名/package.jsonにあるmainキーのjsファイルが実際ロードされる。 これ以外のjsファイルを加えたい場合にはimportを利用する。

import "phoenix_html" # 最初からある
import "モジュール名"   # モジュールのロード
import "モジュール名/パス" # モジュールの一部をロード

この3つ目の記載の様にする。 import "モジュール名"node_modules/モジュール名まで見てくれるので以降のパスを記載すれば意図したjsファイルをロードしてくれる。

cssを追加してみる

基本でjsコードのみのモジュールの追加方法は終わりだが、jquery-uiの様にcssも追加する必要がある場合は追加の記載が必要になる。

jqueryと同じ様にbrunch-config.jsにstylesキーの項目を追加するとcssを読み込んでくれる。

.
.
.
  npm: {
    enabled: true,
    globals: {
      jQuery: "jquery",
      $: "jquery"
    },
    styles: {
      "jquery-ui-dist": ["jquery-ui.min.css"]
    }
  }
};

テストではjquery-ui-distを利用 npm install jquery-uiをした時はパスが変わるので注意

cssもjsと同様にmix phoenix.digestを行うと1つのファイルにまとめてくれる。 priv/static/css/app.js にまとめたファイルを出力してくれるのでちゃんと含まれているかを調べたい場合このファイルを見れば良さそう。

jsファイルと同様にcssにもweb/static/js/app.cssがありここに書いてもimportできそうだったが、importを書いてみたがビルド後のソースを見るとapp.cssに書いたままの内容がビルド後のcssに展開されているだけで含めてくれてなかった。

Unityパフォーマンス最適化のポイントメモ

【Unity道場16】パフォーマンス最適化のポイント に行ってきました。 その時のメモ ちょいちょい抜けてる所あるのでそこだけ注意

パフォーマンスの良いアプリ

パフォーマンス, 反応
CPU と GPU どっちをチューニングするべき -> 両方
どちらか片方でも時間かかった場合目標値にたどり着かないから

パフォーマンスはプラットフォームによって特性がある その特性に沿ったチューニングが必要

それぞれのCPU、GPU、メモリの参考

種類 CPU GPU MEM
スタンドアローン 潤沢 潤沢 潤沢
ゲーム機 物による 物による 物による
スマフォ 弱い
webGL 弱い 絶望
VR 弱い |

他要因 IOのアクセス メモリバンド

Unityエディタの特性はスタンドアローンに近い Unityエディタはアセットをキャッシュしまくる!

CPU、GPUの確認する方法は ー> とりあえずプロファイラを使う CPUとGPU時間が表示されるよ(単位はms)
より細かいのだと各プラットフォームに依存したプロファイラを利用したほうがよい

GPUについて

重くなる原因 - 重いシェーダ - フィルレート - オーバードロー - ハイポリ - 描画内容が多い - 画面外の描画 - ミップマップ未使用

解像度の高いデバイスでピクセルシェーダーは負荷が高い
pow exp log cos sin tan等の利用を避ける
normalize, dot, inversesqrt等を自前で記述は避ける
浮動小数点の精度を下げる

Unityはアルファ分を計算して最小限のポリゴンにしてフィルレートを下げてる
今まではハイポリでそこまで問題になってなかった、VRは2回描画するという特性で問題になってきた

表示するポリゴンを削減する方法
- LOD - OcculusionCulling

処理はCPUで行っている点に注意

ポリゴンを結合するアイディア (CPU負荷軽減) - オブジェクトを結合し、単一メッシュとして描画 - 欠点:カリングによってポリゴンが削減されない

影の描画負荷を減らす(GPU負荷軽減) - CastShadowsをOFFに - Received ShadowをOFFに

ローディングについて

不要なものはロードしない

アセットバンドルの場合は少し特殊
アセットバンドルに格納した場合、暗黙的に参照しているアセットを含めてしまう
シーンとアセットバンドルで同じアセットを参照した場合、それぞれは別の存在として認識される
アセットバンドルの依存関係の設計で重複ロードを避けるようにする必要がある

メモリが多いならロードしたアセットを破棄しないという方法もある

アセットバンドルの中にFBXを含んだ場合

ほしいのはアニメーション情報だが実際にデシリアライズするのはFBXの中身を行おうとする
FBXを読むのではなく、対象のアセットを参照するアセットを読む
プレハブなど必要なアセットを参照している場合はFBX全体をアセットバンドルに含まれるということはない

シーンで配置をして画面構築か ランタイムでの画面構築か

同じオブジェクトが1万個の並ぶ実態を作る場合
Create(Instantiate) Load(Scene) Clone(実態1つ作って)の中ならCloneが一番はやい
Createの中の処理だとadd componentが重い
Loadでは値の反映が一番重い パラメータの流し込む処理
スクリプタブルオブジェクトを利用することで改善をすることができる(デシリアライズが減る)

Clone処理が実験では一番早かったが、親子関係の構築するところで負荷が高い
GameObject.Instantiate(newGo, parentTransform)というAPIがある(5.4から)
alloc無しで親子関係が構築できる
parentTransform.hierarchyCapacity = 1000
GameObject生成・破棄のコストが安くなる
親の変更コストが上がる
InstantiateParentをした後に親子を変更すると負荷が高い

CPUに付いて

CPUの効率を求めるならCPUに働かせないこと!
けど、一切働かないのは無理 なので、バレないようにサボる

Culling Group - 部屋のレベルで処理のON/OFF - OnBecameVisible/OnBecameInvisible - 毎フレーム処理しない

CPU関連の一番の大きな問題 GameObject

ヒエラルキーの運用
UnityでGameObjectをフォルダ代わりに利用していることが多いが階層は作るのも動かす(座標だけ動かすだけ)のも負荷が高い
5.4でマシになった 5.6で早くなる
5.6になるまではGameObjectの階層には注意が必要

エディタの時だけ親子構築するなどの対策必要かも

じゃあAnimatorは? -> 回避策がある
FBXのインスペクターでRigタグでOptimize Game Objectを有効に オブジェクトの結合をする
手のボーンなど一部の座標が欲しい場合はExtra Transform to Exposeを利用 ただしリードオンリー
スキニングの処理負荷がガッツリ下がる
Animatorの場合Culling Groupを利用なくてもカリングができる
Culling ModeでCull Completelyに設定

非アクティブオブジェクトの扱い

有効な方法だが注意の必要な項目がある - Collider - Rigidbody2D - Canvas (ジオメトリ情報が破棄され、UIの再構築が走る 負荷が高い) - Animator

この3つはアクティブを切り替えると初期化される

対策
Canvas GameObjectのアクティブをするのではなく、Canvasをdisableにする
Animatorも同様 (初期化おきない)
Collider Rigidboyはどうしようもない
Rigidbody2DはSimulatedのチェックを外す

補足 Animatorのアクティブ切り替えの場合パラメータの流し込みの負荷が高い

UIについて

UIのバッチング UnityのUIは動かさない場合負荷がとても軽い
ただ 1つでも変化するとCanvas以下でオブジェクトの再構築が走る
変化するオブジェクトにCanvasを追加してやる
Canvas単位で再構築されるため

Fill Centerの無効

スクリプトのパフォーマンスの稼ぎ方)

効果の高い順に数字

1. local positionを使用する
2. VectorのOperatorを減らす
3. VectorのMathfを利用しない
4. transformをキャッシュする
5. engine codeの呼び出しを削減
6. Time.deltatimeをキャッシュする

Unity5.6でlocal position使用しない場合の負荷が軽減される

その他 - foreachを利用しない (5.5でコンパイラが変わったので効果がないかも) - ListではなくArray - NonAllocと名のつくAPIを使う - stringの結合を避ける - UpdateやLateUpdateのコールバックを避ける

おさらい - CPUとGPU両方共チューニングする - どの程度かはプラットフォームによって変わるy - CPUはシェーダーやポリゴンの設定を見直す - ローディングは余計なものはなくす - アニメーションhあオプティマイズを利用する - Transformの移動は控えめに - localPositionを使う - Vectorのオペレーションは避ける

Unity5.4くらいからマルチスレッドレンダリングが行えるようになった

TOMLというミニマル言語

設定ファイルをjsonからTOMLに移行

ツールなんかを作るときある程度は自由度持たせるために設定をjsonで書いていたが、最近どうしてもカッコが面倒になってきた。
パラメータが少ない時なんかは問題無いんだけど、データが増えてくるとどうしても書くのが手間。(コメントも書けないし)
そんなことを思っていたらTOMLという物を発見。

# コメント 
Key1 = "Value1"
Array = [1, 2, 3]
[Object]
Key3 = "Value2"
[ObjectInArray]
Array = ["a","b","c"]

キーバリューペアの形式で書いていく形になる。
jsonの様にArrayやObject型などがあるため移行もしやすそう。

コメント

ハッシュ記号(#)から改行までがコメントとなる。

# コメントだよ
Key = Value #こんなのも可能

文字列

文字列の記述は4種類の方法がある。

基本

ダブルクォートで囲むと文字列として扱われる。
クォート、バックスラッシュなどはエスケープをする必要がある。

エスケープシーケンスの短縮形も用意されている。

\b         - backspace       (U+0008)
\t         - tab             (U+0009)
\n         - linefeed        (U+000A)
\f         - form feed       (U+000C)
\r         - carriage return (U+000D)
\"         - quote           (U+0022)
\/         - slash           (U+002F)
\\         - backslash       (U+005C)
\uXXXX     - unicode         (U+XXXX)
\UXXXXXXXX - unicode         (U+XXXXXXXX)

複数行文字列

改行も文字列として扱いたい場合、ダブルクォート3つで囲むことで表現出来る。
文字列の頭にすぐ改行を入れた場合その改行は無視される。

string = """
TOML Test
TOML Test"""

リテラル文字

正規表現のパターンを書くような場合、エスケープをするケースが増えとても面倒なケースが多い。
TOMLはリテラル文字列をサポートしているので、利用することでエスケープを省略して利用することが可能となっている。
リテラル文字を利用する場合シングルクォート囲むことで利用できる。

regex = \`[\d]+\'

複数リテラル文字

リテラル文字ではエスケープを利用することができないため、シングルクォートを利用することができない。
TOMLではリテラル文字の複数行をサポートしているのでそちらを利用する。
シングルクォートを3つで囲むことでリテラル文字として扱われる。

整数

全ての数は整数として扱われる。
正の数は頭にプラスを付けても、つけなくても表現できる。
負の数は頭にマイナスを付けて表現する。
大きな数字を表現する場合、アンダースコアを数字の間に挟むことで区切り表現をすることができる。

1_000
123_456_789

整数の表記を0から始めることはできず、2進数8進数16進数の形式で表現することは出来ない。
精度は64bit(long)

小数

小数部と指数部で表現が可能。
マイナス、プラスやアンダースコアでの区切りは整数と同じルールで利用可能

123.456_790

精度は64bit(double)

ブーリアン

ブーリアンはただのトークン。
小文字のみ利用可能。

日付

日付型はRFC 3339に準じる。(この規格しらないけど)
年-月-日T時:分:秒-UTCオフセットの形式になる。
UTCオフセットを0にする場合"年-月-日T時:分:秒Z"と記述すればよい。
日本のUTCオフセットは09:00なので 2016-02-29T00:00:00-09:00 となる。
タイムゾーンを利用するような場合以外は使わないとは思うが。
ちなみに秒には小数表現が可能。

配列

配列は[]この括弧で囲まれたプリミティブ型の集まり。
空白は無視され、要素はカンマで句切られる。
データ型は共通である必要がある。

array = [1, 2, 3]
matrix = [[1, 2, 3],[4, 5, 6]]
arrayStr = ["aa", "bb", "cc"]

テーブル

キーとバリューのペアからなる集まり。(jsonのオブジェクト型と同じ扱いができる)
テーブルはで囲まれたテーブル名から始まる。
配列表現と同じ括弧を利用するが、区別はしやすい。
配列の場合必ず値として表現するが、テーブルは値である必要はなく [table name] この様に記載する。

テーブルはで開始され、次のテーブルの開始またはファイルの終端までこのテーブルに属する。
使える文字は英数字、アンダースコア、ダッシュのみ(a-zA-Z0-9_-)

[table]
key1 = "value1"

key2 = "value2"

[table2]
key1 = "value1"
key2 = "value2"

と書いた場合jsonでは以下の構造を表現する。

{
  "table": {
    "key1": "value1",
    "key2": "value2"
  },
  "table2": {
    "key1": "value1",
    "key2": "value2"
  }
}

ネストしたテーブルを表現する場合ドットを利用する。

[table]
key1 = "value1"

key2 = "value2"

[table.NestTable]
key1 = "value1"
key2 = "value2"

この場合jsonで以下の構造を表現する。

{
  "table": {
    "key1": "value1",
    "key2": "value2",
    "NestTable": {
      "key1": "value1",
      "key2": "value2"
    }
  }
}

上位のテーブルを記載する必要が無い(値が存在しない)なら省略することも可能。
上位のテーブルを省略しても、後の行で上位のテーブルの内容を書くことはできる。
ただ、キーやテーブルの再定義は出来ないため、各定義は必ず1つである必要がある。

[table]
kay1 = "value1"

[table] # これはNG
kay2 = "value2"

テーブル配列

テーブルの再定義は行えないが、テーブルの配列を定義することはできる。
テーブル名を [ [table name] ] 括弧2重で囲むことで同じ名前のテーブルは配列の要素となる。
テーブルは表記順番で挿入され、キーとバリューのペアを持たないテーブルはからのテーブルとして扱われる。

[[TableArray]]
Key1 = "Value1"
[[TableArray]]
Key2 = "Value2"

これは以下のjsonの構造となる。

{
    "TableArray":[
        { "Key1" : "Value1" },
        { "Key2" : "Value2" }
    ]
}

ネストしたテーブルの配列も定義することができる。
その場合子テーブルにも括弧2重で囲めばよい。

まとめ

自分が使いそうな表記はこのぐらい。
頭でも書いたがjsonから機能の置き換えとしては十分。(むしろこっちのが良く思える)
パーサーも自分が使う言語はほぼあるので困ることはなさそう。

ただ、javascriptを使う時だけはなんとも言えない。
jsonはそのままjavascriptで使うことが出来るのに対して、TOMLは一度パーサーを通さないといけないから悩みどころ。
でも、製品で使う様な機能でない限りTOML採用かな。

参考URL
https://github.com/toml-lang/toml

※まだバージョンは0.4.0と1.0になってない所だけ注意