自从上次 CAP 2.3 版本发布 以来,已经过去了几个月的时间,这几个月比较忙,所以也没有怎么写博客,趁着2019年到来之际(现在应该是2019年开始的时候),CAP也发布了2018年的最后一个大版本 2.4,在这个版本中,我们引入了一个新的特性,叫做“版本隔离”。
简介可能有些人还不知道 CAP 是什么,老规矩来一个简介。
CAP 是一个用来解决微服务或者分布式系统中分布式事务问题的一个开源项目解决方案(https://github.com/dotnetcore/CAP)同样可以用来作为EventBus使用,目前已经2岁了,目前已经应用到了很多的公司和项目中,
想对 CAP 更多了解的同学可以看下我的这篇文章。
在 这里 你可以查看到入门文档,在 这里 你可以查看CAP背后的设计原理。
下面我们就来看一下这个新的特性。
版本隔离不知道你们在实际开发的过程中有没有遇到这种场景。
业务快速迭代,需要向前兼容
由于业务的快速迭代,在各个服务集成的过程中,消息的数据结构并不是固定不变的,有些时候我们为了适应新引入的需求,会添加或者修改一些数据结构。如果你是一套全新的系统这没有什么问题,但是如果你的系统已经部署到生产环境了并且正在服务客户,这就会导致新的功能在上线的时候和旧的数据结构发生不兼容,那么这些改变可能会导致出现严重的问题,要想解决这个问题,只能把消息队列和持久化的消息全部清空,然后才能启动应用程序,这对于生产环境来说显然是致命的。
多个版本的服务端
有些时候,App的服务端需要提供多套接口,来支持不同版本的App,这些不同版本的App相同的接口和服务端交互的数据结构可能是不一样的,所以通常情况下服务端提供不用的路由地址来适配不同版本的App调用。随着容器技术的流行,目前有一种流行的版本隔离方案就是利用容器来提供多套不同环境的服务端,然后网关根据不同的地址来进行路由,我们目前就是这种方案,这种方案的优点是程序不用写多套路由Action来提供多个地址,方便维护。
不同实例,使用相同的持久化表/集合
之前有同学表示,希望多个不同实例的程序可以公用相同的数据库,在 2.4 之前的版本,我们可以通过指定不同的表名来隔离不同实例的数据库表,即在CAP配置的时候通过配置不同的表名前缀来实现。
public void ConfigureServices(IServiceCollection services) { services.AddCap(x => { x.UseKafka(""); x.UseMySql(opt => { opt.ConnectionString = "connection string"; opt.TableNamePrefix = "appone"; // 在这里配置不同的实例使用的表名前缀 }); }); }但是,如果想不同的实例使用相同的数据库表怎么办呢?也许我们可以借助这个新特性来实现。
下面我们就来给版本隔离下个定义吧。
什么是版本隔离呢? 版本隔离就是利用版本特性将消息对象按照版本划分,从而来隔离不同版本的业务或实例。
为了增加这个特性,我们做出了以下改变:
数据表新增版本号字段
为了实现版本隔离特性,我们在各个需要持久化的数据库中添加了一个版本号字段,你可以使用下面的脚本来更新你的数据库表。
**MySQL** ALTER TABLE `cap.published` ADD Version VARCHAR(20) NULL; ALTER TABLE `cap.received` ADD Version VARCHAR(20) NULL; **SQL Server** ALTER TABLE Cap.[Published] ADD Version VARCHAR(20) NULL; ALTER TABLE Cap.[Received] ADD Version VARCHAR(20) NULL; **PostgreSQL** ALTER TABLE cap.published ADD "Version" VARCHAR(20) NULL; ALTER TABLE cap.received ADD "Version" VARCHAR(20) NULL; **MongoDb** db.CapPublishedMessage.update({},{"$set" : {"Version" : "1"}}); db.CapReceivedMessage.update({},{"$set" : {"Version" : "1"}});新的版本配置项
我们在CAP的配置项中新增了一个版本号属性用来配置实例的版本,默认情况如果不进行配置那么会使用 v1 来作为默认值
services.AddCap(x => { ... x.Version="" // 设置版本号,默认值 v1 });新的 Dashboard 显示项
我们在新的Dashboard面消息列表中添加了版本号显示列,以提供对版本隔离特型的支持,你可以在已发送或者已接收的消息列表中查看到版本号这一列。
利用版本隔离特性那么我们如何利用版本隔离特性来处理前面提到不同场景的问题呢?
针对不同版本的服务端,有了版本隔离特性,你现在只需要在配置的时候配置版本号字段,CAP 将自动在不同的实例中将消息划分为不同的版本,从而可以确保不同版本的消息不会被错误的消费或者发送。
举例来说,App 的服务端具有不同的 Api 版本,这么不同版本的程序部署在不同的 Docker 容器中提供服务,但是 这些 Api 版本可能具有很多,通常情况下,我们会保留2-3个不同的服务端版本已适配不同的客户端版本。那么这个时候有同学可能会问,程序做了不同版本的时候,依赖的那些具有状态的中间件怎么办呢?所以,这就是 CAP 提供版本隔离特性的价值所在了,众所周知 CAP 也算是有状态的了,因为会依赖 MQ 的一些持久化以及数据库表的持久化,利用 CAP 提供的版本隔离特性你就不必去部署多套的消息队列了,是不是非常棒? :)
下面是举例 Basket 服务,不同版本的配置示例
// Basket v1 services.AddCap(x => { ... x.Version="v1" }); // Basket v2 services.AddCap(x => { ... x.Version="v2" });同样的,针对上面提到的 “不同实例,使用相同的持久化表/集合” 这个问题怎么利用这个特性来解决呢?