排查calibre-web服务阻塞问题
问题
k8s集群中使用linuxServer的linuxserver/docker-calibre-web镜像部署了janeczku/calibre-web,在211011升级了最新的镜像后,发现网页频繁出现无响应的状况:浏览器标签页持续保持转圈的状态,直到很久以后才会报超时,且从此之后所有请求都无法正常完成。
经过多次尝试,发现在前端复现该问题的操作方法是:编辑书籍,获取元数据,点击保存。
排查
-
首先直接用
kubectl port-forward calibre-6c5c84fd4f-z2mgb 8083:12345
代理方式访问集群内calibre的pod,绕过集群中pod以外的其他网络组件,比如traefik,排除集群本身问题。发现问题依旧,初步排除集群网络问题。 -
此时需要看calibre-web日志:经过一番搜索研究,发现该项目的日志不会输出到stdout,而是位于项目目录里的
calibre-web.log
文件。相应的,在容器内的位置是/app/calibre-web/calibre-web.log
-
日志文件中看到很多重复的日志块如下。初步怀疑是由于编辑书的元数据时请求了google scholar,但是由于返回了status code 403,而相关代码在处理请求重试时逻辑有误导致了无限重试,进一步导致阻塞了web server主线程。
INFO {scholarly:116} Got an access denied error (403). [2021-10-13 10:10:29,801] INFO {scholarly:118} No other connections possible. [2021-10-13 10:10:29,801] INFO {scholarly:124} Will retry after 80.11549845444705 seconds (with another session). [2021-10-13 10:11:54,220] INFO {scholarly:105} Session proxy config is {}
-
此时需要查看janeczku/calibre-web的代码逻辑,相关代码如下:如果pip安装了scholarly库,则会去请求google scholor,否则会跳过。而项目把一些额外的pip依赖放到了单独的requirements文件
optional-requirements.txt
中。# Improve this to check if scholarly is available in a global way, like other pythonic libraries try: from scholarly import scholarly have_scholar = True except ImportError: have_scholar = False @editbook.route("/scholarsearch/<query>",methods=['GET']) @login_required_if_no_ano @edit_required def scholar_search(query): if have_scholar: scholar_gen = scholarly.search_pubs(' '.join(query.split('+'))) i=0 result = [] for publication in scholar_gen: del publication['source'] result.append(publication) i+=1 if(i>=10): break return Response(json.dumps(result),mimetype='application/json') else: return "[]"
-
查看linuxserver/docker-calibre-web中的Dockerfile可以看到确实默认安装
optional-requirements.txt
。
本地复现
-
第一次在本地启动calibre-web,仅安装
requirements.txt
里的依赖,此时可以正常编辑书籍的元数据。 -
pip install optional-requirements.txt
后,再编辑书籍元数据,问题被复现。 -
本文问题的出现,和calibre web server侧的代理情况密切相关,总结如下:
- 首先,显然,不安装scholarly,无论对google scholar的可达性如何都不会有问题
- 安装启用了scholarly后,如果访问google scholar正常,也不会有问题
- 安装启用了scholarly后,如果访问gogole scholar返回403,则会出现本文描述的问题。
- 安装启用了scholarly后,如果server在国内,且未有任何proxy的情况下,经过测试,scholarly在重试超过最大次数后会抛出
MaxTriesExceededException
异常,在异常抛出之前server也会被阻塞,但抛出异常后恢复正常。
溯源
安装scholarly,该库提供访问google scholar的api。pip install scholarly
❯ pip show scholarly
Name: scholarly
Version: 1.2.2
截取有问题的代码片段如下,可以看到访问google 得到403 status code时会导致循环无法跳出。
# scholarly/_navigator.py
if resp.status_code == 200 and not has_captcha:
return resp.text
elif has_captcha:
self.logger.info("Got a captcha request.")
self._session = self.pm._handle_captcha2(pagerequest)
continue # Retry request within same session
elif resp.status_code == 403:
self.logger.info(f"Got an access denied error (403).")
if not self.pm.has_proxy():
self.logger.info("No other connections possible.")
if not self.got_403:
self.logger.info("Retrying immediately with another session.")
else:
if not self.pm._use_luminati:
w = random.uniform(60, 2*60)
self.logger.info("Will retry after {} seconds (with another session).".format(w))
time.sleep(w)
self._new_session()
self.got_403 = True
continue # Retry request within same session
else:
self.logger.info("We can use another connection... let's try that.")
else:
self.logger.info(f"""Response code {resp.status_code}.
Retrying...""")
临时解决
docker pull lwabish/calibre-web:china
较快速的解决方法是:fork linuxserver/docker-calibre-web,在其中的dockerfile中加入pip uninstall scholarly -y
,取消google scholar支持。
新构建的镜像已上传至docker hub:lwabish/calibre-web - Docker Image | Docker Hub。