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

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 –X POST http://127.0.0.1:端口/pause

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(不推荐,如果切换为成其他容器则无效了)

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import org.apache.catalina.connector.Connector;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextClosedEvent; import lombok.extern.slf4j.Slf4j; /**
* 优雅关闭tomcat
* @author yangzl
* @data 2019年4月2日
*
*/
@Slf4j
@Configuration
public class TomcatGracefulShutdown implements TomcatConnectorCustomizer,
ApplicationListener<ContextClosedEvent> {
// 有个等待时间的配置
@Autowired
private ShutdownProperties properties; private volatile Connector connector; @Override
public void customize(Connector connector) {
this.connector = connector;
} @Override
public void onApplicationEvent(final ContextClosedEvent 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 instanceof ThreadPoolExecutor) {
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 (final InterruptedException 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,不够通用。

springcloud如何实现服务的平滑发布的更多相关文章

  1. springcloud添加自定义的endpoint来实现平滑发布

    在我之前的文章  springcloud如何实现服务的平滑发布 里介绍了基于pause的发布方案. 平滑发布的核心思想就是:所有服务的调用者不再调用该服务了就表示安全的将服务kill掉. 另外actu ...

  2. 通过Nginx、Consul、Upsync实现动态负载均衡和服务平滑发布

    前提 前段时间顺利地把整个服务集群和中间件全部从UCloud迁移到阿里云,笔者担任了架构和半个运维的角色.这里详细记录一下通过Nginx.Consul.Upsync实现动态负载均衡和服务平滑发布的核心 ...

  3. 实战SpringCloud响应式微服务系列教程(第二章)

    接上一篇:实战SpringCloud响应式微服务系列教程(第一章) 1.1.2背压 背压是响应式编程的核心概念,这一节也是我们了解响应式编程的重点. 1.背压的机制 在生产者/消费者模型中,我们意识到 ...

  4. 实战SpringCloud响应式微服务系列教程(第十章)响应式RESTful服务完整代码示例

    本文为实战SpringCloud响应式微服务系列教程第十章,本章给出响应式RESTful服务完整代码示例.建议没有之前基础的童鞋,先看之前的章节,章节目录放在文末. 1.搭建响应式RESTful服务. ...

  5. 系统架构设计:平滑发布和ABTesting

    平滑发布的介绍 背景 单位的云办公相关系统没有成熟的平滑发布方案,导致每一次发布都是直接发布,dll文件或配置文件的变更会引起站点的重启. 云办公系统的常驻用户有10000+,即使短短半分多钟,也会收 ...

  6. 一个C#开发者学习SpringCloud搭建微服务的心路历程

    前言 Spring Cloud很火,很多文章都有介绍如何使用,但对于我这种初学者,我需要从创建项目开始学起,所以这些文章对于我的启蒙,帮助不大,所以只好自己写一篇文章,用于备忘. SpringClou ...

  7. wcf服务与web发布时无法访问 几种解决办法

    1   如果wcf服务与web发布在同一iis目录下,没有跨域的问题   但发布前要修改 ServiceReferences.ClientConfig 把wcf服务地址改为你发布后的iis的ip地址( ...

  8. WCF报 当前已禁用此服务的元数据发布的错误

    这是 Windows© Communication Foundation 服务. 当前已禁用此服务的元数据发布. 如果具有该服务的访问权限,则可以通过完成下列步骤来修改 Web 或应用程序配置文件以便 ...

  9. springcloud干货之服务消费者(ribbon)

    本章介绍springcloud中的服务消费者 springcloud服务调用方式有两种实现方式: 1,restTemplate+ribbon, 2,feign 本来想一篇讲完,发现篇幅有点长,所以本章 ...

随机推荐

  1. 20165235 祁瑛 2018-3 《Java程序设计》第四周学习总结

    20165235 祁瑛 2018-3 <Java程序设计>第四周学习总结 教材学习内容总结 第五单云总结 (一)子类与继承1.java中子类只能有一个父类,在类名前用extends标记.2 ...

  2. pageHelper多个sql分页

    之前有个需求,在一个页面中需要有多个sql分页查询然后放到一个list中,展示,但是会出现一个bug,就是每次分页都会展示第一条查出的所有的数据: 第一页 第二页 因为是截的生产环境,第一条数据被处理 ...

  3. 关于XML的简单整理

  4. Shell学习之环境变量配置文件(三)

    Shell学习之环境变量配置文件 目录 环境变量配置文件简介 环境变量配置文件作用 其他配置文件和登录信息 环境变量配置文件简介 环境变量配置文件简介 环境变量配置文件中主要是定义对系统操作环境生效的 ...

  5. 潭州课堂25班:Ph201805201 django 项目 第三十三课 后台文章标签查询提交到前台,删除功能实现(课堂笔记)

    在视图中创建个类,要实现此功能,并把结果返回前台 , from django.shortcuts import render from django.views import View from dj ...

  6. Hibernate关联关系之多对多

    多对多映射关系 建立多对多单向关联关系 创建Employee类: public class Employee { private Integer empid; //员工编号 private Strin ...

  7. equals()与hashCode()

    两个都可以用来判断两个对象是否相同一致. hashCode相同的不一定是同一个对象:hashCode不同的一定不是相同对象 equals相同的一定是相同对象,是绝对可靠的 既然equals这么可靠,那 ...

  8. 封装、property特性及绑定与非绑定方法

    1.封装 (1)什么是封装? 封:属性对外是隐藏的,但对内是开放的: 装:申请一个名称空间,往里面装入一系列名字/属性 (2)为什么要封装? 封装数据属性的目的 首先定义属性的目的就是为了给类外部的使 ...

  9. python 有参装饰器与迭代器

    1.有参装饰器 模板: def auth(x): def deco(func): def timmer(*args,**kwargs ): res = func(*args,**kwargs ) re ...

  10. BZOJ3490 : Pa2011 Laser Pool

    与横线以及竖线的交点个数很容易求,那么只要求出横线竖线交点与运动轨迹的交点数即可. 运动轨迹可以划分成若干条贯穿边界的斜线,对于第一条和最后一条,可以用bitset暴力统计. 对于中间的部分,斜线都是 ...