前言
自从上次 CAP 2.3 版本发布 以来, 已经过去了几个月的时间, 这几个月比较忙, 所以也没有怎么写博客, 趁着 2019 年到来之际(现在应该是 2019 年开始的时候),CAP 也发布了 2018 年的最后一个大版本 2.4, 在这个版本中, 我们引入了一个新的特性, 叫做 "版本隔离".
简介
可能有些人还不知道 CAP 是什么, 老规矩来一个简介.
https://github.com/dotnetcore/CAP 是一个用来解决微服务或者分布式系统中分布式事务问题的一个开源项目解决方案 ( https://github.com/dotnetcore/CAP ) 同样可以用来作为 EventBus 使用, 目前已经 2 岁了, 目前已经应用到了很多的公司和项目中,
想对 CAP 更多了解的同学可以看下我的这篇文章.
在 这里 你可以查看到入门文档, 在 这里 https://github.com/dotnetcore/CAP/wiki 你可以查看 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"
- });
同样的, 针对上面提到的 "不同实例, 使用相同的持久化表 / 集合" 这个问题怎么利用这个特性来解决呢?
很简单, 我们可以在配置的时候将版本号字段配置为你要想实例名即可. 例如我有两个服务, 分别为购物车服务和个人中心服务, 他们都使用的相同的数据库, 在这之前 CAP 要求必须分配不同的持久化表来存储消息(通过配置表明前缀), 例如发消息的表需要为类似 cap.basket.published 和 cap.usercenter.published. 现在利用版本隔离特性就可以使用同一张表了, 可以这样配置:
- // Basket 服务配置
- services.AddCap(x =>
- {
- ...
- x.Version="basket-v1" // 设置实例名称 + 版本号
- });
- // UserCenter 服务配置
- services.AddCap(x =>
- {
- ...
- x.Version="usercenter-v1" // 设置实例名称 + 版本号
- });
注意: basket-v1 这后面的 -v1 是可选的, 如果你的服务端用不到版本这个特性, 去掉即可.
总结
CAP 经过两年的发展, GitHub 已经超过了 2000 Star, 目前已经是一个成熟的组件了. 有同学反映, 他们最近有一些大型的微服务项目也开始使用 CAP 了, CAP 在我们系统上线也接近一年了, 非常的稳定, 没有出过一次问题, 所以大家在做微服务项目的时候, 不用再有什么担心的了, 不管是线上成功案例还是文档示例视频教程这些都非常完善成熟了.
也感谢大家两年来的支持, 我们很开心能够帮助到大家
. 大家在使用的过程中遇到问题希望也能够积极的反馈, 帮助 CAP 变得越来越好.:)
如果你觉得本篇文章对您有帮助的话, 感谢您的[推荐] .
如果你对 .NET Core 有兴趣的话可以关注我, 我会定期的在博客分享我的学习心得.
本文地址: http://www.cnblogs.com/savorboard/p/cap-2-4.html
作者博客: Savorboard
本文原创授权为: 署名 - 非商业性使用 - 禁止演绎, 协议普通文本 https://creativecommons.org/licenses/by-nc-nd/4.0/ | 协议法律文本
来源: https://www.cnblogs.com/savorboard/p/cap-2-4.html