导读:近期有一个业务部门的同学反馈说他负责的C工程在小概率情况下SpringMvc会返回415,通过输出的日志可以确定是SpringMvc找不到content-type这个头了,具体为什么找不到了呢?请听我娓娓道来。

关键词:http 415,SpringMvc,nginx,lua,wireshark,jmeter

问题现象:

近期接到一个同学的反馈说,他负责的C工程在小概率的情况下SpringMvc会返回415,通过输出的日志发现请求头里面并没有content-type了,所以才导致SpringMvc返回415,他猜测可能是我在nginx这边加的lua脚本给影响了(我们用lua脚本做了一些灰度跳转的逻辑),虽然我心里很清楚lua这块并没有修改请求头的逻辑,但是没办法,嘴上说说并不能服众,我需要拿出证据自证清白。

第一把斧亮相:

既然是说lua把请求头给改了,那把content-type输出来看看不就完事了。很简单,直接修改nginx access日志的log_format即可,在原有的基础上增加$http_content_type。就这样很快加完了,那就开始守株待兔吧,运气还不错,几分钟以后就命中一个415的请求,查看access日志里记录的content-type值,一点问题没有,就是我们熟悉的那个“application/json”,这一刻心里窃喜“不是哥的问题,回邮件该干啥干啥了”。

第二把斧亮相:

半天过后负责Resin(一种web服务器,和Tomcat差不多)的同学找到了我,猜测可能是请求头里面有某些特殊字符导致请求头的解析发生了问题,所以请求头乱了,请看下面这张图,仔细找找其实是能找到content-type的。

话到这里我已经知道他要干什么了,给我几分钟我想想办法,既然怀疑是特殊字符导致那我就将所有的头信息都打印出来,查了一下lua-nginx-module的使用手册,有一个声明叫log-by-lua可以使用一下,它类似于一个过滤器一样,如果配置了,nginx会在特定的阶段触发它,更具体的说nginx会在log阶段且在记录access log之前触发。所以我的大概思路是这样“如果请求后端服务器返回415那就输出请求头信息到日志中”,具体的脚本写出来就是这样(我当时想是不是可以用在服务的监控上):

location  ~ \.do$ {
        proxy_pass http://$backend;
        log_by_lua_block {
          if tonumber(ngx.var.status) == 415 then
                ngx.log(ngx.ERR,"upstream reponse status is 415,please notice it,here are request headers:")
                local h, err = ngx.req.get_headers(100,true)
 
               if err == "truncated" then
                    ngx.log(ngx.ERR,"request headers beyond 100,will not return")
                else
                   local cjson = require("cjson")
                   ngx.log(ngx.ERR,cjson.encode(h))
              end
          end
       }
   }
和之前一样,继续开始守株待兔,运气不错,几分钟过后就出现了415,立马将请求头信息发给负责Resin的那个大哥,感觉马上要水落石出了,下楼抽根烟放松放松。
 
第三把斧亮相:
第二天刚到工位我就询问负责Resin那个大哥是否复现了问题,结果让人很意外,拿着我给的请求头信息居然没有复现问题,这太不科学了,大家在群里你一言我一嘴,感觉有些黔驴技穷,有人说要review Resin的源代码(线上出了问题,review我觉得真的是一种解决问题的下策),有人在那里抱怨说早应该把Resin替换掉的(事后诸葛亮),最后我提了一个土办法,应该可以复现问题,要是还不能复现真的就不科学了。兄台,tcpdump+wireshark了解一下,分几步来说:
        1.在服务器上使用tcpdump抓一段时间的包,因为不确定什么时候会出现,所以暂定10分钟;
        2.将第一步中产生的抓包文件使用wireshark打开,过滤http状态为415的请求,http.response.code==415;
        

        3.选择其中一条报文,右键“追踪流-tcp流”;
        4.复制第三步产生的tcp报文;
        

5.将第四步复制的tcp报文直接贴到jmeter里面进行测试(我为什么选择jmeter,因为可以直接模拟发送tcp报文,不会像postman增加一些自己的头);

6.成功复现结果;

7.导出jmeter脚本给负责Resin的同学修改bug;

问题原因分析:

最终确定是Resin出了问题,但这里有个前提是我们公司自己改造过的Resin,官方的Resin并没有这个问题,至于为什么我们改造过的Resin会出问题,这里就不多说了,只是一些内部的技术债罢了,说了可能也不会明白,明白了可能也不会碰上。

总结:

十一长假的第五天,此刻刚把孩子哄睡着,在床边小心翼翼的打开电脑整理一下上次排查问题的过程,上面提到的几个工具是我想推荐给大家使用的,这里拿自己经历过的真实场景做一个抛转引用,至于更深层次的使用网络上不乏铺天盖地的介绍,我这里就不啰嗦了,祝大家假期愉快。

参考资料:

https://github.com/openresty/lua-nginx-module#log_by_lua

http://jmeter.apache.org/usermanual/index.html

https://www.wireshark.org/docs/

 

推荐几个我近期排查线上http接口偶发415时用到的工具的更多相关文章

  1. Linux命令排查线上问题常用的几个

    排查线上问题常用的几个Linux命令 https://www.cnblogs.com/cjsblog/p/9562380.html top 相当于Windows任务管理器 可以看到,输出结果分两部分, ...

  2. 记一次linux通过jstack定位CPU使用过高问题或排查线上死锁问题

    一.java定位进程 在服务器中终端输入命令:top 可以看到进程ID,为5421的cpu这列100多了. 记下这个数字:5421 二.定位问题进程对应的线程 然后在服务器中终端输入命令:top -H ...

  3. JVM jmap dump 分析dump文件 / 如何使用Eclipse MemoryAnalyzer MAT 排查线上问题

    jhat简介 jhat用来分析java堆的命令,可以将堆中的对象以html的形式显示出来,包括对象的数量,大小等等,并支持对象查询语言 这个工具并不是想用于应用系统中而是用于"离线" ...

  4. 使用Dump转储文件排查线上环境服务未知问题

    利用Dump转储文件获取正式环境程序堆栈状态 服务异常找不到原因时,我们通常通过重新启动服务来尝试解决问题,但是在决定重启之前,请不要立刻重启Windows服务或站点 重启服务会让当前案发现场的内存证 ...

  5. 轻松排查线上Node内存泄漏问题

    I. 三种比较典型的内存泄漏 一. 闭包引用导致的泄漏 这段代码已经在很多讲解内存泄漏的地方引用了,非常经典,所以拿出来作为第一个例子,以下是泄漏代码: 'use strict'; const exp ...

  6. 利用JVM在线调试工具排查线上问题

    在生产上我们经常会碰到一些不好排查的问题,例如线程安全问题,用最简单的threaddump或者heapdump不好查到问题原因.为了排查这些问题,有时我们会临时加一些日志,比如在一些关键的函数里打印出 ...

  7. Arthas协助排查线上skywalking不可用问题

    前言 首先描述下问题的背景,博主有个习惯,每天上下班的时候看下skywalking的trace页面的error情况.但是某天突然发现生产环境skywalking页面没有任何数据了,页面也没有显示任何的 ...

  8. 你要偷偷学会排查线上CPU飙高的问题,然后惊艳所有人!

    GitHub 20k Star 的Java工程师成神之路,不来了解一下吗! GitHub 20k Star 的Java工程师成神之路,真的不来了解一下吗! GitHub 20k Star 的Java工 ...

  9. 记一次排查线上MySQL死锁过程,不能只会curd,还要知道加锁原理

    昨晚我正在床上睡得着着的,突然来了一条短信. 啥,线上MySQL死锁了,我赶紧登录线上系统,查看业务日志. 能清楚看到是这条insert语句发生了死锁. MySQL如果检测到两个事务发生了死锁,会回滚 ...

随机推荐

  1. 转载-Spring Boot应用监控实战

    概述 之前讲过Docker容器的可视化监控,即监控容器的运行情况,包括 CPU使用率.内存占用.网络状况以及磁盘空间等等一系列信息.同样利用SpringBoot作为微服务单元的实例化技术选型时,我们不 ...

  2. Mac迅雷瘦身精简教程

    迅雷是个大家很熟悉的工具了,尽管吐槽的人不少,但相信大家也都是口嫌体直,边骂边用. 其实 macOS 版迅雷在界面上,相比于 Windows 的客户端来说,已经很克制了,但有些功能仍然对用户造成了干扰 ...

  3. 学生管理系统 Python语言

    def show_student(): print(('*'*20).center(55)) print('1.添加学生信息'.center(50)) print('2.修改学生信息'.center( ...

  4. Keras(三)backend 兼容 Regressor 回归 Classifier 分类 原理及实例

    backend 兼容 backend,即基于什么来做运算 Keras 可以基于两个Backend,一个是 Theano,一个是 Tensorflow 查看当前backend import keras ...

  5. odoo 恢复数据库前端报错

    Could not get content for…… jQuery is not defined 原因:数据库缓存 解决方法: select id, create_date, store_fname ...

  6. 2019nc#9

    题号 标题 已通过代码 题解/讨论 通过率 团队的状态 A The power of Fibonacci 点击查看 进入讨论 69/227 未通过 B Quadratic equation 点击查看 ...

  7. POJ-1847 Tram( 最短路 )

    题目链接:http://poj.org/problem?id=1847 Description Tram network in Zagreb consists of a number of inter ...

  8. codeforces 509 D. Restoring Numbers(数学+构造)

    题目链接:http://codeforces.com/problemset/problem/509/D 题意:题目给出公式w[i][j]= (a[i] + b[j])% k; 给出w,要求是否存在这样 ...

  9. PAT L1-043. 阅览室

    L1-043. 阅览室 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 天梯图书阅览室请你编写一个简单的图书借阅统计程序.当读者 ...

  10. JavaScript new的运行过程

    参考 MDN网站的运算符 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/new new 运算符 ...