You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Heroku部署的Django站点无请求时离线问题求助

Django部署在Heroku上频繁崩溃,Idle后需多次刷新才能恢复

我部署在Heroku上的Django网站频繁出现问题:当一段时间没有新请求触发时,站点会显示**“There's nothing here, yet.”**的提示,得多次刷新页面才能重新加载正常内容。我用了django_on_heroku和dj_database_url来配置Heroku的自动设置,怀疑是配置问题但找不到具体原因,相关配置文件和崩溃前的日志如下:

base.py

"""
Django settings for setup project.

Generated by 'django-admin startproject' using Django 4.0.6.

For more information on this file, see
https://docs.djangoproject.com/en/4.0/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.0/ref/settings/
"""
import dj_database_url
from dotenv import load_dotenv, find_dotenv

from pathlib import Path, os

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent.parent


# Application definition

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    # 自定义应用
    "users.apps.UsersConfig",
    "accounts.apps.AccountsConfig",
    "produtos.apps.ProdutosConfig",
    "lojista.apps.LojistaConfig",
    # 第三方应用
    "localflavor",
    "tempus_dominus",
    "django_extensions",
]

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "whitenoise.middleware.WhiteNoiseMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
]

ROOT_URLCONF = "setup.urls"

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "templates"],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
            ],
        },
    },
]

WSGI_APPLICATION = "setup.wsgi.application"


# Database
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases

# DATABASES = {
#     "default": {
#         "ENGINE": "django.db.backends.sqlite3",
#         "NAME": BASE_DIR / "db.sqlite3",
#     }
# }

load_dotenv(find_dotenv())

DATABASES = {
    "default": dj_database_url.config(default="sqlite://db.slite3")
}

# Password validation
# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
    },
]


# Internationalization
# https://docs.djangoproject.com/en/4.0/topics/i18n/

LANGUAGE_CODE = "pt-Br"

TIME_ZONE = "America/Sao_Paulo"

USE_I18N = True
USE_L10N = True
USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.0/howto/static-files/

STATIC_URL = "/static/"
STATIC_ROOT = BASE_DIR / "staticfiles"
STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"),)

MEDIA_ROOT = os.path.join(BASE_DIR, "media")
MEDIA_URL = "/media/"

STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"

# Default primary key field type
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

# 自定义用户模型
AUTH_USER_MODEL = "users.CustomUser"


LOGIN_REDIRECT_URL = "/"
LOGOUT_REDIRECT_URL = "/"
TEMPUS_DOMINUS_LOCALIZE = True


USE_L10N = True

# 消息标签配置
from django.contrib.messages import constants as messages

MESSAGE_TAGS = {
    messages.DEBUG: "alert-info",
    messages.INFO: "alert-info",
    messages.SUCCESS: "alert-success",
    messages.WARNING: "alert-warning",
    messages.ERROR: "alert-danger",
}

prod.py

import django_on_heroku
from decouple import config

from .base import *

SECRET_KEY = config("SECRET_KEY")

DEBUG = False

ALLOWED_HOSTS = [
    "e-commerce-validity.herokuapp.com",
]

DEBUG_PROPAGATE_EXPECTIONS = True

LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "formatters": {
        "verbose": {
            "format": "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s",
            "datefmt": "%d/%b/%Y %H:%M:%S",
        },
        "simple": {"format": "%(levelname)s %(message)s"},
    },
    "handlers": {
        "console": {
            "level": "DEBUG",
            "class": "logging.StreamHandler",
        },
    },
    "loggers": {
        "MYAPP": {
            "handlers": ["console"],
            'level': os.getenv('DJANGO_LOG_LEVEL', 'DEBUG'),
        },
    },
}

# Heroku 配置
django_on_heroku.settings(locals(), staticfiles=False)
del DATABASES["default"]["OPTIONS"]["sslmode"]

站点离线前的最后日志

2022-09-20T09:22:00.227176+00:00 app[web.1]: 10.1.5.86 - - [20/set/2022:06:22:00 -0300] "GET /media/products/2022/09/20/shopping_1.webp HTTP/1.1" 404 179 "https://e-commerce-validity.herokuapp.com/categoria/bebidas" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36"
2022-09-20T09:22:00.227188+00:00 heroku[router]: at=info method=GET path="/media/products/2022/09/20/shopping_1.webp" host=e-commerce-validity.herokuapp.com request_id=1b0debf4-5a8c-407d-a589-e17a602d23b4 fwd="179.66.122.162" dyno=web.1 connect=0ms service=1ms status=404 bytes=452 protocol=https
2022-09-20T09:22:01.046037+00:00 heroku[router]: at=info method=GET path="/produto/coca-cola-zero" host=e-commerce-validity.herokuapp.com request_id=1c953d08-eb6f-4dd2-8a97-de9f805d3df8 fwd="179.66.122.162" dyno=web.1 connect=0ms service=14ms status=200 bytes=5704 protocol=https
2022-09-20T09:22:01.046086+00:00 app[web.1]: 10.1.5.86 - - [20/set/2022:06:22:01 -0300] "GET /produto/coca-cola-zero HTTP/1.1" 200 5241 "https://e-commerce-validity.herokuapp.com/categoria/bebidas" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36"
2022-09-20T09:22:01.318131+00:00 heroku[router]: at=info method=GET path="/media/products/2022/09/20/shopping_1.webp" host=e-commerce-validity.herokuapp.com request_id=9316ccb6-f7fd-43ab-b545-47b1442f3f3d fwd="179.66.122.162" dyno=web.1 connect=0ms service=2ms status=404 bytes=452 protocol=https
2022-09-20T09:22:01.318224+00:00 app[web.1]: 10.1.5.86 - - [20/set/2022:06:22:01 -0300] "GET /media/products/2022/09/20/shopping_1.webp HTTP/1.1" 404 179 "https://e-commerce-validity.herokuapp.com/produto/coca-cola-zero" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36"
2022-09-20T09:22:02.380148+00:00 heroku[router]: at=info method=GET path="/carrinho/" host=e-commerce-validity.herokuapp.com request_id=e0d79cfd-4b64-4305-9025-a863cade10d3 fwd="179.66.122.162" dyno=web.1 connect=0ms service=11ms status=200 bytes=10006 protocol=https
2022-09-20T09:22:02.380234+00:00 app[web.1]: 10.1.5.86 - - [20/set/2022:06:22:02 -0300] "GET /carrinho/ HTTP/1.1" 200 9543 "https://e-commerce-validity.herokuapp.com/produto/coca-cola-zero" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36"
2022-09-20T09:22:02.655168+00:00 heroku[router]: at=info method=GET path="/media/products/2022/09/20/shopping.webp" host=e-commerce-validity.herokuapp.com request_id=55c3277d-ea8b-4506-a37e-a79f029845dc fwd="179.66.122.162" dyno=web.1 connect=0ms service=2ms status=404 bytes=452 protocol=https
2022-09-20T09:22:02.655208+00:00 app[web.1]: 10.1.5.86 - - [20/set/2022:06:22:02 -0300] "GET /media/products/2022/09/20/shopping.webp HTTP/1.1" 404 179 "https://e-commerce-validity.herokuapp.com/carrinho/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36"

问题分析与解决方案

你看到的**“There's nothing here, yet.”**是Heroku dyno休眠后首次请求的默认提示,但多次刷新才恢复,核心问题集中在数据库连接、dyno唤醒配置这两块,结合你的代码和日志,逐个修复:

1. 修复数据库SSL连接配置

你直接删除了sslmode参数,导致Heroku PostgreSQL连接不稳定,dyno唤醒时无法快速重建连接,请求失败。

修改prod.py的数据库配置:

# 替换原来的del语句
DATABASES["default"]["OPTIONS"]["sslmode"] = "require"

2. 配置数据库连接池,减少唤醒延迟

默认Django没有连接池,dyno唤醒时重新建立数据库连接的速度慢,导致首次请求超时。

修改base.py的数据库配置:

DATABASES = {
    "default": dj_database_url.config(
        default="sqlite://db.sqlite3",
        conn_max_age=600,  # 保持连接10分钟,避免频繁重建
        ssl_require=True
    )
}

3. 统一静态文件配置,避免冲突

你关闭了django_on_heroku的静态文件处理,但自己配置了Whitenoise,两者冲突可能导致站点启动异常。

修改prod.py的Heroku配置:

# 让django_on_heroku处理静态文件,再手动恢复Whitenoise配置
django_on_heroku.settings(locals(), staticfiles=True)
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"

4. 优化dyno唤醒速度,避免超时

Heroku对dyno唤醒的首次请求有超时限制,如果站点启动慢就会返回默认页面。

  • 清理INSTALLED_APPS,移除没用的第三方库(比如如果django_extensions只在本地用,就移到dev配置里)
  • 用定时工具(比如UptimeRobot)每隔15分钟请求一次站点,防止dyno休眠
  • 升级到专业级dyno,启用Preboot功能,实现无缝重启

5. 解决媒体文件404问题(辅助优化)

日志里大量媒体文件找不到,因为Heroku本地存储是临时的,dyno重启后文件会丢失。把媒体文件迁移到云存储(比如Cloudinary、AWS S3),不要存在Heroku本地目录。


内容的提问来源于stack exchange,提问作者xSkolz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 23:15:41