spring boot + Eureka如何平滑上下线服务
spring boot + Eureka如何平滑上下线服务目录服务平滑上下线-单机版服务平滑上下线-微服务版目录“ 系统正常运行,如果要新发版程序,如何保证程序平滑上线,不影响前端的请求?使用Eureka作为注册中心时,会有哪些地方会导致新服务上下线延迟?如何优化并解决服务的正常上下线?”技术版本信息:spring-boot-starter-parent-2.2.4-RELEASEspring-b
spring boot + Eureka如何平滑上下线服务
目录
“ 系统正常运行,如果要新发版程序,如何保证程序平滑上线,不影响前端的请求?使用Eureka作为注册中心时,会有哪些地方会导致新服务上下线延迟?如何优化并解决服务的正常上下线?”
技术版本信息:
spring-boot-starter-parent-2.2.4-RELEASE
spring-boot-starter-web-2.2.4-RELEASE
spring-boot-starter-undertow-2.2.4-RELEASE
spring-cloud-starter-netflix-eureka-server-2.2.1.RELEASE
spring-cloud-starter-netflix-eureka-client-2.2.1.RELEASE
spring-boot-starter-openfeign-2.2.1-RELEASE
spring-cloud.version-Hoxton.SR1
服务平滑上下线-单机版
场景描述:
对于传统的单机JAVA WEB程序部署新的服务,就是停止当前的服务,然后部署新的服务,这样的操作会导致一个问题,就是在旧服务下线前,新服务上线前的这段时间,服务是不可用的,而且在服务下线前,如果当前还有请求没有执行完毕,也有可能会被异常中止。
可能有些同学会想到,部署多个服务在一个SLB下,暴露SLB供前端调用,这样后端就可以部署好了新的服务之后,然后在停掉旧的服务。这个解决办法,的确可以保证服务的上下线之间的时差为0,但是这个办法还不能解决当前旧服务还有部分请求没有执行完毕,就被中止的问题。
对于上述问题,还有些聪明的同学会思考到,是不是可以监控容器中是否还有请求的方式,保证容器中所有请求都执行完毕之后,在执行服务停止呢?
答案,当然是可以的。那具体如何操作呢?接下来就以undewtow容器作为例子给大家演示一下监听方法。
解决方案:
1、引入undertow容器pom文件,这里需要去掉web自带的tomcat容器
<!-- 移除掉默认支持的 Tomcat -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.2.4.RELEASE</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 添加 undertow -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
2、增加undertow配置文件
@Configuration
public class UndertowConfig {
@Autowired
private UndertowGracefulShutdownWrapper gracefulShutdownWrapper;
@Bean
public UndertowServletWebServerFactory servletWebServerFactory() {
UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
factory.addDeploymentInfoCustomizers(deploymentInfo -> deploymentInfo.addOuterHandlerChainWrapper(gracefulShutdownWrapper));
factory.addBuilderCustomizers(builder -> builder.setServerOption(UndertowOptions.ENABLE_STATISTICS, true));
return factory;
}
}
3、增加undertow操作程序链类
@Component
public class UndertowGracefulShutdownWrapper implements HandlerWrapper {
private GracefulShutdownHandler gracefulShutdownHandler;
@Override
public HttpHandler wrap(HttpHandler handler) {
if (null == gracefulShutdownHandler) {
this.gracefulShutdownHandler = new GracefulShutdownHandler(handler);
}
return gracefulShutdownHandler;
}
public GracefulShutdownHandler getGracefulShutdownHandler() {
return gracefulShutdownHandler;
}
}
4、增加undertow优雅停止核心处理类
@Component
public class UndertowGracefulShutdown implements ApplicationListener<ContextClosedEvent> {
private static final Logger LOGGER = LoggerFactory.getLogger(UndertowGracefulShutdown.class);
@Autowired
private UndertowGracefulShutdownWrapper gracefulShutdownWrapper;
@Autowired
private ServletWebServerApplicationContext context;
@Override
public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
gracefulShutdownWrapper.getGracefulShutdownHandler().shutdown();
try {
UndertowServletWebServer servletContainer = (UndertowServletWebServer)context.getWebServer();
Field field = servletContainer.getClass().getDeclaredField("undertow");
field.setAccessible(true);
// API活跃连接统计
Undertow undertow = (Undertow) field.get(servletContainer);
List<Undertow.ListenerInfo> listenerInfo = undertow.getListenerInfo();
Undertow.ListenerInfo listener = listenerInfo.get(0);
ConnectorStatistics connectorStatistics = listener.getConnectorStatistics();
// 每隔1秒检测是否已经处理完停止服务之前接收的request
while (!gracefulShutdownWrapper.getGracefulShutdownHandler().awaitShutdown(1000)) {
if (null != connectorStatistics) {
LOGGER.info("Can't shutdown undertow, requests still processing. And there are {} activeConnections...", connectorStatistics.getActiveConnections());
} else {
LOGGER.info("Can shutdown undertow.");
}
}
} catch (Exception e) {
LOGGER.error("undertow graceful shutdown error!", e);
}
}
服务平滑上下线-微服务版
场景描述:
在微服务的环境下,spring boot 常与 Eureka注册中心一起使用,从CAP理论来分析Eureka注册中心,它是一个满足AP模式的注册中心,底层通过Eureka服务端以及客户端的缓存的方式来保证服务的高可用性。本文主要以spring boot+openfegin+eureka来演示,微服务环境下,服务平滑上下线的问题以及优化方案。
下面简单先来描述一下Eureka服务的注册方式,客户端启动之后会发送一条注册的指令到服务端,服务端收到指令之后,刷新本地的服务列表信息,客户端默认30秒一次,来获取服务列表信息,这里使用的openfeign默认是在集成ribbon组件上,并且ribbon每次选择负载均衡的目的机器信息时,是从ribbon缓存中获取,所以总结一下整个微服务架构环境中,存在Eureka客户端缓存、ribbon缓存、服务端缓存,具体缓存见下图:
通过上述分析可以简单统计一下服务正常上下线需要多久时间:
优化方案:
1、增加undertow优雅停止方法,见第一点单机版解决方案
2、增加eureka client下线接口类,执行服务下线前调用一下,等待服务过期时间(比如优化后需要等待10秒),然后在停止服务
@Resource
private EurekaClient eurekaClient;
@PostMapping("/eureka/stop/client")
public ResponseEntity stopEurekaClient(HttpServletRequest request) {
eurekaClient.shutdown();
return ResponseEntity.ok("ok");
}
3、优化eureka client 、eureka server、ribbon的参数
#eureka server端
eureka:
server:
#取消二级缓存
use-read-only-response-cache: false
#集群里eureka节点的变化信息更新的时间间隔,默认60*10*1000
peer-eureka-nodes-update-interval-ms: 10000
#eureka client端
eureka:
client:
#eureka client间隔多久去拉取服务注册信息,默认为30秒
registry-fetch-interval-seconds: 5
#ribbon本地服务列表缓存时间,默认30S
ribbon:
ServerListRefreshInterval: 5000
更多推荐
所有评论(0)