ゴリアテの紹介

読者にさまざまなWebフレームワークを紹介する一連の記事を続けます。 そして今日、Goliath(Goliath、 http: //postrank-labs.github.com/goliath/)を紹介します。これは、PostRank( http://postrank.com/ )によって作成され、現在Googleに買収されています。



Goliathの主な機能は、Ruby 1.9で登場したファイバー(ファイバー)のメカニズムと同様に、EventMachineライブラリーを介した入出力用のイベントモデルの使用です。 今日のファッショナブルなNode.jsの類似物と考えることができます。



記事では、このような質問を検討します。 結論として、従来のパフォーマンステストが見つかります。



ファイバーとイベント





ファイバーは、スレッドに論理的に似た一種の実行コンテキストですが、スレッドではありません。 スレッドはタスクの並列化に使用され、ファイバーは非同期I / Oにより適しています。 実際、ファイバーはスレッドに似た抽象概念に包まれた高度なgotoであり、ファイバーのみが実際に同じ物理スレッドで実行されます。 現在のスレッドで、オペレーティングシステムの意志により、コードの任意の場所で実行が中断された場合、ファイバーの場合、開発者はプログラムコードの別のセクションにいつどこで制御を移すかを決定します。



Rubyのファイバーは、Fiberクラスとその新しい、yield、およびresumeメソッドによって実装されます。 ファイバーは、スレッドと同様に実行できるコードのブロックとして作成されますが、すぐには開始されません。 その後、一部のファイバーの再開呼び出しにより、ブロック内で制御が移ります。 ファイバー内のコードブロックは、終了するか、yieldコールが満たされるまで実行されます。 yield呼び出しとは、この場所でコードを一時停止し、状態を記憶して、resumeを呼び出したメインコードに進むことです。 通常、すべてのファイバーに共通のイベントループがあり、ファイバーの1つが作業を終了または一時停止するたびに制御が転送されます-イベントループ。 ループでは、プログラムはイベントの発生を待機し、これらのイベントが予期したファイバーの呼び出しを再開します。



このアプローチには、通常のスレッドよりも多くの利点があります。



ただし、制限を覚えておく価値があります。

繊維の仕組みの簡単な例を次に示します。



require 'fiber' # Fiber.new     , #      ,    resume. my_fiber = Fiber.new do puts 'fiber> started' Fiber.yield #   ,   . puts 'fiber> resumed' end puts 'main> let\'s start our fiber:' my_fiber.resume puts 'main> we\'re back in the main flow. Let\'s resume the fiber again:' my_fiber.resume puts 'main> end.'
      
      







バージョン1.9以降、ファイバーはRubyでサポートされています。 Goliathと他の興味深いライブラリの著者であるIlya Grigorikブログで 、ファイバーとEventMachineライブラリの詳細を読むことができます。



作業環境の設定





Helicon Zooのインストール方法については説明しませんが、製品のホームページhttp://www.helicontech.com/zoo/で詳細に説明されています



Goliathをインストールするには、Web Platform Installerを起動し、Zoo-> Engines-> Goliathを選択します。 Goliathをインストールすると、Ruby 1.9.3がまだシステムにない場合は自動的にインストールされます。 これは現在、最適なバージョンです。 GoliathはJRubyをサポートしていますが、ファイバーはそこでストリームを介して実装されるため、速度はRuby 1.9の場合よりもはるかに遅くなります。 JRubyチームは、近い将来にファイバーサポートを改善する予定です。



前の記事で、 WebMatrixとIIS Expressを使用して新しいアプリケーションを作成する方法を示しました。 今回は、システムにWebMatrixとIIS Expressをインストールせずに、IISマネージャーから同じことを直接行う方法を示します。 このリンクに従って、Goliathプロジェクトのzipファイルをダウンロードします。 次に、IISマネージャーを起動し、新しいWebサイトを作成して、タブで[デポリ]-> [アプリケーションのインポート]を選択します。 次に、ダウンロードしたファイルを見つけて、ウィザードの指示に従います。







まあ、起こっていることは魔法のように見えないように、ゴリアテのプロジェクトは非常にシンプルであることを追加する価値があります。 フォルダーと権限はなく、デプロイスクリプトもありません。 アプリケーションフォルダーにapp.rbとweb.configの2つのファイルが作成されます。 以下に、コメント付きのweb.configの内容を示します。 IISアプリケーションでこのようなファイルを作成するだけで、そこで動作するGoliathアプリケーションを取得できます。



 <?xml version="1.0" encoding="UTF-8"?> <configuration> <system.webServer> <heliconZoo> <!--      --> <application name="goliath.project" > <environmentVariables> <!--      --> <add name="APP_WORKER" value="app.rb" /> <!-- Deploy-,      .. --> <add name="DEPLOY_FILE" value="deploy.rb" /> <!--     deploy- --> <add name="DEPLOY_LOG" value="log\zoo-deploy.log" /> <!--    --> <add name="RACK_ENV" value="development" /> </environmentVariables> </application> </heliconZoo> <handlers> <add name="goliath.project#x86" scriptProcessor="goliath.http" path="*" verb="*" modules="HeliconZoo_x86" preCondition="bitness32" resourceType="Unspecified" requireAccess="Script" /> <add name="goliath.project#x64" scriptProcessor="goliath.http" path="*" verb="*" modules="HeliconZoo_x64" preCondition="bitness64" resourceType="Unspecified" requireAccess="Script" /> </handlers> <!-- Rewrite       --> <!--       /public/   --> <!--   IIS   --> <rewrite> <rules> <rule name="Avoid Static Files" stopProcessing="true"> <match url="^(?!public)(.*)$" ignoreCase="false" /> <conditions logicalGrouping="MatchAll" trackAllCaptures="true"> <add input="{APPL_PHYSICAL_PATH}" pattern="(.*)" ignoreCase="false" /> <add input="{C:1}public\{R:1}" matchType="IsFile" /> </conditions> <action type="Rewrite" url="public/{R:1}" /> </rule> </rules> </rewrite> </system.webServer> </configuration>
      
      







私たちは最初のアプリケーションを書いています





GoliathとIISを使用して長期ポーリングを実装する可能性を示すために、簡単なチャットを作成します。 サーバー側(Ruby、Goliath)とクライアント側(JavaScript)の2つの部分で構成されます。 コードを編集するには、エディターまたは開発環境が必要です。 Aptana( http://aptana.org )を使用しました:







サーバー側-app.rbファイル:



 require 'rubygems' require 'goliath' require 'cgi' class Chat < Goliath::API use Goliath::Rack::Params #   json use Goliath::Rack::Render, 'json' #   callbacks def callbacks @@callbacks ||= [] end #     def response( env ) case env[ 'PATH_INFO' ] when '/' #  index.html [200, {'Content-Type' => 'text/html; charset=utf-8'}, File.read( 'index.html' ) ] when '/send' on_send( env.params ) when '/recv' on_recv end end #         . def on_send( params ) #    .       , #   on_recv. until callbacks.empty? do callbacks.shift.call({ nickname: CGI.escapeHTML( params[ 'nickname' ] || 'Anonymous' ), text: CGI.escapeHTML( params[ 'text' ] || '' ), color: CGI.escapeHTML( params[ 'color' ] || '' ) }) end [200, {}, {status: 'ok'}] end #   long-polling.      ,    ,   . def on_recv #        ,    #         . #  req_fiber    . req_fiber = Fiber.current callbacks.push(proc {|message| req_fiber.resume( message ) }) #   . #   resume         . response = Fiber.yield( nil ) [200, {}, response] end end
      
      







クライアント側、index.html:



 <!DOCTYPE html> <html> <head> <title>Goliath + Helicon Zoo chat</title> <style type="text/css"> body { font-family: Sans-Serif; font-size: 13pt; padding: 0 6px; } h1 { font-family: "Trebuchet MS", Sans-Serif; font-size: 1.5em; color: #FF9933; } #messages { list-style: none; margin-top: 20px; } </style> </head> <body> <h1>Goliath + Helicon Zoo chat</h1> <form action="/send" method="post" id="send"> <label for="nickname">Nickname:</label> <input name="nickname" size="10" id="nickname" /> <label for="text">Message:</label> <input name="text" size="40" id="text" /> <input type="submit" value="Send" /> </form> <li id="messages"></li> </body> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6/jquery.min.js" type="text/javascript"></script> <script type="text/javascript"> //   Submit     function on_send( evt ) { evt.preventDefault(); var arr = $(this).serializeArray(); var message = { nickname : arr[ 0 ].value, text : arr[ 1 ].value, color: window.ClientColor }; $.post( '/send', message, function( data ) { $('#text').val( '' ).focus(); }, 'json' ); } //      function long_polling( message ) { if ( message ) { var $li = $( '<li><b style="color: ' + message.color + ';">' + message.nickname + ':</b> <span>' + message.text + '</span>' ); $li.hide().appendTo('#messages').slideDown(); } //  ,   $.ajax({ cache: false, type: 'GET', url: '/recv', success: long_polling }); } //     $(document).ready(function(){ window.ClientColor = '#' + Math.floor( Math.random() * 16777215 ).toString( 16 ); $('form#send').submit( on_send ); long_polling(); $('#nickname').focus(); }); </script> </html>
      
      







on_recvメソッドに注意してください。 現在のファイバーを取得し、保留中のハンドラーの配列に追加します。 より正確には、chopプロシージャをそこに配置します。このプロシージャでは、再開メソッドが呼び出され、制御がファイバに転送されます。 req_fiber変数は、ローカルではありますが、プロシージャのコンテキストでは「閉じる」ようです。 次に、ファイバーを直ちに停止します。 メッセージが到着すると、すべてのプロシージャが順番に呼び出され、配列から削除されます。



何が起こったのか試してみましょう:







性能試験





サーバーとしてのテストマシンは、Core 2 Quad 2.4 Ghz、8 Gb RAM、ギガビットネットワークです。 負荷を生成するために、コマンド「ab.exe -n 100000 -c 100 -k」とともに、より強力なコンピューターとApache Benchmarkが使用されました。 オペレーティングシステム-Ubuntu 11.04 Server x64およびWindows Server 2008 R2。 virtualoksはありません-正直な鉄。



3つのテストが実行されました。 最初のゴリアテでは、アプリケーションは単に現在の時刻を高解像度でページに表示することでした。 回答がキャッシュから送信されないようにするための時間が必要です。 2番目のテストはMySQLデータベースから読み取り、3番目のテストはデータベースに書き込みます。



テストには、Ruby 1.9.3、Goliath 0.9.4、およびMySQL 5.1.54を使用しました。 IIS、Apache、およびNginxのすべての構成は、HTTPプロキシを使用しました。 Goliath自体はHTTPサーバーです。



結果は次のとおりです(グラフの値は1秒あたりのリクエスト数です)。







最初のテストのより詳細なabグラフ:















結論





Goliathは、軽量でシンプルで便利なフレームワークです。 特に、さまざまなAPIと非同期コードの作成に適しています。 このソリューションは産業環境で繰り返しテストされており、良好な速度を示しています。 そして最も重要なことは、アプリケーションを開発するときに広大なRubyエコシステムを使用できるようにすることです。




All Articles