NGINX 500 Internal Server Error

问题描述

前端调一个 post 接口返回 nginx 重定向的 500.html,并且 后端没有收到请求。参数很大时才会出现这个情况,参数小时接口可以调用成功。

问题排查

第一步

看浏览器的 Network 可以看到接口返回 500 并且返回内容是 nginx 重定向的 500.html

第二步

查看 模块日志 没有打印任何信息。

查看 nginx 日志看到 500 Internal Server Error,打开 debug 模式看到:

# ...
close http upstream connection
# ...
http close request
# ...

此时我们以为是到 nginx 这里就报错了,没有进入后端应用程序。可能是 nginx 配置的 body size 不够。然而,经过一通操作之后发现并没有什么用~

第三步

查看 uwsgi 的日志,也是 500。所以也找了一些 uwsgi 的配置,但也是没用。

此时打开 uwsgidebug 模式,经过仔细查看后发现偶尔报 No handlers could be found for logger "sentry.errors"

sentry 是代码里配置的,怀疑请求已经到达 python 代码里了,但是日志没有打出来。

第四步

检查 settings.py 配置文件,修改 sentryhandlers 的配置,并把 sentryloggers 的日志级别修改为 DEBUG

此时已经可以确定请求已经已经进到应用程序,但日志没有捕获到。

第五步

修改 Django 的 defaultrequest_handler 日志级别为 DEBUG

此时再次查看日志 debug.log,发现报错 (2006, 'MySQL server has gone away')。在 debug.log 里发现 APIAccessLog.objects.create(**args),最终定位到时候 写接口日志 AccessLog 报错。

进一步排查问题

1. 查看 AccessMiddleware 代码

发现确实有这段代码:

APIAccessLog.objects.create(**args)

2. 查看 access_log 表结构

看到请求参数字段最长为 2000

params = models.CharField(max_length=2000, help_text=u'调用参数')

3. 再查看线上数据库实际的表结构

可以看到线上数据库 params 字段为 text 类型:

`params` text COLLATE utf8_bin COMMENT '调用参数',

TEXT 类型最大长度 65535 字节,64kb

4. 查看线上 max_allowed_packet 的大小

看到 max_allowed_packet=4194304 ,而失败的接口请求包的长度是 Content-Length: 5106050 超出限制。

MySQL 下的 max_allowed_packet 参数设置

MySQL 根据配置文件会限制Server接受的数据包大小。有时候大的插入和更新会受 max_allowed_packet 参数限制,导致写入或者更新失败。(比方说导入数据库,数据表)

问题解决

  1. settings.py 中添加 SecurityMiddleware 配置:
    MIDDLEWARE_CLASSES = [
    	'django.middleware.security.SecurityMiddleware',
    	'finance.middleware.AccessMiddleware'
    ]
    
  2. access_log 中的 params 字段需要改成 MEDIUMTEXT 类型,默认 16M
  3. 修改 max_allowed_packetmax_allowed_packet参数

以上。


版权声明:本文为sansan_7957原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。