这个问题对我来说是个谜,我非常高兴能找到解决方案。我将提供我认为相关的环境的详细信息,但如果还需要其他信息,请发表评论,我会添加。
基础设施:
主持人 -数字海洋水滴
操作系统——Ubuntu 17.04
反向代理 -nginx 1.12.0(该问题在 nginx 1.10.0 中也发生过)
应用服务器 -node-js 7.10.0(7.9.0 也发生过此问题)
客户 -安卓(在包括 5.0、6.0.1、7.1.1 在内的多个版本中都发生过,所以很可能不相关)使用 OkHttp 3.8.0
所以 nginx 将流量转发到 nodejs,然后 nodejs 处理来自 Android 客户端的请求。
问题:
对于某些客户端,偶尔对于特定的查询,nodejs 收到的 json 格式不正确。具体来说,在 body 的 json 开头写几个字节,
以下是一个例子:
2017-05-24 15:42:14.899 (+0300) - error: Error summary:
name: SyntaxError
status: 400
request: POST /api/v1/user_details
body:
J ount_id":"217627","user":{"email": "[email protected]" ....<rest of json is ok>
注意正文的开头 - 似乎有几个格式错误的字节覆盖了原始 json 的开头。有时没有奇怪的字节,只有原始 json 被剪切,例如 ser":{"email": "[电子邮件保护]“... 。情况变得更加奇怪 - 它只会偶尔发生一次(可能是 100 次调用中的一次),仅针对某些客户端,在特定端点中(尽管我没有很多端点,而且这个端点被大量使用),甚至在那些客户端中 - 在许多请求中也只发生一次。它并不特定于某个设备(至少发生在 LG、三星、OnePlus 上),从我在客户端获得的日志中,使用 OkHttp 拦截器查看发送了哪些数据,它似乎是正确的。我甚至尝试在我的设备上发送与客户端日志中失败的完全相同的请求,并且成功了。我试图重建这个错误,但没有成功 - 我用来自多个设备的请求轰炸服务器,看看这是否是一个大数字的问题,但没有一个产生这个问题。在进一步的调查中,我试图查看数据究竟在哪里出现格式错误 - 我注意到在所有中间件之前,nodejs 都会收到格式错误的正文。我在 nginx 中添加了正文日志,并注意到 nginx 本身收到了格式错误的数据。
这是我遇到过的最奇怪的问题之一,我真的希望有人能告诉我到底发生了什么。
Nginx 配置:
/etc/nginx/sites-enabled/default
# HTTP - redirect all requests to HTTPS:
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name my-service.com, app.my-service.com, www.my-service.com;
# letsencrypt auto-renew
location ~ /.well-known {
root /var/www/html;
}
location / {
return 301 https://$server_name$request_uri;
}
#return 301 https://$server_name$request_uri;
}
#log_format postdata '[$time_local] "$request" $status '
# '$request_body';
server {
# SSL configuration
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
include snippets/ssl-my-service.com.conf;
include snippets/ssl-params.conf;
# Pass requests for / to localhost:3000:
location / {
# used to log traffic's post body data when uncommented:
# access_log /var/log/nginx/postdata.log postdata;
# echo_read_request_body;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-NginX-Proxy true;
proxy_pass https://localhost:3000/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
片段/ssl-params.conf
ssl_certificate /etc/letsencrypt/live/my-service.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/my-service.com/privkey.pem;
片段/ssl-my-service.com.conf
server_tokens off;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 5m;
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/my-service.com/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=86400;
resolver_timeout 10s;
更新 1:
以下是Android客户端发送的请求头:
内容类型:application/json;字符集=UTF-8,内容长度:5689,授权:Bearer,用户代理:MyServiceApp/1.0.0(Android 7.1.2)