サーバアーキテクチャ

本章では,Bokehサーバの内部構成を“深く検討”する.この論文では、Bokehサーバの情報に慣れていると仮定します。 Bokehサーバの実行 それがそうです。

もしあなたがこのような人なら、この記事を見たいかもしれません。

  • Bokehコードベースで仕事をしようとしています

  • あなた自身のカスタムサーバプロセスを作成して、使用のために使用するのではありません bokeh serve

カスタムサーバプロセスは,TornadoのWebフレームワークを用いて余分なルーティング(WebページやRESTサイト)を追加することができる.

アプリケーション開発者が利用すれば bokeh serve 通常は bokeh.server 全然ありません。アプリケーション開発者はご利用いただけません Server カスタマイズまたは埋め込まれたサーバプロセスのようないくつかの専用の動作が実行されている場合、初期化される。

アプリケーション、セッション、および接続

各サーバには、1つまたは複数のアプリケーションが含まれています。アプリケーションをセッションテンプレートまたはセッション工場と見なすことができます。会話は以下の例と1対1の関係を持つ bokeh.document.Document :各セッションには1つの文書インスタンスがある.ブラウザがサーバに接続されると、新しいセッションが取得され、アプリケーションは、所望の任意の描画、ウィジェット、または他のコンテンツを使用してセッションの文書を充填する。アプリケーションはまた、定期的に実行するか、または文書変更時に実行するようにコールバックを設定することができる。

アプリケーションは Application 級友たち。これにはリストが含まれています Handler インスタンスおよびオプションのメタデータ。処理プログラムは様々な方法で作成することができます:JSONファイルから、Python関数から、Pythonファイルから、将来もっと多くの方法があるかもしれません。オプションのメタデータはJSON BLOBの形で /metadata 端点.例えば1つの Application インスタンスは、:

Application(metadata=dict(hi="hi", there="there"))

あると思います。 http://server/myapp/metadata 戻ります。 (application/json ):

{
    "data": {
        "hi": "hi",
        "there": "there"
    },
    "url": "/myapp"
}

各アプリケーションの周囲で、サーバが1つを作成する ApplicationContext それがそうです。その主な役割は、アプリケーションのためのセッションのセットを保存することである。

会話はクラスで表される ServerSession それがそうです。

各アプリケーションはルーティングを持っています app_path クライアントAPI)では、各セッションは1つのIDを有する。 Document インスタンス(サーバは、経路ごとにアプリケーションを検索し、その後、IDでセッションを検索する)。

各セッションには0-N個の接続があり, ServerConnection 級友たち。接続はWebSocket接続である.一般に,セッションは接続があれば継続するが,タイムアウト後にのみ有効期限が切れる(ページの再ロードを許可するなど).

アプリケーションやアプリケーションハンドラはアクセスできません Server ServerSession あるいは、あるいは ApplicationContext 2つの部分として定義されたより限られたインターフェースがあります ServerContext そして SessionContext それがそうです。 ServerContext いくつかの側面に限られたインタフェースを提供しています ApplicationContext そして Server そして同時に SessionContext いくつかの側面に限られたインタフェースを提供しています ServerSession それがそうです。これらのインタフェースの具体的な実現は以下のとおりである BokehServerContext そして BokehSessionContext それがそうです。

まとめ対象図:

  • Server implemented by BokehTornado

  • N個あります ApplicationContext

  • 1つあります Application 新しい会話を作ることができます

  • URLでそれを識別するための経路があります

  • 1つあります ServerContext アプリケーションコードが見えるサーバの様々な側面を表す.

  • N個あります ServerSession

  • セッションIDが1つあり,セッションと名付けられた文字列である

  • 1つあります Document 会話状態を表す

  • N個あります ServerConnection 会話に付加されたWebSocketを表示する

  • 1つあります SessionContext アプリケーションコードが見えるセッションの様々な側面を表す

竜巻. IOLoop 非同期コードと

サーバーで働くにはTornadoのことを知る必要があります IOLoop そして tornado.gen モジュールです。

Tornado文書は最高の資源になりますが、ここには迅速に理解しなければならない内容があります:

  • Bokehサーバはシングルスレッドであるため,“ブロッキング”コードを作成しないことが重要であり,IO待ちや長時間計算実行時にシングルスレッドのコードが枯渇することを意味する.そうすれば、アプリケーションユーザが見る遅延を迅速に増加させることができます。例えば、スライダを誰かが移動するたびに100 ms閉塞し、同時に10人のユーザがこの操作を実行している場合、10人のユーザのみの場合、ユーザは、10*100 ms=1 sの遅延を容易に見ることができる。

  • Tornadoでは、非ブロッキングコードは、関数または方法を用いてモデル化され、これらの関数または方法は返される。 Future 級友たち。ご覧のように @gen.coroutine 装飾師です。この修飾子は修飾されたメソッドを返しに変換する. Future それがそうです。

  • コードが実行されていない場合、Tornadoは IOLoop (“主ループ”または“イベントループ”と呼ばれることがあります)、これは、何かが起こるのを待っていることを意味します。何かが起こったとき IOLoop このイベントに興味のある任意のコールバックを実行する.

アプリケーションと IOLoop

アプリが竜巻に触れたくない IOLoop セッションが期限切れになったり、アプリケーションを再ロードしたりする場合には、セッションまたはアプリケーションに属するすべてのコールバックを削除できる必要があるので、コールバックを直接追加します。

To enable this, applications should only add callbacks using the APIs on Document and ServerContext. Methods on those classes allow applications to add_periodic_callback, add_timeout_callback, and add_next_tick_callback. We intercept these callback additions and are able to remove them when we unload an application or destroy a session.

ライフサイクル.

見てみると Application クラスでは,サーバは2つの方法でそれを呼び出すことができる.

  1. ♪the modify_document() 方法はそれが言ったように会話に入ってきました Document また、アプリケーションが修正することが可能である(いくつかの描画およびウィジェットが追加される場合がある)。

  2. a set of "hooks" on_server_loaded(), on_server_unloaded(), on_session_created(), on_session_destroyed().

これらの“フック”は,アプリケーションやセッションのライフサイクルで定義された時点で発生するため“ライフサイクルフック”と呼ばれる.

以下にライフサイクルにおける手順を示す.

  1. サーバプロセスが起動すると呼び出します on_server_loaded() アプリケーションごとに。

  2. クライアントが以前使用していなかったセッションIDを使用して接続すると、サーバが作成される ServerSession 電話と on_session_created() 空のを持っている Document そして、そして modify_document() 初期化する Document それがそうです。♪the on_session_created() 初期化部も可能です Document もしそれが好きなら。 on_session_created() その前に起こったこと modify_document() それがそうです。

  3. 会話に接続されていない場合、最終的にタイムアウトし、 on_session_destroyed() 呼ばれるだろう。

  4. もしサーバプロセスが完全にオフになったら、それは呼び出されます on_server_unloaded() アプリケーションごとに。これは生産では珍しいかもしれない:サーバプロセスは通常信号で殺される。 on_server_unloaded() リソースを漏洩することなく、アプリケーションを再ロードすることができるように、開発中により有用である可能性がある。

これらのフックは周期的または一度にコールバックを追加することができます ServerContext それがそうです。これらのコールバックは、非同期(Tornadoの非同期IOデバイスを使用)であってもよく、すべてのリアルタイムセッション文書を更新することができる。

Critical consideration when using ``on_server_loaded()`` :process-globalはcluster-globalとは異なる.Bokehアプリケーションを拡張すると、CPUコアごとに個々のプロセスが必要になります。クラスタ内のプロセスは同じ機械にさえ存在しない可能性がある.サーバプロセスは、“すべての存在するセッション”を知っていると仮定してはならず、“このプロセスでホストされているすべてのセッション”のみを知っている。

詳細: ServerSession

セッションオブジェクトは,クライアントとサーバ間のインタラクションの大部分を処理する.

鍵がしっかりしている

最も厄介な点は ServerSession 鍵がかかっているかもしれません。一般に、私たちは、コールバックまたはWebSocket要求を一度に処理することを望んでいる;コールバックおよび要求処理プログラムがインターリーブを心配しなければならない場合、それらを実現することが困難であるので、それらをインターリーブすることは望ましくない。

だから ServerSession 一度に一つしかしないことは、 ServerSession._lock これは竜巻の鍵です

ロックとスレッドをよく知っていれば、ここの状況は概念的に同じです。しかし、競合条件は“降伏点”でしか発生しません(私たちが戻ると IOLoop )ではなく、任意の点ではなく、ロックは、スレッドロックではなく、ドラゴンロックである。

The rule is: to touch ServerSession.document code must hold ServerSession._lock.

通過上の Document APIは,コールバックを実行する前に自動的にロックを取得し,それを解放する.

方法追加のコールバック ServerContext APIは,以下の命令を用いて会話文書への参照しか取得できない. SessionContext.with_locked_document() それがそうです。これは,文書ロックを持つ場合に提供される関数を実行し,その関数に文書を渡す.

警告

関数実行時にロックを維持する この関数が非同期であっても よし!もし関数が1つ戻ったら Future この鍵をずっと持っています Future できました。

It is very easy to modify the server code in such a way that you're touching the document without holding the lock. If you do this, things will break in subtle and painful-to-debug ways. When you touch the session document, triple-check that the lock is held.

会話の安全

我々が依存するセッションIDは暗号化ランダムであり,推測が困難である.攻撃者が誰かのセッションIDを知っている場合、彼らはそのセッションを盗聴または修正することができる。Bokehアプリケーションを組み込んだ大規模なWebアプリケーションを作成していると、大きなアプリケーションをどのように設計するかに影響を与える可能性があります。

サーバ上でハッカー攻撃を行う場合,ほとんどのセッションIDは不透明な文字列であり,最初にIDを検証した後,IDが何であるかはサーバコードにとっては無関係である.

会話がタイムアウトする

リソース枯渇を回避するために、使用されていないセッションは、中のコードによってタイムアウトします application_context.py

WebSocketプロトコル

サーバには、各クライアントにオープンなWebSocket接続があります(通常は各ブラウザオプションカードが使用されています)。WebSocketの主な役割は会話を保つことです Document クライアントとサーバの間で同期する.

Bokehライブラリには2つのクライアントが実装されています1つはPythonです ClientSession もう1つはJavaScriptです ClientSession それがそうです。クライアントとサーバセッションは基本的に対称である.双方とも、相手から変更通知を受けました Document また、当方が行った変更に関する通知を送ります。これでお二人は Document 同期を保つ。

WebSocketプロトコルのPython実装で bokeh.server.protocol クライアント側もサーバ側もそれを使用しているにもかかわらず.

WebSocketsはすでに“フレーム”を実現しており,フレーム到着順序が送信順序と同じであることを保証している.フレームは、文字列またはバイト配列(またはpingのような特別な内部フレームタイプ)である。ネットワークソケットは、各方向に1つのシーケンス(“全二重”)2つのフレームシーケンスのように見える。

WebSocketの枠組みの上で私たちは私たち自身の Message 概念です。アポック Message 複数のWebSocketフレームワークにまたがる.それは常にタイトルフレーム、メタデータフレーム、およびコンテンツフレームを含む。これら3つのフレームはそれぞれ1つのJSON文字列を含む.コードは、これら3つのフレームの後に任意のバイナリデータフレームに続くことを可能にする。例えば、原則として、これは、追加のコピーを必要とすることなく、NumPy配列をそれらのメモリバッファからWebSocketに直接送信することを可能にすることができる。しかしながら、バイナリデータフレームは、Bokehでは使用されていない。

ヘッダフレームは、メッセージタイプを示し、メッセージにIDを与える。メッセージIDは、応答および要求と一致するために使用される(返信は、“IDがxyzである要求の応答である”と書かれたフィールドを含む)。

メタデータフレームワークは現在何もないが、データのデバッグまたは将来の他の目的のために使用することができる。

コンテンツフレームはメッセージの“本文”を持つ.

今メッセージが多くありません。迅速な概要:

  • ACK 接続時の初期ハンドシェイクを設定するための

  • OK 要求がもっと具体的な返事を必要としない場合は、一般的な返事です

  • ERROR 誤りが発生した場合の一般的な誤り返信である

  • SERVER-INFO-REQ そして SERVER-INFO-REPLY 要求-応答ペアであり、応答には、そのBokehバージョンのようなサーバに関する情報が含まれている

  • PULL-DOC-REQ セッションの全内容の取得を要求する Document JSONとして PULL-DOC-REPLY 上記JSONを含む返信である.

  • PUSH-DOC 会話の全内容を送信する Document JSONとしては,他端はその文書をこれらの新しいコンテンツで置き換えるべきである.

  • PATCH-DOC 会話文書の変更を反対側に送信します

一般に、接続を開くと、一端は文書全体を引き込むか押し込むか、最初に引き込むか押し出すと、両端が使用される。 PATCH-DOC 伝言をお願いします。

現在の合意のいくつかの警告

  1. 現在のプロトコルでは,双方が同時に同じ内容を変更する衝突は処理されない(このような場合,双方は最終的に同期しない可能性がある. PATCH-DOC 同時飛行)。このような状況を検出するためのシナリオを設計することは容易であるが,それを検出するとどうすればよいのかよく分からないため,現在は検出されておらず,何もしていない.多くの場合、アプリケーションは、それを理解して何らかの方法で処理することができても、アプリケーションの双方が同じ値で“争い”をすることは非効率である可能性があるので、これを避けるべきである。(現実世界のアプリケーションがこの問題に遭遇した場合、彼らが何をしようとしているのかを明らかにし、解決策を設計しなければならないだろう。)

  2. 現在私たちは集合を修復するのに賢くありません Model 属性これは巨大な辞書で、その中のどの項目も変更されれば、私たちは巨大な辞書全体を送ります。

  3. At the moment, we do not optimize binary data by sending it over binary websocket frames. However, NumPy arrays of dtype float32, float64, and integer types smaller than int32 are base64 encoded in a content frame to avoid performance limitations of naive JSON string serialization. JavaScript's lack of native 64-bit integer support precludes them from inclusion in this optimization. The base64 encoding should be entirely transparent to all but those who look at the actual wire protocol. For more information, refer to bokah.util.serialization.

HTTP端点

サーバはいくつかのHTTPルーティングしかサポートしていません bokeh.server.urls それがそうです。

簡単に言うと

  • /static/ BokehのJSとCSSリソースにサービスを提供

  • /app_path/ 新しいセッションを表示するためのページ

  • /app_path/ws WebSocket接続URLです

  • /app_path/autoload.js 大きなJavaScriptを提供しています bokeh.embed.server_document() そして bokeh.embed.server_session() 機能性

Bokehサーバは汎用的なWebフレームワークではない.しかし新しいエンドポイントを Server 使用 extra_patterns パラメータとTornado API。

その他の詳細

事件

通常,モデル属性を修正するたびに,まず新しい値を検証し,その後 Document この変更が通知されます。モデルがいるかもしれないように on_change コールバックですので、ご利用いただけます Document それがそうです。一人になる Document そのモデルが変更されたことが通知された場合、それは対応するイベント(一般には)を生成する。 ModelChangedEvent )を起動します on_change コールバックして、この新しい事件をそれらに伝えます。セッションは,イベントをWebソケット接続で送信可能なパッチに変換するコールバックである.クライアントまたはサーバセッションがメッセージを受信すると、パッチを抽出し、直接適用する。 Document それがそうです。

イベントがクライアントとサーバの間を行き来することを避けるために(パッチごとに新しいイベントが生成され,これらのイベントがまた返信されるため),通知される. Document これは,パッチと任意の後続して生成されたイベントの生成を担当する.このようにして Session 通知文書が変更された場合、それをチェックすることができます event.setter 自身と同じであるため,そのイベントをスキップする.

直列化する.

一般に,以上のすべての概念がモデルや変更イベントの符号化や復号に対してどれだけ正確であるかは分からない.各モデルおよびその属性は、WebSocket接続を介して送信することができるJSONのようなフォーマットに変換することを担当する。ここでの困難の1つは、1つのモデルが、一般に、高度に相互接続されてループする方法である他のモデルを参照することができるしたがって,JSONフォーマットのような変換では,1つのモデルの他のモデルへのすべての参照をID参照に置き換える.また,モデルや属性は特殊な直列化行為を定義することができる.このような例の1つは ColumnData 属性上の ColumnDataSource これは,NumPy配列をBase 64符号化表現形式に変換し,文字列ベースのフォーマットでデジタル配列を送信するよりもはるかに効率が良い.♪the ColumnData 財産性. serializable_value メソッドはこのコードを適用し,from_jsonメソッドはデータを返す.同様にJSに基づく ColumnDataSource Base 64符号化されたデータを解釈してJavaScriptタイプの配列に変換する方法を知っている. attributes_as_json 方法はまた,データをどのように符号化するかを知っている.これにより,モデルは最適化された直列化フォーマットを実現することができる.

テストをする

クライアント-サーバ機能をテストするには、使用中のユーティリティを使用してください bokeh.server.tests.utils それがそうです。

Vbl.使用 ManagedServerLoop プロセス内でサーバインスタンスを起動することができます。分かち合う server.io_loop クライアントを使用して、サーバの任意の側面をテストすることができます。既存のテストを見て、多くの例を得る。新しいWebSocketメッセージやHTTPサイトをいつでも追加しても、追加テストを確保しましょう!