一次因时区问题引发的血案——尽量别改全局配置
缘起
测试报bug说传入的生日时间戳是651078000000(对应时间是1990-08-20 00:00:00),但是存在数据库是1990-08-19,事关我写的微服务,便决定查一下。
还没解决问题,先发现了feign的bug
- 通过postman使用测试给的数据测试了一下,发现还真是有这个问题,怎么会差一天呢,然后考虑到我们微服务之间是使用feign调用的,序列化和反序列化使用的是fastjson,我用postman直接调用微服务序列化和反序列化是jackson,于是决定使用仿真环境调用,先查一下fastjson在fegin调用的时候,序列化完成的数据对不对,于是在
feign.SynchronousMethodHandler
的第90行代码
Response response;
try {
response = this.client.execute(request, this.options);
} catch (IOException var15) {
if (this.logLevel != Level.NONE) {
this.logger.logIOException(this.metadata.configKey(), this.logLevel, var15, this.elapsedTime(start));
}
throw FeignException.errorExecuting(request, var15);
}
response = this.client.execute(request, this.options);
这一行打一个断点,请求的时候查一下request的数据,发现出去时就错了,我了个去,然后单步调试发现序列化器用的是jackson,原来配置了fastjson序列化器没生效,去了解了一下feign这个项目发现feign使用的httpconverter和springMVC的不是同一个,于是配置feign的序列化和反序列化器:
@Configuration
public class MyFeignClientsConfiguration {
@Bean
public Encoder feignEncoder(){
return new SpringEncoder(feignHttpMessageConverter());
}
@Bean
public Decoder feignDecoder(){
return new SpringDecoder(feignHttpMessageConverter());
}
/**
*feign和Springboot使用的都是jackson,可以都修改为fastjson解析方式
*/
private ObjectFactory<HttpMessageConverters> feignHttpMessageConverter(){
final HttpMessageConverters httpMessageConverters = new HttpMessageConverters(this.getFastJsonConverter());
return () -> httpMessageConverters;
}
private FastJsonHttpMessageConverter getFastJsonConverter(){
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
List<MediaType> supportedMediaTypes = new ArrayList<>();
supportedMediaTypes.add(MediaType.APPLICATION_JSON);
supportedMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
supportedMediaTypes.add(MediaType.APPLICATION_ATOM_XML);
supportedMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
supportedMediaTypes.add(MediaType.APPLICATION_OCTET_STREAM);
supportedMediaTypes.add(MediaType.APPLICATION_PDF);
supportedMediaTypes.add(MediaType.APPLICATION_RSS_XML);
supportedMediaTypes.add(MediaType.APPLICATION_XHTML_XML);
supportedMediaTypes.add(MediaType.APPLICATION_XML);
supportedMediaTypes.add(MediaType.IMAGE_GIF);
supportedMediaTypes.add(MediaType.IMAGE_JPEG);
supportedMediaTypes.add(MediaType.IMAGE_PNG);
supportedMediaTypes.add(MediaType.TEXT_EVENT_STREAM);
supportedMediaTypes.add(MediaType.TEXT_HTML);
supportedMediaTypes.add(MediaType.TEXT_MARKDOWN);
supportedMediaTypes.add(MediaType.TEXT_PLAIN);
supportedMediaTypes.add(MediaType.TEXT_XML);
converter.setSupportedMediaTypes(supportedMediaTypes);
FastJsonConfig config = new FastJsonConfig();
config.getSerializeConfig().put(JSON.class,new SwaggerJsonSerializer());
config.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);
converter.setFastJsonConfig(config);
return converter;
}
}
期间还遇到一个什么content-type不能包含*
的错误,通过手动组建supportedMediaTypes
数组解决。
feign序列化和反序列化修正后,format有问题
在feign的调用方和服务提供方都添加上上面的配置后,还是有问题,具体表现在feign的调用request中日期是时间戳,并没有问题,feign的服务提供方能接收到调用方传过来的数据,是时间戳,没问题,但是通过SimpleDateFormate输出后还是有问题,这就不得不怀疑SimpleDateFormate的时区用的啥,SimpleDateFormate的时区默认是GMT,后面发现了有人在SpringBoot启动的时候设置了时区:
TimeZone.getTimeZone("GMT+8")
问题估计就出在这了,查git,发现是某个同事,于是去问了一下原因,得到的解释是logback在客户机上输出的时间不对,所以设置了时区。我晕,想吐槽:日志时间不对去搞logback啊,全局设置时区影响整个服务啊,再说了,日志时间不对和生产数据不对,哪个比较重要?这用屁股想想都优先生产数据。于是果断删除时区设置。
修正logback输出时间
logback配置文件中pattern
有:
%d [%thread] %-5p [%c] [%F:%L] [trace=%X{X-B3-TraceId:-},span=%X{X-B3-SpanId:-},parent=%X{X-B3-ParentSpanId:-}] - %msg%n
去翻了一下logback的文档,http://logback.qos.ch/manual/layouts.html
,
根据第二个框的描述,.SSS
是可以配置为时区的,于是改pattern
为:
%date{yyyy-MM-dd HH:mm:ss.Asia/Shanghai} [%thread] %-5p [%c] [%F:%L] [trace=%X{X-B3-TraceId:-},span=%X{X-B3-SpanId:-},parent=%X{X-B3-ParentSpanId:-}] - %msg%n
耗时一天加一上午解决,如果不设置全局时区的话,这个问题可能就只停留在logback了,别轻易修改全局配置。