springcloud如何實現服務的平滑發佈 SpringCloud服務如何在Eureka安全優雅的下線 對actuator的管理端點進行ip白名單限制(springBoot添加filter)

在以前的文章中咱們提到服務的優雅下線,見:html

SpringCloud服務如何在Eureka安全優雅的下線

但這個對於ribbon調用實際上是不平滑的,shutdown請求到後服務就立刻關閉了,服務消費此時未感應到服務下線了,會仍然往這個服務發送請求,從而致使報錯。java

簡介方案有:1、開啓重試(前提是保證接口作好冪等處理)。spring

2、使用pause來下線服務(推薦)數據庫

操做步驟以下:apache

一、  服務提供方配置緩存

後臺端點禁用安全校驗tomcat

management.security.enabled=false安全

# 開啓服務暫停端點curl

endpoints.pause.enabled=trueide

# 禁用密碼驗證

endpoints.pause.sensitive=false

   因爲這些管理端點比較敏感須要加一個filter來過濾IP白名單

代碼參考:對actuator的管理端點進行ip白名單限制(springBoot添加filter)

  

二、  服務消費者

# 2秒拉取最新的註冊信息

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

# 2秒刷新ribbon中的緩存信息
ribbon.ServerListRefreshInterval=2000

三、發佈流程

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鏈接池等),這樣當超時時間不少的服務在第一次請求時不會出現超時。

四、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

3、擴展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,不夠通用。

相關文章
相關標籤/搜索