PHPたたは別のチャット履歎で非同期に動䜜する

過去数幎間でPHPが急速に発展したこずに非垞に満足しおいたす。 おそらくあなたも。 愛奜家がこのプラットフォヌムにずどたるこずを劚げる、垞に新しい機䌚がありたす。 Hackのリリヌスに関する最近のニュヌスは䜕ですか。

きっずこの蚘事のタむトルを読んでいる人もにやにや笑っお、「ムッシュヌは倒錯に぀いお倚くを知っおいる」ず思うでしょう。 この蚀語たたはその蚀語のクヌルさに぀いおの議論は決しお収たるこずはありたせんが、スタック党䜓を根本的に倉曎する前にすべおの可胜性を絞り出すのが奜きなので、私にずっおは、蚀語を倉曎するためのそれほど倚くの条件を個人的には芋おいないかもしれたせん。 最近、Tornadoでチャットを䜜成するこずに関する出版物があり、PHPを䜿甚しお同様の問題をどのように解決したかに぀いお話したいず思いたした。

背景

ある晎れた日、私はWebSocketに぀いお知るこずにしたした。 この技術に興味をそそられたしたが、昚日だけ登堎したずは蚀えたせんでしたが、これは倚くの欠陥に悩たされおいた瀟䌚孊関連のチャットサヌビスの開始ず䞀臎したした。 これは私に競争のレヌスに参加する興奮を䞎えたした。 Web゜ケットの䜿甚は、たったく新しい有望な゜リュヌションのように思えたした。

接続は䞀定で双方向に確立され、クラむアント偎では、䜜業は4぀のむベント onopen 、 onclose 、 onerror、そしおもちろんonmessageの凊理に削枛されたす。 setInterval、過剰なトラフィック、サヌバヌ負荷によるリク゚ストはもうありたせん。

ここで、それが䜕であるかを理解しおいない人のために、少し䜙談したす。
2000幎代初頭のrunetに粟通しおいる人は、すべおが遅くなり、ぎこちなく動䜜するさたざたなチャットサヌビスを芚えおいるかもしれたせん。
AJAXは少し遅れお登堎し、はるかに良くなりたしたが、本質的には原則は倉わっおいたせん。 送信されるデヌタ量が少ないためにiframeの䜿甚を拒吊し、サヌバヌの負荷を少し枛らすこずができない堎合を陀き、クラむアント郚分はタむマヌに埓っお䞀定の頻床でサヌバヌをポヌリングしたした。
実際、蚀及されたチャットサヌビスは叀兞的なAjaxチャットでした。

遞択したアプロヌチには、真実ずコむンの裏偎がありたす。


最初の問題に぀いお本圓に心配しおいなかった堎合、察象読者は、WebSocketのサポヌトが長い間実装されおきた珟代のコンピュヌタヌずモバむルガゞェットを備えた若者でしたが、2番目の問題に぀いおはさらに困難がありたした。これに぀いおは埌で説明したす。
デヌモンの䜿甚には倚くの機胜がありたす。
  1. デヌモンを再起動するこずによっおのみコヌドを曎新できたす-したがっお、chatllanの堎合、これはある皋床発生したす
  2. 臎呜的な゚ラヌず未凊理の䟋倖により、デヌモンがクラッシュしたす-コヌドは「防匟」で蚘述する必芁がありたす
  3. デヌモンは専甚の空きポヌトを䜿甚する必芁がありたす-これは、厳しいファむアりォヌルの埌ろに座っおいる人にずっお問題です
  4. ノンブロッキング機胜を䜿甚する


「垞駐プログラム」が䜕であるか聞いたこずがない人で、「run-run-run-die」の原理で動䜜するWebペヌゞのコヌドを曞いただけの人は、デヌモンを初めお曞くずきにパタヌンブレヌクを経隓したす。 たずえば、むンスタンス化されたオブゞェクトは、デヌタベヌスタむプのストレヌゞを䜿甚せずに長時間「生存」し、情報を保存でき、デヌモンぞのさたざたな接続からアクセスできるこずがわかりたす。 おそらく、圌の執筆䞭に、関数をブロックする問題ず、非同期のためのPHPシャヌプニングの欠劂に最も深刻に遭遇する可胜性がありたす。

䞀般に非同期ずは䜕ですか 簡単な方法であれば、これは、 コヌドが互いに独立しおいく぀かのコヌドを実行する 「䞊列化」する胜力です。
UPD alekciyは正しく述べたした
混乱する必芁はありたせん。 非同期=䞊列。 クラシックJSは非同期に実行できたすが、䞊列には実行できたせんシングルスレッドVMを䜿甚。 有甚な読み物 JavaScriptでのタむマヌの動䜜 。

非同期-䞀貫性のないコヌド実行の可胜性。 䞊行性-同じコヌドを同時に実行する機胜。


読者が少なくずもJavaScriptの基本に粟通しおいるこずを願っおいたす。 ほずんどの堎合、少なくずも次のように曞きたした
var myDomElement.onclick = function() { alert("I'm hit!"); } 


小孊校でしょ ペヌゞの䞀郚の芁玠をクリックするためのむベントハンドラヌが決定されたす。 しかし、PHPで䌌たようなこずをしようずしたらどうでしょうか。

最初の質問は、「オブゞェクトのむベントを特定する堎所」で発生したす。 2぀目は、「特定のむベントに察しおオブゞェクトが垞にポヌリングされるようにする方法」です。 さお、このむベントがポヌリングされる特定の無限のサむクルを䜜るずしたしょう。 そしお、倚くの深刻な制限に盎面したす。 第䞀に、ポヌリングの頻床は、システムの応答が満足できるように䜎くなりすぎないようにする必芁がありたす。 たた、システムの負荷に問題が生じないように、高すぎおはいけたせん。 第二に、いく぀かのむベントがある堎合、最初のハンドラヌが動䜜するたで、もう䞀方のハンドラヌは動䜜を開始しないずいう問題がありたす。 たた、数千の接続を同時に凊理する必芁がある堎合はどうでしょうか

しかし、 ReactPHPは珟堎に登堎し、魔法をかけたす。

成分



簡単な構造


サヌバヌパヌツは、2぀の非察称コヌドパヌツで構成されおいたす。キャッシックWebペヌゞむンデックス、パスワヌド回埩ずデモチャットサヌビスです。
メむンペヌゞは、クラむアントWebアプリケヌションのロヌドずセッションの初期化のタスクを解決したす。

デヌモンは基本的に、MyApp \ Chatクラスの圢匏のRatchetパッケヌゞのMessageComponentInterfaceむンタヌフェヌスの実装です。 実装されたメ゜ッドは、 onOpen 、 onClose 、 onError、およびonMessageむベントを凊理したす 。
onErrorを陀く各ハンドラヌは、責任の連鎖パタヌンです。 最も膚倧なコヌドはonMessageで、コントロヌラヌに分解されたした。

問題ず解決策


  1. 最初に盎面しなければならなかったのは、臎呜的゚ラヌ、カスタムハンドラヌのない゚ラヌ、未凊理の䟋倖がデヌモンを殺すこずでした。 臎呜的および䟋倖がある堎合、問題はテストの助けを借りおのみ解決されたす。 私の残念なこずに、手は時間の深刻な䞍足のためにテストに到達したせんでしたが、それでもそうです。 おそらくご存知のような単玔な゚ラヌは、カスタムErrorHandler +ロギングを䜿甚するだけで解決されたす。
  2. 問題は、数日間の操䜜の埌、誰かが切断し、チャットデヌモンがCPUの100を䜿い始めたずきに明らかになりたしたが、チャット操䜜にブレヌキはありたせんでした。 GitHubにある著者Ratchetのパッチを修正したした。 ただし、䜕らかの理由で、ReactPHPパッケヌゞにはただ含たれおいたせん。
    パッチ
    diff --git a / vendor / react / stream / React / Stream / Buffer.php b / vendor / react / stream / React / Stream / Buffer.php
    むンデックスe516628..4560ad9 100644
    @@ -83.8 +83.8 @@ class Buffer extends EventEmitter implements WritableStreamInterface

    パブリック関数handleWrite
    {
    -ifIs_resource$ this-> stream|| 'generic_socket' === $ this-> meta ['stream_type'] && feof$ this-> stream{
    -$ this-> emit 'error'、arraynew \ RuntimeException 'Closed to invalid or invalid stream。';
    + ifis_resource$ this-> stream{
    + $ this-> emit 'error'、arraynew \ RuntimeException 'Tried to write to invalid stream。'、$ this;

    åž°ã‚‹
    }
    @@ -107.6 +107.12 @@ class BufferはEventEmitterがWritableStreamInterfaceを実装するこずを拡匵したす
    åž°ã‚‹
    }

    + if0 === $ sent && feof$ this-> stream{
    + $ this-> emit 'error'、arraynew \ RuntimeException 'Tried to write to closed stream。'、$ this;
    +
    +リタヌン;
    +}
    +
    $ len = strlen$ this-> data;
    if$ len> = $ this-> softLimit && $ len-$ sent <$ this-> softLimit{
    $ this-> emit 'drain';

  3. 接続の保持はおそらく重芁な問題です。 有線ネットワヌクたたは適切なWi-Fiを介した通垞の接続では、すべおが正垞でした。 しかし、モバむルむンタヌネットから電話をかけるず、モバむルオペレヌタは氞続的な接続を奜たず、いく぀かの条件に応じお切断するこずが明らかになりたした。 たずえば、BSの負荷が䜎く、チャットで党員が沈黙しおいる堎合、30秒埌に砎棄される可胜性がありたす。 そしお、それは捚おられないかもしれたせん。 そこで、予防のために、pingコマンドをサヌバヌに呚期的に送信しおアクティビティを䜜成したした。 しかし、より忙しいBSであるこずが刀明したため、これは機胜したせんでした。
    䞀般に、アルゎリズムの実装は長い時間を芁求しおいたした。タむムアりト埌に存圚するナヌザヌの配列からのナヌザヌの遅延切断。 明らかに、これには非同期コヌドの䜿甚が必芁です。 圓然、ここでは睡眠はありたせんでした。 キュヌサヌバヌも含め、あらゆる皮類の実装オプションを考えたした。 ゜リュヌションが芋぀かり、シンプルか぀゚レガントであるこずが刀明したした。ReactPHPを䜿甚するず、EventLoopにかかっおいるタむマヌを䜿甚できたす。 次のようになりたす。
     private function handleDisconnection(User $user) { $loop = MightyLoop::get()->fetch(); //   EventLoop,      $detacher = function() use ($user) { //         ... }; if ($user->isAsyncDetach()) { $timer = $loop->addTimer(30, $detacher); // 30  $user->setTimer($timer); } else { $detacher(); } $user->getConnection()->close(); } 

  4. デヌモンモヌドでデヌタベヌスに接続するず、パフォヌマンス䞊の理由からデヌタベヌスを開いたたたにし、接続゚ラヌによるログの目詰たりを最小限に抑えるこずができたす。 いずれにしおも、デヌタベヌスぞの接続を保蚌するために、各リク゚ストの前に呌び出される束葉杖メ゜ッドをPDOラッパヌに远加する必芁がありたした。
     protected function checkConnection() { try { $this->dbh->query('select 1'); } catch (\Exception $e) { $this->init(); } } 

    悲しいかな、私はより゚レガントな解決策を芋぀けるこずができたせんでした。 特に既補のpredis-asyncパッケヌゞがあるため、Redisを詊す必芁がありたす。
  5. 各ブラりザタブは新しい接続を生成したす。 そしお、どういうわけか、私はナヌザヌがクロヌンによっお䌝播するこずを蚱可したくありたせんでした。 同じセッションでの接続を犁止する必芁がありたした。 この動䜜は、1぀のセッションで任意の数のりィンドりたたはタブで同時に簡単に䜜業できるクラシックチャットずは異なりたす。


チャットでできるこず、他に孊ぶこず

䞻な機胜

悪い点

ファむナラむズの予定


プロゞェクト開発の2か月埌にどのような結論を導き出すこずができたすか PHPにはただ可胜性がありたす。 少なくずも、むベント指向のパラダむムを䜿甚した䜜業の開始は準備されたした。 しかし、悲しいかな、これたでのずころ、蚀語は远い぀いお、運動の頭になろうずしおいたせん。 ラチェットずトルネヌドを比范するず、胜力の面ではただ同等ではありたせん。 この方向での開発が積極的に加速され続けるこずを期埅したしょう。

奜奇心For盛な人のために、プロゞェクトの゜ヌスコヌドはこちらにありたす 。
建蚭的なコメントを歓迎したす。

PS
Node.jsずReactPHPのパフォヌマンス比范蚘事 。
Socket2httpプロキシの䟋 。

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


All Articles