前情摘要
网上下载了一个renren-fast-vue
作为一个前端项目,用来方便管理后端项目,还下载了renren-fast
项目用来为前端项目提供后端的支持(在本次问题中,可以理解为这个后台项目仅仅是用来提供验证码图片的)。同时自己还配置了nacos作为服务的发现中心。
renren-fast-vue的地址是:localhost:8001
renren-fast的地址是localhost:8080/renren-fast
网关的地址是localhost:12000
具体步骤
原先的renren-fast-vue和renren-fast这两个默认就是在一起进行工作的,即vue是去localhost:8080/renren-fast
这个地址获取的验证码(及进行后端的相关操作)。
我们修改了renren-fast-vue的地址为,window.SITE_CONFIG['baseUrl'] = 'http://localhost:12000/api'
,也就是让它指向了配置好的网关,那么网关就需要将其转发到8080端口的renren-fast项目,并且需要进行路径的替换,进行配置的是如下所示:
1 | routes: |
这条规则说的是,只要路径里面有/api/
,就把它替换成在配置中心中的renren-fast
的地址并且通过路径替换得到我们想要的真正的验证码地址。
简单来说就是,会将localhost:12000/api/captcha.jpg
这个Url转化成localhost:8080/renren-fast/captcha.jpg
,然后交给目标服务器来获取验证码图片地址。
最终上线进行测试,emmm,图片没加载出来。通过F12发现是404,也就是前端会发送localhost:12000/api/captcha.jpg
这个请求,但是最终得不到验证码图片。
分析
大致分析一下可能出现问题的点:
- 网关没有收到请求。
- 网关收到了请求,但是没有进行相应的规则处理。
- 网关进行了处理,但是最终得到的目标Url错误。
- 因为网关会向nacos索要信息,也可能是nacos出现了问题。
- 其它各种杂七杂八的问题。
检查网关
由于真的是刚刚接触spring cloud gateway这个网关,上面的这些问题其实只需要了解过程,然后看下日志就应该非常容易解决,我走了不少弯路,不过记录下这些弯路,也可以让以后的自己少踩点坑,也算划算了。
配置问题
首先我得排除我的网关配置的有没有问题,于是写了一个最简单的匹配规则,看能否生效,确实生效了,也就是说,我的网关至少在最简单的情况下,是可以正常工作的。
我之后就把我的目标转向了上面贴出来的那个配置,既然网关是正常工作的,那么我自己的这个配置会不会出现问题呢?然后试了好多次,都不行。后来想想干脆破罐子破摔,我又复制了一条最简单的那个情况下的配置,然后只是修改了uri,发现居然不生效!真是奇了怪了。
就这样查找了好久的原因,我突然看到了bootstrap这个配置文件,也就是说,我现在其实用的根本不是本地的配置,而是用的nacos上的配置…..
然后把远程的这些配置关掉,确保用的是自己的本地的配置文件。发现还是404。
怀疑服务发现
接着由于之前的nacos的问题嘛,我就怀疑是不是我写的lb://renren-fast
这里没有从nacos获取到相关的信息,这样导致了网关根本获取不到目的地的ip信息,自然也就无法跳转了。于是我修改了uri这条属性,让它指向了localhost:8080,然后试了一下,200!说明没问题哎!
走的远路
我测试了一下发现是200,自己以为解决了问题,但是刷新发现还是获得不了验证码。还是通过调试发现,获得的居然是document类型的,而且文件大小明显不对。这个就很奇怪了,首先,我认为这个配置的网关路由没有问题,它确实到了它该到的地方,而且对方也给了响应,是200。但是对方虽然给了响应,但是给的响应又不对,这就非常奇怪了。
之后我又断断续续查了不少资料,怀疑了以下这几点:
- 是不是浏览器的缓存再捣乱? 为了以防万一,我装上了一个叫
Cache Killer
的插件来搞缓存。 - 是不是idea的缓存没弄干净? 先删了targets文件夹,然后rebuild一下项目。
- 是不是配置文件出了问题? 找个官方文档的,仔仔细细复制下来并且校对一遍。
- 是不是防火墙的原因? 关闭防火墙再试试。
随着我的多次尝试,结果还是一样的。尝试了很久,都没有结果,我甚至开始怀疑起来是不是系统的问题….就在这里绕了很久很久。
走上正轨
搞到这里我觉得不能这样瞎试,不然变量实在是太大了,我为什么要测试这么长长的一条链路呢?我决定从结果开始往回推(其实我一开始确实是这么做的,但是搞了很久没搞出来,反而变量越搞越多,心里就越来越烦躁了…这里其实是静下心来重新理清思绪开始)。
首先我直接先清空了浏览器缓存,然后开始先测试下,我返回验证码的后端它有没有问题。直接在浏览器里输入localhost:8080/renren-fast/captcha.jpg
,嗯,确实看到了回显,说明验证码的后端是没有问题的。然后倒退一步,走到网关,我输入localhost:12000/api/captcha.jpg
,嗯,出现了404。冷静下来的我,决定先去看下gateway的日志,进入配置文件,输入
1 | logging: |
打开日志,然后进行调试。在日志里我看到了网关转发的日志,是正确的转化;但是其中有这么一条很奇怪:
1 | [id: 0xf7ee8650, L:/0:0:0:0:0:0:0:1:12000 - R:/0:0:0:0:0:0:0:1:56659] Subscribing inbound receiver [pending: 0, cancelled:false, inboundDone: true] |
虽然不敢断定是ipv6的锅,但是出于谨慎,我还是关闭了ipv6,发现还是404。
最后我试了下,发现如果通过localhost:12000/api/abcd
,得到的结果是200,并不显示任何信息。但是我去访问了一下localhost:8080/renren-fast/abcd
,发现得到的结果也是200,但是页面上会回显信息。在结合之前的日志,发现其实网关确实能把结果捕捉到,并且转发到正确的位置,但是对方就是给出不同的答案:一个给值,另外一个不给。
这样又持续了很久,我突然瞥了一眼日志,然后看了下浏览器的地址栏,这该不会是localhost和127.0.0.1之间的问题吧?然后我在浏览器里输入了127.0.0.1:8080/renren-fast/abcd
,发现也没有回显!问题找到了!也就是说,对于这两个url,服务器给出的响应居然不同,那么它们俩到底有什么区别呢(其实我还测试了一下本机的内网ip地址)
接下来这句话是我在网上找的:
localhost就算没有网卡,仍然存在。
错误。我自己测试了一下,把lo网卡给down掉,ping 127和localhost都无法通,所以这条应该有问题。
按照之前我的理解,是通过读取/etc/hosts下的文件,然后把localhost转化成了127.0.0.1,从这点推论,也就是说,实际上应该127.0.0.1不能访问的,localhost都不能访问。但是在实际中却出现了127不能访问,但是localhost能访问的。
我继续去stackoverflow上找,看到了一个答案,似乎和我相关:
some applications will treat “localhost” specially. the mysql client will treat localhost as a request to connect to the local unix domain socket instead of using tcp to connect to the server on 127.0.0.1. This may be faster, and may be in a different authentication zone.
如果通过localhost,那么使用的是socket;如果用的127.0.0.1,那么使用tcp来访问。似乎确实能够完美解释哎!但是,我当时其实没有想到这一层…而且事后证明,也确实不是上面这个问题….
问题最后的解决
虽然我之前是没想到上面的这个不同软件对待localhost和127之间的不同,但是我决定修改server的绑定地址,通过显示指定绑定0.0.0.0
,来看看效果——失败了。最后死马当活马医,改了个端口,居然成了!!
我特别好奇,于是去看了下端口绑定的东西。TMD居然有个nginx??!!看来是找到罪魁祸首了。
解释
我猜测最后的结果应该是这样,当我向localhost访问索要资源的时候,那个正确的后端返回了数据;而当我使用127的时候,这个时候是这个nginx在给我回消息。至于它们俩为什么能同时监听同一个端口,而且我的应用居然没有报错….这确实值得探索。
总结
这个问题从下午的6点开始,一直调试到第二天1点,历经7小时。其实仔细看来,稍微有点经验的应该分分钟就能搞定了…不过通过这次,还是学到了不少经验:
- localhost和127之间的事,网上还不少错误的解释…
- 下次用Postman来进行调试了,burp一直抓不到127的包…
- 调试的时候心态要好,要有耐心。
- 尽量控制变量。
所以还遗留一个问题:为什么nginx和我的tomcat能够同时监听8080端口而不报错呢?
这里我只能提供我的猜想了:nginx监听了ipv6的8080,且只能ipv6;而tomcat监听了ipv4的8080,这样首先它们是不冲突的。其次我访问localhost的时候,被映射成了ipv6,找到了nginx,但是nginx不知道为什么回复了一个200。然后我访问127的时候,只能去tomcat,然后tomcat拒绝了。
由于现在已经没有环境了,这个问题也无法复现了,但是确实提供了一个非常不错的思考过程。