news 2026/9/1 16:21:45

一次现网问题定位-com.google.gson.JsonSyntaxExceptio on: java.io.EOFException: End of input

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
一次现网问题定位-com.google.gson.JsonSyntaxExceptio on: java.io.EOFException: End of input

背景

收到隔壁组请求支援,定位调用接口报错,报错日志如下:

2026-08-28 16:07:22.462 [http-nio-8003-exec-14] ERROR [UnknownExceptionHandler:28]com.google.gson.JsonSyntaxException on: java.io.EOFException: End of input at line 1 column 1479 path $.notification[5].lastUpdateDate
at com.google.gson.Gson.fromJson(Gson.java:1370)
at com.google.gson.Gson.fromJson(Gson.java:1262)
at com.google.gson.Gson.fromJson(Gson.java:1171)
at com.google.gson.Gson.fromJson(Gson.java:1107)

从报错日志初步来看,像是json不完整,解析过程中,碰到结束符了。

他们提供了代码,从代码逻辑看非常简单,他们先通过RestTemplate转成String,然后在手工序列化。报错就发生在序列化那一行。

Stringresponse=restTemplate.doGet(url,headers,String.class);TkbHotResponsetkbHotResponse=GSON.fromJson(response,TkbHotResponse.class);

系统组网如下:

日志告警的系统

ALB

上游系统

定位思路

先定界-是不是json不完整

从报错来看,像是json不完整,首先得把这个方向确认了,才好继续往下定位,因此通过堆栈,稍微看了下Gson的源码,确认后着手定位为什么输入的字符串不是完整的json。

初步想法是不是上游因为啥原因导致传输的json就不完整。隔壁组提供了使用postman调用的响应,里面内容是完整的。该场景排除。

为什么restTemplate返回的String不是完整的json

通过上一步的分析,上游传的数据是没问题的,那初步认为是不是偶发网络提前中断了,导致数据没传输完?
如果是上述场景的话,那按说RestTemplate会抛异常吧?看了前后日志并没有异常,难道RestTemplate接受到200的响应,如果读取响应过程中连接中断,不会有异常?遂初步翻翻RestTemplate的源码,看看反序列化(网络字节流转String)的过程是怎么样的。

protectedStringreadInternal(Class<?extendsString>clazz,HttpInputMessageinputMessage)throwsIOException{Charsetcharset=getContentTypeCharset(inputMessage.getHeaders().getContentType());longlength=inputMessage.getHeaders().getContentLength();byte[]bytes=(length>=0&&length<=Integer.MAX_VALUE?inputMessage.getBody().readNBytes((int)length):inputMessage.getBody().readAllBytes());returnnewString(bytes,charset);}

通过阅读StringHttpMessageConverter代码发现以下可疑点:
如果传来"Content-Length"头,会根据这个头的长度来读取响应。

我感觉像是传了错误的"Content-Length"头,因为如果是网络问题,报错概率应该很小,但是我看日志还挺多的。
为此我找他们用postman调用了一下,看看是不是有传这个头。结果还真有

从他们上面截图可以看到,除了传"Content-Length"头,还传了"Content-Encoding:gzip"。
我感觉应该是启用了压缩,然后"Content-Length"头传的是压缩后的值。如果像猜想一样的话,那这个问题应该是必现,找隔壁项目组求证后,得到了肯定的答复。为此坚定了这个定位方向。接下来就是找上游确认"Content-Length"是怎么写进去的,压缩到底是谁做的。

和上游定位Content-Length是怎么传的里面的值怎么来的

先是找他们确认"Content-Length"头是他们传的还是ALB传的,通过访问了单边地址(防止ALB的干扰);发现这个头确实是他们自己传的。
然后就联合上游,分析他们代码咋写的这头?是他们自己代码写的还是tomcat写的,通过分析代码发现,确实是他们自己写的这个头。

publicvoiddoWrite(byte[]content,HttpServletResponsehttpResponse)throwsIOException{httpResponse.setContentType("text/html; charset=UTF-8");httpResponse.addHeader("Content-Length",Integer.toString(content.length));httpResponse.addHeader("Content-Encoding","gzip");StreamUtil.transfer(newBufferedInputStream(newByteArrayInputStream(content)),httpResponse.getOutputStream());}# doWrite中的content参数,从pullHtml函数中获取publicbyte[]pullHtml(HttpServletRequestrequest,HttpServletResponseresponse,WebRequestVOwebRequest,XhtmBeanxhtmBean)throwsIOException{returntoGzipByteArray(createHtml(request,response,webRequest,xhtmBean),UTF_8);}

从他们代码中得到一下信息,他们会自己对数据做压缩,压缩完后,同时传了Content-Length和gzip的压缩头。所以"Content-Length"头的值是没问题的,代表的是HTTP响应中,body的真实字节数。
至此,问题根因初步明确了,RestTemplate拿到的Content-Length的值是压缩后的,导致反序列化时字节数读少了。
接下来的问题就是为什么测试环境没这个问题,而生产有这个问题。

生产和测试的差异

通过对比生产和测试环境的响应,发现响应头有些差异:测试环境没有Content-Length头,变成了"Transfer-Encoding:chunked"

生产环境测试环境
Content-Length: 1542Transfer-Encoding:chunked
Content-Encoding:gzipContent-Encoding:gzip

从结果来看像是生产环境ALB完全透传后台的数据,而测试环境像是先解压然后重新压缩,这样有"Transfer-Encoding:chunked"。
为了确认是ALB的问题,我们绕过ALB调用了单边地址(直接调用微服务),发现测试/生产都一样,返回的头与代码逻辑一致(包含"Content-Length"和"Content-Encoding:gzip")。以上得出结论:相同响应,经过不同环境的ALB后产生了差异。
ALB其实也就是ningx,在找他们之前自己也做了功课,搜了一下"nginx什么情况下会解压后端数据后,重新压缩",AI告知:

场景一:Nginx 开启了内容替换或修改模块(最常见)如果你在 Nginx 中配置了对网页内容的实时修改(例如修改域名、注入脚本、替换文本),Nginx 必须拿到明文内容才能进行字符串匹配和替换。

我们并看不到ALB的配置,因此找他们求证,得到的答复是,我们测试环境的网页上面会显示"BETA"的样式,这个是他们拦截了请求,并注入了特定的内容,而生产没有,到此疑问得以消除,AI的答案还是很有参考性的。

目前还剩最后一个疑问,为什么之前没有,而现在才有,双方都检查一下包,上游没有任何修改,而隔壁组,最近升级了开源软件。HttpClient5由5.5升级到5.6.x了,通过调试发现:在内容压缩的情况下,两者对"Content-Length"头的处理由差异,前者会吞掉它,而后者原封不动的给调用方,因此RestTemplate拿到了错误的值。

此致所有疑问得以解决。

小插曲

实际上前面,我并不知道上游应用自己做了压缩(没看到对应代码),为此我还看了tomcat的源码,看应用层传"Content-Encoding:gzip"头,tomcat会做何处理,tomcat何种情况下会进行压缩,压缩后会不会覆盖应用层写的"Content-Length"。
毕竟初步看他们代码时,没看仔细,没发现他们自己有做压缩。既然他们应用层传的值没问题,那很有可能是tomcat进行了压缩,并覆盖了该值。
通过源码确认以下事实:
tomcat靠全局配置启用压缩,应用层传了"Content-Encoding:gzip"头后,tomcat认为应用层已经压缩过了,就算tomcat启用了压缩,也不会再次做压缩。
经此分析后,出现了矛盾的地方,应用层传了正确的值,tomcat又不做压缩,那为什么通过访问单边地址,看到的"Content-Length"头的值不正确?为此和他们一起仔细调试了代码,然后就发现他们自己代码做了压缩,该值是压缩后的值。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/1 16:21:41

从脚本到服务:视觉引导机械臂抓取的系统工程封装实践

你有没有试过把一次成功的机械臂抓取&#xff0c;从“偶然能行”变成“次次都能行”&#xff1f;上个月&#xff0c;我帮一个做自动化产线集成的朋友调试一个视觉引导抓取工位。硬件很标准&#xff1a;一个工业相机&#xff0c;一个三轴机械臂&#xff0c;一个传送带。第一次手…

作者头像 李华
网站建设 2026/9/1 16:19:45

半导体、芯片、科创50与设备:一张逻辑地图让你不再只问明天涨跌

8月13日前后&#xff0c;半导体和芯片又一次成为投资者最密集的关键词。看盘软件里&#xff0c;科创50、半导体设备、国产替代、材料、先进封装轮番出现在热榜前列&#xff1b;讨论区里&#xff0c;问得最多的一句话是“明天策略怎么定”。这个问题看起来很具体&#xff0c;但真…

作者头像 李华
网站建设 2026/9/1 16:16:03

在线配音软件 3 款实测:浏览器直接生成音频效果测评

做自媒体久了&#xff0c;谁都遇过这种糟心时刻&#xff1a;临时要改配音&#xff0c;电脑里没装客户端&#xff0c;手机上的APP又登不上账号&#xff0c;翻遍收藏夹找了好几个工具&#xff0c;要么强制跳转下载&#xff0c;要么注册流程卡十分钟&#xff0c;好好的创作节奏全被…

作者头像 李华
网站建设 2026/9/1 16:12:05

【单片机课程设计/毕业设计】基于 STM32 或 51 单片机的多传感垃圾桶状态监测设备设计 基于 STM32 或 51 单片机的声光报警智能垃圾桶控制系统开发(025005)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机&#xff0c;Java、小程序技术领域和毕业项目实战 ✌️…

作者头像 李华
网站建设 2026/9/1 16:11:40

Spring Boot+Vue火车票订票系统实战:搞定并发防超卖与订单管理

简介&#xff1a;这是一套面向计算机专业本科生的毕业设计级SpringBootVue火车票订票系统完整源码&#xff0c;聚焦Web全栈开发实践&#xff0c;解决传统购票流程线上化、交互体验优化与前后端协同落地等典型工程问题。资源包共1197个文件&#xff0c;涵盖85个Java后端业务类&a…

作者头像 李华
网站建设 2026/9/1 16:10:13

LangGraph实战:从零构建可控的Agent流程

如果你正在做 Agent 开发&#xff0c;或者准备从 LangChain 进入 Agent 工程化&#xff0c;最近一定频繁听到 LangGraph 这个名字。但真正动手用过之后&#xff0c;很多人会有一种共同的困惑&#xff1a;它不过是一个“画流程图”的框架&#xff0c;为什么社区把它捧得这么高&a…

作者头像 李华