2021年01月23日の日記です


100倍規模  2021-01-23 12:02:32  コンピュータ

お仕事の話。ほぼ愚痴。


100名程度の使用を想定して作ったシステムがあり、開発をお手伝いしている。

ありがたいことに人気が出て、人気が出ると想定外の使われ方をし始める。


特に、多人数で使いたいという要望が多く、1000人規模でも使えるように1年ほど前に大規模改修を行っている。

僕はこの改修の途中から参加させていただいた形だが、システムのスループットは上がり、1000名でも使えるようにはなった。


この際に、さらに人数を増やすこともできるような設計にはしていたが、それは「設計」だけで、実際には対応できていなかった。



それが、今回1万人で使いたいという依頼が来て…

いや、依頼はしばらく前から来ていたのだ。というか、依頼は1万人とも限定していなかった。

最大5万人くらいということだったが、それはさすがに難しそうなので、1万人に制限してもらったのだ。




システムはいくつかのパーツに分かれているが、サーバー部分は改修の際に、node.js で作り替えられていた。

node.js はサーバー側で動作する javascript 環境だ。


細かな話を書くと長くなるが、もともと javascript はブラウザ向けの技術だった。

そのため、非常に癖の強い特徴を持つ。


一番重要なのが「ノンブロッキング」であるということだ。

ブラウザはユーザーが操作するものなので、その操作を阻害…「ブロック」してはならない。

これがノンブロッキングという思想。


言語が動いているときは、ユーザーの操作を受け付けられない。

そう仮定すると、ユーザーの操作を阻害しないため、言語は無駄な実行時間をかけてはならない。


とはいえ、普通のプログラム言語にはたいてい「何かを待つ」処理が入るものだ。

ファイルを読んだり、ネットで通信をすると、CPU よりもはるかに遅いディスク装置やネットワークのデータを待つ。

プログラムをしていると、ごく当たり前のことだ。


javascript では、これらのことは「行えない」。

待たせてはならない、という設計思想だからだ。


とはいえ、ディスクを読んだり通信をしたり、というのは当たり前のことで、もちろんできる。

どうやるかというと、待つのではなく、いったん終了してしまうのだ。


「通信してね」って頼んだら、終了する。

そして、通信が終わると、「通信終わったよ」ともう一度処理を起動してもらう。


javascript では、一事が万事この調子。プログラムを組んでいると、非常に組みづらい。

まぁ、最近では昔より環境が整えられ、かなりマシになったのだけど。



そして、あえてこの使いにくい環境をサーバーで使うと、「待ち時間のない」プログラム環境を実現できる。

待ち時間がないということは、常にプログラムが動いているし、リクエストに応えられるということだ。


従来…というとちょっと古いのだが、Wordpress などに使われる PHP では、1秒間にせいぜい 10リクエスト

くらいしか答えられない。


でも、同じことを node.js で実装すると、1000リクエストくらいは答えられるのだ。


#PHP はインタプリタだが、node.js は即時コンパイルだ、とか、PHP はプロセスフォークで動く Apache 上で動作するが、node.js はシングルプロセスなのでタスクスイッチのオーバーヘッドがないとか、他の要因も多分にあるのだけど。




それでも期待できるのは 1000人レベルだ。

大規模改修で node.js を使って 1000人規模に対応した、というのがこの段階。


今回1万人規模のリクエストなので、node.js をクラスタ化して対応することにした。


node.js は、シングルプロセスで動作する。

たった一つのプログラムで、1000以上の接続に対応するのだ。


しかし、これは CPU がマルチコアだったとしても、1コアしか使わないという意味でもある。

CPU のコアの数だけプロセスを増やせば、理論上はもっと多くの接続を処理できる。


もちろん、そのための制約はいろいろとあるのだが、最初から「マルチプロセスで動かすことができるように」という設計をしていた。


そして、pm2 という node.js 対応のプロセス管理ツールを使うと、プログラムは無改造のまま、マルチプロセス化できる。


(通常、プロセスが増えると、それぞれが別の通信ポートなどを用意しないといけなくなる。

 通信ポートが増えれば、アクセスするクライアント側もそれに対応しなくてはならなくなる。

 しかし、pm2 は代表して1つの通信ポートを作り、配下のプロセスのポートに適切にデータを流してくれるので、外部から見るとそれまでのプログラムと何ら変わらないように見える)


というわけで、処理性能は何倍かに跳ね上がった。



…でももちろん、話はそんなに単純ではなかった。




接続が増えたら、当たり前だがそれらの接続ごとに、データを収めた DB へのアクセスが発生する。

1プロセスで 1000 人程度をさばいているときには問題にならなかったが、1万人規模になると接続ができなくなった。


DB の接続数の上限設定があるのだ。これは、 DB の設定を書き換えて解消する。

これに伴い、適切なワークメモリの割り当てなども変える必要があるが、とにかく設定した。


DB が接続を受け付けられるようになっても、その接続のための通信ポートが十分作れない。

OS 側に、通信ポートをいくつ作れるか、という設定があるのだ。

それらも設定する必要がある。


UNIX では、すべてのリソースがファイルとして扱われる。

通信ポートも1種のファイルだ。そして、UNIX には、プロセスごとに開けるファイル数の制限がある。

この制限も増やす必要がある。


100 人程度なら問題なし、1000人でも大丈夫だったとしても、1万人規模になるとそれまで気にしていなかった設定が問題になるので、問題発生のたびに何が悪いのか調べ、設定をチューニングする必要が出る。


それまでは問題にならなかった DB の Query が、突如として slow query になる、というのもあった。

DB へのアクセスが激しすぎると、特に問題がなかった部分がボトルネックになりだすのだ。


…これらは、上に書いたような順序で、理路整然と起こったわけではない。

とにかく不具合が続出し、原因を切り分け、考えられる要素をひとつづつ潰していく必要があった。


上に書いたのは、「結果としてそういうことだった」というだけで、原因がわからないので見当違いの設定に手を出して、何も変わらないので元に戻す、というようなこともやっている。




問題はサーバー側だけではなかった。


1万人規模、と書いたが、その規模の人たちがサーバーに接続し、操作した結果を集計して表示する画面がある。

普通の WEB ページとして作られているので、PC さえあればインストール作業などなしで表示できる。


この画面自体は、サーバーと接続しているだけだ。接続が増えるようなことは無い。

だから特に問題は出ないだろうと思っていたら、1万人分…1人についてのデータが複数あるため、数万件のデータになると通信に支障が出た。


特に、何らかの不具合が起こって画面のリロード…と考えたとき、数万件のデータ再送信はそれだけで1分程度の時間がかかる。


通信したデータを、ローカルにキャッシュして持つように改良した。

再起動が行われた際は、まずキャッシュからデータを読み込み、続きのデータの送信をサーバーに依頼する。

5秒程度で表示が行えるようになった。



この表示も1万件あると遅くなった。

全部を表示したいのだが、ただ1万行の HTML というだけで、重くてスクロールもままならないのだ。


これはプログラムテクニックで乗り切った

どこかに詳細なテクニック紹介をしたいのだけど、忙しくて概要説明だけ行ったままだ。



100人を想定して作ったものを、1万人向けに提供する…となると、思わぬことがいろいろと発生する。




同じテーマの日記(最近の一覧)

コンピュータ

別年同日の日記

12年 ゲームボーイの CPU

15年 アプリケーションサーバとしてのQNAP

15年 宮永好道 命日(1993)

16年 iOSでtextのコピー・ペーストができないバグの回避

18年 サターンポリゴンのゆがみ


申し訳ありませんが、現在意見投稿をできない状態にしています


戻る
トップページへ

-- share --

0000

-- follow --




- Reverse Link -