Spring Cloud Config Server迁移节点或容器化带来的问题

如果您跟我一样,目前正在使用Spring Cloud Config做为配置中心的话,本篇将来要描述的问题,强烈推荐了解和关注!因为这个问题目前存在于所有的版本中,还没有完全修复。 2018-05-16 15:45:19 SpringCloudConfig 手把手教你写网络爬虫(4):Scrapy入门 本文介绍Scrapy的架构,包括组件以及在系统中发生的数据流的概览(红色箭头所示)。 之后会对每个组件做简单介绍,数据流也会做一个简要描述。 2018-05-16 13:50:30 Python网络爬虫Scrapy 2017年平均工资出炉 IT业超13万元居首 5 月 16 日电2017 年平均工资数据出炉,全国城镇非私营单位就业人员年平均工资为 74318 元,私营单位为 45761 元。分行业看,IT 业平均工资水平突破 13 万元为最高,连续两年超过金融业,位列各行业门类首位。

如果您跟我一样,目前正在使用Spring Cloud Config做为配置中心的话,本篇将来要描述的问题,强烈推荐了解和关注!因为这个问题目前存在于所有的版本中,还没有完全修复。

如果您跟我一样,目前正在使用Spring Cloud Config做为配置中心的话,本篇将来要描述的问题,强烈推荐了解和关注!因为这个问题目前存在于所有的版本中,还没有完全修复。

[[229497]]

问题现象

为了说明下面的内容,我们可以先尝试重现一下问题:在一个测试环境中,将Spring Cloud Config的配置中心迁移到另外一个节点上,即配置中心的IP地址发生了变化。在完成迁移之后,我们会发现该环境下各个微服务应用的健康状态会变得时好时坏,并且在日志中会出现类似下面的报错:

  1. 2018-05-1317:01:28,569WARN[http-nio-9920-exec-1]org.springframework.cloud.config.client.ConfigServerHealthIndicator-Healthcheckfailed
  2. java.lang.IllegalStateException:CouldnotlocatePropertySourceandthefailfastpropertyisset,failing
  3. atorg.springframework.cloud.config.client.ConfigServicePropertySourceLocator.locate(ConfigServicePropertySourceLocator.java:132)
  4. atorg.springframework.cloud.config.client.ConfigServicePropertySourceLocator$$FastClassBySpringCGLIB$$fa44b2a.invoke(<generated>)
  5. atorg.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
  6. atorg.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738)
  7. atorg.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
  8. atorg.springframework.retry.interceptor.RetryOperationsInterceptor$1.doWithRetry(RetryOperationsInterceptor.java:91)
  9. atorg.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:287)
  10. atorg.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:164)
  11. atorg.springframework.retry.interceptor.RetryOperationsInterceptor.invoke(RetryOperationsInterceptor.java:118)
  12. atorg.springframework.retry.annotation.AnnotationAwareRetryOperationsInterceptor.invoke(AnnotationAwareRetryOperationsInterceptor.java:153)
  13. atorg.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
  14. atorg.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)
  15. atorg.springframework.cloud.config.client.ConfigServicePropertySourceLocator$$EnhancerBySpringCGLIB$$3a43a1f4.locate(<generated>)
  16. atorg.springframework.cloud.config.client.ConfigServerHealthIndicator.getPropertySource(ConfigServerHealthIndicator.java:54)
  17. atorg.springframework.cloud.config.client.ConfigServerHealthIndicator.doHealthCheck(ConfigServerHealthIndicator.java:35)
  18. atorg.springframework.boot.actuate.health.AbstractHealthIndicator.health(AbstractHealthIndicator.java:43)
  19. atorg.springframework.boot.actuate.health.CompositeHealthIndicator.health(CompositeHealthIndicator.java:68)
  20. atorg.springframework.boot.actuate.endpoint.HealthEndpoint.invoke(HealthEndpoint.java:85)
  21. atorg.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint.getCurrentHealth(HealthMvcEndpoint.java:177)
  22. atorg.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint.getHealth(HealthMvcEndpoint.java:166)
  23. atorg.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint.invoke(HealthMvcEndpoint.java:143)
  24. atsun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethod)
  25. atsun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  26. atsun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  27. atjava.lang.reflect.Method.invoke(Method.java:498)
  28. atorg.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
  29. atorg.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133)
  30. atorg.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)
  31. atorg.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
  32. atorg.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
  33. atorg.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
  34. atorg.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)
  35. atorg.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
  36. atorg.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
  37. atorg.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
  38. atjavax.servlet.http.HttpServlet.service(HttpServlet.java:687)
  39. atorg.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
  40. atjavax.servlet.http.HttpServlet.service(HttpServlet.java:790)
  41. atorg.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
  42. atorg.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
  43. atorg.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
  44. atorg.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
  45. atorg.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
  46. atorg.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55)
  47. atorg.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
  48. atorg.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
  49. atorg.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
  50. atcom.yonghui.feign.filter.RequestOriginFilter.doFilter(RequestOriginFilter.java:41)
  51. atorg.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
  52. atorg.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
  53. atorg.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:110)
  54. atorg.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
  55. atorg.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
  56. atorg.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
  57. atorg.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
  58. atcom.yonghui.rpc.feature.web.boot.RpcHolder4BootFilter.doFilterInternal(RpcHolder4BootFilter.java:29)
  59. atorg.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
  60. atorg.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
  61. atorg.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
  62. atorg.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
  63. atcom.yonghui.rpc.feature.web.boot.FeatureSupport4BootFilter.doFilterInternal(FeatureSupport4BootFilter.java:24)
  64. atorg.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
  65. atorg.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
  66. atorg.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
  67. atorg.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:108)
  68. atorg.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
  69. atorg.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
  70. atorg.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
  71. atorg.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)
  72. atorg.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
  73. atorg.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
  74. atorg.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
  75. atorg.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
  76. atorg.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
  77. atorg.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
  78. atorg.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
  79. atorg.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:106)
  80. atorg.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
  81. atorg.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
  82. atorg.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
  83. atorg.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
  84. atorg.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
  85. atorg.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:504)
  86. atorg.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
  87. atorg.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
  88. atorg.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
  89. atorg.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
  90. atorg.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)
  91. atorg.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
  92. atorg.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
  93. atorg.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459)
  94. atorg.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
  95. atjava.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
  96. atjava.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
  97. atorg.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
  98. atjava.lang.Thread.run(Thread.java:748)
  99. Causedby:org.springframework.web.client.ResourceAccessException:I/OerroronGETrequestfor"http://192.168.5.103:9010/config-server/test":Connectionrefused(Connectionrefused);nestedexceptionisjava.net.ConnectException:Connectionrefused(Connectionrefused)
  100. atorg.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:674)
  101. atorg.springframework.web.client.RestTemplate.execute(RestTemplate.java:621)
  102. atorg.springframework.web.client.RestTemplate.exchange(RestTemplate.java:539)
  103. atorg.springframework.cloud.config.client.ConfigServicePropertySourceLocator.getRemoteEnvironment(ConfigServicePropertySourceLocator.java:172)
  104. atorg.springframework.cloud.config.client.ConfigServicePropertySourceLocator.locate(ConfigServicePropertySourceLocator.java:93)
  105. ...95more
  106. Causedby:java.net.ConnectException:Connectionrefused(Connectionrefused)
  107. atjava.net.PlainSocketImpl.socketConnect(NativeMethod)
  108. atjava.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
  109. atjava.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
  110. atjava.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
  111. atjava.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
  112. atjava.net.Socket.connect(Socket.java:589)
  113. atjava.net.Socket.connect(Socket.java:538)
  114. atsun.net.NetworkClient.doConnect(NetworkClient.java:180)
  115. atsun.net.www.http.HttpClient.openServer(HttpClient.java:463)
  116. atsun.net.www.http.HttpClient.openServer(HttpClient.java:558)
  117. atsun.net.www.http.HttpClient.<init>(HttpClient.java:242)
  118. atsun.net.www.http.HttpClient.New(HttpClient.java:339)
  119. atsun.net.www.http.HttpClient.New(HttpClient.java:357)
  120. atsun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:1220)
  121. atsun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1156)
  122. atsun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1050)
  123. atsun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:984)
  124. atorg.springframework.http.client.SimpleBufferingClientHttpRequest.executeInternal(SimpleBufferingClientHttpRequest.java:78)
  125. atorg.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
  126. atorg.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
  127. atorg.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:660)
  128. ...99more

可以看到类似上面的健康检查失败错误,但是并不是一直这样,这个环境下的微服务会出现时好时坏的情况,那么为什么会出现这种现象呢?

原因分析

从错误日志中我们可以发现一个非常关键的信息:I/O error on GET request for "http://192.168.5.103:9010/config-server/test"。

报错说明了微服务检查配置中心获取配置的连接是否畅通的时候出现了连接不上的情况,但是这个链接信息其实并不是当前配置中心的地址,而是我们迁移之前的配置中心的地址。

从健康检查的实现源码ConfigServerHealthIndicator中为入口去分析和调试,我们可以解答上面现象的两个疑问:

  1. @Override
  2. protectedvoiddoHealthCheck(Builderbuilder)throwsException{
  3. PropertySource<?>propertySource=getPropertySource();
  4. builder.up();
  5. if(propertySourceinstanceofCompositePropertySource){
  6. List<String>sources=newArrayList<>();
  7. for(PropertySource<?>ps:((CompositePropertySource)propertySource).getPropertySources()){
  8. sources.add(ps.getName());
  9. }
  10. builder.withDetail("propertySources",sources);
  11. }elseif(propertySource!=null){
  12. builder.withDetail("propertySources",propertySource.toString());
  13. }else{
  14. builder.unknown().withDetail("error","nopropertysourceslocated");
  15. }
  16. }
  17. privatePropertySource<?>getPropertySource(){
  18. longaccessTime=System.currentTimeMillis();
  19. if(isCacheStale(accessTime)){
  20. this.lastAccess=accessTime;
  21. this.cached=locator.locate(this.environment);
  22. }
  23. returnthis.cached;
  24. }

为什么会健康检查访问的还是老的配置中心地址?

真正导致健康检查失败的语句是getPropertySource中的 this.cached = locator.locate(this.environment);而这里的具体实现在org.springframework.cloud.config.client.ConfigServicePropertySourceLocator类中,具体实现如下:

  1. @Override
  2. @Retryable(interceptor="configServerRetryInterceptor")
  3. publicorg.springframework.core.env.PropertySource<?>locate(
  4. org.springframework.core.env.Environmentenvironment){
  5. ConfigClientPropertiesproperties=this.defaultProperties.override(environment);
  6. CompositePropertySourcecomposite=newCompositePropertySource("configService");
  7. RestTemplaterestTemplate=this.restTemplate==null?getSecureRestTemplate(properties)
  8. :this.restTemplate;
  9. Exceptionerror=null;
  10. StringerrorBody=null;
  11. logger.info("Fetchingconfigfromserverat:"+properties.getRawUri());
  12. ...
  13. }

可以看到,真正去访问的地址是直接从properties.getRawUri()获取的,它已经是一个固化的值,而不是通过服务发现机制来动态获取的。这就导致了当我们把配置中心做了迁移,或者直接部署在容器中出现重启的时候,IP发生变化,而所有的微服务还以为访问的是原来的配置中心地址,就会出现健康检查失败的问题,导致服务不可用的现象。

为什么健康检查时好时坏?

上面的问题会导致健康检查失败,但是这个服务并不是一直都不好,而是间断性的出现不健康。这主要还是健康检查时间中的机制导致,这里可以具体看ConfigServerHealthIndicator的getPropertySource函数,该方法执行的时候中间并不是每一次检查都会去访问配置中心(执行locator.locate(this.environment)方法),因此客户端的健康检查并不会每次都健康检查失败,从而出现了微服务健康检查时好时坏的情况。

如何解决

该问题目前也在官方的issue中被提出,还处于open状态,具体可见:https://github.com/spring-cloud/spring-cloud-config/issues/514

由于该问题目前并没有得到解决,虽然提交了一个PR,但是还有待完善以及提供一些测试,原本想完全处理好之后再写一篇文章,但是发现最近不少问过类似问题,所以索性先写一篇文章,提醒一下用户以及给一些相关的建议。

当前版本上不太容易通过扩展的方式去解决的这个问题,所以大家可以变通的去避免这个问题:

  • 部署在虚拟机上而不是容器上,避免IP的变动
  • 可以考虑关闭微服务队config客户端的健康检查,增加参数management.health.config.enabled=false;但是这个操作有一个弊端,虽然迁移不会引发服务时好时坏的问题了,但是如果有动态配置刷新需求,如果迁移了配置中心,刷新配置操作也是会失败的。

【本文为清一色专栏作者“翟永超”的原创稿件,转载请通过清一色联系作者获取授权】

戳这里,看该作者更多好文

©本文为清一色官方代发,观点仅代表作者本人,与清一色无关。清一色对文中陈述、观点判断保持中立,不对所包含内容的准确性、可靠性或完整性提供任何明示或暗示的保证。本文不作为投资理财建议,请读者仅作参考,并请自行承担全部责任。文中部分文字/图片/视频/音频等来源于网络,如侵犯到著作权人的权利,请与我们联系(微信/QQ:1074760229)。转载请注明出处:清一色财经

(0)
打赏 微信扫码打赏 微信扫码打赏 支付宝扫码打赏 支付宝扫码打赏
清一色的头像清一色管理团队
上一篇 2023年5月5日 03:25
下一篇 2023年5月5日 03:25

相关推荐

发表评论

登录后才能评论

联系我们

在线咨询:1643011589-QQbutton

手机:13798586780

QQ/微信:1074760229

QQ群:551893940

工作时间:工作日9:00-18:00,节假日休息

关注微信