项目场景:

  客户端是小程序,管理端需要生成一个小程序入口的二维码。


问题描述:

  生成小程序二维码,首先调用 https://api.weixin.qq.com/cgi-bin/token 获取 access_token,报了 java.net.ConnectException: connection refused (connection refused)异常。
在这里插入图片描述


原因分析:

1、检查网络权限

  报错信息比较明显,第一时间找到运维检查网络权限。由于是测试环境之前一直没有加网络出口限制,外网地址都是可以直接访问,考虑到安全问题,近期加了外网白名单限制。于是就开通了 api.weixin.qq.com 的白名单,但是调用接口还是报错,难道权限没开好?

2、检查网络拒绝策略

  ping 了下域名 (ping api.weixin.qq.com),通的!telnet 了下443端口(telnet api.weixin.qq.com 443),也是通的!卧槽,为啥调不通,运维设置了拒绝策略?

  之前确实踩过太多类似的坑,印象比较深刻的有两次:一次是调用另一个团队的服务,结果接口时不时的报参数不合法,搞得两个团队疯狂检查代码一通忙,最后层层抓包发现运维在网络层设置了参数长度限制;另一次也是调其他团队的服务,偶发性的报 connection reset caused by:socketTimeout,于是程序里加大 httpclient 的 socketTimeout 时长,但是还是没解决,最后发现运维给服务端的网络设置了 socketTimeout 限制,超过限制服务端主动断开连接。

  出于之前的经验,再次询问运维是不是设置了网络拒绝策略,运维同学也很无奈,于是要要走了接口地址和参数,命令执行了一下(curl 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wx66666666666&secret=ssssssssssssssss PS:id 和 秘钥保密),接口正常返回!!我 emo 了!!

3、扯皮大战

  命令执行通过之后,运维同学让检查程序,可程序之前一直是好的从来没动过,本地执行也是没问题的。于是又把锅甩给运维:不排除程序有问题的可能,既然连接被拒绝,总要有个拒绝的理由,哪些设置不符合你的要求程序里可以改!程序里用的 httpclient ,是不是针对 httpclient 客户端的某些参数配置进行了拦截?运维同学一阵懵,然后继续看报错信息,发现这几个 IP 很陌生:
在这里插入图片描述
  网络白名单通过解析域名加的 IP 白名单,这几个 IP 加白名单解析时没有, 他们哪来的?DNS 解析有问题?但是命令执行接口不也走的同样的 DNS 吗?难道有缓存?

4、找到问题

  经过研究发现,确实存在 DNS 缓存的问题:HttpClient 抓取 URL 时,JVM 会通过 Java 网络栈自动缓存这个域名解析的 IP,当下次域名解析时,如果缓存中数据未过期,可以直接使用缓存数据。怪不得程序里执行一直报 connection refused,而命令执行却正常返回!

5、java程序访问域名链接的大概步骤

  1. DNS 解析:请求操作系统进行 DNS 解析,将域名转换为 IP 地址。
  2. DNS 缓存:操作系统和网络库可能会缓存 DNS 解析结果,以减少未来的解析延迟和 DNS 服务器的负载。
  3. 发送请求:通过解析得到的 IP 地址发送请求,在 HTTP 请求中,Host 头部通常包含原始的域名。
  4. 白名单校验:服务器检查提取的域名是否存在于其配置的域名白名单中,服务器检查请求的 IP 是否存在于其配置的 IP 白名单中。

解决方案:

1、清除缓存,防止已缓存的IP失效。

  通过 java.security 包下的 Security 的 setProperty 属性把缓存时间设为0:

Security.setProperty("networkaddress.cache.ttl" , "0");

2、设置域名白名单,防止IP频繁变更。

  域名,尤其是用户量很高的公有云域名,对应的 IP 发生变化已经是常态,最好设置域名白名单。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐