最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java微服務(wù)無損發(fā)布生產(chǎn)實(shí)戰(zhàn)案例及驗(yàn)證

 更新時間:2025年10月27日 09:58:13   作者:遙遠(yuǎn)_  
微服務(wù)架構(gòu)是一項(xiàng)在云中部署應(yīng)用和服務(wù)的新技術(shù),這篇文章主要介紹了Java微服務(wù)無損發(fā)布生產(chǎn)實(shí)戰(zhàn)及驗(yàn)證的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下

1. 背景

項(xiàng)目為Spring Cloud微服務(wù),注冊中心使用nacos。在上線發(fā)布過程中發(fā)現(xiàn)存在feign調(diào)用失敗的情況,即發(fā)布過程沒有做到無損。上線發(fā)布采用灰度發(fā)布策略,即先發(fā)布一臺機(jī)器,這臺機(jī)器成功發(fā)布后再接著發(fā)布其他機(jī)器,理論上應(yīng)該無損。

2. 排查

在上線發(fā)布微服務(wù)a第xx臺機(jī)器過程中,a應(yīng)該處于下線狀態(tài),不應(yīng)該再請求到這臺機(jī)器,但是從日志看有流量進(jìn)來并調(diào)用a失敗了:

3. 方案

借助AI及經(jīng)驗(yàn)排查項(xiàng)目代碼和停機(jī)sh腳本后發(fā)現(xiàn)代碼層面及運(yùn)維腳本層面都沒有實(shí)現(xiàn)優(yōu)雅停機(jī),基于盡量少改動代碼考慮,決定采用修改停機(jī)sh腳本的方案實(shí)現(xiàn)優(yōu)雅停機(jī)。

3.1. 外部腳本控制(運(yùn)維層面)

這是本案例采用的優(yōu)雅停機(jī)方案,代碼無侵入,只需完善停機(jī)sh腳本邏輯以支持優(yōu)雅停機(jī)。

3.1.1. 優(yōu)化前的sh腳本邏輯

停止實(shí)例時腳本沒有先下線nacos注冊中心服務(wù)實(shí)例,存在被調(diào)用方服務(wù)B剛停止但nacos還未感知到,此時調(diào)用方A從nacos獲取到這個已停止的服務(wù)B的實(shí)例,導(dǎo)致調(diào)用失敗。

3.1.2. 優(yōu)化后的sh腳本邏輯

采取主動標(biāo)記nacos實(shí)例下線 + 休眠等待 + 停止java進(jìn)程方案:先標(biāo)記nacos實(shí)例下線(主動下線而不是被動等待nacos自動檢測下線),再設(shè)置一個休眠時間一是為了等待nacos更新,二是讓正在處理的業(yè)務(wù)請求有時間完成。最后再停止java進(jìn)程,避免請求丟失,實(shí)現(xiàn)微服務(wù)優(yōu)雅停機(jī)。

#!/bin/bash
# stop.sh - 優(yōu)雅停機(jī)腳本

APP_NAME="provider"
APP_PORT=8082
NACOS_SERVER="127.0.0.1:8848"

echo "開始優(yōu)雅停止 $APP_NAME..."

# 1. 獲取服務(wù)ip
get_nacos_registered_ip() {
    
    # 調(diào)用Nacos查詢接口
    local response=$(curl -s "http://${NACOS_SERVER}/nacos/v1/ns/instance/list?serviceName=${APP_NAME}")
    
    if [ $? -ne 0 ] || [ -z "$response" ]; then
        echo "查詢Nacos服務(wù)失敗" >&2
        return 1
    fi
    
    # 解析JSON獲取IP(需要jq工具)
    if command -v jq >/dev/null 2>&1; then
        local ip=$(echo "$response" | jq -r '.hosts[0].ip' 2>/dev/null)
        if [ "$ip" != "null" ] && [ -n "$ip" ]; then
            echo $ip
            return 0
        fi
    fi
    
    # 如果沒有jq,使用grep/awk解析
    local ip=$(echo "$response" | grep -o '"ip":"[^"]*' | cut -d'"' -f4 | head -1)
    if [ -n "$ip" ]; then
        echo $ip
        return 0
    fi
    
    echo "無法從Nacos響應(yīng)中解析IP" >&2
    return 1
}

LOCAL_IP=$(get_nacos_registered_ip)
echo "檢測到服務(wù)注冊IP: $LOCAL_IP"

# 2. 從Nacos下線服務(wù)
echo "從Nacos注銷服務(wù)..."
curl -X DELETE "${NACOS_SERVER}/nacos/v1/ns/instance?serviceName=${APP_NAME}&ip=${LOCAL_IP}&port=${APP_PORT}"

# 3. 等待流量切換
echo "等待流量切換(15秒)..."
sleep 15

# 4. 根據(jù)jar名稱查找應(yīng)用進(jìn)程,比如provider2
echo "查找應(yīng)用進(jìn)程..."
# 更精確的進(jìn)程查找方式
APP_PID=$(ps aux | grep "[j]ava.*provider2" | awk '{print $2}')

if [ -n "$APP_PID" ]; then
    echo "找到應(yīng)用進(jìn)程,PID: $APP_PID"
    
    # 5. 發(fā)送優(yōu)雅停止信號
    echo "發(fā)送優(yōu)雅停止信號..."
    kill -15 $APP_PID
    
    # 6. 等待進(jìn)程停止,最多30秒
    for i in {1..30}; do
        if kill -0 $APP_PID 2>/dev/null; then
            echo "等待應(yīng)用停止...($i/30)"
            sleep 1
        else
            echo "應(yīng)用已優(yōu)雅停止"
            exit 0
        fi
    done
    
    # 7. 強(qiáng)制停止
    echo "優(yōu)雅停止超時,強(qiáng)制停止"
    kill -9 $APP_PID
else
    echo "應(yīng)用未運(yùn)行"
fi

echo "停止腳本執(zhí)行完成"

另外,springboot項(xiàng)目配置文件application.yml配置優(yōu)雅停機(jī):

server:
  shutdown: graceful

3.1.3. 測試結(jié)果

腳本運(yùn)行結(jié)果:

應(yīng)用日志:

3.2. 應(yīng)用內(nèi)優(yōu)雅停機(jī)(代碼層面)

3.2.1. 優(yōu)雅停機(jī)實(shí)現(xiàn)類

package com.example.provider;

import com.alibaba.cloud.nacos.registry.NacosRegistration;
import com.alibaba.cloud.nacos.registry.NacosServiceRegistry;
import org.apache.catalina.connector.Connector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Component
public class TomcatNacosGracefulShutdown implements TomcatConnectorCustomizer,
        ApplicationListener<ContextClosedEvent> {

    private static final Logger logger = LoggerFactory.getLogger(TomcatNacosGracefulShutdown.class);

    private volatile Connector connector;

    @Lazy
    @Resource
    private NacosServiceRegistry nacosServiceRegistry;

    @Lazy
    @Resource
    private NacosRegistration nacosRegistration;

    @Override
    public void customize(Connector connector) {
        this.connector = connector;
        logger.info("Tomcat連接器自定義配置完成");
    }

    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        logger.info("=== 開始Tomcat+Nacos優(yōu)雅停機(jī) ===");
        startGracefulShutdown();
    }

    public void startGracefulShutdown() {
        try {
            // 步驟1: Nacos注銷
            deregisterFromNacos();

            // 步驟2: 等待注冊中心傳播
            waitForRegistryPropagation();

            // 步驟3: 暫停Tomcat接收新請求
            pauseTomcat();

            // 步驟4: 等待活躍請求完成
            waitForActiveRequests();

            logger.info("=== Tomcat+Nacos優(yōu)雅停機(jī)完成 ===");

        } catch (Exception e) {
            logger.error("優(yōu)雅停機(jī)失敗", e);
        }
    }

    private void deregisterFromNacos() {
        if (nacosServiceRegistry != null && nacosRegistration != null) {
            try {
                logger.info("從Nacos注銷服務(wù)實(shí)例...");
                nacosServiceRegistry.deregister(nacosRegistration);
                logger.info("Nacos注銷成功: {}", nacosRegistration.getServiceId());
            } catch (Exception e) {
                logger.error("Nacos注銷失敗", e);
            }
        } else {
            logger.warn("Nacos注冊組件未找到,跳過注銷");
        }
    }

    private void waitForRegistryPropagation() {
        try {
            long waitTime = 15000;
            logger.info("等待注冊中心傳播: {}ms", waitTime);
            Thread.sleep(waitTime);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            logger.warn("等待過程被中斷");
        }
    }

    private void pauseTomcat() {
        if (connector != null) {
            logger.info("暫停Tomcat連接器,停止接收新請求");
            connector.pause();
        } else {
            logger.warn("Tomcat連接器未找到,無法暫停");
        }
    }

    private void waitForActiveRequests() {
        if (connector == null) {
            logger.warn("Tomcat連接器未找到,跳過等待");
            return;
        }

        Executor executor = connector.getProtocolHandler().getExecutor();
        if (executor instanceof ThreadPoolExecutor) {
            ThreadPoolExecutor threadPool = (ThreadPoolExecutor) executor;

            logger.info("等待活躍請求完成...");
            threadPool.shutdown();

            try {
                long maxWaitTime = 30000;
                if (!threadPool.awaitTermination(maxWaitTime, TimeUnit.MILLISECONDS)) {
                    logger.warn("等待請求超時,強(qiáng)制關(guān)閉線程池");
                    threadPool.shutdownNow();

                    if (!threadPool.awaitTermination(5000, TimeUnit.MILLISECONDS)) {
                        logger.error("線程池未能正常終止");
                    }
                } else {
                    logger.info("所有活躍請求處理完成");
                }
            } catch (InterruptedException e) {
                threadPool.shutdownNow();
                Thread.currentThread().interrupt();
                logger.warn("等待過程被中斷");
            }
        } else {
            logger.warn("無法獲取Tomcat線程池,跳過等待");
        }
    }

}

3.2.2. 測試結(jié)果

4. 驗(yàn)證

在測試環(huán)境用jemeter做接口測試,觀察在執(zhí)行停機(jī)腳本發(fā)布期間有無失敗用例。

5. 上線

測試環(huán)境驗(yàn)證沒問題后上線,生產(chǎn)發(fā)布時通過可觀測平臺查看發(fā)布服務(wù)的請求列表,發(fā)現(xiàn)錯誤數(shù)為0,成功率100%,且發(fā)布過程中沒有日志告警,說明問題已修復(fù)。

6. 參考資料

nacos官方open api

nacos官方FAQ

到此這篇關(guān)于Java微服務(wù)無損發(fā)布生產(chǎn)實(shí)戰(zhàn)案例及驗(yàn)證的文章就介紹到這了,更多相關(guān)Java微服務(wù)無損發(fā)布生產(chǎn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java 仿天貓服裝商城系統(tǒng)的實(shí)現(xiàn)流程

    Java 仿天貓服裝商城系統(tǒng)的實(shí)現(xiàn)流程

    讀萬卷書不如行萬里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+SSM+jsp+mysql+maven實(shí)現(xiàn)一個仿天貓服裝商城系統(tǒng),大家可以在過程中查缺補(bǔ)漏,提升水平
    2021-11-11
  • Java Predicate接口源碼使用示例

    Java Predicate接口源碼使用示例

    Java8引入了許多函數(shù)式接口(Functional Interface),Predicate(斷言)就是其中一個,它的主要作用可以簡單描述為:向其傳入一個對象(可以理解為參數(shù)),將得到一個布爾值作為輸出,這篇文章主要介紹了Java Predicate接口源碼使用示例,需要的朋友可以參考下
    2025-04-04
  • Java通過數(shù)據(jù)庫表生成實(shí)體類詳細(xì)過程

    Java通過數(shù)據(jù)庫表生成實(shí)體類詳細(xì)過程

    這篇文章主要介紹了Java通過數(shù)據(jù)庫表生成實(shí)體類,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2023-02-02
  • Java中String字符串常量池和intern方法源碼分析

    Java中String字符串常量池和intern方法源碼分析

    在之前的文章中,小編給大家介紹了String字符串的不可變性及其實(shí)現(xiàn)原理,其中給大家提到了字符串常量池的概念,那么什么是常量池,String字符串與常量池有什么關(guān)系,本文給大家嘮嘮字符串常量池及String#intern()方法的作用,需要的朋友可以參考下
    2023-05-05
  • 微服務(wù)SpringConfig配置中心詳解

    微服務(wù)SpringConfig配置中心詳解

    這篇文章主要介紹了微服務(wù)SpringConfig配置中心,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-04-04
  • 輕量級聲明式的Http庫——Feign的獨(dú)立使用

    輕量級聲明式的Http庫——Feign的獨(dú)立使用

    這篇文章主要介紹了輕量級聲明式的Http庫——Feign的使用教程,幫助大家更好的理解和學(xué)習(xí)使用feign,感興趣的朋友可以了解下
    2021-04-04
  • Java?guava框架LoadingCache及CacheBuilder本地小容量緩存框架總結(jié)

    Java?guava框架LoadingCache及CacheBuilder本地小容量緩存框架總結(jié)

    Guava?Cache本地緩存框架主要是一種將本地數(shù)據(jù)緩存到內(nèi)存中,但數(shù)據(jù)量并不能太大,否則將會占用過多的內(nèi)存,本文給大家介紹Java?guava框架?LoadingCache及CacheBuilder?本地小容量緩存框架總結(jié),感興趣的朋友一起看看吧
    2023-12-12
  • Spring MVC文件配置以及參數(shù)傳遞示例詳解

    Spring MVC文件配置以及參數(shù)傳遞示例詳解

    這篇文章主要給大家介紹了關(guān)于Spring MVC文件配置以及參數(shù)傳遞的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • spring聲明式事務(wù) @Transactional 不回滾的多種情況以及解決方案

    spring聲明式事務(wù) @Transactional 不回滾的多種情況以及解決方案

    本文主要介紹了spring聲明式事務(wù) @Transactional 不回滾的多種情況以及解決方案,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • spring中ApplicationListener的使用小結(jié)

    spring中ApplicationListener的使用小結(jié)

    ApplicationListener是spring提供的一個監(jiān)聽器,本文主要介紹了spring中ApplicationListener的使用小結(jié),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-07-07

最新評論

新昌县| 元谋县| 大同县| 新营市| 孟连| 琼海市| SHOW| 米泉市| 英吉沙县| 廉江市| 科尔| 迁西县| 大同县| 依兰县| 鸡西市| 南涧| 香港 | 山丹县| 邯郸市| 简阳市| 阿拉善左旗| 长宁区| 青州市| 林口县| 彭水| 蕉岭县| 寿光市| 库车县| 陈巴尔虎旗| 临夏市| 景德镇市| 滁州市| 英吉沙县| 壶关县| 铁力市| 景德镇市| 噶尔县| 河间市| 清原| 龙海市| 平顺县|