HMVCを使用したWebアプリケーションのスケーリング

我々は、第二のウェブデザイン・サイクル過去十年間を参照してください - サイトは、アプリケーションに変換され、インタラクティビティの一定割合をせずに事実上、新たなプロジェクトを持っています。 インターネット用に開発されたソフトウェアの複雑さが増すにつれて、構造化されバランスの取れたアプリケーション設計が必要になりました。

現在、最も一般的に使用されているWebサイトのデザインパターンは、Model-View-Controller(MVC)です。 その広範な使用は、Ruby on Railsフレームワークの成功と人気によるものです。 現在、MVCは、すべてのプラットフォーム間でのWeb開発のほぼ同義語です。

プロセッサをアクティブにロードするタスクを実行するとき、現代のサイトはますます専用リソースに依存しています。 これは、特に、開発者が大幅に独自のサーバーのCPUのオーバーヘッドを軽減することができます発見、AmazonやGoogleのクラウドサービスに貢献しました。 通常、各サービスは、ドメイン内で実行され、独自のリソースを使用する個別のソフトウェアとして設計されています。

控えめな予算を扱うとき、通常、ソフトウェアの複数の完全な部分のための利益の資金を調達するために顧客を説得するのは非常に困難です。 私の経験が示すように、それらの多くは、スケーラビリティは緊急の課題ではないという意見です。 彼らは「心配しなければならない日を楽しみにしている

初期投資を軽減するには、通常、そのアプリケーション上の意思決定を取る必要なすべての機能を含む、統合されたプログラムとして設計されなければなりません。 サイトの人気が急速に高まると、問題になります。 不十分なスケーラブルなコードベースのリファクタリングからの印象はまだあまり良くありません。 さらに、多くのリソースとお金が必要になる場合があります。 理想的には、アプリケーションは必要に応じて成長し、プロセスで大きな財務コストを必要としないはずです。


階層モデルビューコントローラパターン



パターン階層モデル - ビュー - コントローラ(HMVC)私たちはすでに拡張性を言及した多くの問題を解決することができますMVCの拡張、です。 なお、第1 JavaWorldブログというタイトルで2000年7月の記事で説明した「クライアントレベルを開発するための多層パターン:«HMVC 。 が知覚あるプレゼンテーションアブストラクション・コントロール(PAC) - - 1987年に記載の著者は実際には異なるパターンを考え直すことは。 記事の目的は、HMVCは、スケーラブルなWebサイトを作成し、グラフィカルなインターフェイスとデスクトップアプリケーションの設計にするために使用する方法を実証することでした。

HMVCは、単一のアプリケーションとして機能する従来のMVCトライアドのコレクションです。 各トライアドは完全に独立しており、他のトライアドがない場合に実行できます。 すべてのトライアドリクエストはコントローラーインターフェイスを使用する必要があり、ドメイン外のモデルやライブラリを接続しないでください。 サーバー上のトライアドの物理的な場所は重要ではありません。主なことは、システムの他のすべての部分からアクセスできることです。 HMVCの特徴的な機能は、コードの再利用を促進し、システムの個々の部分のテストを簡素化し、アプリケーションをそれほど複雑にすることなく複雑に拡張できるようにすることです。

HMVCパターンに基づいたアプリケーション設計を成功させるには、その機能を個別のシステムに分割する必要があります。 各そのようなシステムは、独立して制御手段のとHMVCアプリケーション内の大きな表現を格納する一トライアドMVCであるべきです。 現在、追加モジュールなしでHMVCをサポートするフレームワークがいくつかあります。 それらの1つは、もともとHMVCに焦点を当てて作成された第3バージョンのKohana PHPです 。 さらにすべての例で、このフレームワークを使用します。

Kohana 3は、組み込みのRequestオブジェクトを使用して他のコントローラーを呼び出します。 要求は、アプリケーションコントローラーに対する内部とWebサービスに対する外部の両方にすることができます。 どちらの場合も、同じリクエストクラスが使用されます。 MVCトライアドを別のサーバーに移動する場合、1つのパラメーターのみを変更する必要があります。

<?php class Controller_Default extends Controller { public function action_index() { //    $internal_request = Request::factory('controller/action/param') ->execute(); //    $external_request = Request::factory('http://www.ibuildings.com/controller/action/param') ->execute(); } } 

内部リクエストには、コントローラーとアクションを含む正しいパスが必要です。外部リソースへのリクエストを作成すると、完全なURLが提供されます。 この機能により、内部クエリと外部クエリを複雑にすることなく交換できるため、メインサーバーの外部でトライアドを取得するのは非常に簡単なタスクです。

KohanaのRequestクラスを使用して内部コントローラーからデータを取得すると、Zend Frameworkなどの他のフレームワークでアクションをリダイレクトすることを思い出させる場合があります。 実際、これらはまったく異なる2つの方法です。 Kohanaのリクエストオブジェクトは、一意のリクエストとして単独で動作し、アクションをリダイレクトする場合、作成されたリクエスト内にそれぞれ存在します。 以下は、これを示す例です。

デフォルトのコントローラー-/ application/ controllers/ default.php
 <?php //     class Controller_Default extends Controller { public function action_index() { //     GET if ($this->request->method === 'GET') { //  POST,  array (0) { empty } var_dump($_POST); //       $log = Request::factory('/log/access/'.$page_id); //  POST     $log->method = 'POST'; //  -    $log->post = array( 'uid' => $this->user->id, 'ua' => Request::user_agent('browser'), 'protocol' => Request::$protocol, ); //   $log->execute(); //  POST,   array (0) { empty } var_dump($_POST); } } } 

ログコントローラー-/ application/ controllers/ log.php
 <?php //     class Controller_Log extends Controller { public function action_access($page_id) { //     index ( ) //       // array(3){string (3) 'uid' => int (1) 1, string (2) 'ua' => string(10) 'Mozilla... var_dump($_POST); //    Log $log = new Log_Model; //     $log->set_values($_POST) ->save(); } } 

上記の例は、Requestオブジェクトで利用可能な独立性を示しています。 最初に、GETリクエストがデフォルトコントローラーのインデックスアクションを呼び出し、それに応答して、ログコントローラーのアクセスアクション用のPOSTリクエストが作成されます。 インデックスアクションは、最初のコントローラーからグローバル$_POST配列にアクセスできない3つの変数に値を割り当てます。 2番目の要求が実行されるとき、 $_POST既に利用可能な変数が含まれています。 Index $log->execute()$log->execute()が完了すると、 $_POSTデータが消えることに注意してください。 他のフレームワークでは、この相互作用の実装には、Curlなどを使用した新しいクエリの作成が必要です。

Gazouillement、マイクロブログサービス


Gazouillementと呼ばれる架空のショートメッセージサービス(ステータス)は、本質的にTwitterの類似物であり、HMVCの力を示すのに役立ちます。 Webインターフェースとメッセージングおよび関係コンポーネントが分離されるように、サービス指向アーキテクチャー(SOA)に基づいて設計されたとします。

サービスは新しく、人々はまだそれについて知らないので、最初は、トラフィックは比較的小さくなります。 したがって、今のところすべてのアプリケーションロジックが1つのサーバーで実行される場合、何も悪いことは起こりません。

ユーザーのホームページを表示するコントローラーを作成しましょう。 最新のステータスと、ユーザーがフォローしているユーザーのリストが表示されます。

インデックスコントローラー-/ application/ controllers/ index.php
 <?php //    http://gazouillement.com/samsoir/ class Controller_Index extends Controller { public function action_index() { //   (samsoir)   $user = new Model_User($this->request->param('user')); //    ,   404 if ( ! $user->loaded) throw new Controller_Exception_404('Unable to load user :user', array(':user' => $this->request->param('user'))); //      xhtml $messages = Request::factory('messages/find/'.$user->name.'.xhtml') ->execute() ->response; //      xhtml $relations = Request::factory($user->name.'/following.xhtml') ->execute() ->response; //        //     ,    $this->request->response = View::factory('user/home', array( 'user' => $user, 'messages' => $messages, 'relations' => $relations, )); } } 

それでは、 Controller_Index::action_index()機能を見てみましょう。 最初に、アクションはURLのユーザーパラメーターに基づいてユーザーをロードしようとします。 これが成功しない場合、404ページが表示され、xhtml形式でメッセージを受信するために、リクエストURIの対応するクラスプロパティからのユーザー名を使用する新しいリクエストが作成されます。 同様に、同じ形式で、ユーザー関係が要求されます。 その結果、Viewクラスのオブジェクトは、ユーザー、そのメッセージ、およびそれに関連付けられたリレーションで作成されます。

新しいリクエストを介して既存のメッセージと関係をロードする場合、各サービスのアプリケーションロジック全体がWebサイトから抽象化されたままになります。 このアーキテクチャには、通常のコントローラーの実行に比べて2つの重要な利点があります。
  1. コントローラは、メッセージサービスロジックのどの部分の影響も受けません。 唯一の要件は、結果がxhtml形式でなければならないことです。 実行時に、追加のライブラリまたは拡張機能はロードされませんでした。
  2. 各コントローラーは特定のタスクを1つだけ担当するため、ユニットテストの記述が大幅に簡素化されます。

今示した抽象化のレベルのため、サービスが何をしているかを見るのは不可能です。 したがって、メッセージサービスコントローラーに注目しましょう。 メッセージリクエストの内部管理を担当するブートファイルのルートから始めましょう。 KohanaのRouteクラスは、渡されたURI要素をコントローラー、アクション、およびパラメーターに関連付けることにより、内部URLを解析します。

ルート設定-/ application/bootstrap.php
 Route::set('messages', 'messages/<action>/<user>(<format>)', array('format' => '\.\w+')) ->defaults(array( 'format' => '.json', 'controller' => 'messages', )); 

これにより、メッセージングサービスのルートが作成されます。これは、これまでのところメインアプリケーションのドメイン内にあります。 アドレスmessages / find / samsoir.xhtmlを介したリクエストはメッセージコントローラーにリダイレクトされ、そのfind()アクションはパラメーター'user' => 'samsoir'および'format => '.json'ます。

メッセージコントローラー-/ application/ controllers/ messages.php
 <?php class Controller_Messages extends Controller { //  ,    protected $supported_formats = array( '.xhtml', '.json', '.xml', '.rss', ); //      protected $user; //      //       public function before() { //        if ( ! in_array($this->request->param('format'), $this->supported_formats)) throw new Controller_Exception_404('File not found'); //     $this->user = new Model_User($this->request->param('user')); if ( ! $this->user->loaded()) throw new Controller_Exception_404('File not found'); return parent::before(); } //      public function find() { //         1:M $messages = $this->user->messages; //    ,   prepare    $this->request->response = $this->_prepare_response($messages); } //      protected function _prepare_response(Model_Iterator $messages) { //  ,      switch ($this->request->param('format') { case '.json' : { $this->request->headers['Content-Type'] = 'application/json'; $messages = $messages->as_array(); return json_encode($messages); } case '.xhtml' : { return View::factory('messages/xhtml', $messages); } default : { throw new Controller_Exception_404('File not found!'); } } } } 

ユーザーメッセージを詳細に受信するプロセスは、 Controller_Messages示されていController_Messages 。 すべてのメソッドとプロパティは、ユーザー関係を含むメッセージコンテキストにのみバインドされます。 このコントローラーのコードを見て、何が起こっているのかを理解しましょう。

Requestオブジェクトは、指定されたアクションを呼び出すbefore()に、常にbefore()メソッドbefore()呼び出します。 これにより、メイン作業の前にさまざまなルーチン操作を実行できます。 before()メソッドは最初に要求された形式のサポートをチェックし、次にユーザー名の正当性をチェックします。 before()が例外をスローせずに終了する場合、Requestオブジェクトはfind()アクションを呼び出しfind() 。 ユーザーメッセージをModel_Iteratorオブジェクトとして読み込みます。 ユーザー関係にメッセージが見つからない場合、イテレーターは空になることに注意してください。 最後に、メッセージイテレータを_prepare_response()メソッドに_prepare_response()ます。このメソッドは、出力用のデータを正しくフォーマットし、必要なすべてのヘッダーを設定します。

コントローラーController_IndexController_Messagesは両方とも、単一のアプリケーション要求によって実行されました。 それにもかかわらず、彼らはお互いの存在を知らなかった。 各開発者は、現在のコードベースを読んで、何がどこで実行されているかを理解できます。 これはHMVCのもう1つの優れた機能であり、メンテナンスが容易です。

残りのサービスのコードの作業を終えた後、企業幹部は開発の第1段階に満足を表明し、限定的なベータテストのためにオープンサーバーでの展開を許可しました。 数週間後、アプリケーションは大量使用の準備が整います。 今後数か月にわたって、視聴者は絶えず成長し、サイトのアーキテクチャ全体が改善および最適化されます。

スティーブンフライ効果


Stephen Fry( @stephenfry )は、200万人を超えるフォロワーを持つ最も有名なTwitterユーザーの1人です。 彼は自分のマイクロブログにURLを公開するだけでサイトを無効にし、サーバーにDDOS攻撃を仕掛けることができます。

Gazouillementは過去数か月間、積極的にユーザーを募集しています。 これにより応答時間が増加しましたが、まだ許容範囲内です。 突然、Stephen Fryのような膨大な数のフォロワーを持つTwitterユーザーがGazouillementへのリンクを投稿しています 。 そして、サービスには実際の問題が生じ始めます。

最初は大量のトラフィックになります。 1秒あたり通常の数百のリクエストの代わりに、アプリケーションは数千を処理する必要があります。 このシナリオでは、 Gazouillementは輻輳を経験し始め、サーバーを「置く」可能性があります。 著しく増加するトラフィックに対処するには、アプリケーションに最適化と改善が必要であることが明らかになります。

コードを分析してパフォーマンスを改善するのは簡単なことではありません。 以前の TechPortalの 記事で 、実行時のメモリとプロセッサの詳細な分析のために設計されたXHProfなどのユーティリティを取り上げました 。 Kohana PHPには、Profilerと呼ばれるカーネルパフォーマンス分析プログラムが組み込まれています。 ただし XHProfおよび同様のユーティリティの代替ではなく 、補完的なものです。 プロファイラーを使用すると、開発者は実行可能コード内の遅い場所を検出でき、XHProfまたはフレームワークに組み込まれた別のモジュールCodebenchを使用して詳細に調べることができます。

ブートローダーでパラメーターを1つだけ変更することで、プロファイラーを有効にできます。

ローダー-/ application/bootstrap.php
 //-- Environment setup -------------------------------------------------------- Kohana::$profiling = TRUE; 

最後に、コントローラーの応答の最後にプロファイラービューを追加します。 これはafter()メソッドを使用して行うのが最適で、すべてのアクションに対してプロファイラーが表示されます。

インデックスコントローラー-/ application/ controllers/ index.php
 public function after() { //       $this->request->response .= View::factory('profiler/stats'); } 

プロファイラーをオンにすると、このコントローラーからの各アクションの下部にその情報が表示され始めます。 Controllerを継承するクラスを作成し、プロファイラービューをこのクラスのafter()メソッドに挿入することをお勧めします。 現在、すべてのシステムコントローラーは、開発中にプロファイラーのデバッグ情報を表示します。



プロファイラーは、フレームワークの作業についてレポートし、さまざまなコンテキスト(初期化、クエリ、データベースへのアクセスなど)をグループ化し、対応するプロセッサー時間と割り当てられたメモリーの量を表示します。 作成された各リクエストの前にある情報を見ると、時間がかかりすぎるリクエストを計算する方がはるかに簡単です。 これが発生した場合、XHProfなどのユーティリティを使用して、より詳細な分析を行うことができます。

Gazouillementのスケーリング


Gazouillementのグローバルパフォーマンス分析 、メッセージの受信が非常に難しいこと示しています。 開発チームは対応するMVCトライアドをリファクタリングおよび最適化しましたが、目的のパフォーマンスメトリックが得られることはありませんでした。 サーバーのプロセッサとメモリを特に生産的に改善しなかった後、企業の幹部はメッセージングシステムから始めて、アプリケーションを水平方向に拡張することに同意します。

MVCアーキテクチャでは、通常、新しいサービスは設計、開発、受け入れテスト、実装の段階を経る必要があります。 このプロセスには数か月かかることがあり、多大な投資が必要です。 しかし実際には、 Gazouillementでは、新しいプログラムではなく、新しいソフトウェアが統合されます。

HMVCに基づくアプリケーションでは、既存のコードベースに基づく新しいサービスを最短時間で作成できます。 メッセージサービスとのすべての対話はメインコントローラーを介して実行されるため、メッセージ要求のみを変更する必要があります。

メッセージングサービスは別のサーバーに移動され、データベースを操作するために最適化されました。 この新しいサーバーはメッセージング関連のアクションのみを実行するため、このようなすべての操作の速度が大幅に向上します。

インデックスコントローラー-/ application/ controllers/ index.php
 <?php //    http://gazouillement.com/samsoir/ class Controller_Index extends Controller { protected $_messages_uri = 'http://messages.gazouillement.com'; public function action_index() { //   (samsoir)   $user = new Model_User($this->request->param('user')); //    ,   404 if ( ! $user->loaded) throw new Controller_Exception_404('Unable to load user :user', array(':user' => $this->request->param('user'))); // --    -- //  URI     $messages_uri = $this->_messages_uri.'/find/'.$user->name'.xhtml'; //       xhtml $messages = Request::factory($messages_uri) ->execute() ->response; // --    -- //      xhtml $relations = Request::factory($user->name.'/following.xhtml') ->execute() ->response; //        //     ,    $this->request->response = View::factory('user/home', array( 'user' => $user, 'messages' => $messages, 'relations' => $relations, )); } } 

コードでは、コントローラーController_Index::action_index()で小さな変更が強調表示されていController_Index::action_index() 。 メッセージリクエストは、内部コントローラーのアクションではなく、 messages.gazouillement.comサブドメインで実行されるメッセージサービスに送信されるようになりました。 通常、プロジェクトのアーキテクチャ全体のレビューにつながるものは、小さな追加になりました。 また、コードはほとんど変更されていないため、関連性テストと受け入れテストは大幅に短縮されます。

これは、Hierarchical-Model-View-Controllerパターンを使用して、開発者が最初から垂直および水平に拡張できるWebアプリケーションを設計する方法のほんの一例です。 Requestオブジェクトを使用すると、コントローラーがシンプルなインターフェイスを使用して各コンテキストの正しいデータを返すことができます。 サーバー外のアプリケーションの一部を削除するのが非常に簡単な方法であることがわかりました。 また、スケーリングのコストは、少数の変更により比較的低かったため、会社の幹部は非常に満足していました。

Kohanaの詳細については、フレームワークのWebサイトを参照してください。KohanaPHP 3ファイルはGithubからダウンロードできます。 まだAPIドキュメントがあります: http : //v3.kohanaphp.com/guide

この例で使用されているクエリクラスは、現在、個人のgithubアカウント( http://github.com/samsoir/core)のKohanaカーネル開発ブランチで利用できます 。 公式Kohana PHP 3.0ディストリビューションを使用している場合、クエリ用の修正されたクラス拡張が必要です

Source: https://habr.com/ru/post/J113117/


All Articles