问题描述
前端调一个 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 不够。然而,经过一通操作之后发现并没有什么用~
- Nginx出现500 Internal Server Error 错误的解决方案
- How to Fix 500 Internal Server Error in NGINX
- nginx 500错误(post大文件是出错)
- nginx配置参数解释:client_header_buffer_size、large_client_header_buffers
第三步
查看 uwsgi
的日志,也是 500
。所以也找了一些 uwsgi
的配置,但也是没用。
此时打开 uwsgi
的 debug
模式,经过仔细查看后发现偶尔报 No handlers could be found for logger "sentry.errors"
。
sentry
是代码里配置的,怀疑请求已经到达 python 代码里了,但是日志没有打出来。
第四步
检查 settings.py
配置文件,修改 sentry
的 handlers
的配置,并把 sentry
的 loggers
的日志级别修改为 DEBUG
。
此时已经可以确定请求已经已经进到应用程序,但日志没有捕获到。
第五步
修改 Django 的 default
和 request_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
参数限制,导致写入或者更新失败。(比方说导入数据库,数据表)
问题解决
settings.py
中添加SecurityMiddleware
配置:MIDDLEWARE_CLASSES = [ 'django.middleware.security.SecurityMiddleware', 'finance.middleware.AccessMiddleware' ]
access_log
中的params
字段需要改成MEDIUMTEXT
类型,默认16M
;- 修改
max_allowed_packet
:max_allowed_packet参数。
以上。