.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>();

测试

启动

 测试修改 长虹电视

 修改后

 测试结果成功

 

posted on 2023-07-03 11:42  是水饺不是水饺  阅读(33)  评论(0)    收藏  举报

导航