背景:

项目中一部分数据如页面静态数据需要从redis中获取,以提高页面加载速度。页面数据的修改是通过管理画面修改mysql,通过定时任务或手动将mysql数据同步到redis,以供页面获取。这里数据同步不是增量同步,而是根据条件查询得到数据集后同步。网关中的一些参数同样是在mysql维护,然后定时或手动推送到redis,并通知网关刷新内存数据。有时增加一个数据的变动会出发多个同步操作,如增加一个api,既要将其在门户画面展示,又要推送到网关,如果想实时生效就需要两次手动操作。这样就很繁琐,因此设计一种实时或准实时将mysql同步至redis的方案。

方案1:

更新数据库的同时执行同步redis方法,如在增删改首页数据的方法添加注解,利用aop统一处理数据同步的逻辑。这种方案实际就是spring cache。但是这里不仅仅要将数据同步至redis,还要发布订阅事件,因此需要自己实现aop逻辑。

方案2:

利用canal,监听mysql数据变更进行数据同步和发布订阅事件。这样不必修改原代码,逻辑解耦。并且如果mysql修改失败不会触发同步操作。

综上采用方案2。

详细方案设计:

在canal定义一个destination监听数据库,当有感兴趣的数据变动时,通过feign调用对应服务的同步方法(这里的同步方法复用已有的手动调用方法,不用新写)。因为同步的是准全量数据,为避免mysql每次更新都查询,党一个数据变动后一分钟内没有新的数据变动,才触发同步操作。此外,通过apollo配置中心动态启停同步服务。

canal部署:

参照官网的方案,利用zookeeper进行服务协调,实现canal server,canal client的高可用。
由于测试环境分为SIT,QA和UAT,每个环境有单独的数据库,因此canal定义三个destination监听对应的数据库实例。

未来的优化方向:
  • Redis获取数据失败fallback到数据库
  • 增加一层内存缓存
  • 感知同步数据成功与否
对于canal设计的一些思考
  • 对于canal的高可用,通过zk保证server和client同一时间只能有一个节点工作

    • server能不能根据数据id进行分片读取,提高读取数据的性能,类似kafka的设计,应该是不能的。因为parser向master发起dump请求得到的是字节流,无法获取原始数据。那能不能一个parser对应多个sink再放入store。没必要,因为canal的性能瓶颈在canal与数据库的网络IO,解析及sink是很快的。
    • 客户端能不能多个节点同时工作,从一个destination消费数据。如果不保证数据成功消费及有序性是可以的。如果某一client消费数据失败,当前store的设计(环形结构保存数据)无法做到回滚。如果一个destination分多个队列由client消费,只能保证数据局部有序,同时设计复杂。
  • 当前store的数据保存在内存中,是否有必要持久化到文件

    • 类似于logstash的数据也是保存在内存中,官方文档说会支持,但没有也可以,因为持久化会影响整体性能,通过zk存储client的消费位置会保证数据至少被消费一次。
  • store保存的数据受到大小和条数的限制,当达到限制时,sink会堵塞parser,不会撑爆内存
  • canal与logstash,kafka的一些比较
对以后写公共组件的一些启示:
  • 支持多种配置方式如配置文件,http,并可动态生效
  • 通过协调服务保证系统的高可用
  • 暴露服务监控指标至prometheus
  • 获取数据的方式:达到一定数量或时间

吃水果毫不费力zz
7 声望0 粉丝

啦啦啦