Bokehサーバの実行

目的

Bokehサーバの目的は、フロントエンドUIイベントを実際に実行中のPythonコードに接続することができるPythonユーザが対話型Webアプリケーションを容易に作成できるようにすることです。

Bokehのアーキテクチャは,高度な“モデルオブジェクト”(ブロック,範囲,軸,字形などを表す)である.Pythonで作成し、クライアントライブラリBokehJSで使用するJSONフォーマットに変換します。(参照されたい) 重要な概念を定義する (より詳細な議論について。)それ自体では,このような柔軟かつデカップリングの設計が利点を提供している.たとえば,他の言語(R,Scala,Luaなど)を容易に用いることができる.ブラウザではまったく同じBokeh描画と可視化効果を駆動する.

しかし、Pythonの“モデルオブジェクト”とブラウザの“モデルオブジェクト”を同期させることができれば、すぐにより強力な可能性が出てきます。

  • Pythonのすべての機能を使用して、ブラウザで生成されたUIおよびツール·イベントに計算または照会応答する

  • サーバ側更新プッシュを自動的にUI(すなわち、ブラウザ内のウィジェットまたは描画)に自動的にプッシュする

  • フロー更新は、定期的、タイムアウト、および非同期コールを使用して駆動されます。

このようなPythonとブラウザ間の同期機能は、Bokeh Serverの主な用途です。


次の簡単な例は demo.bokeh.org この点を説明した。

コントロールを操作すると,それらの新しい値はBokehサーバで自動的に同期される.コールバックをトリガして,サーバに描画されたデータを更新する.これらの変更は自動的にブラウザに同期し、印刷は更新されます。

用例場面

Bokehサーバの用途や何ができるかが分かった以上,Bokehサーバを利用したい場合には,いくつかの異なるシーンを考慮する必要がある.

地元や個人が使っています

Bookehサーバを使用したい1つの方法は、探索的なデータ分析の間、Jupyterノートにあるかもしれません。代替的に、ローカルで動作可能なウィジェットを作成したいか、または同僚にローカルで動作するように送信したいかもしれません。Bokehサーバは、このシーンにおいて非常に有用であり、使用しやすい。以下の2つの方法を有効に利用することができる.

展開可能なアプリケーションに最も直接移行できる最も柔軟な方法については、準拠を推奨する Bokehアプリケーションの構築 それがそうです。

展開可能なアプリケーションの作成

あなたがBokehサーバを使用することを望む別の方法は、より広範な視聴者の閲覧および使用のために対話型データ可視化およびアプリケーションを発行することであるかもしれない(インターネット上でも、社内ネットワーク上でも可能性がある)。Bokeh Serverもこの使い方に非常に適していますので、まず以下の各節を参照する必要があります。

共有発表

上記の2つの場合はいずれも関連している 単一創建者 サーバ上でアプリケーションを作成することは,ローカルに利用することも可能であるし,より多くのユーザが利用することも可能である.もう1つはグループが 何人かのクリエイター これらは,異なるアプリケーションを同じサーバに公開することを望んでいる. This is not a good use-case for a single Bokeh server. 任意のPythonコードを実行するアプリケーションを作成することができるので、プロセス隔離とセキュリティの問題は、この共有レンタルを尻込みにしてしまいます。

このような複数の作成者、マルチアプリケーション環境をサポートするために、1つの方法は、必要に応じて、各アプリケーションまたは少なくとも各ユーザに基づいて任意の複数のBokehサーバを実行することができるインフラストラクチャを構築することである。このような用途を実現するために将来的に公共サービスを作成することが可能であり、もちろん、第三者も自分のプライベートインフラを確立してそうすることができるが、これは本ユーザガイドの範囲を超えている。

別の可能性は、多くの異なる人によって発行されたデータまたは他の構成要素(アクセス制御を有する場合がある)にアクセスすることができる集中的に作成されたアプリケーション(ある組織によって作成される可能性がある)を有することである。この場合は is Bokehサーバを使用することは可能であるが,通常はBokehサーバを他のWebアプリケーションフレームワークと統合することに関連する.

Bokehアプリケーションの構築

これまで,Bokehサーバを用いてインタラクティブなデータ可視化を作成する最も柔軟な方法は,Bokehアプリケーションを作成し,利用することであった. bokeh serve 指揮する。このシナリオでは、Bokehサーバは、アプリケーションコードを使用して、以下を接続するすべてのブラウザのセッションおよび文書を作成する:

../../_images/bokeh_serve.svg

Bokehサーバ(左)アプリケーションコードを用いてBokeh文書を作成する.ブラウザ(右側)からの各新しい接続は、Bokehサーバがセッションのために1つの新しいドキュメントのみを作成することになります。

新しい接続を確立するたびに、Bokehサーバでアプリケーションコードを実行して、新しいBokehを作成します。 Document ブラウザに同期されますアプリケーションコードにはどのコールバックも設定されており,小さな部品の値などの属性が変更されれば,これらのコールバックを実行すべきである.

アプリケーションコードを提供するにはいくつかの異なる方法がある.

単モジュール形式

完全な例をもう一度見て、具体的な部分をより詳細にチェックしましょう。

# myapp.py

from random import random

from bokeh.layouts import column
from bokeh.models import Button
from bokeh.palettes import RdYlBu3
from bokeh.plotting import figure, curdoc

# create a plot and style its properties
p = figure(x_range=(0, 100), y_range=(0, 100), toolbar_location=None)
p.border_fill_color = 'black'
p.background_fill_color = 'black'
p.outline_line_color = None
p.grid.grid_line_color = None

# add a text renderer to our plot (no data yet)
r = p.text(x=[], y=[], text=[], text_color=[], text_font_size="26px",
           text_baseline="middle", text_align="center")

i = 0

ds = r.data_source

# create a callback that will add a number in a random location
def callback():
    global i

    # BEST PRACTICE --- update .data in one step with a new dict
    new_data = dict()
    new_data['x'] = ds.data['x'] + [random()*70 + 15]
    new_data['y'] = ds.data['y'] + [random()*70 + 15]
    new_data['text_color'] = ds.data['text_color'] + [RdYlBu3[i%3]]
    new_data['text'] = ds.data['text'] + [str(i)]
    ds.data = new_data

    i = i + 1

# add a button widget and configure with the call back
button = Button(label="Press Me")
button.on_click(callback)

# put the button and plot in a layout and add to the document
curdoc().add_root(column(button, p))

このコード中のどの位置でも出力や接続方法は指定されていないことに注意されたい。オブジェクトを作成して更新する簡単なスクリプトです。その柔軟性は bokeh 命令行ツールは私たちが出力オプションを最後まで延期することができるということを意味する。例えば実行することができます bokeh json myapp.py アプリケーションのJSONシーケンス化バージョンを取得する.しかし,本例では,このアプリケーションをBokehサーバ上で実行することが望ましいため,実行する.

bokeh serve --show myapp.py

♪the --show オプションは、ブラウザに新しいオプションカードを自動的に開き、実行中のアプリケーションのアドレスを指します。本例では:

http://localhost:5006/myapp

アプリケーションが1つしかない場合、サーバルートディレクトリはアプリケーションにリダイレクトされます。そうでなければ、サーバルートディレクトリのすべての実行中のアプリケーションのインデックスを見ることができます:

http://localhost:5006/

以下のコマンドでこのインデックスを無効にすることができます --disable-index オプション、リダイレクト行動が使用できます --disable-index-redirect 選択します。

単一のpythonファイルからBokehアプリケーションを作成するほか、ディレクトリからアプリケーションを作成することができます。

ディレクトリ形式

また、適切なファイルでファイルシステムディレクトリを作成して充填することで、Bokehアプリケーションを作成することもできます。ディレクトリ中のディレクトリアプリケーションを起動するには、以下の操作を実行してください myapp 実行します bokeh serve ディレクトリ名、例えば:

bokeh serve --show myapp

このディレクトリは少なくとも含まれなければならない main.py これは、Bokeh Serverに以下のサービスを提供するためのドキュメントを構築します。

myapp
   |
   +---main.py

Bokehサーバが知っている完全ファイルセットは:

myapp
   |
   +---__init__.py
   +---app_hooks.py
   +---main.py
   +---request_handler.py
   +---static
   +---theme.yaml
   +---templates
        +---index.html

オプションのコンポーネントは

  • Vbl.一種 __init__.py このディレクトリをソフトウェアパッケージのファイルとしてマークする.パッケージは相対的に導入され、例えば from . import mymod そして from .mymod import func すべて可能です。

  • A request_handler.py ファイルは、HTTP要求を処理し、上述したように、セッショントークンに含まれる項目の辞書を返すオプションの関数を宣言することを可能にする。 処理プログラムのフックを要求する それがそうです。

  • A app_hooks.py 上述したように、アプリケーション実行の異なる段階で選択可能なコールバックファイルをトリガすることを可能にする ライフサイクルリンク そして 処理プログラムのフックを要求する それがそうです。

  • A static サブディレクトリは、このアプリケーションに関連する静的リソースを提供するために使用されてもよい。

  • A theme.yaml Bokehモデルタイプに適用されるデフォルト属性のファイルを宣言的に定義します。

  • A templates サブディレクトリに含まれる index.html JJJAテンプレートファイル。このカタログには,以下の項の他のJJJAテンプレートが含まれる可能性がある index.html 参考になります。テンプレートは和を持つべきである FILE テンプレート見 アプリケーションをカスタマイズするJJJAテンプレート もっと細かいことを知っています。

あなたのを実行しています main.py Bokehサーバは標準を確保しています __file__ モジュール属性の動作方式は期待と一致している.したがって,ディレクトリにデータファイルやカスタマイズユーザ定義モデルを自由に含めることができる.

また,アプリケーションディレクトリを追加する. sys.path これにより、アプリケーションディレクトリのPythonモジュールを容易に導入することができます。しかしもし1つが __init__.py ディレクトリに存在し,アプリケーションはパッケージとして利用可能であり,標準的なソフトウェアパッケージに関する導入も利用可能である.

例:

myapp
   |
   +---__init__.py
   |
   +---app_hooks.py
   +---data
   |    +---things.csv
   |
   +---helpers.py
   +---main.py
   |---models
   |    +---custom.js
   |
   +---request_handler.py
   +---static
   |    +---css
   |    |    +---special.css
   |    |
   |    +---images
   |    |    +---foo.png
   |    |    +---bar.png
   |    |
   |    +---js
   |        +---special.js
   |
   |---templates
   |    +---index.html
   |
   +---theme.yaml

本例では、あなたのコードは、以下と同様である場合があります。

from os.path import dirname, join
from .helpers import load_data

load_data(join(dirname(__file__), 'data', 'things.csv'))

カスタムモデルをロードするためのJavaScript実現のための類似コード models/custom.js

アプリケーションをカスタマイズするJJJAテンプレート

以上のように、参照してください ディレクトリ形式 Bokehサーバが使用するデフォルトJJJAテンプレートを上書きして、ユーザブラウザに提供されるHTMLコードを生成することができます。

これにより,クライアントブラウザにおけるアプリケーションレイアウトやBoehJS以外の他のJavascriptライブラリをCSSを用いて管理することが可能となる.

ご参照ください Jinja Project Documentation JJJAテンプレートがどのように動作するかについてのより詳細な情報。

テンプレートに図形を埋め込む

Bokehアプリケーションのメインスレッドでは,すなわち main.py テンプレート化コードに参照されるBokeh図形には,それが必要である. name 属性セットは、現在の文書ルートディレクトリに追加されます。

from bokeh.plotting import curdoc

# templates can refer to a configured name value
plot = figure(name="bokeh_jinja_figure")

curdoc().add_root(plot)

そして、対応するJJJAテンプレートコードでは、 roots テンプレートパラメータ、図を用いた name つまり、そうです。

{% extends base %}

{% block contents %}
<div>
    {{ embed(roots.bokeh_jinja_figure) }}
</div>
{% endblock %}

カスタム変数を定義する

カスタム変数は curdoc().template_variables 辞書は位置に着いていた。

# set a new single key/value
curdoc().template_variables["user_id"] = user_id

# or update multiple at once
curdoc().template_variables.update(first_name="Mary", last_name="Jones")

そして,対応するJJJAテンプレートコードでは,変数を直接参照することができる:

{% extends base %}

{% block contents %}
<div>
    <p> Hello {{ user_id }}, AKA '{{ last_name }}, {{ first_name }}'! </p>
</div>
{% endblock %}

HTTPリクエストへのアクセス

When a session is created for a Bokeh application, the session context is made available as curdoc().session_context. The most useful function of the session context is to make the Tornado HTTP request object available to the application as session_context.request. Due to an incompatibility issue with the usage of --num-procs, the HTTP request is not made available directly. Instead, only the arguments attribute is available in full, and only the subset of cookies and headers which are allowed by the --include-headers, --exclude-headers, --include-cookies and --exclude-cookies are made available. Attempting to access any other attribute on request will result in an error.

前述したように、要求上の他の任意の属性にアクセスすることができます 処理プログラムのフックを要求する それがそうです。

たとえば,以下のコードはこの要求にアクセスする. arguments 変数の値を設定するには、以下の操作を実行してください N (描画中のポイントを制御することができます):

# request.arguments is a dict that maps argument names to lists of strings,
# e.g, the query string ?N=10 will result in {'N': [b'10']}

args = curdoc().session_context.request.arguments

try:
  N = int(args.get('N')[0])
except:
  N = 200

警告

要求対象を提供するのは arguments 検査しやすいかもしれません。Tornadoメソッドのような任意の呼び出し finish() 直接手紙を書いたり request.connection 支持されないことは、定義されていない行動をもたらすだろう。

処理プログラムのフックを要求する

完全なTornado HTTP要求がサービスセッションのプロセスで利用可能であることは保証されないため、追加情報を提供するためにカスタム処理プログラムを定義することができる。

このような接続を定義するためには,アプリケーションを作成する必要がある. ディレクトリ形式 名前の名前も含まれています request_handler.py カタログにあります。この文書には、慣例に従って命名されたものが含まれていなければなりません process_request 機能:

def process_request(request):
    ''' If present this function is called when the HTTP request arrives. '''
    return {}

処理プログラムは、Tornado HTTP要求を受信し、この要求を処理して辞書に戻すことができる。 curdoc().session_context.token_payload それがそうです。このようにして、いくつかの問題を解決するために、以下の場合に他の情報を提供することができる --num-procs 全部使っています。

コールバックと事件

Bokeh Serverコンテキストにおけるコールバックやイベントを専門に紹介する前に,コールバックの異なる用例を検討する必要がある.

ブラウザのJavaScriptコールバック

Bokeh Serverに関連するか否かにかかわらず、以下のコマンドを使用してブラウザで実行されるコールバックを作成することができます。 CustomJS 他の方法もあります見 JavaScriptコールバック より詳細な情報および例については、参照されたい。

重要なのは注意しなければならないことです CustomJSコールバックを使用する場合、Pythonコードは何も実行されません それがそうです。コールバックがJavaScriptに変換するためにPythonコードとして提供された場合でも同様です。A CustomJS コールバックはブラウザのJavaScriptインタプリタ内でのみ実行されるため,JavaScriptデータや関数(たとえばBokehJSモデル)と直接インタラクションすることしかできない.

Jupyterインタラクションを使用したPythonコールバック

Jupyterノートを使用している場合、Jupyterインタラクションを使用して簡単なGUIフォームを自動的に迅速に作成することができます。GUI中小部品の更新は、Jupyter Pythonカーネルで実行されるpythonコールバック関数をトリガすることができます。これらのコールバックを呼び出すのは通常有用です push_notebook() 表示された描画に更新プッシュを送るには、以下の操作を実行してください。詳細についてはご参照ください 木星相互動者 それがそうです。

注釈

現在、PythonからBokehJSに更新をプッシュする(すなわち、描画を更新するなど)を使用することができます。 push_notebook() それがそうです。双方向通信(例えば、範囲を選択させるか、更新をトリガするPythonコールバック)を追加するには、ノートにBokeh Serverを埋め込むようにしてください。見 examples/howto/server_embed/notebook_embed.ipynb

スレッドから更新する

アプリケーションがブロック計算を実行する必要がある場合、その動作は、別個のスレッドで実行されてもよい。しかし,文書の更新は次のコールバックで手配しなければならない.コールバックは、Tornadoイベントループの次の反復でできるだけ早く実行され、文書状態を安全に更新するために必要なロックが自動的に取得される。

警告

異なるスレッドから文書に対して実行される唯一のセキュリティ操作は add_next_tick_callback() そして remove_next_tick_callback()

文書更新は“次の滴下コール”に配置されなければならないことを強調しなければならない。他の文書メソッドを呼び出したり,Bokehモデルの属性を設定したりすることで,別のスレッドから文書状態を直接更新する任意の使用は,データやプロトコルが破損するリスクがある.

保存されているローカルコピーも重要です curdoc() すべてのスレッドが同じ文書にアクセスできるようにする.以下の例は、この点を説明する。

from functools import partial
from random import random
from threading import Thread
import time

from bokeh.models import ColumnDataSource
from bokeh.plotting import curdoc, figure

from tornado import gen

# this must only be modified from a Bokeh session callback
source = ColumnDataSource(data=dict(x=[0], y=[0]))

# This is important! Save curdoc() to make sure all threads
# see the same document.
doc = curdoc()

@gen.coroutine
def update(x, y):
    source.stream(dict(x=[x], y=[y]))

def blocking_task():
    while True:
        # do some blocking computation
        time.sleep(0.1)
        x, y = random(), random()

        # but update the document from callback
        doc.add_next_tick_callback(partial(update, x=x, y=y))

p = figure(x_range=[0, 1], y_range=[0,1])
l = p.circle(x='x', y='y', source=source)

doc.add_root(p)

thread = Thread(target=blocking_task)
thread.start()

この例を実際に見るには、例えばpythonファイルとして保存してください testapp.py そして実行します

bokeh serve --show testapp.py

警告

現在,文書に次のTickコールを追加することはロックされていない.最大1つのスレッドが文書にコールバックを追加することを提案する.将来的にはコールバック方式のためにより多くの細粒度ロックを追加する予定である.

ロック解除からコールバック更新

一般に、Bokehセッションは、それらが開始されたすべての将来の作業が完了するまで、ドキュメントを再帰的にロックする。しかし、Tornadoのコールバック駆動ブロック計算を使いたいかもしれません ThreadPoolExecutor 非同期コールバック中です。これは仕事ができますが、提供するBokehが必要です without_document_lock() 修飾子は正常なロック行為を抑制する.

上のスレッドの例と同様に all actions that update document state must go through a next-tick callback それがそうです。

以下の例は、ロック解除されたBokehセッションコールバック駆動ブロッキング計算からのアプリケーションを示し、方法は、オンラインプールアクチュエータ上で実行されるブロッキング関数に降伏し、次の滴下コールバックを使用して更新される。この例はまた、標準ロックセッションから更新状態を異なる更新率で簡単にコールバックする。

from functools import partial
import time

from concurrent.futures import ThreadPoolExecutor
from tornado import gen

from bokeh.document import without_document_lock
from bokeh.models import ColumnDataSource
from bokeh.plotting import curdoc, figure

source = ColumnDataSource(data=dict(x=[0], y=[0], color=["blue"]))

i = 0

doc = curdoc()

executor = ThreadPoolExecutor(max_workers=2)

def blocking_task(i):
    time.sleep(1)
    return i

# the unlocked callback uses this locked callback to safely update
@gen.coroutine
def locked_update(i):
    source.stream(dict(x=[source.data['x'][-1]+1], y=[i], color=["blue"]))

# this unlocked callback will not prevent other session callbacks from
# executing while it is in flight
@gen.coroutine
@without_document_lock
def unlocked_task():
    global i
    i += 1
    res = yield executor.submit(blocking_task, i)
    doc.add_next_tick_callback(partial(locked_update, i=res))

@gen.coroutine
def update():
    source.stream(dict(x=[source.data['x'][-1]+1], y=[i], color=["red"]))

p = figure(x_range=[0, 100], y_range=[0,20])
l = p.circle(x='x', y='y', color='color', source=source)

doc.add_periodic_callback(unlocked_task, 1000)
doc.add_periodic_callback(update, 200)
doc.add_root(p)

前と同様に、pythonファイルに保存し、以下のコマンドを実行することで、この例を実行することができます bokeh serve 今行きます。

ライフサイクルリンク

時々、コードは、サーバまたはセッション生存期間の特定の時間に実行される必要がある。例えば、Bokeh ServerとDjangoサーバを同時に使用する場合、呼び出しが必要です。 django.setup() 各Bokehサーバの起動時に、Bokehアプリケーションコードの使用のためにDjangoを正しく初期化します。

Bokehはグループを通過します ライフサイクルリンク それがそうです。これらのフックを使用するためには,アプリケーションを作成する必要がある ディレクトリ形式 名前の名前も含まれています app_hooks.py カタログにあります。このファイルには、以下の任意またはすべての従来の名前付け関数を含むことができます。

def on_server_loaded(server_context):
    ''' If present, this function is called when the server first starts. '''
    pass

def on_server_unloaded(server_context):
    ''' If present, this function is called when the server shuts down. '''
    pass

def on_session_created(session_context):
    ''' If present, this function is called when a session is created. '''
    pass

def on_session_destroyed(session_context):
    ''' If present, this function is called when a session is closed. '''
    pass

また、 on_session_destroyed 生命周期フックも直接 Document 招待されました。ユーザがセッションを閉じた後にクリーニングを行うタスクは、データベース接続を閉じるなど、一般的であるので、これは、個々のファイルをバンドルすることなく、そのような動作を実行するための簡単な方法を提供する。このようなコールバックを宣言する場合は、関数を定義して登録してください Document.on_session_destroyed 方法:

doc = Document()

def cleanup_session(session_context):
    ''' This function is called when a session is closed. '''
    pass

doc.on_session_destroyed(cleanup_session)

上記の“ライフサイクル”フックに加えて、ユーザからのHTTP要求にアクセスするための“要求フック”を定義することができます。見 処理プログラムのフックを要求する もっと細かいことを知っています。

Bokeh Serverを図書館に埋め込む

Bokeh Serverを大きなTornadoアプリケーションやJupyterノートに埋め込み、既存のTornadoを使用することは非常に有用です IOloop それがそうです。以下は、このような場合にBokehを統合するための基盤をどのように統合するかである。

from bokeh.server.server import Server

server = Server(
    bokeh_applications,  # list of Bokeh applications
    io_loop=loop,        # Tornado IOLoop
    **server_kwargs      # port, num_procs, etc.
)

# start timers and services and immediately return
server.start()

作成し制御することもできます IOLoop 直接行きましょう。これは、Bokehアプリケーションにサービスを提供する独立した“通常の”Pythonスクリプトを作成したり、Bokehサーバプロセスを単独で実行することなく、FlaskやDjangoなどのフレームワークにBokehアプリケーションを埋め込むために非常に有用である。Examplesディレクトリにこの技術のいくつかの例を見つけることができる:

なお、ほとんどのコマンドラインパラメータは bokeh serve 対応するキーワードパラメータを持つ Server それがそうです。例えば、設定 --allow-websocket-origin コマンドラインパラメータは転送と同じです allow_websocket_origin パラメータとします。

接続しています bokeh.client

Bokeh Serverと直接相互作用するためのクライアントAPIもある。クライアントAPIは、Bokehサーバ内の既存のセッション内のBokeh文書を修正するために使用されてもよい。

../../_images/bokeh_serve_client.svg

通常,WebブラウザはBokehサーバに接続されているが,利用可能である. bokeh.client モジュールです。

例えば、FlaskやDjangoのような別のWebフレームワークに埋め込まれたBokehアプリケーションをユーザ固有のカスタマイズを行う際に有用である可能性がある。以下にこのような例を示す.この場合、“スライダ”の例は、例えば、単独で動作する。 bokeh serve sliders.py それがそうです。フラスコエンドポイントはスライダアプリケーションに埋め込まれていますが、描画タイトルを変更します その前に ユーザーに伝える:

from flask import Flask, render_template

from bokeh.client import pull_session
from bokeh.embed import server_session

app = Flask(__name__)

@app.route('/', methods=['GET'])
def bkapp_page():

    with pull_session(url="http://localhost:5006/sliders") as session:

        # update or customize that session
        session.document.roots[0].children[1].title.text = "Special Sliders For A Specific User!"

        # generate a script to load the customized session
        script = server_session(session_id=session.id, url='http://localhost:5006/sliders')

        # use the script in the rendered page
        return render_template("embed.html", script=script, template="Flask")

if __name__ == '__main__':
    app.run(port=8080)

警告

使用できる bokeh.client ブロックコールによる実行とサービスコールバックなど、Bokehサーバの外で“最初から”アプリケーションを構築します。 session.loop_until_closed 外部Pythonプロセスで使用する bokeh.client それがそうです。このような使用には多くの固有の技術的欠陥があり,支持されていないと考えられるべきである.

配置案.

私たちが開発しているアプリケーションがあれば、それとインタラクションしたいときにローカルに実行することができます。必要なPythonスタックをインストールできる他の人とアプリケーションを共有するためには、アプリケーションを共有し、同じ方法でアプリケーションをローカルに実行させることができます。しかし、他の人がサービスとしてアクセスできる方法でアプリケーションを展開することも望ましいかもしれません。

  • 必要な部品をインストールする必要はありません

  • ソースコードを必要としない場合

  • 他のページのように

本節では,Bokehサーバアプリケーションを他者が利用するためのサービスに展開する際に考慮すべき事項について述べる.

独立Bokehサーバ

まず、ユーザが直接インタラクションするために、Bokehサーバをネットワーク上で簡単に実行することができる。アプリケーションコードの計算負担,ユーザ数,動作に用いる機器の能力などに応じて,内部ネットワークを展開するための簡単で直接的な選択である可能性がある.

しかし,通常は認証,拡張,正常実行時間の需要がある.このような場合、より複雑な配置構成が必要だ。次のいくつかの節で、私たちはその中のいくつかの注意事項について議論するつもりだ。

SSHトンネル

直接アクセスが許可されていないホスト上でBokehサーバを実行する独立したインスタンスは便利である可能性があり,必要である可能性がある.この場合、SSH“トンネル”を用いてサーバに接続することができる。

最も単純なシーンでは、Bokehサーバは、1つのホスト上で動作し、中間機械を必要とすることなく、別の位置(例えば、ノートパソコン)からアクセスするであろう。

いつものようにサーバーを稼働させます リモートホスト.

bokeh server

次は、 地元のコンピュータ リモートホストへのSSHチャネルを確立するためには、以下の操作を実行してください。

ssh -NfL localhost:5006:localhost:5006 user@remote.host

代わる user リモートホストでユーザー名を使用する remote.host ホストBokehサーバを用いたシステムのホスト名/IPアドレス.システムは、リモートシステムの登録証明書を入力するように指示する場合があります。接続を確立すると、ナビゲーションが可能になります localhost:5006 Bokehサーバがローカルマシン上で動作するように.

第2のやや複雑な場合は,サーバとローカル機器の間にゲートウェイがある場合に発生する.この場合、サーバからゲートウェイへの逆方向トンネルが確立されなければならない。さらに、ローカルコンピュータからのトンネルもゲートウェイを指すであろう。

上に以下の命令を出す リモートホスト. Bokehサーバはどこで動作しますか:

nohup bokeh server &
ssh -NfR 5006:localhost:5006 user@gateway.host

代わる user ゲートウェイでユーザー名を使用し、 gateway.host ゲートウェイのホスト名/IPアドレスを用いる.システムは、ゲートウェイの登録証明書を入力するように指示する場合があります。

現在、トンネルの残りの半分は、ローカルコンピュータからゲートウェイに設定されている。論 地元のコンピュータ

ssh -NfL localhost:5006:localhost:5006 user@gateway.host

もう一度言って、交換します user ゲートウェイでユーザー名を使用し、 gateway.host ゲートウェイのホスト名/IPアドレスを用いる.今、Bokehサーバがローカルコンピュータ上で実行されているように、ローカルコンピュータからBokehサーバにアクセスできるはずです。方法はナビゲーションです。 localhost:5006 地元の機械にあります。ローカルコンピュータ上で動作するJupyterノートパソコンからクライアント接続を設定することもできます。

注釈

我々は,他のツールや配置のより多くの教示により本節を拡張する予定である.他のWeb展開プログラムの経験があれば、ここであなたの知識に貢献したい場合は、https://Disourse.boke.orgでご連絡ください

SSL端末

Bokehサーバは、SSL接続を直接終了する(すなわち、安全なHTTPSおよびWSSセッションにサービスを提供する)ように構成されてもよい。少なくとも、 --ssl-certfile パラメータを提供しなければならない。この値は、証明書および証明書の真正性を確立するために必要な任意の数のCA証明書を含むPEMフォーマットの単一のファイルの経路でなければならない。

bokeh serve --ssl-certfile /path/to/cert.pem

環境変数を設定することで証明書ファイルの経路を提供することも可能である. BOKEH_SSL_CERTFILE それがそうです。

秘密鍵が個別に格納されている場合、その位置は設定によって --ssl-keyfile コマンドラインパラメータ、または設定によって BOKEH_SSL_KEYFILE 環境変数秘密鍵にパスワードが必要な場合は、設定によって BOKEH_SSL_PASSWORD 環境変数

代替的に、プロキシの後でBokehサーバを実行し、プロキシにSSLを終了させることを望む場合があります。このシーンは次節で紹介する.

基本リバースエージェント設定

一般インターネットへのWebアプリケーションの提供を目指す場合には,通常,そのアプリケーションを内部ネットワーク上でホストし,ある専用のHTTPサーバエージェントを介してそのアプリケーションに接続することが望ましい.本節では、一般的なリバースエージェントの背後にある基本的な構成ガイドを提供する。

エンギックス

非常に一般的なHTTPおよびリバースプロキシサーバの1つはNginxである.例示的なサーバ構成ブロックを以下に示す.

server {
    listen 80 default_server;
    server_name _;

    access_log  /tmp/bokeh.access.log;
    error_log   /tmp/bokeh.error.log debug;

    location / {
        proxy_pass http://127.0.0.1:5100;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host:$server_port;
        proxy_buffering off;
    }

}

上記のとおり server エージェントが接続するためにNginxを設定することを阻止します 127.0.0.1 ポート80への接続 127.0.0.1:5100 内部にあります。この構成で動作するためには、Bokeh Serverを構成するためにいくつかのコマンドラインオプションを使用する必要があります。特に私たちは --port 指定されたBokehサーバは、ポート5100で自身をリッスンする必要があります。

bokeh serve myapp.py --port 5100

上記の基本サーバブロックでは、静的リソース(例えば、Bokeh JSおよびCSSファイル)のための特別な処理は構成されていないことに留意されたい。これは,これらのファイルがBokehサーバ自体によって直接サービスを提供することを意味する.これは可能であるが、Nginxは高速な静的資産処理プログラムを有するので、Bokehサーバに不必要な追加負荷をもたらす。NginxサービスBokehの静的資産を利用するには server ブロックは,以下のようになる.

location /static {
    alias /path/to/bokeh/server/static;
}

Nginxサーバプロセスを実行する任意のユーザアカウントは、Bokehリソースのファイル権限にアクセスすることができることに注意されたい。代替的に、展開中にリソースをグローバル静的ディレクトリにコピーすることができます。

プロセス間でクッキーおよびヘッダを通信するために、Bokehは、WebSocketを介して送信されるJWTトークンにこの情報を含めることができる。場合によっては、この内の識別情報が非常に大きくなる可能性があり、nginxは要求を破棄する可能性がある。したがって、nginxのデフォルト値をカバーしなければならないかもしれません large_client_header_buffers 設定:

large_client_header_buffers 4 24k;

アパッチ!

もう1つの一般的なHTTPサーバとプロキシはApacheである.以下にApacheの後でBokehサーバを実行する構成例を示す.

<VirtualHost *:80>
    ServerName localhost

    CustomLog "/path/to/logs/access_log" combined
    ErrorLog "/path/to/logs/error_log"

    ProxyPreserveHost On
    ProxyPass /myapp/ws ws://127.0.0.1:5100/myapp/ws
    ProxyPassReverse /myapp/ws ws://127.0.0.1:5100/myapp/ws

    ProxyPass /myapp http://127.0.0.1:5100/myapp/
    ProxyPassReverse /myapp http://127.0.0.1:5100/myapp/

    <Directory />
        Require all granted
        Options -Indexes
    </Directory>

    Alias /static /path/to/bokeh/server/static
    <Directory /path/to/bokeh/server/static>
        # directives to effect the static directory
        Options +Indexes
    </Directory>

</VirtualHost>

上記配置別名 /static Bokeh静的リソーステーブルの位置に設定する.しかし,Bokeh静的リソースを配備の一部としてApacheのために構成された任意の標準的な静的ファイル位置にコピーすることも可能である(より望ましい場合がある).

なお、上記の構成のためにいくつかのモジュールを有効にする必要がある場合があります。

a2enmod proxy
a2enmod proxy_http
a2enmod proxy_wstunnel
apache2ctl restart

これらは一緒に実行する必要があるかもしれません sudo 具体的には、あなたのシステムにかかっています。

前述と同様に、以下のコマンドを使用してBokehサーバを実行することができます。

bokeh serve myapp.py --port 5100

NginxとSSLを用いたリバースプロキシ

SSLで終了したNginxプロキシの後にBokeh Serverを配備したい場合、いくつかの追加のカスタマイズが必要です。特に,Bokehサーバは配置されていなければならない. --use-xheaders 旗:

bokeh serve myapp.py --port 5100 --use-xheaders

The --use-xheaders option causes Bokeh to override the remote IP and URI scheme/protocol for all requests with X-Real-Ip, X-Forwarded-For, X-Scheme, X-Forwarded-Proto headers when they are available.

あなたはまたNginxを定義しなければならない。特に、nginxを送信するように構成する必要があります X-Forwarded-Proto ヘッダ,およびSSL端末にNginxを配置する.代替的に、すべてのHTTPトラフィックをHTTPSにリダイレクトすることを望むことができる。この構成の完全な詳細(例えば、SSL証明書および鍵をどのようにおよびどこにインストールするか)は、プラットフォームによって異なるが、参照のためにのみ使用される。 nginx.conf 以下のように提供される.

# redirect HTTP traffic to HTTPS (optional)
server {
    listen      80;
    server_name foo.com;
    return      301 https://$server_name$request_uri;
}

server {
    listen      443 default_server;
    server_name foo.com;

    # add Strict-Transport-Security to prevent man in the middle attacks
    add_header Strict-Transport-Security "max-age=31536000";

    ssl on;

    # SSL installation details will vary by platform
    ssl_certificate /etc/ssl/certs/my-ssl-bundle.crt;
    ssl_certificate_key /etc/ssl/private/my_ssl.key;

    # enables all versions of TLS, but not SSLv2 or v3 which are deprecated.
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

    # disables all weak ciphers
    ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";

    ssl_prefer_server_ciphers on;

    location / {
        proxy_pass http://127.0.0.1:5100;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host:$server_port;
        proxy_buffering off;
    }

}

この構成では、すべての着信HTTPS接続エージェントを foo.com 内部で動作するBokehサーバに接続されています http://127.0.0.1:5100 それがそうです。

Nginxを使用した負荷分散の実現

Bokehサーバのアーキテクチャは、スケーラブルに特化されている-全体的に、より多くの容量が必要であれば、追加のサーバを実行すればよい。この場合、負荷分散器の後にすべてのBokehサーバインスタンスを実行して、各サーバ間に新しい接続を分散させることが一般的に意図される。

../../_images/bokeh_serve_scale.svg

Bokehサーバは水平拡張可能である.より多くの容量を増加させるためには、負荷分散器の後でより多くのサーバを実行することができる。

Nginxは負荷分散能力を提供する。構成可能な基礎知識をご紹介しますが、ご参照ください Nginx load balancer documentation それがそうです。例えば、次にどのサーバに接続するかを選択するために、様々な異なるポリシーを使用することができる。

まず追加する必要があります upstream 節は私たちのnginx構成に追加され、通常は server 小節です。この部分は以下のようなものです

upstream myapp {
    least_conn;                 # Use Least Connections strategy
    server 127.0.0.1:5100;      # Bokeh Server 0
    server 127.0.0.1:5101;      # Bokeh Server 1
    server 127.0.0.1:5102;      # Bokeh Server 2
    server 127.0.0.1:5103;      # Bokeh Server 3
    server 127.0.0.1:5104;      # Bokeh Server 4
    server 127.0.0.1:5105;      # Bokeh Server 5
}

これにラベルを貼りました upstream 抜粋して選ぶ. myapp それがそうです。以下ではこの名前を用いる.また,節では6つの異なるBokehサーバインスタンス(各インスタンスが異なるポート上で実行される)の内部接続情報を示す.必要に応じて任意の数のBokehサーバを実行して一覧表示することができます。

以下のコマンドと同様のコマンドを使用してBokehサーバを実行します。

serve myapp.py --port 5100
serve myapp.py --port 5101
...

次は、 location 当社のBokehサーバについては、変更してください proxy_pass 引用に値をつける upstream 私たちが上で作った節。この例では proxy_pass http://myapp; 以下に示す.

server {

    location / {
        proxy_pass http://myapp;

        # all other settings unchanged
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host:$server_port;
        proxy_buffering off;
    }

}

認証

Bokehサーバ自体は、認証または許可のためのツールを持っていません。しかしながら、Bokehサーバは、Tornadoの下位機能に接続することができる“認証提供プログラム”を構成することができる。背景情報については、竜巻文書を参照してください Authentication and security それがそうです。本節の残りの部分はあなたがこの材料について一定の理解を持っていると仮定する。

認証モジュール

Bokehサーバは、正しい認証を受けたユーザが存在する場合にのみ接続を許可するように構成することができる。これは、コマンドライン上に必要な機能を実現するモジュールの経路を提供することによって実現される。

bokeh serve --auth-module=/path/to/auth.py

あるいはそれを BOKEH_AUTH_MODULE 環境変数

このモジュールは one 以下の2つの関数は、現在のユーザ(またはなし)に戻る。

def get_user(request_handler):
    pass

async def get_user_async(request_handler):
    pass

この関数はTornadoに伝達されます RequestHandler また、認証されたユーザを決定するためにクッキーまたは要求ヘッダをチェックすることができる。有効な認証されたユーザがいない場合、これらの関数はNONEに返されるべきである。

さらに、モジュールは、認証されていないユーザをどこにリダイレクトするかを指定しなければならない。これは以下のいずれかを含まなければならない。

  • モジュール属性 login_url (オプション) LoginHandler クラス

  • の関数定義 get_login_url

login_url = "..."

class LoginHandler(RequestHandler):
    pass

def get_login_url(request_handler):
    pass

親戚になる login_url 与えられたものはオプションである LoginHandler クラスも提供可能であり,ルーティングとしてBokehサーバに自動的に実装される.

♪the get_login_url 関数は,登録URLが要求やCookieなどに応じて変化しなければならない場合に非常に有用である. LoginHandler 当たる get_url_function 定義されています

ログインオプションと同様に、オプション logout_url そして LogoutHandler 値を使用して、ログアウトユーザのエンドポイントを定義することができる。

認証モジュールが提供されていない場合は,デフォルトユーザと仮定し,Bokehサーバエンドポイントにアクセスするには認証を必要としない.

警告

AUTHモジュールの内容を実行します!

安全なCookie

竜巻を使いたいなら set_secure_cookie そして get_secure_cookie 関数は,Cookie暗号を設定しなければならない.これは使用することで BOKEH_COOKIE_SECRET 環境変数例:

export BOKEH_COOKIE_SECRET=<cookie secret value>

この値は長いランダムバイトシーケンスであるべきです

防衛を強化する.

デフォルトでは、Bokehサーバは、許可されたWebSocketソース上の任意の着信接続を受信します。セッションIDが指定され、サーバ上にIDを有するセッションが存在する場合、セッションへの接続が確立される。そうでなければ、新しいセッションが自動的に作成されて使用されます。

組み込みBokehアプリケーションを大規模組織内またはより広いインターネット上に展開する場合、あなたは、誰がセッションを開始することができ、どこからセッションを開始することができるかを制限することを望むことができるかもしれません。Bokehには、セッション作成を制限するオプションがあります。

WebSocketソース

BokehサーバにHTTP要求を発行すると,WebSocketを起動して接続され,すべての後続通信がそのWebSocketを介して行われるスクリプトがただちに返される.サイトをまたぐ誤用のリスクを低減するために,bokehサーバは明示的に列挙されたソースのみからWebSocket接続を起動する.許可リストと一致しないソース要求は、HTTP 403エラー応答を生成する。

デフォルトの場合、のみ localhost:5006 発売が許可されています。すなわち,以下の2つの呼び出しは同じである.

bokeh serve --show myapp.py

そして

bokeh serve --show --allow-websocket-origin=localhost:5006 myapp.py

どちらのオプションもブラウザを開き、デフォルトのアプリケーションURLを指します。 localhost:5006 そしてその理由は localhost:5006 許可WebSocketソース許可リストでは、Bokehサーバが新しいセッションを作成して表示します。

いま,Bokehサーバが別のページに埋め込まれている場合の利用を考える. server_document() あるいは…。 server_session() それがそうです。本例では,Bokehサーバへの要求の“Origin”ヘッダは,Bokehコンテンツが埋め込まれたページのURLである.例えばユーザが私たちのページにナビゲーションしたら https://acme.com/products Bokehアプリケーションが埋め込まれている場合、ブラウザ報告の元のヘッダは、 acme.com それがそうです。本例では、通常、Bokehサーバを only 私たちから来たのは acme.com ページは,他のページが知らずに我々のBokehアプリケーションを埋め込むことができない.

これは設定することで --allow-websocket-origin コマンドラインパラメータ:

bokeh serve --show --allow-websocket-origin=acme:com myapp.py

これは,これらのページを見るユーザの要求が異なるソースを報告するため,他のサイトがそのページに我々のBokehアプリケーションを埋め込むことを防ぐことになる. acme.com Bokehサーバはそれらを拒否する.

警告

覚えておいてくださいこれは止めるだけです 他のページ 私たちのBokehアプリケーションをこっそり埋め込むことから、標準的なウェブブラウザを使用する視聴者まで。意志が強く博識な攻撃者はOriginヘッダを偽造することができる.

複数の許容原点が必要であれば --allow-websocket-origin コマンドライン上で渡すことができます。

Bokehサーバはまた、ソースを考慮することなく、任意およびすべての接続を可能にするように構成されてもよい。

bokeh serve --show --allow-websocket-origin='*' myapp.py

外部テストと実験ではそうすることは提案されていない。

署名のセッションID

デフォルトでは、Bokehサーバは、許可されたWebSocketソースからのすべての新しい要求に対して、セッションIDが提供されていなくても自動的に新しいセッションを作成する。Bokehアプリケーションを別のWebアプリケーション(例えば,Flask,Django)に埋め込む際には,我々のWebアプリケーションを確保したい. only 我々のWebアプリケーションはBokehサーバに正しい要求を生成することができる.Bokehサーバは、暗号化署名されたセッションIDを提供するときにのみセッションを作成するように構成されてもよい。

そのためには、まずセッションIDに署名するためのパスワードを作成し、使用する必要があります bokeh secret 命令、例えば

export BOKEH_SECRET_KEY=`bokeh secret`

次に、Bokehサーバを起動する際にBOKEH_SIGN_SESSIONSを設定する(通常は許可されているWebSocketソースを設定する必要がある):

BOKEH_SIGN_SESSIONS=yes bokeh serve --allow-websocket-origin=acme.com myapp.py

そして、Webアプリケーションでは、以下のコマンドを使用してセッションIDを明示的に提供(署名)します。 generate_session_id

from bokeh.util.token import generate_session_id

script = server_session(url='http://localhost:5006/bkapp',
                        session_id=generate_session_id())
return render_template("embed.html", script=script, template="Flask")

確保する. BOKEH_SECRET_KEY BokehサーバおよびWebアプリケーションプロセス(例えば、FlaskまたはDjangoまたは使用中の任意のツール)に環境変数を設定(および同じ)する。

注釈

署名されたセッションIDは、実際にはアクセストークンである。いずれのトークンシステムと同様に,セキュリティはトークンを秘密にすることを前提としている.SSLを終了するプロキシの後にBokehサーバを実行して、セッションIDをユーザのブラウザに安全に送信することも提案されている。

XSRF Cookie

Bokehは、Tornadoのサイト横断要求偽造保護を有効にすることができる。この機能を開くには、ご利用ください --enable-xsrf-cookies オプション、または環境変数の設定 BOKEH_XSRF_COOKIES=yes それがそうです。この設定を有効にすると、カスタムハンドラまたは登録ハンドラ上の任意のPUT、POST、またはDELETE動作を正確に検出して正常に動作しなければなりません。一般に、これはコードを追加することを意味します:

{% module xsrf_form_html() %}

すべてのHTMLフォームはテンプレートを提出する.詳細は上記Tornado文書を参照 XSRF Cookies それがそうです。

サーバの拡張

属性は複数のサーバプロセスを派生する. num-procs 選択します。たとえば,3つのプロセスを派生させるには,以下の操作を実行してください.

bokeh serve --num-procs 3

なお、派生操作は下位のTornadoサーバで発生しますので、ご参照ください Tornado docs それがそうです。

さらに読む

お慣れになった以上 Bokehサーバの実行 その中で、Bokehサーバの内部構造を詳しく知ることに興味があるかもしれません。 サーバアーキテクチャ それがそうです。