无Beta环境的Java项目中新功能上线的状态控制

1 需求

一般情况下,一个服务具有测试环境线上沙箱环境(beta环境)生产环境三种过度环境。对于那些需要多个服务共同合作的项目,服务双方需要在相应的环境下进行测试通过才能正式投入线上运行。常规的测试流程是:测试环境联调--> beta环境联调--> 生产环境正式上线。如下图所示:

上线步骤.png

但如果服务目前并不具备独立的线上Beta环境的话,和那些具备Beta环境的服务合作的时候,服务Beta环境的缺失会给双方联调测试带来诸多问题。服务方解决这个问题的常见方法如下所述(假设有两个服务,称为A和B,其中服务A没有Beta环境,服务B有Beta环境):

  1. 服务B调用服务A的测试环境服务
上线步骤-测试环境.png

这种情况下,如果服务B的Beta环境需要的是生产环境的数据(线上线下的数据往往是隔离的),那么服务A的测试环境提供的服务将无法满足其需求。

  1. 服务B调用服务A的生产环境服务
上线步骤-生产环境.png

这种情况下,如果服务A的生产环境新作的修改会导致当前生产环境的状态发生变化,而服务B又没有正式上线(Beta环境和生产环境是隔离的),且服务A的正常服务需要服务B的支持,那么服务A直接上线就会影响线上的正常服务。

另一方面,对于一个服务来说,如果短期内有多个项目需要上线,但是其中的一个项目因为某些原因不能在生产环境中生效,那么就会影响后续项目的上线计划。有如下两种方式解决这个问题:

  1. 该项目不上线,其他项目并行开发,优先考虑当前能够上线的功能。但是如果项目之间有共同的修改(譬如说添加了某一通用模块,该模块在后续项目中也可以使用),那么后续的项目将无法共享其修改。
  2. 简单的添加一个开关,满足条件的话采用新的修改。但是这个方法会在线上环境中引入新旧不同的版本,功能修改过程中,需要保留原始逻辑。

为了不影响后续的开发,服务中需要添加功能上线版本控制模块,该模块的目的是:

  1. 解决和拥有Beta环境的服务共同联调的困难,支撑调用发或者依赖方在Beta环境上的服务。
  2. 能够对上线的功能进行版本控制,支持特定功能延迟上线的需求

2 思路

直接引入开关变量虽然能够满足大体的需求,但是该方法没有统一的模板,如果新功能中修改的地方比较分散,比较容易出错。因此,可以在开关变量的基础上,抽象出功能上线版本控制模块

  1. 功能版本的分类

    对于一个新的项目,其所提供的功能的状态无非是原始版本新版本混合版本三种:

    • 原始版本:新功能上线前的状态
    • 新版本:新功能替换原始功能
    • 混合版本:线上同时运行新旧两种版本,只不过新版本的调用需要满足一定的条件,例如白名单

    引入混合版本后,就可以通过提供一个混合版本中的过滤条件控制线上功能的服务状态。当满足过滤条件的时候,调用新版本的服务,当不满足条件的时候,依旧调用老版本的服务。可以将此过滤条件理解为类似白名单的作用。

    上线步骤-混合版本.png
  1. 功能版本的切换

    版本的切换可以通过一个简单的状态值进行控制,控制流程如下:

    上线步骤-控制流程图.png

版本切换的逻辑在任何功能中都是一致的,因此这部分逻辑是通用逻辑,可以通过设计模式中的模板方法实现这个逻辑,也可以通过接口中的默认函数实现这个逻辑。

  1. 版本不同状态的封装

    既然需要控制新功能在线上的情况,在开发新功能的时候,就需要将原始的功能代码保留下来,与新的更新的功能一起被提取出来,作为新旧两个版本对外提供服务。
    这里的难点在于如何提供通用的入参(不同功能的参数肯定不一样)和通用的返回对象。为了尽可能的灵活性,采用Java中的泛型机制控制入参和返回。

3 实现

3.1 版本状态变量

通过枚举类定义版本控制的三种状态

public enum FunctionVersionStatusEnum {
    APPLY_ORIGINAL_VERSION(0, "应用原逻辑"),
    APPLY_MIXED_VERSION(1, "应用混合逻辑"),
    APPLY_NEW_VERSION(2, "应用新逻辑");
    // ... 省略其他
}

目前,功能的版本控制在配置文件中设置,在类中读取并转化成FunctionVersionStatusEnum对象

3.2 版本控制通用模板接口

通过接口定义功能上线版本控制模块中需要实现的方法,通过接口的默认方法模拟模板方法完成版本控制逻辑。

public interface FunctionVersionControl<T, R> {
    R applyNewVersion(T request);
    R applyOriginalVersion(T request);
    boolean checkNewVersionConditionInMixedVersionModel(T request);
    FunctionVersionStatusEnum getStatus();
    default R apply(FunctionVersionStatusEnum versionEnum, T request) {
        Objects.requireNonNull(versionEnum, "versionEnum can not be null");
        switch(versionEnum) {
            case APPLY_NEW_VERSION:
                return applyNewVersion(request);
            case APPLY_MIXED_VERSION:
                if (checkNewVersionConditionInMixedVersionModel(request)) {
                    return applyNewVersion(request);
                }
                return applyOriginalVersion(request);
            case APPLY_ORIGINAL_VERSION:
                return applyOriginalVersion(request);
            default:
                throw new AssertionError("FunctionVersionStatusEnum doesn't have type " + versionEnum);
        }
    }

}

其中,applyNewVersionapplyOriginalVersion方法分别表示功能的新旧逻辑;checkNewVersionConditionInMixedVersionModel方法为混合版本模式下能够触发新版本功能的前提条件;getStatus方法用于获取当前功能的状态;apply方法应用版本控制逻辑。

如果是新功能,则applyOriginalVersion为空方法即可
如果有白名单类似的需求,重写checkNewVersionConditionInMixedVersionModel方法,让能够调用新版本逻辑的部分结果为true

4 使用

实现功能上线版本控制的方式有两种:

  1. 直接在相关的类中实现FunctionVersionControl接口,调用apply方法
  2. 新建类实现FunctionVersionControl接口,注入该类并调用其apply
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260