技術間

IT技術(主にwebとインフラ)についてメモを残していきたいと思っています

TypescriptでD3.jsを使用した際に起こるthisの競合

これはtypescript 2.3.3、D3.js 4.9.1の記事です

Typescriptは他のオブジェクト指向言語のようにクラスを使用できます。 Javascriptのオブジェクトをクラスのように使用するのに比べ、非常に分かり易く記述できるのですが、既存のライブラリと干渉することがあります。 やろうとしていることは正しいのだけれども、Typescriptの型チェックに引っかかる、といった具合ですね。 以下引っかかった例です。

class D3Class {
  setDragEvent() {
    d3.select('#circle').call(d3.drag().on("start", this.dragstart));
  }

  private dragstart() {
    // ↓ここで型エラーが起きる 
    d3.select(this).classed("active", true);
  }
}

わかりやすくするため、startイベントのみ記述しています。

この際にコメントの位置でエラーが起きました。 ここでのthisは、本来ならばeventが発生した要素が入るはずですが、Typescriptはクラスを示すthisだと思っているようです。

改善策として、anyでキャストするとエラーを消すことができます。

d3.select(<any>this).classed("active", true);

TypescriptとD3.jsをあわせて使っていると、D3.js単体で使っている状態では使える構文でエラーが起きてしまうことがたまにあります。 @type/d3モジュールのアップデートで改善されるようなものなのでしょうか。 今後修正がくるとありがたいですね。

TypescriptでD3.jsを使う方法

これはtypescript 2.3.3、D3.js 4.9.1の記事です

D3.jsはデータとDOMを紐付けて操作を行う事ができるライブラリです。 単にDOM操作を行うだけでなく様々なことができるとか・・・。 僕自身そこまで深く使うことはないですが、Angularと合わせて使うことがあったので、TypescriptでD3.jsを使用する方法について書かせて頂きます。

といっても特別な手順はなく、package.jsonを書き換えてnpm installを行い、D3.jsを使用するファイルを書き足すのみで動作します。

変更点は以下のとおりです。

"dependencies": {
  "d3": "^4.9.0"
}
"devDependencies": {
  "@type/d3": "~4.9.0",
  "typescript": "~2.3.3"
}
// D3.jsを使用するtsファイルの最上部
import * as d3 from 'd3';

// function tekitou() {
//   let html = d3.select('html'):
// }

package.jsonにはd3とtypescript、そしてd3のtypescript用型定義モジュールを書き足し、npm installでインストールを行います。

そしてD3.jsを使用するtsファイルの上部にd3をインポートする一行を書き足せば、通常のようにd3を使用することができます。

typescriptの型定義の管理はそこそこ頻繁に手法が変わっているようです。 インターネット上の情報の変遷を見ると、
tsd→typings(typings.json使用)→2017-06現在 typings(package.json使用)
となっていっているようです。 変遷を追うのは大変ですが、管理が楽になっているようで助かりますね。

Cloud9をオンプレミスにインストールする方法

Cloud9というクラウド上でWebIDEを利用して開発が行えるサービスがあります。 アカウント登録もしくはGitHubアカウントで使用でき、自ら環境構築を行わなくても開発が行えるとう利点があります。 そんなクラウド上で使用するCloud9ですが、WebIDEの部分はGitHubで公開されているため、手元の仮想環境やローカルに導入することができます。
今回はUbuntu16.04でインストールする場合の手順を示します。

必要なパッケージ

Cloud9を動かすためにnodeが必要になります。 加えて、Cloud9をGitHubからcloneしてくるためにgitもインストールします。

nodeはあまり高いバージョンが必要ではなく、aptで入る程度のバージョンでの動作を確認しました。 Cloud9公式では0.12もしくは0.10を推薦しているようですが、高いバージョンでもうまくいくはず、と述べられています。 今回使用したnodeのバージョンは4.2.6、npmは3.5.2です

  • nodejs
  • npm
  • git

cloud9インストール・実行

GitHubのCloud9のREADMEに倣ってc9sdkという名前でcloneしてきます。 ダウンロード後、付属のインストースクリプトを走らせます。

$ git clone git://github.com/c9/core.git c9sdk
$ ./c9sdk/scripts/install-sdk.sh

インストースクリプトはCloud9に必要なパッケージであったり、nodeのモジュールのインストールなどを行うため暫く時間がかかります。 暫く待っていると、以下のような成功ログが出ます。

Success!
run 'node server.js -p 8080 -a :' to launch Cloud9

今回はaptでnodeをインストールしたため、コマンド名がnodejsとなっていますので、
nodejs server.js -p 8080 -a :
と実行するとCloud9を動作させることができます。

もしローカルに構築している場合はこの時点でブラウザからhttp://localhost:8080/にアクセスすると無事にCloud9にアクセスすることができます。

しかしこの状態ではListenしているのはlocalhostのみのため、ローカル外で構築している場合は接続できません。 その場合はnodejs server.js -p 8080 -a : --listen 0.0.0.0のように、Listenするアドレスを指定する必要があります。 とりあえず0.0.0.0を指定して全てのアドレスで待ち受けるようにすると、外部からも接続できるようになります。

実行時のオプション

よく使うと思われるオプションのみをここに記載します。 他のオプションを見たい場合はnodejs server.js --helpで確認できます。

-p, --port
Listenするポートを指定します。 ポート8080番で待つ例:
-p 8080
-a, --auth
アクセスの際にBasic認証を掛けることができます。 ユーザ名user, パスワードpassに設定する例:
-a user:pass
-a :では認証をかけずにアクセスできるようになります。
-l, --listen
待ち受けるアドレスを指定します。 認証を掛ける掛けない問わず、-aオプションを指定しないとローカルホスト以外で待ち受けることができません。 0.0.0.0、あらゆるアドレスで待ち受ける例:
-l 0.0.0.0
-w
Cloud9で使用するワークスペースとなるディレクトリを指定できます。 指定しない場合はserver.jsがあるディレクトリがワークスペースとなります。 /tmp/workspaceディレクトリをワークスペースにする例:
-w /tmp/workspace

設定書き換え

また今回は書きませんが、c9sdk/settings/standalone.jsが設定ファイルなので、そちらを変更するとオプションを付けずにデフォルト値を変えることができます。

参考

GitHub - c9/core: Cloud9 Core - Part of the Cloud9 SDK for Plugin Development

AngularCLIでSCSSを使用するには

@angular/cli 1.2.0-beta.0の記事です

angular cliで新たにangularアプリケーション(以下プロジェクト)を作成する際に、デフォルトではcssを使用する設定になっていますが、 styleオプションを指定することで別のスタイルシート言語やフレームワークを使用することができます。 この記事では主にSass(SCSS構文)を使用する方法について書いていきます。 css, sass, scssの他にもcompassなども指定できるようです。

新たにプロジェクトを作成する

通常のng newコマンドに--styleオプションを追加するだけでSCSSを使用したプロジェクトを作成できます。

ng new sample_project --style=scss

既存のプロジェクトをSCSSに変更する

–styleオプションでSCSSを指定した場合と指定しなかった場合のdiffを取りました。 以下のコマンドを行った結果で、ディレクトリ名やプロジェクト名による差分は省いています。

$ ng new css
$ ng new scss --style=scss
$ diff -r css scss
diff -r css/.angular-cli.json scss/.angular-cli.json
22c22
<         "styles.css"
---
>         "styles.scss"
54c54
<     "styleExt": "css",
---
>     "styleExt": "scss",

Only in css/src/app: app.component.css
Only in scss/src/app: app.component.scss

diff -r css/src/app/app.component.ts scss/src/app/app.component.ts
6c6
<   styleUrls: ['./app.component.css']
---
>   styleUrls: ['./app.component.scss']

Only in css/src: styles.css
Only in scss/src: styles.scss

そのプロジェクトでどのようなスタイルシート言語を使用するかは.angular-cli.json内のstyleExtで指定しているようです。 この部分はng setコマンドで変更することができます。
ng set defaults.styleExt scss

そして全体でどのスタイルシートを使用するか、という指定はstylesで指定しています。 この部分をコマンドで変更する場合はjsonの構造に従って添字も指定する必要があります。
ng set apps.0.styles styles.scss

あとはapp.componentのstyleUrlsなどを手動で変更し、scssファイルを作成して書き換えは終了です。

また、コマンドを逐一入力しなくても.angular-cli.jsonを直接書き換えても問題なく動作しました。

参考

Angular2 - Angular-CLI SASS options - Stack Overflow

Angularによる右クリック検知

Angular4.2.2に関する記事です。

Angularを使用してWebアプリを開発する日々が続いています。 独自にコンテキストメニューを実装する必要があり、Angularでどのように右クリックを検知するのか調べました。

<!-- app.component.html -->
<button (contextmenu)=contextMenuFunction($event)></button>
// app.component.ts

contextMenuFunction(event) {
  let x = event.x;
  return true;
}

Angularチュートリアルにもあるclickイベントの紐付けを、contextmenuイベントに変えるだけで、特別難しいことはないですね。

自作したメソッド(今回はcontextMenuFunction)に$eventを渡してあげると、MouseEventのプロパティを取得することができます。 クリックされた座標を知りたい、といったときにMouseEventのプロパティを見ることになりますが、MouseEventは座標だけでも様々なプロパティを持っているので、必要に応じて選択しましょう。

MouseEvent.clientX
ローカル (DOM content) 座標における、マウスポインタの X 座標。
MouseEvent.clientY
ローカル (DOM content) 座標における、マウスポインタの Y 座標。
MouseEvent.movementX
前の mousemove イベントの位置に対して相対的な、マウスポインタの X 座標。
MouseEvent.movementY
前の mousemove イベントの位置に対して相対的な、マウスポインタの Y 座標。
MouseEvent.offsetX
対象ノードのパディング境界の位置に対して相対的な、マウスポインタの X 座標。
MouseEvent.offsetY
対象ノードのパディング境界の位置に対して相対的な、マウスポインタの Y 座標。
MouseEvent.pageX
ドキュメント全体に対して相対的な、マウスポインタの X 座標。
MouseEvent.pageY
ドキュメント全体に対して相対的な、マウスポインタの Y 座標。
MouseEvent.screenX
グローバル (スクリーン) 座標における、マウスポインタの X 座標。
MouseEvent.screenY
グローバル (スクリーン) 座標における、マウスポインタの Y 座標。
MouseEvent.x
MouseEvent.clientX の別名。
MouseEvent.y
MouseEvent.clientY の別名。

説明文を見るだけではよくわからないですね・・・


参考

MouseEvent - Web API インターフェイス | MDN
contextmenu - Angular 2 right click events? - Stack Overflow

Atomファイル再読込パッケージ

最近メインエディタをvimからAtomに変更しました。 少し前まではコンソールを叩いている事が多く、コーディングと離れていたのでvimで十分だったのですが、 久しぶりに本腰を入れてアプリケーション開発をすることになったため、使い続けてきたvimを離れ、Atomを使い始めました。 typescriptでもパッケージ1つ入れるだけでシンタックスチェック含め様々な支援をしてくれるので助かっています。

しかし、パッケージ同士の競合かわかりませんが、変にバッファが溜まってしまい(?)正しい箇所でシンタックスエラーが出る事が往々にしてありました。 ファイルを開き直すと解決するため、コマンド1つでファイル再読込できる機能を探していたらこのようなパッケージを見つけました。

revert-buffer
atomの公式パッケージのようですね。

このパッケージを導入後コマンドパレットにてRevert Buffer: Revertを実行すると、今開いているファイルを再読込します。 ファイルを編集中の場合、編集前に戻るため保存を忘れないようにしましょう。

そして逐一コマンド打つのも大変なので、キーマップを編集し、ショートカットキーを登録すると便利かと思います。

'atom-text-editor':
  'ctrl-cmd-r': 'revert-buffer:revert'

とりあえずCtrl-Cmd-rに割り当ててますが、競合してないことを祈っています。

TCPとNginxの設定不足によるトラブル

初の技術的な投稿では、自分が100~200人ぐらいを対象にシステムを提供した際に起きた問題について触れたいと思います。

C言語の演習環境を提供するという目的で、100人ぐらいの同時アクセスがあるという想定のもと、仮想マシン1台に構築しました。 Nginxによるリバースプロキシを前面に置いて、認証結果によってユーザ毎別々のDockerコンテナに振り分ける構成です。

以前40人程度を対象に、DockerではなくVirtualBoxによる仮想マシンを使用し、同じシステムを問題なく提供できていたので特に考えず構築し、デプロイしました。 結果、繋がらないの怨嗟の声がそこら中から上がる展開に。

ロードアベレージの極端な上昇が見られなかったので「あの部屋のネットワークが細いのが原因ではないか」と高を括っていたのですが、ネットワークが整備された部屋でも同じ現象が見られたため、ドキドキしながら原因を探しました。 結果、TIME_WAITになっている大量のポートを発見。

先ずはTIME_WAITが原因だと考え、Linuxパラメータの変更による解決を試みました。

# /etc/sysctl.conf
fs.file-max = 5242880

net.ipv4.ip_local_port_range = 18000 65535
net.core.netdev_max_backlog = 10240
net.core.somaxconn = 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 0
net.ipv4.tcp_fin_timeout = 20
net.ipv4.tcp_max_syn_backlog = 10240
net.ipv4.tcp_rfc1337=1

設定後seleniumを使用して150人同時アクセス再現を試みた結果、まだ繋がらないホストがあったためnginxの設定変更を行いました。

# nginx.conf
# 差分箇所のみ記述

worker_rlimit_nofile 150000;

events {
  multi_accept on;
  worker_connections 65535;
}

http {
  tcp_nopush on;
  sendfile on;
  server_tokens off;
  reset_timeout_connection on;
}

ここまでやった結果、seleniumでのアクセス再現も問題なく成功し、実稼働でも繋がらないという声は(ほとんど)なくなりました。 設定内容の解説については後日記事にできたら、と思います。


参考 Nginxのパフォーマンスを極限にするための考察 – Qiita http://qiita.com/iwai/items/1e29adbdd269380167d2