java平滑发布_springcloud如何实现服务的平滑发布

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

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

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

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

操作步骤如下:

1、  服务提供方配置

后台端点禁用安全校验

management.security.enabled=false

# 开启服务暂停端点

endpoints.pause.enabled=true

# 禁用密码验证

endpoints.pause.sensitive=false

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

2、  服务消费者

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

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

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

ribbon.ServerListRefreshInterval=2000

3、发布流程

Sleep 6S

Kill -9

Java –jar xx.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

* @data 2019年4月2日

**/@Slf4j

@Configurationpublic class TomcatGracefulShutdown implementsTomcatConnectorCustomizer,

ApplicationListener{//有个等待时间的配置

@AutowiredprivateShutdownProperties properties;private volatileConnector connector;

@Overridepublic voidcustomize(Connector connector) {this.connector =connector;

}

@Overridepublic void onApplicationEvent(finalContextClosedEvent event) {

LocalDateTime startShutdown=LocalDateTime.now();

LocalDateTime stopShutdown=LocalDateTime.now();try{

log.info("We are now in down mode, please wait " + properties.getWaitSecond() + " second(s)...");if (connector == null) {

log.info("We are running unit test ... ");

Thread.sleep(properties.getWaitSecond()* 1000);return;

}

connector.pause();final Executor executor =connector.getProtocolHandler().getExecutor();if (executor instanceofThreadPoolExecutor) {

log.info("executor is ThreadPoolExecutor");final ThreadPoolExecutor threadPoolExecutor =(ThreadPoolExecutor) executor;

threadPoolExecutor.shutdown();if (!threadPoolExecutor.awaitTermination(properties.getWaitSecond(), TimeUnit.SECONDS)) {

log.warn("Tomcat thread pool did not shut down gracefully within " + properties.getWaitSecond() + " second(s). Proceeding with force shutdown");

}else{

log.debug("Tomcat thread pool is empty, we stop now");

}

}

stopShutdown=LocalDateTime.now();

}catch (finalInterruptedException ex) {

log.error("The await termination has been interrupted : " +ex.getMessage());

Thread.currentThread().interrupt();

}finally{final long seconds =Duration.between(startShutdown, stopShutdown).getSeconds();

log.info("Shutdown performed in " + seconds + " second(s)");

}

}

}

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值