如何避免Bokeh Server中不同应用实例出现相同图表?
解决Bokeh Server多应用图表重复的问题
我之前也碰到过一模一样的问题——Bokeh Server上部署的多个应用总是显示相同的图表,排查下来核心原因就是多个应用之间意外共享了同一个Bokeh组件实例(比如Figure、ColumnDataSource这些),导致状态串了。下面是几个能彻底解决这个问题的关键方案:
1. 确保每个应用创建独立的Bokeh组件
绝对不要在应用函数的外部定义图表、数据源这些对象,一定要把它们放在应用的回调函数内部,让每个应用实例都生成全新的组件副本。
举个错误示范(会导致重复):
# 错误:全局共享图表对象,所有应用都会用这个p p = figure(title="Shared Plot") p.line([1,2,3], [4,5,6]) def bad_app(doc): doc.add_root(p)
正确的写法(每个应用独立生成组件):
# 正确:每次调用应用函数都创建新的图表 def good_app(doc): p = figure(title="Unique Plot") p.line([1,2,3], [4,5,6]) doc.add_root(p)
2. 严格隔离应用的状态数据
如果你的应用依赖外部数据或者可变状态(比如动态更新的数据源),一定要给每个应用分配独立的状态副本,不能让多个应用共享同一个可变对象。比如:
def app_with_unique_data(doc): # 每个应用实例都创建自己的数据源 source = ColumnDataSource(data={'x': [1,2,3], 'y': [4,5,6]}) p = figure() p.circle(x='x', y='y', source=source) doc.add_root(p)
这样即使多个用户访问同一个应用,或者多个应用同时运行,它们的数据源也不会互相干扰。
3. 优化Bokeh Server的启动逻辑
看你的代码,用multiprocessing启动Server是没问题的,但要注意几个细节:
- 不要在主进程中提前创建Bokeh应用的组件,最好把组件创建逻辑放在子进程执行的代码里,避免父进程的状态被继承。
- 可以考虑去掉
p.join(),否则主进程会被阻塞,无法处理其他任务(如果不需要等待子进程结束的话)。
修改后的示例代码:
import multiprocessing from bokeh.server.server import Server def setup_bokeh_server(data): ''' Sets up Bokeh Application instances on server. ''' def run_server(url_func_map): # 在子进程内部初始化Server,避免状态共享 server = Server(url_func_map, allow_websocket_origin=['localhost:8888', 'localhost:5006']) server.start() server.io_loop.start() url_func_map = {} for name, app_func in data['bokeh_apps'].items(): # 确保每个app_func都是独立无状态的,内部创建自己的组件 url_func_map['/'+name] = app_func p = multiprocessing.Process(target=run_server, args=(url_func_map,)) p.start() # 若主进程不需要阻塞,注释掉下面这行 # p.join()
4. 用Bokeh Application Handler封装应用
更规范的做法是用FunctionHandler和Application类来封装每个应用,确保每个应用都是完全独立的实例:
from bokeh.application import Application from bokeh.application.handlers import FunctionHandler from bokeh.plotting import figure def create_app_one(): def app(doc): p = figure(title="App 1 Unique Plot") p.circle([1,2,3], [4,5,6]) doc.add_root(p) return Application(FunctionHandler(app)) def create_app_two(): def app(doc): p = figure(title="App 2 Unique Plot") p.line([1,2,3], [6,5,4]) doc.add_root(p) return Application(FunctionHandler(app)) # 构建应用映射时使用独立的Application实例 data = { 'bokeh_apps': { 'app1': create_app_one(), 'app2': create_app_two() } }
这样每个应用都有自己的Handler和文档上下文,完全不会出现图表重复的问题。
核心总结下来就是:状态隔离是关键——每个Bokeh应用必须拥有自己独立的文档、图表、数据源等组件,绝对不能在多个应用之间复用这些对象。
内容的提问来源于stack exchange,提问作者Martin Sundequist
相关产品推荐
相关产品推荐

