John Stone

1996-04-04

中国 河北

解决Fegin请求远程微服务时有几率出现时间解析异常: through reference chain: xxx->xxxDto[“xxxTime“]

芦苇小白

Dec 29, 2020 12:39:59 PM 116

异常场景

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);
            }
        }
    }
}

参考资料

@daveford/numberformatexception-multiple-points-when-parsing-date-650baa6829b6"">https://medium.com/@daveford/numberformatexception-multiple-points-when-parsing-date-650baa6829b6

拖动滑块验证
验证通过 验证失败

全部评论