先给大家推荐一个好玩的网站MyChatGPT(免梯子,国内直接用,不需要OpenAI账号):https://chat.icoding.ink/
异常场景
Spring Cloud + Alibaba
异常特征
当请求远程微服务时, 有
一定几率
产生时间对象解析失败. 通信框架使用fegin
. 频繁请求时容易复现.
异常报文
2020-12-04 20:07:25.103 ERROR 1 --- [nio-9999-exec-5] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is feign.codec.DecodeException: Error while extracting response for type [java.util.List<com.nhi.common.dto.HomeWorkDetailSectionDto>] and content type [application/json]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: For input string: "743E2.743E2"; nested exception is com.fasterxml.jackson.databind.JsonMappingException: For input string: "743E2.743E2" (through reference chain: java.util.ArrayList[0]->com.nhi.common.dto.HomeWorkDetailSectionDto["createTime"])] with root cause
java.lang.NumberFormatException: For input string: "743E2.743E2"
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2043) ~[na:1.8.0_275]
at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110) ~[na:1.8.0_275]
at java.lang.Double.parseDouble(Double.java:538) ~[na:1.8.0_275]
at java.text.DigitList.getDouble(DigitList.java:169) ~[na:1.8.0_275]
at java.text.DecimalFormat.parse(DecimalFormat.java:2089) ~[na:1.8.0_275]
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:2162) ~[na:1.8.0_275]
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514) ~[na:1.8.0_275]
at java.text.DateFormat.parse(DateFormat.java:364) ~[na:1.8.0_275]
at com.nhi.common.config.DateFormatConfig$DateJsonDeserializer.deserialize(DateFormatConfig.java:47) ~[en-common-0.0.1-SNAPSHOT.jar!/:0.0.1-SNAPSHOT]
.....
异常分析
最终定位到类com.nhi.common.config.DateFormatConfig
, 该类是我在项目中写的一个全局时间格式化个配置类. 用于统一微服务通信和前后端通信时的Date
对象的格式.
代码如下:
package com.nhi.common.config;
import com.fasterxml.*;
import java.text.*;
import java.io.IOException;
import java.util.Date;
/**
* @Author: 郭胜凯
* @Date: 2019-06-11 11:36
* @Email 719348277@qq.com
* @Description: 全局Rest full API Date类型JSON 格式化
*/
@JsonComponent
public class DateFormatConfig {
/**
* 时间解析器
*/
final static SimpleDateFormat DF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 日期格式化
*/
public static class DateJsonSerializer extends JsonSerializer<Date> {
@Override
public void serialize(Date date, JsonGenerator jsonGenerator,
SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(DF.format(date));
}
}
/**
* 解析日期字符串
*/
public static class DateJsonDeserializer extends JsonDeserializer<Date> {
@Override
public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
throws IOException, JsonProcessingException {
try {
return DF.parse(jsonParser.getText());
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
}
根据异常报文, 可以确定异常发生在DF.parse(jsonParser.getText());
这句代码中, 远程服务的时间传值正常, 本地微服务解析时有几率报错.
产生原因
期初为了提升性能, 减少重复创建SimpleDateFormat
对象, 我将其实例放到了全局静态表中. 但这个对象线程不安全, 当并发请求时, 由于多个线程同时使用其实例会出现线程安全问题.
解决方案
将全局的SimpleDateFormat
对象放到方法内部, 每个线程使用私有的实例, 修改后的代码如下:
package com.nhi.common.config;
import com.fasterxml.*;
import java.text.*;
import java.io.IOException;
import java.util.Date;
/**
* @Author: 郭胜凯
* @Date: 2019-06-11 11:36
* @Email 719348277@qq.com
* @Description: 全局Rest full API Date类型JSON 格式化
*/
@JsonComponent
public class DateFormatConfig {
/**
* 日期格式化
*/
public static class DateJsonSerializer extends JsonSerializer<Date> {
@Override
public void serialize(Date date, JsonGenerator jsonGenerator,
SerializerProvider serializerProvider) throws IOException {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
jsonGenerator.writeString(dateFormat.format(date));
}
}
/**
* 解析日期字符串
*/
public static class DateJsonDeserializer extends JsonDeserializer<Date> {
@Override
public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
throws IOException, JsonProcessingException {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
return dateFormat.parse(jsonParser.getText());
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
}
参考资料
https://medium.com/@daveford/numberformatexception-multiple-points-when-parsing-date-650baa6829b6