.net core canal 使用 (三) cannal 的封装

后台运行服务

Canal 中 Extend 代码
public static class CanalExtend { public static IServiceCollection AddCanal(this IServiceCollection services, IConfiguration configuration) { services.Configure<CanalOptions>(configuration.GetSection("CanalOptions")); services.AddTransient(p => { return p.GetRequiredService<IOptionsMonitor<CanalOptions>>().CurrentValue; }); services.AddTransient<CanalHandle<Product>, CanalProduct>(); return services.AddTransient<CanalProvide>(); } }
public class CanalOptions { /// <summary> /// 主机地址 /// </summary> public string Host { get; set; } /// <summary> /// 端口 /// </summary> public int Port { get; set; } /// <summary> /// 客户端id /// </summary> public string ClientId { get; set; } /// <summary> /// 默认主题 /// </summary> public string DefaultSubject { get; set; } //public string UserName { get; set; } //public string Password { get; set; } }
Canal 中 Handle 代码
/// <summary>
/// canal处理抽象基类
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class CanalHandle<T>
{
/// <summary>
/// 行变化对象
/// </summary>
private RowChange _rowChange;
/// <summary>
/// 行变化对象
/// </summary>
public RowChange RowChange => _rowChange;
/// <summary>
/// 消息id
/// </summary>
public long messageId;
/// <summary>
/// canal实例提供者
/// </summary>
public CanalProvide canalProvide;
/// <summary>
/// 消息id
/// </summary>
public long MessageId => messageId;
/// <summary>
/// canal实例提供者
/// </summary>
public CanalProvide CanalProvide => canalProvide;
/// <summary>
/// 子类实现 删除
/// </summary>
/// <param name="columns"></param>
/// <param name="t"></param>
public abstract void Delete(T t);
/// <summary>
/// 子类实现 添加
/// </summary>
/// <param name="columns"></param>
/// <param name="t"></param>
public abstract void Insert(T t);
/// <summary>
/// 子类实现 更新
/// </summary>
/// <param name="columns"></param>
/// <param name="t"></param>
public abstract void Update(T t);
/// <summary>
/// 子类实现 获取操作对象 例如 product
/// </summary>
/// <param name="columns"></param>
/// <returns></returns>
public abstract T GetModel(List<Column> columns);
/// <summary>
/// 设置行变化
/// </summary>
/// <param name="RowChange"></param>
/// <returns></returns>
public CanalHandle<T> SetRowChange(RowChange RowChange)
{
_rowChange = RowChange;
return this;
}
public void Handle(long MessageId, CanalProvide canalProvide)
{
messageId = MessageId;
this.canalProvide = canalProvide;
foreach (var rowData in _rowChange.RowDatas)
{
//修改前的 值
// List<Column> columns = rowData.BeforeColumns.ToList();
//修改后的 值
List<Column> columns = rowData.AfterColumns.ToList();
T t = GetModel(columns);
//删除的数据
if (_rowChange.EventType == EventType.Delete)
{
Delete(t);
}
//插入的数据
else if (_rowChange.EventType == EventType.Insert)
{
Insert(t);
}
//更新的数据
else if (_rowChange.EventType == EventType.Update)
{
Update(t);
}
}
}
}
/// <summary> /// product 处理 /// </summary> public class CanalProduct : CanalHandle<Product> { private readonly ILogger<CanalProduct> _logger; public CanalProduct(ILogger<CanalProduct> logger) { _logger = logger; } /// <summary> /// 获取转换后的product 实例 /// </summary> /// <param name="columns"></param> /// <returns></returns> public override Product GetModel(List<Column> columns) { Product product = new Product(); foreach (var column in columns) { if (column.Name.Equals(nameof(Product.Id))) { product.Id = long.Parse(column.Value.ToString()); } if (column.Name.Equals(nameof(Product.Name))) { product.Name = column.Value.ToString(); } if (column.Name.Equals(nameof(Product.Price))) { product.Price = Convert.ToDecimal(column.Value); } } return product; } public override async void Insert(Product t) { _logger.LogInformation($@"Insert开始 id={t.Id} name= {t.Name}, Price = {t.Price}"); //消息确认 await CanalProvide.AckAsync(MessageId); } public override async void Update(Product t) { _logger.LogInformation($@"Update开始 id={t.Id} name= {t.Name}, Price = {t.Price}"); //消息确认 await CanalProvide.AckAsync(MessageId); } public override async void Delete(Product t) { _logger.LogInformation($@"Delete开始 id={t.Id} name= {t.Name}, Price = {t.Price}"); //消息确认 await CanalProvide.AckAsync(MessageId); } }
/// <summary> /// canal实例提供者 /// </summary> public class CanalProvide { private readonly CanalOptions _canalOptions; private readonly ILogger<SimpleCanalConnection> logger; private SimpleCanalConnection _simpleCanal; public CanalProvide(CanalOptions canalOptions, ILogger<SimpleCanalConnection> logger) { _canalOptions = canalOptions; this.logger = logger; _simpleCanal = new SimpleCanalConnection(new SimpleCanalOptions(_canalOptions.Host, _canalOptions.Port, _canalOptions.ClientId), this.logger); } public SimpleCanalConnection SimpleCanal { get; set; } /// <summary> /// 默认连接 /// </summary> /// <returns></returns> public async Task DefaultConnectionAsync() { await _simpleCanal.ConnectAsync(); await _simpleCanal.SubscribeAsync(_canalOptions.DefaultSubject); } /// <summary> /// 有主题的连接 /// </summary> /// <param name="subject"></param> /// <returns></returns> public async Task ConnectionAsync(string subject) { await _simpleCanal.ConnectAsync(); await _simpleCanal.SubscribeAsync(subject); } /// <summary> /// 取消默认订阅 /// </summary> /// <returns></returns> public async Task UnDefaultSubscribeAsync() { await _simpleCanal.UnSubscribeAsync(_canalOptions.DefaultSubject); } /// <summary> /// 取消默认订阅 /// </summary> /// <param name="subject">主题名称</param> /// <returns></returns> public async Task UnSubscribeAsync(string subject) { await _simpleCanal.UnSubscribeAsync(subject); } /// <summary> /// 获取默认1024大小消息 /// </summary> /// <returns></returns> public async Task<Message> GetDefaultMessage() { return await _simpleCanal.GetWithoutAckAsync(1024); } /// <summary> /// 自定义获取大小消息 /// </summary> /// <param name="fetchSize"></param> /// <returns></returns> public async Task<Message> GetMessage(int fetchSize) { return await _simpleCanal.GetWithoutAckAsync(fetchSize); } /// <summary> /// 消息确认 /// </summary> /// <param name="messageId">消息id</param> /// <returns></returns> public async Task AckAsync(long messageId) { await _simpleCanal.AckAsync(messageId); } /// <summary> /// 消息回滚 /// </summary> /// <param name="messageId"></param> /// <returns></returns> public async Task RollbackAsync(long messageId) { await _simpleCanal.RollbackAsync(messageId); } /// <summary> /// 关闭连接 释放资源 /// </summary> /// <returns></returns> public async Task CloseAsync() { await _simpleCanal.DisConnectAsync(); await _simpleCanal.DisposeAsync(); } }
Works 代码
public class CanalHostedService : IHostedService { private readonly CanalHandle<Product> _canalProductHandle; private readonly CanalProvide _canalProvide; public CanalHostedService(CanalHandle<Product> canalProductHandle, CanalProvide canalProvide) { _canalProductHandle = canalProductHandle; _canalProvide = canalProvide; } public async Task StartAsync(CancellationToken cancellationToken) { //默认主题连接 await _canalProvide.DefaultConnectionAsync(); //这里的是死循环 不需要等待业务逻辑结束 //如果 等待会阻塞主线程 因为一直死循环 //TaskCreationOptions.LongRunning 由于是长时间运行 //需要在线程池外开启一个独立线程 不阻塞当前线程池的线程 Task.Factory.StartNew(async () => { while (true) { //获取订阅消息 Message message = await _canalProvide.GetMessage(1024); var entries = message.Entries; foreach (var entry in entries) { //不处理事务标记 if (entry.EntryType == EntryType.Transactionbegin || entry.EntryType == EntryType.Transactionend) { continue; } //获取架构名称 entry.Header.SchemaName //获取表名称 entry.Header.TableName if (entry.Header.TableName == "Products") //商品 { _canalProductHandle .SetRowChange(RowChange.Parser.ParseFrom(entry.StoreValue)) .Handle(message.Id, _canalProvide); } else if (entry.Header.TableName == "SparingProducts")//热备 { } } } }, cancellationToken, TaskCreationOptions.LongRunning, scheduler: TaskScheduler.Default); } public async Task StopAsync(CancellationToken cancellationToken) { await _canalProvide.CloseAsync(); } }
appsettings.json 配置
"CanalOptions": {
"Host": "127.0.0.1",
"Port": 11111,
"ClientId": "101",
"UserName": "canal",
"Password": 123456,
"DefaultSubject": "testhigh.Products,testhigh.SparingProducts"
}
调用
//注入canal
builder.Services.AddCanal(builder.Configuration);
//添加 canal 后台任务
builder.Services.AddHostedService<CanalHostedService>();
测试
启动

测试修改 长虹电视

修改后

测试结果成功

浙公网安备 33010602011771号