排查calibre-web服務阻塞問題


問題

k8s集群中使用linuxServer的linuxserver/docker-calibre-web鏡像部署了janeczku/calibre-web,在211011升級了最新的鏡像后,發現網頁頻繁出現無響應的狀況:瀏覽器標簽頁持續保持轉圈的狀態,直到很久以后才會報超時,且從此之后所有請求都無法正常完成。

經過多次嘗試,發現在前端復現該問題的操作方法是:編輯書籍,獲取元數據,點擊保存。

排查

  1. 首先直接用kubectl port-forward calibre-6c5c84fd4f-z2mgb 8083:12345代理方式訪問集群內calibre的pod,繞過集群中pod以外的其他網絡組件,比如traefik,排除集群本身問題。發現問題依舊,初步排除集群網絡問題。

  2. 此時需要看calibre-web日志:經過一番搜索研究,發現該項目的日志不會輸出到stdout,而是位於項目目錄里的calibre-web.log文件。相應的,在容器內的位置是/app/calibre-web/calibre-web.log

  3. 日志文件中看到很多重復的日志塊如下。初步懷疑是由於編輯書的元數據時請求了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 {}
    
  4. 此時需要查看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 "[]"
    
  5. 查看linuxserver/docker-calibre-web中的Dockerfile可以看到確實默認安裝optional-requirements.txt

本地復現

  1. 第一次在本地啟動calibre-web,僅安裝requirements.txt里的依賴,此時可以正常編輯書籍的元數據。

  2. pip install optional-requirements.txt后,再編輯書籍元數據,問題被復現。

  3. 本文問題的出現,和calibre web server側的代理情況密切相關,總結如下:

    1. 首先,顯然,不安裝scholarly,無論對google scholar的可達性如何都不會有問題
    2. 安裝啟用了scholarly后,如果訪問google scholar正常,也不會有問題
    3. 安裝啟用了scholarly后,如果訪問gogole scholar返回403,則會出現本文描述的問題。
    4. 安裝啟用了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...""")

臨時解決

較快速的解決方法是:fork linuxserver/docker-calibre-web,在其中的dockerfile中加入pip uninstall scholarly -y,取消google scholar支持。

新構建的鏡像已上傳至docker hub:lwabish/calibre-web - Docker Image | Docker Hub


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM