Java技术栈

www.javastack.cn

优秀的Java技术公众号

为了解决分布式链路追踪的问题,我们引入了实现OpenTracing的Jaeger来实现。然后我们为SpringBoot框架写了一个starter以让用户实现近零改造接入全链路。

由于公司有一个封装了SpringBoot的内部框架,然后我们的starter就以最新框架所使用的SpringBoot版本为基础进行开发。所以业务系统在接入的时候需要先升级框架,然后再引入我们的starter才行无缝接入全链路。

故障描述

然后有一个业务系统就按照步骤,升级框架,引入starter就接入了全链路系统,并且功能测试压力测试都已经通过了。结果我们满怀信心地就上线了。结果,线上nginx报大量http 400错误。

故障排查

出现故障后,业务系统的研发人员查了所有的日志,包括elk以及机器上的日志,都没有发现明显的错误日志。这个就。。。

几番挣扎后还是没有在线上的日志中找到任何蛛丝马迹。这个就比较绝望了。更奇怪的是在测试环境中是正常的,这个就比较诡异了。

然后我们猜想是不是之前压力测试做得不够啊,我们还是在压测环境中再压测一下看看会不会复现。然后正好之前这个业务系统做过压测,那就赶紧找运维搭建一个压测环境。结果刚搭建完就非常给面子地复现了400错误。

然后运维同学就各种折腾,然后神奇般地在nginx中的location下加了一行配置后就好了.
proxy_set_header HOST $host

然后就开始各种查这个配置是啥意思。

这个配置的主要是在nginx在转发htp请求的时候会加上实际的Host请求头。如http请求是 http://abc.com/hello,那么nginx在转发http请求的时候会原封不动的把host请求头(Host:abc.com)转发给后台服务。对于nginx而言,如果没有配置proxysetheader HOST $host的时候会默认修改Host为upstream的名称。

然后我们又在压测环境中试了一下修改之前的版本,发现是正常的。我们nginx的配置大体如下

那总结一下现在的现象:

  • 在nginx没有配置proxysetheader HOST $host的时候,修改之前的版本是正常的,修改之后的版本报400错误

  • 在nginx配置了proxysetheader HOST $host之后,两个版本都是正常的

那我们到底修改了什么呢?

  • 升级SpringBoot的版本

  • 引入全链路starter

然后我们试了下去掉全链路starter的引用,发现还是400错误。然后再回退SpringBoot版本,发现是正常的

综上:是因为升级了SpringBoot版本导致了该问题,又因为是http的头部变化导致的问题,故可以大胆猜测是因为升级了Tomcat版本导致的该问题

tomcat版本从8.5.11升级到8.5.31

故障本地复现

由前面的分析可知,nginx在没有配置proxysetheader HOST $host 的时候,在转发http请求的时候会默认把upstream的名称作为Host头部的内容。

也就是说新版的tomcat在接收Host为sc_java(带有下划线)的http请求报了400错误

下面我们来复现一下这个错误:如下,本地部署两个使用新版本tomcat的后台服务,端口分别为8083和8084

nginx配置如下。重点是upstream是带下划线的

然后使用postman请求nginx,复现400错误

调整nginx配置,主要修改upstream为没有下划线的

然后再请求,发现是正常的

故障修复方案

  • 回退tomcat版本。代价较大

  • 线上修改nginx配置:加上配置proxysetheader HOST $host 或者修改upstream为没有下划线的名称

根因分析

我们虽然知道了故障的原因,也知道了怎么修复这个故障。但是就是不知道新版的tomcat为什么出现这个问题。带着这个疑问,我们组的同事在SpringBoot项目的issue中搜索了下400问题,发现确实有相关的issue

[tomcat] Spring boot web always return 400 when use a domain name

虽然看上去跟我们的问题是一样的,都是400问题,但是具体发生的原因是不一样的。这个issue是说,如果domain name .ext 包含数字,比如 "domain.sf1m",会出现400问题。这个问题也已经在tomcat的新版本中修复了。

但是即使我使用最新的8.5.x版本的tomcat,用带有下划线的Host的http去请求tomcat的时候依然会报400错误。

也就是说,带有下划线的Host的http请求,tomcat认为是有问题的

那为什么之前版本的tomcat是正常的呢?带着这个疑问我们来分析一下tomcat的源代码。

由于之前没有看过tomcat的源代码,所以要分析出到底是哪一行代码有问题是很困难的,所以我查看了下tomcat的相关的bugImprove logging in AbstractProcessor.parseHost()

下面是bug中的错误stack

发现对应的代码改动如下

到这里我们也就知道了处理Host头部的类就是这个HttpParser类。

然后我在本次check了下tomcat8.5.31 和8.5.11的代码,比对了一下HttpParser以及AbstractProcessor类。对比结果如下:

发现8.5.31版本的AbstractProcessor类中多了一个parseHost的方法,然后主要解析方法是Host.parse(valueMB);

到这里我们就已经知道了为什么8.5.11版本的tomcat是正常的,主要是因为8.5.11版本的tomcat没有对Host头部进行校验,而在8.5.31版本的tomcat增加了该校验。

我们来看一下tomcat源代码的提交记录

我们发现在 2018/4/6增加了对host/port的校验。

跟因之跟因

那为什么tomcat增加了这个Host的校验呢,而且不允许使用带有下划线的Host呢?实际上这个是有规范的,可以访问下面地址

https://www.ietf.org/rfc/rfc1034.txt

经验教训

好了,到这里我们就知道了,其实对于带有下划线的Host,tomcat是遵循的RFC1-1034的规范的,所以tomcat的处理是正确的。但是tomcat在处理某些其他合法的Host的时候历史上出现过bug,但是对于下划线的处理一直是正确的。

所以,以后nginx在配置upstream的时候不能使用带有下划线的名称,还有最好在location位置上加上proxysetheader HOST $host。

作者:藤伦柳揶

https://www.jianshu.com/p/d50bc43f505e

- END -
推荐阅读:
1、

2、

3、

4、

5、

关注Java技术栈公众号在后台回复:Java,可获取一份栈长整理的最新Java 技术干货。

点击「阅读原文」和栈长学更多~

用了 10 多年的 Tomcat 居然有bug !的更多相关文章

  1. Eclipse提示Tomcat miss丢失bug:The Tomcat server configuration at \Servers\Tomcat v5.5 Server at localhost-config is missing.

    Eclipse提示Tomcat miss丢失bug:The Tomcat server configuration at \Servers\Tomcat v5.5 Server at localhos ...

  2. Solr-4.10.2与Tomcat整合

    1.将下载的solr解压至D:\solr,拷贝d:\solr\solr-4.10.2\example\webapps\solr.war到Tomcat的webapps\目录中.直接解压 solr.war ...

  3. 在MyEclipse 10中配置tomcat田服务器时出现的问题以及解觉办法

    今天刚刚重装电脑,为了实训的一个项目要使用到MyEclipse开发工具但是在配置服务器之后运行时出现了问题 错误:java.lang.UnsupportedClassVersionError: org ...

  4. JAXB在Java 9/10并且使用Tomcat 9的问题

    Implementation of JAXB-API has not been found on module path or classpath. JAXB API是java EE 的API,jav ...

  5. Mac下安装eclipse(Mac 10.12/JDK/tomcat)

    1.到官网https://www.eclipse.org/downloads/eclipse-packages/下载安装包 2.安装 注意:安装ecllipse时一定要安装JDK先,最新版本的ecli ...

  6. 深入刨析tomcat 之---第10篇 how tomcat works 第13章,Response 发送错误信息 sendError

    writedby 张艳涛 在浏览器中发送一个错误应用url 那么tomcat是如何发送错误的呢? 基本上是发送http 的response协议,分为两部分一部分是response设置头信息, 那么先分 ...

  7. 10:基于Tomcat部署Web工程

    1创建目录, 2.标识目录java文件,资源文件

  8. 记一次解决tomcat自动关闭的bug

    最近一个运行了4年的javaee web项目,经常接到客户反馈系统无法打开.登录服务器查看服务,发现是tomcat自动关闭了.基本是3到4天发生一次. 运维人员开始以为是其他服务杀死了tomcat服务 ...

  9. python programming作业10(仍有一点点小bug)

    # -*- coding: utf-8 -*- import os import platform import sys from PyQt5.QtCore import * from PyQt5.Q ...

随机推荐

  1. Docker(2)--Centos7 上安装部署

    Centos7 上安装docker Docker从1.13版本之后采用时间线的方式作为版本号,分为社区版CE和企业版EE. 社区版是免费提供给个人开发者和小型团体使用的,企业版会提供额外的收费服务,比 ...

  2. 40 final、finally、finalize的区别

    1.final 用于声明属性.方法.类.分别表示属性不可被改变,方法不可被覆盖,类不可被继承. (1)一个类不能既被声明为abstract的,又被声明为final的. (2)被声明为final的变量必 ...

  3. 【NOIP2014模拟8.24】小X 的道路修建

    题目 因为一场不小的地震,Y 省n 个城市之间的道路都损坏掉了,省长希望小X 将城市之间的道路重修一遍. 很多城市之间的地基都被地震破坏导致不能修路了,因此可供修建的道路只有m 条.因为施工队伍有限, ...

  4. C#对应JavaScript的银行家舍入规则(Math.Round()对应toFixed(f))

    Math.Round((n * u - t * u )/ u, f);//这里使用银行家四舍五入对应JS的 toFixed() ((n * u - t * u) / u).toFixed(f) f为小 ...

  5. java文件断点续传的简单实现

    一.概述 所谓断点续传,其实只是指下载,也就是要从文件已经下载的地方开始继续下载.在以前版本的HTTP协议是不支持断点的,HTTP/1.1开始就支持了.一般断点下载时才用到Range和Content- ...

  6. java jts

    来自:UCMapForOpenGIS https://bbs.csdn.net/topics/380204896?list=992863 对比 其实geotools就是基于jts开发的,而geoser ...

  7. 《数据结构(C语言)》苏小红 课本案例

    期末了,赶紧复习一波,手打一份书上的代码以便随时查阅 第二章: //顺序表存储结构 #define MAXSIZE 100 typedef struct { Elemtype *elemt; int ...

  8. sqli-labs(30)

    0X01 观摩源码 和29关没区别 只是这里闭合变成了“ 0X02构造语句 爆库名 ?id=&id=-"union select 1,database(),3%23 0X03组合拳打 ...

  9. [CSP-S模拟测试]:最大异或和(数学)

    题目传送门(内部题81) 输入格式 第一行一个整数$T(T\leqslant 20)$,表示测试数据组数 接下来$T$组,对于每一组,第一行一个整数$n$ 第二行有$n$个整数,为$w_1,w_2.. ...

  10. centos7 修改ali yum源

    centos7 修改yum源为阿里源,某下网络下速度比较快 首先是到yum源设置文件夹里 安装base reop源 cd /etc/yum.repos.d 接着备份旧的配置文件 sudo mv Cen ...