apeescape2.com
  • メイン
  • その他
  • 製品ライフサイクル
  • Uiデザイン
  • 革新
バックエンド

ソフトウェアリエンジニアリング:スパゲッティからクリーンデザインまで

私たちのシステムを見ていただけますか?ソフトウェアを書いた人はもういませんし、私たちは多くの問題を抱えています。私たちは誰かがそれを調べて私たちのためにそれをきれいにする必要があります。

に行ったことがある人 ソフトウェア工学 かなりの時間、この一見無邪気な要求が、「災害がそこらじゅうに書かれている」プロジェクトの始まりであることが多いことを知っています。他の誰かのコードを継承することは、特にコードの設計が不十分でドキュメントが不足している場合、悪夢になる可能性があります。

それで、私が最近、私たちの顧客の1人から、彼の既存の socket.io チャットサーバーアプリケーション( Node.js )そしてそれを改善するために、私は非常に警戒していました。しかし、丘に向かって走る前に、私は少なくともコードを見ることに同意することにしました。



残念ながら、コードを見ると、私の懸念が再確認されただけです。このチャットサーバーは、単一の大きなJavaScriptファイルとして実装されていました。この単一のモノリシックファイルを、すっきりと設計され、保守が容易なソフトウェアに再設計することは、確かに課題です。しかし、私は挑戦を楽しんでいるので、同意しました。

ソフトウェアのリエンジニアリング

出発点-リエンジニアリングの準備

既存のソフトウェアは、1,200行の文書化されていないコードを含む単一のファイルで構成されていました。うわぁ。さらに、いくつかのバグが含まれ、いくつかのパフォーマンスの問題があることが知られていました。

さらに、ログファイル(他の人のコードを継承する場合は常に開始するのに適した場所)を調べると、潜在的なメモリリークの問題が明らかになりました。ある時点で、プロセスは1GBを超えるRAMを使用していると報告されました。

これらの問題を考えると、ビジネスロジックのデバッグや拡張を試みる前に、コードを再編成してモジュール化する必要があることがすぐに明らかになりました。そのために、対処する必要のある初期の問題のいくつかは次のとおりです。

  • コード構造。 コードには実際の構造がまったくなく、構成とインフラストラクチャ、およびビジネスロジックを区別することが困難でした。基本的に、モジュール化や関心の分離はありませんでした。
  • 冗長コード。 コードの一部(すべてのイベントハンドラーのエラー処理コード、Web要求を行うためのコードなど)が複数回複製されました。複製されたコードは決して良いことではなく、コードの保守が非常に難しくなり、エラーが発生しやすくなります(冗長なコードが一方の場所で修正または更新され、もう一方の場所では更新されない場合)。
  • ハードコードされた値。 コードには、ハードコードされた値がいくつか含まれていました(めったに良いことではありません)。 (コード内のハードコードされた値の変更を要求するのではなく)構成パラメーターを介してこれらの値を変更できると、柔軟性が向上し、テストとデバッグが容易になります。
  • ロギング。 ロギングシステムは非常に基本的でした。分析や解析が困難で扱いにくい単一の巨大なログファイルが生成されます。

主要なアーキテクチャの目的

コードの再構築を開始する過程で、上記で特定された特定の問題に対処することに加えて、ソフトウェアシステムの設計に共通する(または少なくとも共通である必要がある)主要なアーキテクチャの目的のいくつかに対処し始めたいと思いました。 。これらには以下が含まれます:

  • 保守性。 それを維持する必要がある唯一の人になることを期待してソフトウェアを書かないでください。あなたのコードが他の誰かにとってどれほど理解しやすいか、そして彼らが修正したりデバッグしたりするのがどれほど簡単かを常に考えてください。
  • 拡張性。 現在実装している機能だけが必要になると思い込まないでください。拡張が容易な方法でソフトウェアを設計します。
  • モジュール性。 機能を論理モジュールと個別モジュールに分割し、それぞれに独自の明確な目的と機能を持たせます。
  • スケーラビリティ。 今日のユーザーはますます焦り、即時の(または少なくとも即時に近い)応答時間を期待しています。パフォーマンスが低く、待ち時間が長いと、最も有用なアプリケーションでさえ市場で失敗する可能性があります。同時ユーザー数と帯域幅要件が増加すると、ソフトウェアはどのように機能しますか?並列化、データベースの最適化、非同期処理などの手法は、負荷とリソースの需要が増加しているにもかかわらず、システムの応答性を維持する能力を向上させるのに役立ちます。

コードの再構築

私たちの目標は、単一のモノリシックmongoソースコードファイルから、モジュール化されたクリーンに設計されたコンポーネントのセットに移行することです。結果として得られるコードは、保守、拡張、およびデバッグが大幅に容易になるはずです。

このアプリケーションでは、コードを次の個別のアーキテクチャコンポーネントに編成することにしました。

  • app.js -これがエントリポイントです。コードはここから実行されます
  • 設定 -これは、構成設定が存在する場所です
  • ioW -すべてのIO(およびビジネス)ロジックを含む「IOラッパー」
  • ロギング -すべてのログ関連コード(ディレクトリ構造には、すべてのログファイルを含む新しいlogsフォルダも含まれることに注意してください)
  • package.json -Node.jsのパッケージ依存関係のリスト
  • node_modules -Node.jsに必要なすべてのモジュール

この特定のアプローチには魔法はありません。コードを再構築するには、さまざまな方法があります。私は個人的に、この組織は過度に複雑になることなく、十分にクリーンでよく組織されていると感じました。

結果のディレクトリとファイルの編成を以下に示します。

再構築されたコード

ロギング

ロギングパッケージは、今日のほとんどの開発環境と言語向けに開発されているため、今日では「独自の」ロギング機能が必要になることはめったにありません。

Node.jsを使用しているので、 log4js-node 、これは基本的にのバージョンです log4js Node.jsで使用するためのライブラリ。このライブラリには、いくつかのレベルのメッセージ(WARNING、ERRORなど)をログに記録する機能などのいくつかの優れた機能があり、たとえば毎日分割できるローリングファイルを作成できるため、その必要はありません。開くのに時間がかかり、分析や解析が難しい巨大なファイルを処理します。

私たちの目的のために、log4js-nodeの周りに小さなラッパーを作成して、特定の追加の必要な機能を追加しました。 log4js-nodeのラッパーを作成することを選択したことに注意してください。これは、コード全体で使用します。これにより、これらの拡張ロギング機能の実装が1つの場所にローカライズされるため、ロギングを呼び出すときにコード全体の冗長性と不要な複雑さが回避されます。

I / Oを使用していて、複数の接続(ソケット)を生成する複数のクライアント(ユーザー)が存在するため、ログファイルで特定のユーザーのアクティビティを追跡できるようにし、また知りたいと思います。各ログエントリのソース。したがって、アプリケーションのステータスに関するログエントリと、ユーザーアクティビティに固有のログエントリがいくつかあることを期待しています。

ロギングラッパーコードでは、ユーザーIDとソケットをマッピングできます。これにより、ERRORイベントの前後に実行されたアクションを追跡できます。ロギングラッパーを使用すると、イベントハンドラーに渡すことができるさまざまなコンテキスト情報を使用してさまざまなロガーを作成できるため、ログエントリのソースを知ることができます。

ロギングラッパーのコードが利用可能です ここに 。

構成

多くの場合、システムのさまざまな構成をサポートする必要があります。これらの違いは、開発環境と本番環境の違いである場合もあれば、さまざまな顧客環境や使用シナリオを表示する必要性に基づく場合もあります。

これをサポートするためにコードの変更を要求するのではなく、一般的な方法は、構成パラメーターを使用してこれらの動作の違いを制御することです。私の場合、異なる設定を持つ可能性のある異なる実行環境(ステージングと本番)を持つ機能が必要でした。また、テストされたコードがステージングと本番の両方で適切に機能することを確認したかったので、この目的でコードを変更する必要があった場合、テストプロセスが無効になります。

Node.js環境変数を使用して、特定の実行に使用する構成ファイルを指定できます。したがって、以前にハードコードされたすべての構成パラメーターを構成ファイルに移動し、必要な設定で適切な構成ファイルをロードする単純な構成モジュールを作成しました。また、すべての設定を分類して、構成ファイルにある程度の編成を適用し、ナビゲートしやすくしました。

結果の構成ファイルの例を次に示します。

{ 'app': { 'port': 8889, 'invRepeatInterval':1000, 'invTimeOut':300000, 'chatLogInterval':60000, 'updateUsersInterval':600000, 'dbgCurrentStatusInterval':3600000, 'roomDelimiter':'_', 'roomPrefix':'/' }, 'webSite':{ 'host': 'mysite.com', 'port': 80, 'friendListHandler':'/MyMethods.aspx/FriendsList', 'userCanChatHandler':'/MyMethods.aspx/UserCanChat', 'chatLogsHandler':'/MyMethods.aspx/SaveLogs' }, 'logging': { 'appenders': [ { 'type': 'dateFile', 'filename': 'logs/chat-server', 'pattern': '-yyyy-MM-dd', 'alwaysIncludePattern': false } ], 'level': 'DEBUG' } }

コードフロー

これまで、さまざまなモジュールをホストするフォルダー構造を作成し、環境固有の情報を読み込む方法を設定し、ロギングシステムを作成したので、ビジネス固有のコードを変更せずにすべての要素を結び付ける方法を見てみましょう。

コードの新しいモジュラー構造のおかげで、エントリポイントapp.js初期化コードのみを含む、十分に単純です。

var config = require('./config'); var logging = require('./logging'); var ioW = require('./ioW'); var obj = config.getCurrent(); logging.initialize(obj.logging); ioW.initialize(config);

コード構造を定義したとき、ioWと言いましたフォルダには、businessおよびsocket.io関連のコードが含まれます。具体的には、次のファイルが含まれます(リストされているファイル名のいずれかをクリックすると、対応するソースコードが表示されます)。

  • index.js – socket.ioの初期化と接続、イベントサブスクリプション、およびイベントの集中エラーハンドラーを処理します
  • eventManager.js –すべてのビジネス関連ロジックをホストします(イベントハンドラー)
  • webHelper.js –Webリクエストを実行するためのヘルパーメソッド。
  • linkedList.js –リンクリストユーティリティクラス

Webリクエストを作成するコードをリファクタリングして別のファイルに移動し、ビジネスロジックを同じ場所に変更せずに維持することができました。

1つの重要な注意: この段階で、eventManager.jsまだ別のモジュールに実際に抽出する必要があるいくつかのヘルパー関数が含まれています。ただし、この最初のパスの目的は、ビジネスロジックへの影響を最小限に抑えながらコードを再編成することであり、これらのヘルパー関数はビジネスロジックに複雑に関連付けられているため、これを後続のパスに延期して、コード。

Node.jsは定義上非同期であるため、コードのナビゲートとデバッグを特に困難にする「コールバック地獄」のネズミの巣に遭遇することがよくあります。この落とし穴を避けるために、私の新しい実装では、 パターンを約束します 特に活用しています 青い鳥 これは非常に素晴らしく、速い約束のライブラリです。 Promiseを使用すると、コードが同期しているかのようにコードを追跡できるようになり、エラー管理と、呼び出し間の応答を標準化するためのクリーンな方法も提供されます。コードには、一元化されたエラー処理とロギングを管理できるように、すべてのイベントハンドラーがpromiseを返す必要があるという暗黙のコントラクトがあります。

すべてのイベントハンドラーは、(非同期呼び出しを行うかどうかに関係なく)promiseを返します。これにより、エラー処理とログ記録を一元化でき、イベントハンドラー内に未処理のエラーが発生した場合に、そのエラーを確実にキャッチできます。

function execEventHandler(socket, eventName, eventHandler, data){ var sLogger = logging.createLogger(socket.id + ' - ' + eventName); sLogger.info(''); eventHandler(socket, data, sLogger).then(null, function(err){ sLogger.error(err.stack); }); };

ロギングの説明では、すべての接続にコンテキスト情報を含む独自のロガーがあると述べました。具体的には、ソケットIDとイベント名を作成時にロガーに関連付けているため、そのロガーをイベントハンドラーに渡すと、すべてのログ行にその情報が含まれます。

w9のc法人vss法人
var sLogger = logging.createLogger(socket.id + ' - ' + eventName);

イベント処理に関して言及する価値のあるもう1つのポイント:元のファイルにはsetIntervalがありました。 socket.io接続イベントのイベントハンドラー内にあった関数呼び出し。この関数を問題として識別しました。

io.on('connection', function (socket) { ... Several event handlers .... setInterval(function() { try { var date = Date.now(); var tmp = []; while (0

このコードは、指定された間隔(この場合は1分)でタイマーを作成しています。 すべての接続要求 私たちが得ること。したがって、たとえば、常に300のオンラインソケットがある場合、毎分300のタイマーが実行されます。上記のコードでわかるように、これに関する問題は、イベントハンドラーのスコープ内で定義されたソケットや変数が使用されていないことです。使用されている唯一の変数はmessageHubです。モジュールレベルで宣言される変数。これは、すべての接続で同じであることを意味します。したがって、接続ごとに個別のタイマーはまったく必要ありません。そのため、これを接続イベントハンドラーから削除し、一般的な初期化コード(この場合はinitialize)に含めました。関数。

最後に、応答の処理でwebHelper.jsに、デバッグプロセスに役立つ情報をログに記録する、認識されない応答の処理を追加しました。

if (!res || !res.d || !res.d.IsValid){ logger.debug(sendData); logger.debug(data); reject(new Error('Request failed. Path ' + params.path + ' . Invalid return data.')); return; }

最後のステップは、Node.jsの標準エラーのログファイルを設定することです。このファイルには、見逃した可能性のある未処理のエラーが含まれています。 Windowsのノードプロセス(理想的ではありませんが、ご存知のとおり…)をサービスとして設定するには、次のツールを使用します。 nssm これには、標準出力ファイル、標準エラーファイル、および環境変数を定義できるビジュアルUIがあります。

Node.jsのパフォーマンスについて

Node.jsは、シングルスレッドのプログラミング言語です。スケーラビリティを向上させるために、採用できるいくつかの選択肢があります。ノードクラスターモジュールがあるか、単にノードプロセスを追加し、それらの上にnginxを配置して、転送と負荷分散を行います。

ただし、この場合、すべてのノードクラスタサブプロセスまたはノードプロセスに独自のメモリスペースがあるため、これらのプロセス間で情報を簡単に共有することはできません。したがって、この特定のケースでは、外部データストア(など)を使用する必要があります 繰り返す )さまざまなプロセスでオンラインソケットを利用できるようにするため。

結論

これらすべてが整ったので、最初に渡されたコードの大幅なクリーンアップを達成しました。これは、コードを完璧にすることではなく、コードを再設計して、サポートと保守が容易になり、デバッグが容易になり、簡素化されるクリーンなアーキテクチャ基盤を作成することです。

以前に列挙した主要なソフトウェア設計原則(保守性、拡張性、モジュール性、スケーラビリティ)に準拠して、さまざまなモジュールの責任を明確かつ明確に識別するモジュールとコード構造を作成しました。また、元の実装で、パフォーマンスを低下させる高いメモリ消費につながるいくつかの問題を特定しました。

この記事を楽しんでいただけたでしょうか。さらにコメントや質問がある場合はお知らせください。

Django、Flask、およびRedisチュートリアル:Pythonフレームワーク間のWebアプリケーションセッション管理

Webフロントエンド

Django、Flask、およびRedisチュートリアル:Pythonフレームワーク間のWebアプリケーションセッション管理
サブスクリプションビジネスモデルに疲れた

サブスクリプションビジネスモデルに疲れた

投資家と資金調達

人気の投稿
直帰率を下げるためにカスタム読み込みアニメーションを作成する方法
直帰率を下げるためにカスタム読み込みアニメーションを作成する方法
デジタル遊牧民の冒険:ハッカーパラダイスと一緒に旅行
デジタル遊牧民の冒険:ハッカーパラダイスと一緒に旅行
WebpackまたはBrowserify&Gulp:どちらが良いですか?
WebpackまたはBrowserify&Gulp:どちらが良いですか?
強力なリモートワーク文化を構築する方法:ChristySchumannへのインタビュー
強力なリモートワーク文化を構築する方法:ChristySchumannへのインタビュー
これらのダッシュボードデザインのインスピレーションでアナリティクスをアップグレードする
これらのダッシュボードデザインのインスピレーションでアナリティクスをアップグレードする
 
devRantの禅
devRantの禅
プロジェクト管理会議2020の完全なリスト
プロジェクト管理会議2020の完全なリスト
人工知能が金融の世界にどのように影響しているか
人工知能が金融の世界にどのように影響しているか
モバイルユーザビリティの基本ガイド
モバイルユーザビリティの基本ガイド
UXデザイントレンドレトロスペクティブ2019
UXデザイントレンドレトロスペクティブ2019
人気の投稿
  • ウェブとアプリのトリッキーな方法
  • C ++コードを学ぶ
  • 無料のfullzクレジットカード2017
  • レスポンシブデザインのメディア画面サイズ
  • 不和ボット2018の作り方
  • c ++学習リソース
カテゴリー
モバイル 製品ライフサイクル リモートの台頭 ヒントとツール 製品の担当者とチーム アジャイル 人とチーム Uxデザイン データサイエンスとデータベース 財務プロセス

© 2021 | 全著作権所有

apeescape2.com