部署在Nginx下的Flask应用搜索结果页需刷新才能显示的问题排查
问题描述
- 基于Flask搭建的网站,本地运行完全正常,部署到VPS通过Nginx提供服务时,搜索操作会跳转至错误页面,但刷新页面(有时需多次)后结果可正常显示。
- 若直接在VPS上通过
gunicorn --bind 0.0.0.0:5000运行应用,访问example.com:5000时功能完全正常。
Nginx错误日志警告
搜索失败时,Nginx错误日志出现如下警告:
2023/05/22 12:23:07 [warn] 994#994: *519 an upstream response is buffered to a temporary file /var/lib/nginx/tmp/proxy/2/00/0000000002 while reading upstream, client: 216.227.60.13, server: , request: "GET /us_news HTTP/1.1", upstream: "http://unix:/home/siteadmin/personal-website/siteapp.sock:/us_news", host: "example.com", referrer: "https://example.com/"
调试核心错误与尝试的解决方法
开启Flask调试模式后发现:访问/news_results路由时出现KeyError: 'states',即session中不存在'states'键,但刷新后该键又存在。
尝试过以下方法,均未解决问题:
- 在报错代码前添加
time.sleep(5) - 在Nginx配置中设置
proxy_request_buffering off;
相关代码
Flask视图代码
from flask import Blueprint, render_template, redirect, url_for, session, make_response from flask import current_app as app, Flask, request, jsonify, g from flask_paginate import Pagination, get_page_args import json import psycopg2 import click from ..dbcon import makeConn from datetime import datetime from KnowledgeSchema import schema from sqlalchemy.orm import Session def addError(error): with Session(schema.engine, expire_on_commit=False) as session: session.add(error) session.commit() # session.close() # Blueprint Configuration us_news_bp = Blueprint('us_news_bp', __name__, url_prefix='', template_folder='templates', static_folder='static') def getArticles(states, topics, startDate, endDate): print(states) if states != '%': states = '|'.join(states) if topics != '%': topics = '|'.join(topics) search = {'states' : tuple(states), 'topics' : tuple(topics)} q = "select count(*) from articles" conn = makeConn() dict_cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) dict_cur.execute(q) total = dict_cur.fetchone()[0] page, per_page, offset = get_page_args( page_parameter="p", per_page_parameter="pp", pp=20 ) if per_page: sqlStr = """ SELECT title,pubdate,feed_id,articles.url,thumbnails[1],description,state_code, keywords, creators, news_outlets.outlet_name,news_outlets.main_url, news_outlets.mbfc_bias, news_outlets.mbfc_factual, news_outlets.mbfc_cred, news_outlets.twitter, news_outlets.mbfc_url, news_outlets.facebook, news_outlets.youtube, news_outlets.wikipedia, news_feeds.section as section, news_feeds.url AS feedurl, supersection, COUNT(*) OVER() AS num_rows FROM articles INNER JOIN news_feeds USING (feed_id) INNER JOIN states USING (state_id) INNER JOIN news_outlets USING (outlet_id) WHERE states.state_code SIMILAR TO %s AND supersection SIMILAR TO %s AND pubdate BETWEEN TO_DATE(%s, 'YYYY-MM-DD') and TO_DATE(%s, 'YYYY-MM-DD') ORDER BY pubdate_linux ASC LIMIT {} OFFSET {} """ print(sqlStr) sql = dict_cur.mogrify(sqlStr.format(per_page, offset), (states, topics, startDate, endDate)) else: sql = dict_cur.mogrify( """ SELECT title,pubdate,feed_id,articles.url,thumbnails[1],description,state_code, keywords, creators, news_outlets.outlet_name,news_outlets.main_url, news_outlets.mbfc_bias, news_outlets.mbfc_factual, news_outlets.mbfc_cred, news_outlets.twitter, news_outlets.mbfc_url, news_outlets.facebook, news_outlets.youtube, news_outlets.wikipedia, news_feeds.section as section, news_feeds.url AS feedurl, supersection, COUNT(*) OVER() AS num_rows FROM articles INNER JOIN news_feeds USING (feed_id) INNER JOIN states USING (state_id) INNER JOIN news_outlets USING (outlet_id) WHERE states.state_code SIMILAR TO %s AND supersection SIMILAR TO %s AND pubdate BETWEEN TO_DATE(%s, 'YYYY-MM-DD') and TO_DATE(%s, 'YYYY-MM-DD' ORDER BY pubdate_linux ASC) """.format((states, topics, startDate, endDate))) print("sql is:", sql) print("SqL") dict_cur.execute(sql) articles = dict_cur.fetchall() total = articles[0][-1] pagination = get_pagination( p=page, pp=per_page, total=total, format_total=True, format_number=True, page_parameter="p", per_page_parameter="pp", ) return articles, pagination @us_news_bp.route('/us_news', methods=['GET', 'POST']) def us_news(): if request.method == 'POST': states = request.form.getlist('jsonval') topics = request.form.getlist('topics') startDate = request.form.getlist('startDate') endDate = request.form.getlist('endDate') states = json.loads(states[0]) print("start making session") try: session['states'] = states except: pass try: session['topics'] = topics except: pass try: session['startDate'] = startDate[0] except: pass try: session['endDate'] = endDate[0] except: pass print("END getting session") return redirect(url_for('us_news_bp.news_results', states=states, **request.args)) else: q = "select DISTINCT supersection FROM articles;" conn = makeConn() dict_cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) dict_cur.execute(q) topics = dict_cur.fetchall() return render_template('us_news.html', title='Skeleton Site', # states=states, topics=topics, subtitle='Homepage', template='us_news-template') def saveError(err): file = open("recent_error", "w") file.write(str(err)) file.close() @us_news_bp.route('/news_results') def news_results(): try: states = session['states'] if len(states) == 0: states= '%' topics = session['topics'] if len(topics) == 0: topics= '%' startDate = session['startDate'] if len(startDate) == 0: startDate ='2000-01-01' endDate = session['endDate'] if len(endDate) == 0: endDate = '2123-04-01' articles, pagination = getArticles(states, topics, startDate, endDate) return make_response(render_template( "news_results.html", articles=articles, pagination=pagination)) except Exception as e: print(e) saveError(e) failure = schema.Error( error = str(e), step = 'news search', date = datetime.now().isoformat()) return render_template("errorpage.html") def get_css_framework(): return app.config.get("CSS_FRAMEWORK", "bootstrap4") def get_link_size(): return app.config.get("LINK_SIZE", "sm") def get_alignment(): return app.config.get("LINK_ALIGNMENT", "") def show_single_page_or_not(): return app.config.get("SHOW_SINGLE_PAGE", False) def get_pagination(**kwargs): kwargs.setdefault("record_name", "records") return Pagination( css_framework=get_css_framework(), link_size=get_link_size(), alignment=get_alignment(), show_single_page=show_single_page_or_not(), **kwargs ) @click.command() @click.option("--port", "-p", default=5000, help="listening port") def run(port): app.run(debug=True, port=port) if __name__ == "__main__": run()
Nginx配置
user nginx; worker_processes auto; error_log /var/log/nginx/error.log warn; pid /run/nginx.pid; include /usr/share/nginx/modules/*.conf; events { worker_connections 64; } http { log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 4096; proxy_request_buffering off; include /etc/nginx/mime.types; default_type application/octet-stream; # Load modular configuration files from the /etc/nginx/conf.d directory. # See http://nginx.org/en/docs/ngx_core_module.html#include # for more information. include /etc/nginx/conf.d/*.conf; # map $http_upgrade $connection_upgrade { # default upgrade; # '' close; #} server { listen 80; listen [::]:80; server_name _; # root /usr/share/nginx/html; return 301 https://$host$request_uri; # Load configuration files for the default server block. include /etc/nginx/default.d/*.conf; error_page 404 /404.html; location = /404.html { } error_page 500 502 503 504 /50x.html; location = /50x.html { } } } ## site.conf server { listen 443 ssl; listen [::]:443 ssl; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot include /etc/nginx/default.d/*.conf; location / { include proxy_params; # proxy_http_version 1.1; # proxy_pass_request_headers on; proxy_request_buffering off; proxy_pass http://unix:/home/siteadmin/personal-website/siteapp.sock; # proxy_set_header Upgrade $http_upgrade; # proxy_set_header Connection $connection_upgrade; # proxy_ssl_session_reuse on; # proxy_intercept_errors off; } } ## Proxy proxy_params proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme;
内容的提问来源于stack exchange,提问作者Logos Masters
相关产品推荐
相关产品推荐

