持續發佈

發佈模式

藍綠髮布

在發佈的過程中用戶無感知服務的重啓,通常情況下是通過新舊版本並存的方式實現,也就是說在發佈的流程中,新的版本和舊的版本是相互熱備的,通過切換路由權重的方式(非0即100)實現不同的應用的上線或者下線。

金絲雀發佈

通過在線上運行的服務中,新加入少量的新版本的服務,然後從這少量的新版本中快速獲得反饋,根據反饋決定最後的交付形態。

灰度發佈

灰度發佈是通過切換線上並存版本之間的路由權重,逐步從一個版本切換爲另一個版本的過程。雖然有很多人包括專業大牛認爲灰度發佈與金絲雀發佈是等同的,但是在具體的操作和目的上面個還是有些許差別的。金絲雀發佈更傾向於獲取快速的反饋,而灰度發佈更傾向於從一個版本到另一個版本平穩的切換。

AB測試

AB測試和灰度發佈非常像,但是從發佈的目的上,可以簡單的區分灰度發佈與AB測試,AB測試側重的是從A版本或者B版本之間的差異,並根據這個結果進行決策。最終選擇一個版本進行部署。因此和灰度發佈相比,AB測試更傾向於去決策,和金絲雀發佈相比,AB測試在權重和流量的切換上更靈活。

應用場景

背景介紹

我們的應用是基於Spring Cloud搭建,每當我們的後臺部署應用時,都需要停止tomcat,由於Eureka自身的緩存和Ribbon的刷新不及時,導致服務下線不能做到及時感知,讓服務在這段時間調用已停服務,這樣出現了異常的業務現象。

追溯原因

首先理解Eureka的緩存機制,響應緩存實現類。

com.netflix.eureka.registry.ResponseCacheImpl

在 ResponseCacheImpl 裏,將緩存拆分成兩層 :

  • 只讀緩存( readOnlyCacheMap )
  • 固定過期 + 固定大小的讀寫緩存( readWriteCacheMap )

默認配置下,緩存讀取策略如下:
在這裏插入圖片描述
readWriteCacheMap用了谷歌的guava。

  • expireAfterWrite設置緩存多少時間後失效。
  • CacheLoader裏的load方法是個抽象方法。當從readWriteCacheMap獲取指定的key時,就會觸發這個方法。
  • generatePayload 是具體的實現。
ResponseCacheImpl(EurekaServerConfig serverConfig, ServerCodecs serverCodecs, AbstractInstanceRegistry registry) {
        this.serverConfig = serverConfig;
        this.serverCodecs = serverCodecs;
        this.shouldUseReadOnlyResponseCache = serverConfig.shouldUseReadOnlyResponseCache();
        this.registry = registry;

        long responseCacheUpdateIntervalMs = serverConfig.getResponseCacheUpdateIntervalMs();
        this.readWriteCacheMap =
                CacheBuilder.newBuilder().initialCapacity(1000)
                        .expireAfterWrite(serverConfig.getResponseCacheAutoExpirationInSeconds(), TimeUnit.SECONDS)
                        .removalListener(new RemovalListener<Key, Value>() {
                            @Override
                            public void onRemoval(RemovalNotification<Key, Value> notification) {
                                Key removedKey = notification.getKey();
                                if (removedKey.hasRegions()) {
                                    Key cloneWithNoRegions = removedKey.cloneWithoutRegions();
                                    regionSpecificKeys.remove(cloneWithNoRegions, removedKey);
                                }
                            }
                        })
                        .build(new CacheLoader<Key, Value>() {
                            @Override
                            public Value load(Key key) throws Exception {
                                if (key.hasRegions()) {
                                    Key cloneWithNoRegions = key.cloneWithoutRegions();
                                    regionSpecificKeys.put(cloneWithNoRegions, key);
                                }
                                Value value = generatePayload(key);
                                return value;
                            }
                        });

        if (shouldUseReadOnlyResponseCache) {
            timer.schedule(getCacheUpdateTask(),
                    new Date(((System.currentTimeMillis() / responseCacheUpdateIntervalMs) * responseCacheUpdateIntervalMs)
                            + responseCacheUpdateIntervalMs),
                    responseCacheUpdateIntervalMs);
        }

        try {
            Monitors.registerObject(this);
        } catch (Throwable e) {
            logger.warn("Cannot register the JMX monitor for the InstanceRegistry", e);
        }
    }

備註 通過Eureka請求的方式實現服務的上下線,對應的在eureka-core包裏的resource目錄下。獲取所有的節點ApplicationsResource的getContainers,下線節點InstanceResource的cancelLease,它採用的Jersey實現的,所以不是很直觀。

  • AbstractInstanceRegistry 裏面幾個重要的方法 internalCancel 和 register。這裏學習了一個新的事件功能,
    這樣我們可以監聽 EurekaInstanceCanceledEvent 下線節點事件,這裏會執行倆次。
@Component
public class EurekaStateChangeListener {
    @EventListener
    public void listen(EurekaInstanceCanceledEvent eurekaInstanceCanceledEvent)
    {
        String appName = eurekaInstanceCanceledEvent.getAppName();
        String serverId = eurekaInstanceCanceledEvent.getServerId();
        System.out.println(appName);
        System.out.println(serverId);
    }
}
ribbon的處理

ribbon有倆種更新ServerList的方式一個是基於Java裏ScheduledThreadPoolExecutor類的定時任務由PollingServerListUpdater實現。一個是EurekaNotificationServerListUpdater類,基於事件的通知。
在這裏插入圖片描述

監測方法

我是通過wireshake監控,當我們手動下線服務的時候,用Jmeter進行壓力測試,看ribbon的負載均衡是不是還有流量流向我們的下線服務。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章