shop_platform - 部署到 Heroku 後,session 有問題

問題描述

部署到 heroku 後,出現一些奇怪的現象:

  • 輸入正確登入資訊,按下登入按鈕後:

    • 有時候會登入成功,跳轉到首頁,但切換幾個畫面後,又變成沒有登入的狀態

    • 跳轉到首頁,但仍是未登入狀態,也沒有出現任何 flash 訊息。切換幾個畫面後,出現遲來的 flash 訊息

    • 仍是跳轉到登入頁面,沒有出現任何 flash 訊息

  • 在未登入狀態下,將物品加入購物車(此時購物車內容會存在 session)。點選購物車頁面後:

    • 有時候會跳轉到首頁,也沒有出現任何 flash 訊息。切換幾個畫面後,出現遲來的 flash 訊息

    • 成功顯示購物車中的商品,但若繼續點選購物車頁面,過沒多久就提示「購物車中沒有商品」

上述操作(導覽列判斷使用者是否已登入、flash、未登入時的購物車),多半是跟 session 有關係,讓我有一種「cookie 或 session 好像會突然出現,又突然消失」的感覺。

另外,如果在本地也跟 heroku 一樣使用 gunicorn "app:create_app()" 指令開啟伺服器,一切功能都正常,無法重現 heroku 上的問題。

查資料

  • Flask sessions not persisting on heroku - stackoverflow

  • Application Server、WSGI Server、Web Server 之間的關係

  • Heroku gunicorn flask login is not working properly - stackoverflow

  • gunicorn 19 with WebSocket on Heroku

    These applications based on the Flask-SocketIO extension are stateful. If multiple workers are used, then sticky sessions are required at the load balancer. Unfortunately gunicorn does not support sticky sessions, so when using gunicorn it is required to run with one worker.

    Flask-SocketIO keeps the client session in memory. If you have one server process, then it is easy, all the clients are kept in that single process. When working with multiple servers, each server owns a subset of the clients, so in that case, it is required that all the requests for a given client are always sent to the same server. Any operations that require working on the complete client list are coordinated through messages on a message queue such as Redis or RabbitMQ. The set up that I recommend for the multi-server scenario is nginx as load balancer with the ip_hash option to enable sticky sessions. Behind nginx, multiple gunicorn processes with a single worker each, either eventlet or gevent. Then a redis store is added for the server-to-server communication.

  • Gunicorn多worker跑Flask应用Session数据共享问题

確定問題範圍

所以問題可能是出在多個 worker 時,每個 worker 都有一個新的 Flask instance,因為我的 session SECRET_KEY 是設成隨機變數,所以每個 instance 的 SECRET_KEY 都不一樣。客戶端送出的請求由不同 worker 處理,但是因為 SECRET_KEY 不同,所以 session 無法共用。

在本地用 gunicorn -w 3 "app:create_app()" 開啟伺服器的話,就能重現 heroku 上的狀況。在本地,指令裡沒有加 -w 3 的話,只會開一個 worker。

抓到兇手啦!只要把 session SECRET_KEY 設成常數,而不是隨機的變數就可以了:commit

Comments

Popular posts from this blog

Alpha Camp 全端開發課程學習心得

在 javascript 用 regular expression 為金額加上千位數分隔符號

shop_platform - sqlalchemy.exc.TimeoutError