site_review: 2021-12-10 03:36:37.776046 【技術本】Node.jsデザインパターン

【技術本】Node.jsデザインパターン

https://amzn.to/3TIYOCs


【技術本】Node.jsデザインパターン


x postへポスト
Node.jsのシングルスレッド、ノンブロッキングI/Oによる独自のアーキテクチャを踏まえた実装における細かなコーディング作法からデザインパターンまでを学ぶことができる。
大規模開発のための整備されたコード設計、関数設計、クラス設計、さらには分散スケール環境でのアーキテクチャのデザインパターンまでを紹介してくれている。node.jsはv11.9.0に対応。
gitからソースコードを取得できるが、著書に記載されているソースコードだけでも処理の概要を把握できるよう配慮されているのがありがたい。
基本的に各チャプターごとにプロジェクトファイルが用意されpackage.jsonで必要関連ライブラリをバージョン指定されているので、フォルダ内でnpm install の一発で環境が揃うのはありがたい配慮だ。著書では、ライブラリの個別インストールのコマンドが表記されているが、上記を実行していれば特に必要はない。
※一部でグローバルインストールが別途必要なものあり
※redis, rabbitMQなどミドルウェアは、別途構築する必要あり
コードを追いながら、高度なアーキテクチャの技術を身に付けたい人向け。
Node.jsの中上級者向けに響く著作だと感じた。

-------------------
#注意点

8章 webpackをグローバルインストールする記載がされている> P255
バージョンを指定しないと最新版がインストールされ、著書と挙動が異なって検証ができ
なくなるので、以下のバージョンでインストールすること。(package.jsonにも記されているので確認できる。)
npm install webpack@1.12.14 -g
※package.jsonのwebpackと合わせる
他のnpmでグローバルインストールしているものは、package.jsonのバージョンに合わせたほうがいい

8章:sample12,13,14
P285 に実行コマンドにnode serverと記載されているが、コードに記載されたJSXを変換実行する必要があるので、babel-nodeまたは、babel-cliをかましてスクリプトを実行する必要がある。
例:./node_modules/babel-cli/bin/babel-node.js server

9章以降:
node-gyp系リビルド処理でpython2.7系が必要なケースもあり。
nvmで異なるnodeのバージョンが必要になるケースもあり
macで検証している場合は xcode, xcode toolを最新版にしておくこと

--------------------
#以下、内容メモ

1章:Node.jsの世界へようこそ

モジュールは小さく設計すべき
・コードを少なく
・機能を最小限
npmによってモジュールの依存地獄から解放
->パッケージが個別に依存モジュールを定義できる

ブロッキングI/O:
I/O処理は時間がかかる→サーバがブロッキングI/Oの場合、複数リクエストの場合は新たにスレッドを起こす必要がある。消費するメモリや、コンテキストスイッチのコストを考えると、効率は良くない。

ノンブロッキングI/O:
データの書き込み完了をまたずに一旦終了できる。
同一スレッドで、複数リソースを扱えるようになる。
イベントループでビジーウェイトせずに複数のI/Oを処理
並行性の扱いが単純になる。スレッドの競合、同期を考慮しなくて良い

・リアクタパターン
I/Oタスクのそれぞれにハンドラ(コールバック関数)を対応させるもの
コールバック関数を使用して、すべてのリソース処理を後回しにするようなプログラミン
グスタイル。

Node.jsのアーキテクチャ
libuv:node.jsのCライブラリで、OSに依存せず同じ手法でブロッキングI/Oを使える。
v8:GoogleによりChromeブラウザのために作られら高速効率的メモリ管理のJavaScriptエンジン
バインディング:libuvmやその他の低レベルの機能をラップし、JavaScriptで利用できる
ようにするための抽象化レイヤー
node-core:ハイレベルなNode APIを実装したJavaScriptライブラリ

2章:Node.jsの基本パターン

・コールバックパターン
同期プログラミングにおけるreturn文に相当。非同期プログラミングでは必須。
JavaScriptのクロージャを使うことで、コールバック関数内にその関数で生成された環境
を参照することができる。

関数によって同期、非同期で呼ばれるかの違いによって呼び出し元の実装も変わる。
関数内で分岐判定処理によって同期や非同期で値が返る仕様は思わぬバグを産む。
->どちらかに明示的に仕様を決める。

process.nextTick() イベントループで次まで関数の実行を遅延(先延ばし)してくれる。登録されたI/Oイベントの前に実行->nextTIck()の再帰呼び出しはI/O starvationに陥るケースがあるので注意。
setImmediate():登録されたI/Oイベントの後に実行

try catchでキャッチされない例外の補足方法
 process.on('uncaughtException',(err)=>{ //todo process.exit(1); }):

依存解決の仕組み:ロード元によて異なるモジュールのバージョンをロードする機能
循環参照の注意点:読み込む順番によって初期化されるモジュールが異なる問題が起きる

・モジュール定義におけるパターン
オブジェクト、コンストラクタ、関数のエクスポート
インスタンスのエクスポート

・オブザーバパターン
EventEmitterをつかって複数のオブザーバに対して通知を行う。
on, emit, return emitter P62
複数イベントを扱う、イベントの発生回数が予測できない場合に向いている。

コールバックと、EventEmitterを組み合わせた関数
glon(pattern, [option], callback).on('match', match => console.log(`${match}`))
コールバックは結果データを返すが、EventEmitter(`match`)で中間処理情報を通知する仕組み

3章:コールバックを用いた非同期パターン

コールバックのネストが幾つも重なる=コールバック地獄 p51
視認性が悪く、クロージャを解放し忘れるとメモリリークの原因にもなる
・クロージャを乱用しない
・if文にelseをかかず、returnでなるべく早く抜ける:ネストレベルを下げる
・コールバックを独立した関数で定義し、クロージャを使わない。
・一つの関数にネストしたコールバックを書かず、複数の関数に分割する
※クロージャ:自分を囲むスコープにある変数を参照できる関数:関数の内側の関数

逐次処理におけるコールバックの伝播
並行処理におけるコールバックの伝播

・並行処理:非同期でイベントループ内で並列数分インタリーブされ処理される。
マルチスレッドでないので特別な技術は必要ない。Nodeの強み。
完全に競合状態がないわけではない。複数タスク新しいファイルだった場合ファイル読み込み処理するケースで、複数タスクが同時に新しいファイルだと判定して、同じ処理をしてしまう場合がある。

同時実行数制限をした並行処理
キューを使った同時実行数制御

委譲:delegate

asyncライブラリを使用した逐次、並行処理、同時実行数を制御した並行処理
series:非同期逐次実行
eachSeries:配列要素を捜査して非同期逐次実行
each:並行eachSeriesからの移行に便利
eachLimit,mapLimit,parallelLimit,queue,cargo:同時実行数を制限した並行処理
前半の並行処理実装は、asyncライブラリで簡潔に置き換えられる

4章:ES2015以降の機能を使った非同期パターン

Promise:コールバックの欠点を補うために考案された。非同期処理の結果を表現するオブジェクト。callbackなどの橋渡しスタイルからの変更
Generator:functionの後ろに*を付加して宣言すれば、yieldにより処理を中断できる。
 非同期の制御フローを簡単に記述(yield, next()を活用)し、関数を一旦抜けたり、そこから再開できるようにする
coライブラリを利用したジェネレータベースの制御フロー
 async/awaitを使用した制御フローでGeneratorを使用することで複雑化した実装をシンプルにする。

5章:ストリーム

ストリームとバッファの違い
バッファ:データが全て揃ってからコールバック通知
ストリーム:データを受け取った時点で途中でもコールバック通知可能
巨大なデータも少ないリソースでやりとりできる。
ファイル転送後の解凍処理もチャンク単位で圧縮して転送し解凍することができる。
バイナリモード:チャンクの形でストリーム化する
オブジェクトモード:オブジェクトとしてデータを処理
・Readable:ソースを表現
 non-flowingモード:readableイベントにアタッチ、read()でバッファが空になるまで読み込む
 flowingモード:dataイベントにアタッチ、データがリスナーにプッシュされる
Readableストリームの実装:_read()は継承したReadable内部で呼ばれる
・Writable:データの行き先
writable.write でチャンク書き込み
writable.end で最後のチャンク書き込み
 バックプレッシャ:データが消費されるよりも速く書き込まれてしまう場合の対処
 Writableストリームの実装:_write()は継承したWritable内部で呼ばれる
・Duplex:ReadableかつWritableなストリーム、双方を継承しread(),write()を行う
・Transform:データ変換用 write -> read
・パイプを使ったストリームの接続
 unixでは、次のプログラムの入力として簡単に接続できる
echo Hello World! | sed s/World/Node.js/g
node.js -> pipe()で行う
・パイプ処理パターン
 Combinedストリーム:ストリームの合併
 ストリームのフォーク
 ストリームのマージ
 マルチプレクシングとでマルチプレクシング

6章:オブジェクト指向デザインパターンのNode.jsへの適用

・ファクトリ:
p152 模擬オブジェクトを返す(start:function{}, end:function{})
合成可能ファクトリ:ファイプラリstampit
・公開コンストラクタ
・プロキシ
データ妥当性検討、権限チェック、キャ種保存、遅延初期化など
応用:配列の仮想化
・デコレータ
・アダプタ
・ストラテジー
・ステート
 コンテクストの状態によって変化するストラテジーとして見れる
・テンプレート
具象クラスが定義された瞬時にアルゴリズムが決定される。ストラテジとの違いは、ストラテジは動的に変更が可能、テンプレートはクラスが定義された瞬間にアルゴリズムが決定する。
・ミドルウェア
 玉ねぎの皮のようにミドルウェアの層をつくり、順に層を呼び出し中心のアプリケーションのコアに到達させる。
・コマンド

7章:モジュールの接続

・シングルトンとしてモジュールを利用するパターン
 modele.exportsを活用
 require()関数呼び出し時にキャッシュされ、以降はキャッシュを返すため
->落とし穴がある。パッケージごとに異なるnode_modulesのパッケージを参照してしまう恐れあり:global変数で対応できるが、この方法は避けるべき。

・モジュール接続のためのパターン
require()でステートを持ったインスタンスへの依存をハードコードすることによる制限
 DI(依存性注入)のケース:Factoryパターンを活用して後からモジュールを指定する
 ->利点:dependencyから分離し再利用しやすくなる
  欠点:依存関係の把握が増加複雑化すると管理が大変
 サービスロケータ:DI同様、コンポーネント間の把握が難しくなる
 DIコンテナ:依存するもの以外の余計なサービスにモジュールを依存させなくできる
・プラグインの接続
 require()するだけで、プラグイン機能を付加できる実装:関数を変数に格納する手法とデコレータパターンを駆使して行う
 サービスロケータ、DIコンテナをつかった公開方法

8章:ユニバーサルJavaScript

サーバサイドとフロントエンドでのコード共有
Reactを使用したSPA(Single-Page-Application)の作成
・モジュールの共有
 UMDパターン:定型的なコードを沢山書かなければならない
 ES2015モジュール:サポートされていないブラウザに注意
・Webpackの導入
node用に作成したモジュールをブラウザ用に書き直してくれる
 ES2015で提供される機能を実装していても変換してくれる
・クロスプラットフォーム開発の基礎
 実行時コード分岐
 ビルド時コード分岐:Webpackの例
 ビルド時にモジュール置換:Webpackの例
 クロスプラットフォーム開発向けのデザインパターン
 ・ストラテジー、テンプレート:用途によって切り替え処理ができる
 ・アダプター:互換性を持たすようにアダプターで置換する
 ・プロキシ:サーバ側のモジュールをリモートプロキシで呼び出す
 ・オブザーバ:イベントを発生、受け取りのコンポーネントを抽象化
 ・DIとサービスロケータ:依存性注入時にモジュール入れ替えする
・React
サーバ内とクライアントでほぼ同じコードでのビューのレンダリングができる
 Nodeサーバでビューをレンダリングしてブラウザが受け取り表示可能=SPAを作成できる。
JSX:HTMLとDOMツリー表現の中間フォーマット的な位置付け
expressのstaticを利用してwebpackしたモジュールを読み込めるようにして、1度目はサーバでレンダリングし、以降はブラウザで再描画する仕組み
babel-cliでサーバサイドでJSX記述を残したまま、実行時変換起動させる方法
 Webサーバ(プロキシとして活用)、APIサーバとスケーラブルな構成のパターン

9章;特殊な問題を解決するためのパターン

・非同期に初期化されるもモジュールのrequire
ステートパターンを利用し実行リクエストをキューにため、非同期実行が完了してからキューのリクエストを実行する堅牢な作りの紹介
・非同期のバッチ処理とキャッシュの利用
・CPUバウンドなタスクの実行
setImmediateによるインタリーブ:cpuバウンドの処理のステップが完了するたびにイベントループに制御を戻す
マルチプロセスの利用:子プロセスへ移譲

10章:スケーラビリティとアーキテクチャ

複数プロセス利用、複数サーバ利用にしてスケールする
下記3つの観点でのスケールを考える。
 クローニング:アプリをn回クローニングして各インスタンスに1/nの処理をさせる
 サービスと機能による分割;機能、サービス、ユースケースで分割
 データパーティションによる分割:データの一部を各インスタンスが分担して担当
  水平分割、シャーディング
・クローニングと負荷分散
 モジュールcluster
 ・バージョン0.11.2以降からラウンドロビン方式の振り分け
 ・障害時の再起動(zero-downtime restart)の実装
 ・負荷分散
  ステートフルにしたいデータの共有問題
  ・共有メモリを利用した認証データの保存
  ・スティッキーな負荷分散(値判定で特定ノードへ割り振る)による特定なインスタンスへのマッピング
 ->問題点あり:インスタンスの代替が効かなくなる問題
  リバースプロキシによるスケーリング
 ・ロードバランサとして機能するリバースプロキシ
  ・URLリライト、キャッシング、SSLターミネーション
  ・nginxをロードバランサとしてバックエンドにnodeのwebサーバを稼働させたパターン
  サービスレジストリを活用し、最新の稼働サーバ情報を登録し、ロードバランサがその内容を参照するパターン
  ・nodeでの負荷分散装置の実装
  ピアツーピア負荷分散:ロードバランサの処理量に依存させない方法:その分、接続方法は複雑になる
・サービスと機能の分割
マイクロサービスアーキテクチャのパターン
 各サービスにデータ所有権があることがマイクロサービスアーキテクチャの特徴
 正しいレベルの分離を独立維持するために、データベースも分割する
 データベースを分割した分、システム全体の一貫性を維持するために、互いのデータを参照更新する多くの通信が必要になる。
 クラッシュ、バグ、互換性を破る変更をシステム全体に波及させないようにする。
 利点:
 ・再利用性向上
 ・スケールは、複数マシンに負荷分散。独立してスケール可能
 難点:
 ・管理すべきノードが増えると、統合、デプロイ、コード共有が複雑になる。
 ・APIオーケストレーション層で複数のマイクロサービスのAPIを実行し、データを集約、処理する。
 ・メッセージブローかを使ったケース;フロントエンドのリクエストを受け取ったマイクロサービスが、関連するマイクロサービスにメッセージブローカを通して処理を行う

11章;メッセージ通信と統合

分散アプリケーションを統合する手段
・共有ストレージを中央の調整器兼情報の保持器として使う。
・メッセージを使ってデータ、胃炎と、コマンドを各ノードに行き渡らせる

メッセージ通信
・非同期メッセージと送信キュー:SMSのようなもの
・ピアツーピアメッセージ通信
・ブローカを使ったメッセージ通信:プロトコル間のブリッジにも利用できる、ブローカ
が単一障害になるデメリットはある。

パブリッシュ/サブスクライブ(pub/sub)パターン
 分散オブザーバーパターンでもある
 ・ピアツーピア型Pub/Sub
 ・ブローカ型Pub/Sub
 メッセージブローカにredisを適用したチャットサーバの分散スケール
 ZeroMQを使ったピアツーピア型PubSubで分散処理するチャットサーバ
 サブスクライバに障害が起きても確実にメッセージを送信できるような永続サブスクライバの実現
 ・多くのメッセージキューシステムが採用しているプロトコル:AMQP
 ラビットMQを使用したAMQPによるチャットサーバ

パイプラインとタスク分散パターン
・ZeroMQのファンアウト/ファンインパターン
 ハッシュサムを総当たりでしてアルファベットの考えられるすべての組み合わせを照合
するシステムで実践
 ベンチレータ->ワーカー->シンク
・パイプラインとAMQPの競合コンシューマ
 競合コンシューマ;複数のコンシューマが同じキューをリッスンし、メッセージをファ
ンアウト分散する

リクエスト/リプライ・パターン
・相関識別子を使用した、リクエストとリプライの紐付けで、リクエスト順序に影響しない
実装方法

付録A:ES2015以降のJavaScriptの主要機能
let, const:変数定義
アロー関数:=> コールバック関数を簡潔化記述できる
class構文
オブジェクトリテラルの改善
MapとSet
WeakMapとWeakSet
テンプレートリテラル
etc...

以上。


x postへポスト


  Copyright 2021-2025 REVIEW_SITE ALL RIGHTS RESERVED.
  このサイトについて/お問い合わせ
  プライバシーポリシー
  総合トップページへ