问题描述
前端调一个 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参数。
以上。