捂脸斋

江山风月 🌘 本无常主 🌈 闲者便是主人 🍃🍃🍃

关于蓝绿发布的调查

zero downtime deployment架构前提

服务集群

Cluster Server

节点间负载均衡

Load Balance

服务无状态

Stateless

可以使用redismysql等中间件保存会话状态。

向后兼容 Backward Compatibility

  • 场景一:新增字段

    有一张User表,在v1.1有name、age两个字段。在v1.2中,新增了一个SQL脚本。

    1
    2
    alter table user add address varchar(1024);
    update user set address = 'shanghai' where address is null;

    我们必须保证,在发布v1.2之前,先执行sql脚本,让User表中已经存在address字段。此时v1.1对address字段无感知,因为v1.1不会操作address。

  • 场景二:重命名字段

    有一张User表,在v1.1有name字段。

    在v1.2中,我们想把name字段改成nickname。为了保证数据库向后兼容,我们新增了一个SQL脚本。

    1
    alter table user add nickname varchar(1024);

    不能直接在脚本中把name改成nickname,因为在v1.1的服务中,用的还是name。

    v1.2的code中,也需要做到向后兼容。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    class User {

    /**
    * 在v1.2中,name属性不对外暴露。
    */
    @Getter(access = private)
    @Setter
    private String name;

    @Setter
    private String nickname;

    /**
    * 在v1.2中,数据库同时存在name和nickname,旧数据name有值,新数据nickname有值。
    * 所以,这里的get方法,需要考虑从两个字段取值。
    */
    public String getNickname {
    return name == null ? name : nickname;
    }

    public static List<User> selectAll() {
    String sql = "select * from user;";
    // setName(); setNickname(); ...
    }

    }

    可以看到,在整个v1.2的生命周期中,不管是数据库还是服务端,同时支持name和nickname两个版本。

    只有在v1.3中,我们才可以彻底抛弃name。

    1
    2
    update user set nickname = name where nickname is null;
    alter table user drop name;
    1
    2
    3
    4
    5
    6
    7
    class User {

    @Getter
    @Setter
    private String nickname;

    }

    在实际开发中,向后兼容往往比上面这两个场景更加复杂。

参考文件

Zero Downtime Deployment