背景
近期在做一个用户行为采集的系统,可选方案基本就是埋点或基于DB监控,考虑到埋点需要各业务系统配合,对原有业务逻辑有一定侵入性,最终选用DB监控为主,埋点为辅(由于部分行为存储在mongo)的方案。canal是阿里开源的数据同步工具,相对比较成熟,而且公司已经有canal的应用,可以说踩过一些坑,当然也提前意识到了一些坑,比如canal强调自己支持HA架构,但只有一个节点处于working状态,而其他节点则处于standby状态,这在分布式应用中显然是不理想的,因为我部署了多个节点,但事实上只有一个节点在working,这样的结果就是一个节点累死累活,而其他节点却悠哉悠哉,对此初步的方案如下
这个方案的主要思想是单独部署client,client通过rpc或mq的方式把数据推送给应用,由于client只是透传,没有处理逻辑,所以系统压力并不大,而作为消费方的应用,由于经过rpc或mq分发的方式可以做到相对均衡的消费,可以说这种方案基本可以适用于大部分的分布式系统消费,当然,考虑到我们这种方案其实是通过rpc或mq方式实现的分布式系统的负载均衡,不可避免的拉长了整个消费的链路,也就使得消费延迟边长,对于新系统希望达到准实时相应来说,这种方案仍有不足,那么还有没有更为合适的方案呢,接下来将结合canal的官方文档(https://github.com/alibaba/canal)和源码,进一步分析。
canal-server架构
一个server可以包含1到n个instance,没个instance又是由eventParser 、eventSink 、eventStore 、metaManager 组成
- eventParser (数据源接入,模拟slave协议和master进行交互,协议解析)
- eventSink (Parser和Store链接器,进行数据过滤,加工,分发的工作)
- eventStore (数据存储)
server/client交互协议
参照官方提供的example
```java
protected void process() {
int batchSize = 5 * 1024;
while (running) {
try {
MDC.put("destination", destination);
connector.connect();// 1.connect
connector.subscribe();// 2.subscribe
while (running) {
Message message = connector.getWithoutAck(batchSize); // 3.getWithoutAck
long batchId = message.getId();
int size = message.getEntries().size();
if (batchId == -1 || size == 0) {
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// }
} else {
printSummary(message, batchId, size);
printEntry(message.getEntries());
}
connector.ack(batchId); // 4.ack
// connector.rollback(batchId); // 4.rollback
}
} catch (Exception e) {
logger.error("process error!", e);
} finally {
connector.disconnect();// 6.disconnect
MDC.remove("destination");
}
}
}
```
再看建立连接的过程
```java
CanalConnector connector = CanalConnectors.newClusterConnector("127.0.0.1:2181", destination, "", "");
```
也就是每个connector都是基于destination的,而一个destination事实上对于一个instance,也就是说无论是canal-server还是client,可拆分的最小粒度都是instance,再来看canal的HA机制实现
```
canal的ha分为两部分,canal server和canal client分别有对应的ha实现
- canal server: 为了减少对mysql dump的请求,不同server上的instance要求同一时间只能有一个处于running,其他的处于standby状态.
- canal client: 为了保证有序性,一份instance同一时间只能由一个canal client进行get/ack/rollback操作,否则客户端接收无法保证有序。
```
也就是canal作为一个数据同步工具,有着自身的限制,而我认为这种限制也是有必要的,同时也可以看出官方也说明了,canal的HA机制是在instance这个粒度上的,而不是client或server,这就给了我们操作的空间,而分布式系统的核心就是怎么协调资源的分配,那么现在需要考虑的就是如何分配instance。相对于server,client是内嵌的(虽然canal也提供内嵌的server),所以client的可操作空间更大,我们只需要基于client实现instance的分布式算法,就可以使得分布式部署的应用各接口均匀分布这working的instance,最终方案如下
通过一段时间的试运行,方案基本得到了验证,在采集分析3天,每天高峰时间段1W条数据初步分析,DB变动到应用采集平均耗时在100ms左右,达到预期。