持续发布

发布模式

蓝绿发布

在发布的过程中用户无感知服务的重启,通常情况下是通过新旧版本并存的方式实现,也就是说在发布的流程中,新的版本和旧的版本是相互热备的,通过切换路由权重的方式(非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的负载均衡是不是还有流量流向我们的下线服务。

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