一次因時區問題引發的血案——儘量別改全局配置
緣起
測試報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了,別輕易修改全局配置。