首页 文章资讯内容详情

springcloud如何实现服务的平滑发布 SpringCloud服务如何在Eureka安全优雅的下线

2026-06-01 4 花语

本文内容纲要:

-SpringCloud服务如何在Eureka安全优雅的下线

在之前的文章中我们提到服务的优雅下线,见:

SpringCloud服务如何在Eureka安全优雅的下线

但这个对于ribbon调用其实是不平滑的,shutdown请求到后服务就马上关闭了,服务消费此时未感应到服务下线了,会仍然往这个服务发送请求,从而导致报错。

简介方案有:一、开启重试(前提是保证接口做好幂等处理)。

二、使用pause来下线服务(推荐)

操作步骤如下:

1、服务提供方配置

后台端点禁用安全校验

management.security.enabled=false

#开启服务暂停端点

endpoints.pause.enabled=true

#禁用密码验证

endpoints.pause.sensitive=false

由于这些管理端点比较敏感需要加一个filter来过滤IP白名单

代码参考:对actuator的管理端点进行ip白名单限制(springBoot添加filter)

2、服务消费者

#2秒拉取最新的注册信息

eureka.client.registry-fetch-interval-seconds=2

#2秒刷新ribbon中的缓存信息

ribbon.ServerListRefreshInterval=2000

3、发布流程

Curl–XPOSThttp://127.0.0.1:端口/pause

Sleep6S

Kill-9

Java–jarxx.jar启动服务

curl -I -m 10 -o /dev/null -s -w %{http_code}http://127.0.0.1:端口/health 来检测是否是200,持续N秒,如果失败则需要回滚发布并终止后续节点的发布。

说明:这里的sleep的最大理论值为:eureka.client.registry-fetch-interval-seconds+(ribbon.ServerListRefreshInterval+eureka.client.registry-fetch-interval-seconds)=6S;

后面括号里的相加是因为这2个定时有可能恰好非常巧的错过了才会出现,为了安全起见我们可以基于上述的公式再加个一两秒。

为什么要访问/health呢?主要是为了对服务进行预热(主要是数据库连接池/jedis连接池等),这样当超时时间很多的服务在第一次请求时不会出现超时。

4、eureka

#5秒清理一次过期的注册信息

#如果是按照上面的流程来执行发布则其实可以不配,使用默认值

eureka.server.eviction-interval-timer-in-ms=5000

#关闭自我保护

#内网服务不需要进行分区保护

eureka.server.enable-self-preservation=false

#服务注册5秒即可被发现

eureka.server.response-cache-update-interval-ms=5000

三、扩展tomcat的shutdownhook(不推荐,如果切换为成其他容器则无效了)

importjava.time.Duration; importjava.time.LocalDateTime; importjava.util.concurrent.Executor; importjava.util.concurrent.ThreadPoolExecutor; importjava.util.concurrent.TimeUnit; importorg.apache.catalina.connector.Connector; importorg.springframework.beans.factory.annotation.Autowired; importorg.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer; importorg.springframework.context.ApplicationListener; importorg.springframework.context.annotation.Configuration; importorg.springframework.context.event.ContextClosedEvent; importlombok.extern.slf4j.Slf4j; /** *优雅关闭tomcat *@authoryangzl *@data2019年4月2日 * */ @Slf4j @Configuration publicclassTomcatGracefulShutdownimplementsTomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent>{ //有个等待时间的配置 @Autowired privateShutdownPropertiesproperties; privatevolatileConnectorconnector; @Override publicvoidcustomize(Connectorconnector){ this.connector=connector; } @Override publicvoidonApplicationEvent(finalContextClosedEventevent){ LocalDateTimestartShutdown=LocalDateTime.now(); LocalDateTimestopShutdown=LocalDateTime.now(); try{ log.info("Wearenowindownmode,pleasewait"+properties.getWaitSecond()+"second(s)..."); if(connector==null){ log.info("Wearerunningunittest..."); Thread.sleep(properties.getWaitSecond()*1000); return; } connector.pause(); finalExecutorexecutor=connector.getProtocolHandler().getExecutor(); if(executorinstanceofThreadPoolExecutor){ log.info("executorisThreadPoolExecutor"); finalThreadPoolExecutorthreadPoolExecutor=(ThreadPoolExecutor)executor; threadPoolExecutor.shutdown(); if(!threadPoolExecutor.awaitTermination(properties.getWaitSecond(),TimeUnit.SECONDS)){ log.warn("Tomcatthreadpooldidnotshutdowngracefullywithin"+properties.getWaitSecond()+"second(s).Proceedingwithforceshutdown"); }else{ log.debug("Tomcatthreadpoolisempty,westopnow"); } } stopShutdown=LocalDateTime.now(); }catch(finalInterruptedExceptionex){ log.error("Theawaitterminationhasbeeninterrupted:"+ex.getMessage()); Thread.currentThread().interrupt(); }finally{ finallongseconds=Duration.between(startShutdown,stopShutdown).getSeconds(); log.info("Shutdownperformedin"+seconds+"second(s)"); } } }

调用shutdown时tomcat会此等待M秒后再退出,效果基本等同于第二种方案,但最终退出时有时会报错,而且也仅仅适配tomcat,不够通用。

本文内容总结:SpringCloud服务如何在Eureka安全优雅的下线,

原文链接:https://www.cnblogs.com/yangzhilong/p/10646773.html