├── .DS_Store
├── README.md
├── canal-client
├── .DS_Store
├── .classpath
├── .project
├── .settings
│ └── org.eclipse.jdt.core.prefs
├── README.md
├── bin
│ ├── .DS_Store
│ └── canal
│ │ └── client
│ │ ├── CanalClientTest.class
│ │ ├── rabbitmq.class
│ │ └── redis.class
├── canal_client.jar
├── conf
│ ├── .DS_Store
│ └── canal.properties
├── data
│ ├── .DS_Store
│ └── binlog_2016.log
├── lib
│ ├── .DS_Store
│ ├── canal.client-1.0.22.jar
│ ├── canal.common-1.0.22.jar
│ ├── canal.example-1.0.22.jar
│ ├── canal.protocol-1.0.22.jar
│ ├── commons-io-2.4.jar
│ ├── commons-lang-2.6.jar
│ ├── commons-logging-1.1.1.jar
│ ├── fastjson-1.1.35.jar
│ ├── guava-18.0.jar
│ ├── jcl-over-slf4j-1.7.12.jar
│ ├── jedis-2.9.0.jar
│ ├── log4j-1.2.14.jar
│ ├── logback-classic-1.1.3.jar
│ ├── logback-core-1.1.3.jar
│ ├── netty-3.2.5.Final.jar
│ ├── protobuf-java-2.4.1.jar
│ ├── rabbitmq-client.jar
│ ├── slf4j-api-1.7.12.jar
│ ├── spring-2.5.6.jar
│ ├── zkclient-0.1.jar
│ └── zookeeper-3.4.5.jar
├── src
│ ├── .DS_Store
│ └── canal
│ │ ├── .DS_Store
│ │ └── client
│ │ ├── .DS_Store
│ │ ├── CanalClientTest.java
│ │ ├── rabbitmq.java
│ │ └── redis.java
└── start_canal_client.sh
├── img
├── .DS_Store
├── canal-mysql-nosql.png
├── mongo.png
├── redis-hash.png
└── system-image.png
└── python_sync_nosql
├── .DS_Store
├── __pycache__
├── config.cpython-35.pyc
├── queue_rabbitmq.cpython-35.pyc
└── sync_redis.cpython-35.pyc
├── config.py
├── config.pyc
├── get_file.py
├── meta.log
├── queue_rabbitmq.py
├── startup.py
├── sync_mongo.py
└── sync_redis.py
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/.DS_Store
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 下图是最基本的web服务器的结构图。
5 | 
6 |
7 | 基于 Canal 的 MySql RabbitMQ Redis/memcached/mongodb 的nosql同步 (多读、nosql延时不严格 需求)
8 |
9 | 1.mysql主从配置
10 |
11 | 2.对mysql binlog(row) parser 这一步交给canal
12 |
13 | 3.MQ对解析后binlog增量数据的推送
14 |
15 | 4.对MQ数据的消费(接收+数据解析,考虑消费速度,MQ队列的阻塞)
16 |
17 | 5.数据写入/修改到nosql (redis的主从/hash分片)
18 |
19 | 6.保证对应关系的简单性:一个mysql表对应一个 redis实例(redis单线程,多实例保证分流不阻塞),关联关系数据交给接口业务
20 |
21 | 数据:mysql->binlog->MQ->redis(不过期、关闭RDB、AOF保证读写性能) (nosql数据仅用crontab脚本维护)
22 |
23 | 请求:http->webserver->redis(有数据)->返回数据 (完全避免用户直接读取mysql)
24 |
25 | ->redis(无数据)->返回空
26 |
27 | 7.可将它视为一个触发器,binlog为记录触发事件,canal的作用是将事件实时通知出来,并将binlog解析成了所有语言可读的工具。
28 | 在事件传输的各个环节 提高 可用性 和 扩展性 (加入MQ等方法)最终提高系统的稳定。
29 |
30 |
31 |
32 | 传统 Mysql Redis/memcached nosql的缓存 (业务同步)
33 | 从cache读取数据->
34 |
35 | 1.对数据在mysql的hash算法分布(db/table/分区),每个hash为节点(nosql数据全部失效时候,可保证mysql各节点可支持直接读取的性能)
36 |
37 | 2.mysql主从
38 |
39 | 3.nosql数据的hash算法分布(多实例、DB),每个hash为节点
40 |
41 | 4.nosql数据震荡处理 (当某节点挂了寻找替代节点算法(多层hash替代节点)。。。)
42 |
43 | 5.恢复节点数据
44 |
45 | 6.请求:http->webserver->【对key计算一致性hash节点】->connect对应的redis实例
46 |
47 | ->1.redis(有数据)-> 返回数据
48 |
49 | ->2.redis(无数据)-> mysql (并写入数据redis) -> 返回数据
50 |
51 | ->3.redis节点挂掉-> 业务寻址hash替代节点
52 | -> 3.1 redis(有数据) -> 返回数据
53 |
54 | -> 3.2 redis(无数据) -> mysql(并写入数据redis) -> 返回数据
55 |
56 |
57 | 
58 |
59 |
60 | 为什么要使用消息队列(MQ)进行binlog传输:
61 |
62 | 1.增加缓冲,binlog生产端(canal client)只负责生产而不需要考虑消费端的消费能力, 不等待阻塞。
63 |
64 | 2.binlog 消费端: 可实时根据MQ消息的堆积情况,动态 增加/减少 消费端的数量,达到合理的资源利用和消费
65 |
66 |
67 | 部署:
68 |
69 | 阿里canal纯java开发,所以要先安装java环境
70 |
71 | 安装jdk(推荐jdk1.8):
72 | 安装过程参考网上资料,(注意环境变量配置)
73 |
74 | mysql配置:
75 | 1.编辑mysql配置文件
76 | $ sudo vim /etc/my.cnf
77 |
78 | [mysqld]
79 | log-bin=mysql-bin #binlog文件名(也可以使用绝对路径)
80 | binlog-format=ROW #选择row模式
81 | server_id=1 #实例唯一ID,不能和canal的slaveId重复
82 |
83 | 保存并退出,并重启mysql
84 | $ sudo service mysql restart
85 |
86 | 2.创建 mysql账号密码(账号密码自定 权限自定)
87 |
88 | CREATE USER canal IDENTIFIED BY 'canal';
89 | GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';
90 | -- GRANT ALL PRIVILEGES ON *.* TO 'canal'@'%' ;
91 | FLUSH PRIVILEGES;
92 |
93 | canal server 配置启动:
94 |
95 | canal server 模拟mysql从库并向mysql发送dump命令获取mysql binlog数据。
96 |
97 | 1.下载解压项目,这里提供了1.0.22版本:
98 | [canal.deployer-1.0.22.tar.gz](https://github.com/liukelin/canal_mysql_nosql_sync/releases)
99 | 可从阿里项目下载最新版本 deployer :
100 | [https://github.com/alibaba/canal/releases](https://github.com/alibaba/canal/releases)
101 |
102 | 2.配置项目:
103 | # 公共配置
104 | $ sudo vim conf/canal.properties
105 |
106 | canal.port= 11111 # canal server 运行端口,保证该端口为占用状态,或者使用其他未占用端口
107 |
108 | 保存退出。
109 |
110 | # 实例配置
111 | $ sudo vim conf/example/instance.properties
112 |
113 | # position info
114 | canal.instance.master.address = 127.0.0.1:3306 # mysql连接
115 |
116 | canal.instance.dbUsername = canal # mysql账号
117 | canal.instance.dbPassword = canal # 密码
118 | canal.instance.defaultDatabaseName = test # 需要同步的库名
119 | canal.instance.connectionCharset = UTF-8 # mysql编码
120 |
121 | 保存退出。
122 |
123 | 更多配置查看:
124 | [http://agapple.iteye.com/blog/1831873](http://agapple.iteye.com/blog/1831873)
125 |
126 | 3.启动:
127 | $ sh bin/startup.sh
128 |
129 | 日志文件: $ less logs/canal/canal.log # canal server端运行日志
130 | $ less logs/example/example.log # canal client端连接日志
131 | $ logs/example/meta.log # 实例binlog 读取记录文件(记录变更位置,默认为新增变更(tail))
132 |
133 | canal client 配置启动:
134 |
135 | canal client将从canal server获取的binlog数据最终以json行格式保存到指定文件(也可省略这步,直接发送到MQ)。
136 |
137 | binlog生产端和消费端的之间,增加MQ作为缓冲,增加容错度和动态扩展性
138 |
139 | 1.下载解压项目,这里自己写了个基于1.0.22版本的项目:
140 | [canal_client1.0.22.zip](https://github.com/liukelin/canal_mysql_nosql_sync/releases)
141 |
142 | 源码查看: [canal-client](https://github.com/liukelin/canal_mysql_nosql_sync/tree/master/canal-client)
143 |
144 | 2.基本配置
145 |
146 | $vim conf/canal.properties
147 |
148 | # cancal server host, 上面 canal server的IP
149 | canal.server.host = 127.0.0.1
150 |
151 | # cancal server port,上面 canal server的启动端口
152 | canal.server.port = 11111
153 |
154 | # 数据保存路径 ,自行指定
155 | canal.binlog.dir = db_data
156 |
157 | # 可选rabbitmq/redis/kafka 作为队列(这里使用 rabbitmq 作为队列传输)
158 | canal.mq = rabbitmq
159 |
160 | ###### rabbitmq 基本配置 #####
161 | rabbitmq.host = 127.0.0.1
162 | rabbitmq.port = 5672
163 | rabbitmq.user = test
164 | rabbitmq.pass = 123456
165 |
166 |
167 | 保存退出。
168 |
169 | 3.启动canal client:
170 |
171 | $ sh start_canal_client.sh
172 |
173 |
174 | 修改mysql数据触发。
175 |
176 | 最终结果:
177 |
178 | eventType :操作类型(UPDATE/INSERTDELETE)
179 |
180 | db: 涉及库
181 |
182 | table: 涉及表
183 |
184 | before:变更前数据
185 |
186 | after: 变更后数据
187 |
188 | time: 操作时间
189 |
190 |
191 | $less db_data/binlog_xxxx.log
192 |
193 | {"binlog":"mysql-bin.000009:1235","db":"test","table":"users","eventType":"UPDATE","before":{"uid":"8","username":"duobao153713223"},"after":{"uid":"8","username":"duobao153713223"},"time":"2016-08-22 17:47:25"}
194 |
195 | {"binlog":"mysql-bin.000009:1533","db":"test","table":"users","eventType":"DELETE","before":"","after":{"uid":"8","username":"duobao153713223"},"time":"2016-08-22 17:48:09"}
196 |
197 | {"binlog":"mysql-bin.000009:1790","db":"test","table":"users","eventType":"INSERT","before":"","after":{"uid":"9","username":"test2"},"time":"2016-08-22 17:48:45"}
198 |
199 | 消费数据demo:(这里使用python3 消费rabbitmq同步到redis 作为案例,实际可根据业务需求,因为此时所需要的数据已是通用的json格式,无限可能)
200 |
201 | 流程 :file数据-> MQ -> nosql
202 |
203 | MQ: rabbitMQ
204 |
205 | 语言:python3
206 |
207 | NoSql: redis
208 |
209 |
210 | 多项目订阅需求,如:client1和client2 需要消费这些数据, 他们得到的数据一样
211 | 开始考虑直接用队列:
212 | 队列数据: [A, B, C, D]
213 | client1 :
214 | 消费进程1:获取AB
215 | 消费进程2:获取CD
216 |
217 | client2 :
218 | 消费进程1:获取AB
219 | 消费进程2:获取CD
220 |
221 | 这样的话,如果使用rabbitMQ 就必须给每个 client 提供独立的队列。并独立消费
222 | 1、使用kafka,利用他的分组group,每个client 为一个组,这样就可保证,数据给每个组一致。
223 | 2、对每个项目需求开独立的 canal server instance 和 canal client实例
224 |
225 |
226 | 配置:
227 | 语言:python3
228 | pip:pika redis
229 |
230 | 项目代码: python_sync_nosql
231 | 修改配置文件config.py
232 | # 最终存储数据redis
233 | redis_host = '127.0.0.1'
234 | redis_port = 6379
235 |
236 | ###### rabbitmq 基本配置 #####
237 | rabbitmq_host = '127.0.0.1'
238 | rabbitmq_port = 5672
239 | rabbitmq_user = 'test'
240 | rabbitmq_pass = '123456'
241 |
242 | # 设置对每个table存储使用的key字段
243 | redis_cache_map = {
244 | # db
245 | 'test':{
246 | # table : kid
247 | 'users':'uid',
248 | }
249 | }
250 |
251 | 运行脚本:
252 | $ python3 startup.py
253 |
254 |
255 | 数据最终存储为Redis 的 hash结构,key为 db_table_id
256 | 
257 |
258 | 同步到MongoDB同理
259 |
260 | 这里的demo是将表数据映射到 mongodb 结构
261 | db => db
262 | table => 集合
263 | column=> coll
264 | 
265 |
266 |
267 |
268 | ## 目录结构
269 |
270 | ~~~
271 |
272 | ├─canal-client/ 封装的canal client客户端 和 消息队列MQ 项目
273 | │ ├─src/ 项目代码
274 | │ ├─lib/ jar包依赖
275 | │ ├─conf/ 配置文件
276 | │ ├─canal_client.jar 启动jar
277 | │ └─start_canal_client.sh 启动文件
278 | │
279 | ├─python_sync_nosql/ 消费MQ binlog数据, 将数据写入到NoSql demo
280 | │ ├─queue_rabbitmq.py rabbitmq 消费端
281 | │ ├─sync_redis.py 写入到redis
282 | │ ├─sync_mongo.py 写入到mongo
283 | │ ├─config.py 配置
284 | │ └─startup.py 启动入口
285 | └─
286 |
287 | ~~~
288 |
289 | ## 总结
290 |
291 | 1.使用MQ作为传输,可提高容错度,并且可以起到一个消费速度的缓冲,在程序上加上对队列积压数据的监控,可实时增加或减少MQ消费进程的数量。
292 |
293 | 2.为了提高binlog数据的可靠消费,建议使用带有ACK功能的MQ 做为消息队列使用
294 |
295 | 3.为了避免多进程对MQ消费速度的时序先后不可控,建议binlog数据只作为触发条件(使用id从mysql获取最新数据)作为数据使用,而不作为具体数据使用。
296 |
297 | 3.1.或者弄一个版本号的服务,对每条消息打上一个唯一的版本号,用版本号来确定是否是最新的数据
298 |
299 | 4.接下来我会继续完善otter的实际案例 ...
300 |
301 |
302 | ## 其他
303 | 这里有个用golang实现的binlog解析的项目: [https://github.com/liukelin/bubod](https://github.com/liukelin/bubod)
304 |
305 |
资源下载
306 |
307 | canal server 服务端deployer: [https://github.com/alibaba/canal/releases/tag/canal-1.0.22](https://github.com/alibaba/canal/releases/tag/canal-1.0.22)
308 |
309 |
310 | canal client 客户端: [https://github.com/liukelin/canal_mysql_nosql_sync/releases/tag/1.0.22.2](https://github.com/liukelin/canal_mysql_nosql_sync/releases/tag/1.0.22.2)
311 |
312 | 阿里canal项目原始地址:[https://github.com/alibaba/canal](https://github.com/alibaba/canal)
313 |
314 | 数据消费写入nosql例子: python_sync_nosql 这里是消费rabbitmq数据最终同步到redis
315 |
316 | 其他说明:[使用canal和canal_mysql_nosql_sync同步mysql数据](https://www.aliyun.com/jiaocheng/1114823.html?spm=5176.100033.2.18.hXjlSb)
317 |
318 | 使用golang语言开发的版本:[https://github.com/liukelin/bubod](https://github.com/liukelin/bubod)
319 |
320 |
321 |
--------------------------------------------------------------------------------
/canal-client/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/.DS_Store
--------------------------------------------------------------------------------
/canal-client/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/canal-client/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | canal-client
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 |
15 | org.eclipse.jdt.core.javanature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/canal-client/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
5 | org.eclipse.jdt.core.compiler.compliance=1.7
6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate
7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate
8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate
9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
11 | org.eclipse.jdt.core.compiler.source=1.7
12 |
--------------------------------------------------------------------------------
/canal-client/README.md:
--------------------------------------------------------------------------------
1 | canal client端封装
2 |
3 | 获取canal server 的binlog
4 |
5 | ↓
6 |
7 | 将binlog 数据组装成json格式
8 |
9 | ↓
10 |
11 | 使用MQ传输 (rabbitmq、redis、kafka)
12 |
13 |
--------------------------------------------------------------------------------
/canal-client/bin/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/bin/.DS_Store
--------------------------------------------------------------------------------
/canal-client/bin/canal/client/CanalClientTest.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/bin/canal/client/CanalClientTest.class
--------------------------------------------------------------------------------
/canal-client/bin/canal/client/rabbitmq.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/bin/canal/client/rabbitmq.class
--------------------------------------------------------------------------------
/canal-client/bin/canal/client/redis.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/bin/canal/client/redis.class
--------------------------------------------------------------------------------
/canal-client/canal_client.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/canal_client.jar
--------------------------------------------------------------------------------
/canal-client/conf/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/conf/.DS_Store
--------------------------------------------------------------------------------
/canal-client/conf/canal.properties:
--------------------------------------------------------------------------------
1 | ########### cancal client conf #############
2 |
3 | ########### liukelin #############
4 |
5 | # cancal server host
6 | canal.server.host = 192.168.179.184
7 |
8 | # cancal server port
9 | canal.server.port = 11111
10 |
11 | # 实例 默认 example/instance.properties
12 | canal.server.instance = example
13 |
14 | # 每次获取binlog数据 行数
15 | canal.batchsize = 1000
16 |
17 | # 每次获取等待时间单位/ms
18 | canal.sleep = 1000
19 |
20 |
21 | ############ 写入到文件 ###############
22 | # 数据保存路径
23 | canal.binlog.dir = /Users/liukelin/Desktop/canal-otter-mycat-cobar/canal_object/data
24 |
25 | # 数据保存格式 y=>YY m=>YYMM d=>YYMMdd h每小时 i每分钟
26 | canal.binlog.filename = y
27 |
28 | ########## 启用队列 redis/rabbitmq/kafka ############
29 | canal.mq = redis
30 |
31 | ######写入rabbitmq #####
32 | rabbitmq.host = 192.168.179.184
33 | rabbitmq.port = 5672
34 | rabbitmq.user = test
35 | rabbitmq.pass = 123456
36 | # 队列名称
37 | rabbitmq.queuename = canal_binlog_data
38 | # 队列持久化
39 | rabbitmq.durable = true
40 | # 是否ack
41 | rabbitmq.ack = false
42 |
43 |
44 | ######写入redis ######
45 | #持久化自行设置 RDB/AOF
46 | redis.host = 192.168.179.184
47 | redis.port = 6379
48 | redis.user =
49 | redis.pass =
50 | redis.queuename = canal_binlog_data
51 |
52 |
53 |
54 | # print 是否打印数据
55 | canal.print = true
56 |
--------------------------------------------------------------------------------
/canal-client/data/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/data/.DS_Store
--------------------------------------------------------------------------------
/canal-client/data/binlog_2016.log:
--------------------------------------------------------------------------------
1 | {"binlog":"mysql-bin.000052:256","db":"kelin_test","table":"user","eventType":"UPDATE","before":{"id":"6","img":"ee","sex":"32","uid":"5"},"after":{"id":"6","img":"eeasd 多岁的","sex":"32","uid":"5"},"time":"2016-08-23 15:43:55"}
2 | {"binlog":"mysql-bin.000052:506","db":"kelin_test","table":"user","eventType":"UPDATE","before":{"id":"1","img":"444","sex":"333","uid":"2"},"after":{"id":"1","img":"444设定的","sex":"333","uid":"2"},"time":"2016-08-23 15:44:01"}
3 | {"binlog":"mysql-bin.000052:754","db":"kelin_test","table":"user","eventType":"INSERT","before":"","after":{"id":"9","img":"盛大的","sex":"0","uid":"18"},"time":"2016-08-23 15:44:10"}
4 | {"binlog":"mysql-bin.000052:980","db":"kelin_test","table":"user","eventType":"INSERT","before":"","after":{"id":"10","img":"盛大的","sex":"0","uid":"19"},"time":"2016-08-23 15:44:24"}
5 | {"binlog":"mysql-bin.000052:1206","db":"kelin_test","table":"user","eventType":"DELETE","before":"","after":{"id":"6","img":"eeasd 多岁的","sex":"32","uid":"5"},"time":"2016-08-23 15:44:36"}
6 | {"binlog":"mysql-bin.000058:256","db":"kelin_test","table":"user","eventType":"UPDATE","before":{"id":"9","img":"盛大的","sex":"0","uid":"18"},"after":{"id":"9","img":"盛大的2","sex":"0","uid":"18"},"time":"2016-08-26 17:09:26"}
7 | {"binlog":"mysql-bin.000058:508","db":"kelin_test","table":"user","eventType":"UPDATE","before":{"id":"9","img":"盛大的2","sex":"0","uid":"18"},"after":{"id":"9","img":"盛大的23","sex":"0","uid":"18"},"time":"2016-08-26 17:15:01"}
8 | {"binlog":"mysql-bin.000058:762","db":"kelin_test","table":"user","eventType":"UPDATE","before":{"id":"10","img":"盛大的","sex":"0","uid":"19"},"after":{"id":"10","img":"盛大的4","sex":"0","uid":"19"},"time":"2016-08-26 17:17:05"}
9 | {"binlog":"mysql-bin.000058:1014","db":"kelin_test","table":"user","eventType":"UPDATE","before":{"id":"9","img":"盛大的23","sex":"0","uid":"18"},"after":{"id":"9","img":"2323","sex":"0","uid":"18"},"time":"2016-08-26 17:18:06"}
10 | {"binlog":"mysql-bin.000058:1014","db":"kelin_test","table":"user","eventType":"UPDATE","before":{"id":"10","img":"盛大的4","sex":"0","uid":"19"},"after":{"id":"10","img":"2323","sex":"0","uid":"19"},"time":"2016-08-26 17:18:06"}
11 | {"binlog":"mysql-bin.000058:1306","db":"kelin_test","table":"user","eventType":"UPDATE","before":{"id":"9","img":"2323","sex":"0","uid":"18"},"after":{"id":"9","img":"232we3","sex":"0","uid":"18"},"time":"2016-08-26 17:26:32"}
12 | {"binlog":"mysql-bin.000058:1306","db":"kelin_test","table":"user","eventType":"UPDATE","before":{"id":"10","img":"2323","sex":"0","uid":"19"},"after":{"id":"10","img":"232we3","sex":"0","uid":"19"},"time":"2016-08-26 17:26:32"}
13 | {"binlog":"mysql-bin.000060:256","db":"kelin_test","table":"user","eventType":"UPDATE","before":{"id":"9","img":"232we3","sex":"0","uid":"18"},"after":{"id":"9","img":"dd232we3","sex":"0","uid":"18"},"time":"2016-08-27 16:58:39"}
14 | {"binlog":"mysql-bin.000060:503","db":"kelin_test","table":"user","eventType":"UPDATE","before":{"id":"10","img":"232we3","sex":"0","uid":"19"},"after":{"id":"10","img":"232we3f","sex":"0","uid":"19"},"time":"2016-08-27 16:59:43"}
15 | {"binlog":"mysql-bin.000060:749","db":"kelin_test","table":"user","eventType":"UPDATE","before":{"id":"5","img":"{\"\\\\:xx\"}","sex":"2","uid":"4"},"after":{"id":"5","img":"{\"\\\\:xx\"}是","sex":"2","uid":"4"},"time":"2016-08-27 17:09:50"}
16 | {"binlog":"mysql-bin.000060:749","db":"kelin_test","table":"user","eventType":"UPDATE","before":{"id":"5","img":"{\"\\\\:xx\"}","sex":"2","uid":"4"},"after":{"id":"5","img":"{\"\\\\:xx\"}是","sex":"2","uid":"4"},"time":"2016-08-27 17:13:21"}
17 | {"binlog":"mysql-bin.000060:749","db":"kelin_test","table":"user","eventType":"UPDATE","before":{"id":"5","img":"{\"\\\\:xx\"}","sex":"2","uid":"4"},"after":{"id":"5","img":"{\"\\\\:xx\"}是","sex":"2","uid":"4"},"time":"2016-08-27 17:13:21"}
18 | {"binlog":"mysql-bin.000060:1003","db":"kelin_test","table":"user","eventType":"UPDATE","before":{"id":"9","img":"dd232we3","sex":"0","uid":"18"},"after":{"id":"9","img":"dd232dwe3","sex":"0","uid":"18"},"time":"2016-08-27 17:15:09"}
19 | {"binlog":"mysql-bin.000060:1253","db":"kelin_test","table":"user","eventType":"UPDATE","before":{"id":"9","img":"dd232dwe3","sex":"0","uid":"18"},"after":{"id":"9","img":"dd232dwe3dd","sex":"0","uid":"18"},"time":"2016-08-27 17:15:56"}
20 | {"binlog":"mysql-bin.000060:1506","db":"kelin_test","table":"user","eventType":"UPDATE","before":{"id":"9","img":"dd232dwe3dd","sex":"0","uid":"18"},"after":{"id":"9","img":"dd232dw","sex":"0","uid":"18"},"time":"2016-08-27 17:16:52"}
21 | {"binlog":"mysql-bin.000060:1757","db":"kelin_test","table":"user","eventType":"UPDATE","before":{"id":"10","img":"232we3f","sex":"0","uid":"19"},"after":{"id":"10","img":"232wed3f","sex":"0","uid":"19"},"time":"2016-08-27 17:17:25"}
22 | {"binlog":"mysql-bin.000060:2005","db":"kelin_test","table":"user","eventType":"UPDATE","before":{"id":"5","img":"{\"\\\\:xx\"}是","sex":"2","uid":"4"},"after":{"id":"5","img":"{\"\\\\:xx\"}d是","sex":"2","uid":"4"},"time":"2016-08-27 17:20:10"}
23 | {"binlog":"mysql-bin.000060:2263","db":"kelin_test","table":"user","eventType":"UPDATE","before":{"id":"9","img":"dd232dw","sex":"0","uid":"18"},"after":{"id":"9","img":"dd","sex":"0","uid":"18"},"time":"2016-08-27 17:21:52"}
24 | {"binlog":"mysql-bin.000060:2505","db":"kelin_test","table":"user","eventType":"UPDATE","before":{"id":"1","img":"444设定的","sex":"333","uid":"2"},"after":{"id":"1","img":"444设定的w","sex":"333","uid":"2"},"time":"2016-08-27 17:23:59"}
25 | {"binlog":"mysql-bin.000060:2763","db":"kelin_test","table":"user","eventType":"DELETE","before":"","after":{"id":"9","img":"dd","sex":"0","uid":"18"},"time":"2016-08-27 17:26:19"}
26 | {"binlog":"mysql-bin.000060:2982","db":"kelin_test","table":"user","eventType":"UPDATE","before":{"id":"10","img":"232wed3f","sex":"0","uid":"19"},"after":{"id":"10","img":"232wfed3f","sex":"0","uid":"19"},"time":"2016-08-27 17:27:02"}
27 | {"binlog":"mysql-bin.000060:3232","db":"kelin_test","table":"user","eventType":"UPDATE","before":{"id":"10","img":"232wfed3f","sex":"0","uid":"19"},"after":{"id":"10","img":"的","sex":"0","uid":"19"},"time":"2016-08-27 17:27:29"}
28 | {"binlog":"mysql-bin.000060:3477","db":"kelin_test","table":"user","eventType":"UPDATE","before":{"id":"10","img":"的","sex":"0","uid":"19"},"after":{"id":"10","img":"的都懂得","sex":"0","uid":"19"},"time":"2016-08-27 17:28:12"}
29 | {"binlog":"mysql-bin.000062:256","db":"kelin_test","table":"user","eventType":"UPDATE","before":{"id":"10","img":"的都懂得","sex":"0","uid":"19"},"after":{"id":"10","img":"的都懂得d","sex":"0","uid":"19"},"time":"2016-08-29 17:59:07"}
30 | {"binlog":"mysql-bin.000062:514","db":"kelin_test","table":"user","eventType":"UPDATE","before":{"id":"8","img":"\"马克吐温\"的","sex":"123","uid":"6"},"after":{"id":"8","img":"\"马克吐温\"的ee","sex":"123","uid":"6"},"time":"2016-08-29 18:00:24"}
31 | {"binlog":"mysql-bin.000062:783","db":"kelin_test","table":"user","eventType":"UPDATE","before":{"id":"10","img":"的都懂得d","sex":"0","uid":"19"},"after":{"id":"10","img":"的都懂得ddd","sex":"0","uid":"19"},"time":"2016-08-29 18:01:30"}
32 |
--------------------------------------------------------------------------------
/canal-client/lib/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/lib/.DS_Store
--------------------------------------------------------------------------------
/canal-client/lib/canal.client-1.0.22.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/lib/canal.client-1.0.22.jar
--------------------------------------------------------------------------------
/canal-client/lib/canal.common-1.0.22.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/lib/canal.common-1.0.22.jar
--------------------------------------------------------------------------------
/canal-client/lib/canal.example-1.0.22.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/lib/canal.example-1.0.22.jar
--------------------------------------------------------------------------------
/canal-client/lib/canal.protocol-1.0.22.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/lib/canal.protocol-1.0.22.jar
--------------------------------------------------------------------------------
/canal-client/lib/commons-io-2.4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/lib/commons-io-2.4.jar
--------------------------------------------------------------------------------
/canal-client/lib/commons-lang-2.6.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/lib/commons-lang-2.6.jar
--------------------------------------------------------------------------------
/canal-client/lib/commons-logging-1.1.1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/lib/commons-logging-1.1.1.jar
--------------------------------------------------------------------------------
/canal-client/lib/fastjson-1.1.35.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/lib/fastjson-1.1.35.jar
--------------------------------------------------------------------------------
/canal-client/lib/guava-18.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/lib/guava-18.0.jar
--------------------------------------------------------------------------------
/canal-client/lib/jcl-over-slf4j-1.7.12.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/lib/jcl-over-slf4j-1.7.12.jar
--------------------------------------------------------------------------------
/canal-client/lib/jedis-2.9.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/lib/jedis-2.9.0.jar
--------------------------------------------------------------------------------
/canal-client/lib/log4j-1.2.14.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/lib/log4j-1.2.14.jar
--------------------------------------------------------------------------------
/canal-client/lib/logback-classic-1.1.3.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/lib/logback-classic-1.1.3.jar
--------------------------------------------------------------------------------
/canal-client/lib/logback-core-1.1.3.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/lib/logback-core-1.1.3.jar
--------------------------------------------------------------------------------
/canal-client/lib/netty-3.2.5.Final.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/lib/netty-3.2.5.Final.jar
--------------------------------------------------------------------------------
/canal-client/lib/protobuf-java-2.4.1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/lib/protobuf-java-2.4.1.jar
--------------------------------------------------------------------------------
/canal-client/lib/rabbitmq-client.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/lib/rabbitmq-client.jar
--------------------------------------------------------------------------------
/canal-client/lib/slf4j-api-1.7.12.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/lib/slf4j-api-1.7.12.jar
--------------------------------------------------------------------------------
/canal-client/lib/spring-2.5.6.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/lib/spring-2.5.6.jar
--------------------------------------------------------------------------------
/canal-client/lib/zkclient-0.1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/lib/zkclient-0.1.jar
--------------------------------------------------------------------------------
/canal-client/lib/zookeeper-3.4.5.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/lib/zookeeper-3.4.5.jar
--------------------------------------------------------------------------------
/canal-client/src/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/src/.DS_Store
--------------------------------------------------------------------------------
/canal-client/src/canal/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/src/canal/.DS_Store
--------------------------------------------------------------------------------
/canal-client/src/canal/client/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukelin/canal_mysql_nosql_sync/e5c13c5445566698eda5241e8fe7c902a8a2b04f/canal-client/src/canal/client/.DS_Store
--------------------------------------------------------------------------------
/canal-client/src/canal/client/CanalClientTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * canal client
3 | * 从canal server 获取 binlog,并写入文件
4 | * @date 2016-08-13
5 | * @author liukelin
6 | * @email 314566990@qq.com
7 | */
8 | package canal.client;
9 |
10 | import java.net.InetSocketAddress;
11 | import java.util.ArrayList;
12 | import java.util.List;
13 | import java.util.Map;
14 | import java.util.HashMap;
15 |
16 | import com.alibaba.otter.canal.client.CanalConnector;
17 | import com.alibaba.otter.canal.client.CanalConnectors;
18 | import com.alibaba.otter.canal.protocol.CanalEntry.Column;
19 | import com.alibaba.otter.canal.protocol.CanalEntry.Entry;
20 | import com.alibaba.otter.canal.protocol.CanalEntry.EntryType;
21 | import com.alibaba.otter.canal.protocol.CanalEntry.EventType;
22 | import com.alibaba.otter.canal.protocol.CanalEntry.RowChange;
23 | import com.alibaba.otter.canal.protocol.CanalEntry.RowData;
24 | import com.alibaba.otter.canal.protocol.Message;
25 | import com.alibaba.fastjson.JSON; //alibaba的FastJson(高性能JSON开发包)
26 |
27 | //读取 Properties
28 | import java.util.Properties;
29 | import java.io.InputStream;
30 | import java.io.IOException;
31 | import java.io.FileInputStream;
32 | //import java.io.UnsupportedEncodingException;
33 |
34 | //时间
35 | import java.util.Date;
36 | import java.util.concurrent.TimeoutException;
37 | import java.text.SimpleDateFormat;
38 |
39 | //写入文件
40 | import java.io.FileWriter;
41 | //url encode
42 | //import java.net.URLEncoder;
43 |
44 | //rabbitmq(rabbitmq-client.jar)
45 | //import com.rabbitmq.client.ConnectionFactory;
46 | //import com.rabbitmq.client.Connection;
47 | //import com.rabbitmq.client.Channel;
48 | //import com.rabbitmq.client.MessageProperties;
49 |
50 | public class CanalClientTest {
51 |
52 | private static String path = CanalClientTest.class.getProtectionDomain().getCodeSource().getLocation().getPath();
53 | public static String canal_print = "0";
54 |
55 | //canal server
56 | public static String host = "127.0.0.1";
57 | public static int port = 11111;
58 | public static String instance = "example";
59 | public static int batchSize = 1000; //每次获取数据数量
60 | public static int sleep = 1000; //无数据时等待时间
61 |
62 | //file
63 | public static String canal_binlog_filename = "h"; //保存文件名
64 | public static String data_dir = "data"; //数据保存路径
65 |
66 | //mq
67 | public static String canal_mq; // redis/rabbitmq/kafka
68 |
69 | // rabbitmq
70 | public static String rabbitmq_host = "127.0.0.1";
71 | public static String rabbitmq_port = "5672";
72 | public static String rabbitmq_user = "";
73 | public static String rabbitmq_pass = "";
74 | public static String rabbitmq_queuename = "canal_binlog_data"; //队列名称
75 | public static String rabbitmq_ack = "false"; //ack
76 | public static String rabbitmq_durable = "false"; //队列持久
77 | public static Map rabbitmq_conf;
78 |
79 | // redis
80 | public static String redis_host = "127.0.0.1";
81 | public static String redis_port = "5672";
82 | public static String redis_user = "";
83 | public static String redis_pass = "";
84 | public static String redis_queuename = "canal_binlog_data"; //队列名称
85 | public static Map redis_conf;
86 |
87 | public static void main(String args[]) {
88 | String conf_path = path.substring(0, path.lastIndexOf("/")) + "/conf/canal.properties";
89 | //String host = AddressUtils.getHostIp()
90 |
91 | System.out.println("#=====canal client====================\r\n#=====2016====================\r\n" +
92 | "#=====liukelin====================\r\n" +
93 | "#=====conf:"+conf_path);
94 |
95 | //读取配置
96 | try {
97 | Properties prop = new Properties();
98 | InputStream in = new FileInputStream(conf_path);
99 | // InputStream in = new FileInputStream("/Users/liukelin/Desktop/canal-otter-mycat-cobar/canal_object/conf/canal.properties");
100 |
101 | prop.load(in);
102 | String conf_host = prop.getProperty("canal.server.host");
103 | String conf_port = prop.getProperty("canal.server.port");
104 | String conf_instance = prop.getProperty("canal.server.instance");
105 |
106 | String conf_batchsize = prop.getProperty("canal.batchsize");
107 | String conf_sleep = prop.getProperty("canal.sleep");
108 | String conf_dir = prop.getProperty("canal.binlog.dir");
109 | String conf_filename = prop.getProperty("canal.binlog.filename");
110 | String conf_print = prop.getProperty("canal.print");
111 |
112 | String conf_mq = prop.getProperty("canal.mq");
113 |
114 | String conf_rabbitmq_host = prop.getProperty("rabbitmq.host");
115 | String conf_rabbitmq_port = prop.getProperty("rabbitmq.port");
116 | String conf_rabbitmq_user = prop.getProperty("rabbitmq.user");
117 | String conf_rabbitmq_pass = prop.getProperty("rabbitmq.pass");
118 | String conf_rabbitmq_queuename = prop.getProperty("rabbitmq.queuename");
119 | String conf_rabbitmq_ack = prop.getProperty("rabbitmq.ack");
120 | String conf_rabbitmq_durable = prop.getProperty("rabbitmq.durable");
121 |
122 | String conf_redis_host = prop.getProperty("redis.host");
123 | String conf_redis_port = prop.getProperty("redis.port");
124 | String conf_redis_user = prop.getProperty("redis.user");
125 | String conf_redis_pass = prop.getProperty("redis.pass");
126 | String conf_redis_queuename = prop.getProperty("redis.queuename");
127 |
128 | if ( conf_host!= null && conf_host!=""){
129 | host = conf_host.trim();
130 | }
131 | if ( conf_port!= null && conf_port!=""){
132 | port = Integer.parseInt(conf_port.trim());
133 | }
134 |
135 | if ( conf_instance!= null && conf_instance!=""){
136 | instance = conf_instance.trim();
137 | }
138 | if ( conf_batchsize!= null && conf_batchsize!=""){
139 | batchSize = Integer.parseInt(conf_batchsize.trim());
140 | }
141 | if (conf_sleep!= null && conf_sleep!=""){
142 | sleep = Integer.parseInt(conf_sleep.trim());
143 | }
144 | if (conf_dir!= null && conf_dir!=""){
145 | data_dir = conf_dir.trim();
146 | }
147 | if (conf_filename!= null && conf_filename!=""){
148 | canal_binlog_filename = conf_filename.trim();
149 | }
150 | if (conf_print!= null && conf_print!=""){
151 | canal_print = conf_print.trim();
152 | }
153 | if (conf_mq!= null && conf_mq!=""){
154 | canal_mq = conf_mq.trim();
155 | }
156 |
157 | if (conf_rabbitmq_host!= null && conf_rabbitmq_host!=""){
158 | rabbitmq_host = conf_rabbitmq_host.trim();
159 | }
160 | if (conf_rabbitmq_port!= null && conf_rabbitmq_port!=""){
161 | rabbitmq_port = conf_rabbitmq_port.trim();
162 | }
163 | if (conf_rabbitmq_user!= null && conf_rabbitmq_user!=""){
164 | rabbitmq_user = conf_rabbitmq_user.trim();
165 | }
166 | if (conf_rabbitmq_pass!= null && conf_rabbitmq_pass!=""){
167 | rabbitmq_pass = conf_rabbitmq_pass.trim();
168 | }
169 | if (conf_rabbitmq_queuename!= null && conf_rabbitmq_queuename!=""){
170 | rabbitmq_queuename = conf_rabbitmq_queuename.trim();
171 | }
172 | if (conf_rabbitmq_ack!= null && conf_rabbitmq_ack!=""){
173 | rabbitmq_ack = conf_rabbitmq_ack.trim();
174 | }
175 | if (conf_rabbitmq_durable!= null && conf_rabbitmq_durable!=""){
176 | rabbitmq_durable = conf_rabbitmq_durable.trim();
177 | }
178 |
179 | if (conf_redis_port!= null && conf_redis_port!=""){
180 | redis_host = conf_redis_host.trim();
181 | }
182 | if (conf_redis_port!= null && conf_redis_port!=""){
183 | redis_port = conf_redis_port.trim();
184 | }
185 | if (conf_redis_user!= null && conf_redis_user!=""){
186 | redis_user = conf_redis_user.trim();
187 | }
188 | if (conf_redis_pass!= null && conf_redis_pass!=""){
189 | redis_pass = conf_redis_pass.trim();
190 | }
191 | if (conf_redis_queuename!= null && conf_redis_queuename!=""){
192 | redis_queuename = conf_redis_queuename.trim();
193 | }
194 |
195 | rabbitmq_conf = new HashMap();
196 | rabbitmq_conf.put("rabbitmq_host", rabbitmq_host);
197 | rabbitmq_conf.put("rabbitmq_port", rabbitmq_port);
198 | rabbitmq_conf.put("rabbitmq_user", rabbitmq_user);
199 | rabbitmq_conf.put("rabbitmq_pass", rabbitmq_pass);
200 | rabbitmq_conf.put("rabbitmq_queuename", rabbitmq_queuename);
201 | rabbitmq_conf.put("rabbitmq_ack", rabbitmq_ack);
202 | rabbitmq_conf.put("rabbitmq_durable", rabbitmq_durable);
203 |
204 | redis_conf = new HashMap();
205 | redis_conf.put("host", redis_host);
206 | redis_conf.put("port", redis_port);
207 | redis_conf.put("user", redis_user);
208 | redis_conf.put("pass", redis_pass);
209 | redis_conf.put("queuename", redis_queuename);
210 |
211 |
212 | System.out.println("#=====host:"+host+":"+port+ "\r\n#=====instance:"+instance+"\r\n");
213 |
214 | } catch (IOException e) {
215 | e.printStackTrace();
216 | System.out.println("#=====load conf/canal.properties error!");
217 | }
218 |
219 | // 创建链接 (example 为server conf/example配置)
220 | CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress(host, port), instance, "", "");
221 | //int emptyCount = 0;
222 | try {
223 | connector.connect();
224 | connector.subscribe(".*\\..*");
225 | connector.rollback();
226 |
227 | System.out.println("connect success!\r\n startup...");
228 |
229 | while (true){
230 | Message message = connector.getWithoutAck(batchSize); // 获取指定数量的数据
231 | long batchId = message.getId();
232 | int size = message.getEntries().size();
233 | if (batchId == -1 || size == 0) {
234 | //System.out.println("empty count : " + emptyCount);
235 | try {
236 | Thread.sleep(sleep); // 等待时间
237 | } catch (InterruptedException e) {
238 |
239 | }
240 | } else {
241 | //System.out.printf("message[batchId=%s,size=%s] \n", batchId, size);
242 | printEntry(message.getEntries());
243 | }
244 |
245 | connector.ack(batchId); // 提交确认
246 | // connector.rollback(batchId); // 处理失败, 回滚数据
247 | }
248 | //System.out.println("empty too many times, exit");
249 | } finally {
250 | System.out.println("connect error!");
251 | connector.disconnect();
252 | }
253 | }
254 |
255 | private static void printEntry(List entrys) {
256 | SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
257 | String timeStr = df.format(new Date());
258 |
259 | ArrayList dataArray = new ArrayList ();
260 |
261 | //循环每行binlog
262 | for (Entry entry : entrys) {
263 | if (entry.getEntryType() == EntryType.TRANSACTIONBEGIN || entry.getEntryType() == EntryType.TRANSACTIONEND) {
264 | continue;
265 | }
266 |
267 | RowChange rowChage = null;
268 | try {
269 | rowChage = RowChange.parseFrom(entry.getStoreValue());
270 | } catch (Exception e) {
271 | throw new RuntimeException("ERROR ## parser of eromanga-event has an error , data:" + entry.toString(),e);
272 | }
273 |
274 | //单条 binlog sql
275 | EventType eventType = rowChage.getEventType();
276 | /**
277 | System.out.println(String.format("================> binlog[%s:%s] , name[%s,%s] , eventType : %s",
278 | entry.getHeader().getLogfileName(), entry.getHeader().getLogfileOffset(),
279 | entry.getHeader().getSchemaName(), entry.getHeader().getTableName(),
280 | eventType)); **/
281 |
282 | String header_str = "{\"binlog\":\"" + entry.getHeader().getLogfileName()+ ":" + entry.getHeader().getLogfileOffset() + "\"," +
283 | "\"db\":\"" + entry.getHeader().getSchemaName() + "\"," +
284 | "\"table\":\"" + entry.getHeader().getTableName() + "\",";
285 | //受影响 数据行
286 | for (RowData rowData : rowChage.getRowDatasList()) {
287 | String row_str = "\"eventType\":\"" + eventType +"\",";
288 | String before = "\"\"";
289 | String after = "\"\"";
290 |
291 | //获取字段值
292 | if (eventType == EventType.DELETE) {
293 | after = printColumn(rowData.getBeforeColumnsList());
294 | } else if (eventType == EventType.INSERT) {
295 | after = printColumn(rowData.getAfterColumnsList());
296 | } else { //update
297 | //System.out.println("-------> before");
298 | before = printColumn(rowData.getBeforeColumnsList());
299 | //System.out.println("-------> after");
300 | after = printColumn(rowData.getAfterColumnsList());
301 | }
302 |
303 | String row_data = header_str + row_str + "\"before\":" +before + ",\"after\":" + after + ",\"time\":\"" + timeStr +"\"}";
304 | dataArray.add(row_data);
305 | save_data_logs(row_data);
306 | //System.out.println(row_data);
307 | }
308 | }
309 |
310 | // ArrayList TO String[]
311 | String[] strArr = dataArray.toArray(new String[]{});
312 | try {
313 | if(canal_mq.equals("rabbitmq")){
314 | rabbitmq r = new rabbitmq();
315 | r.push_rabbitmq(rabbitmq_conf,strArr);
316 | //push_rabbitmq(strArr);
317 | }else if(canal_mq.equals("redis")){
318 | redis r = new redis();
319 | r.push_redis(redis_conf, strArr);
320 | }else if(canal_mq.equals("kafka")){
321 |
322 | }
323 | } catch (IOException e) {
324 | // TODO Auto-generated catch block
325 | e.printStackTrace();
326 | System.out.println("push "+ canal_mq +" error!");
327 | }
328 | dataArray = null;
329 | }
330 |
331 | // 获取字段 变更 (1、使用map转换为json。 2、使用urlencode。 避免拼接json错误)
332 | private static String printColumn(List columns) {
333 | //String column_str = "";
334 | Map column_map = new HashMap();
335 | for (Column column : columns) {
336 | String column_name = column.getName();
337 | String column_value = column.getValue();
338 |
339 | /**
340 | String a = "";
341 | String b = "";
342 | String c = "";
343 | try {
344 | column_value = new String(column_value.getBytes(),"UTF-8");
345 | a=new String(column.getValue().getBytes("Shift-JIS"),"GBK");
346 | b= new String(column.getValue().getBytes("Shift_JIS"), "GB2312");
347 | c= new String(column.getValue().getBytes("ISO-8859-1"), "UTF-8");
348 |
349 | } catch (UnsupportedEncodingException e) {
350 | // TODO Auto-generated catch block
351 | //e.printStackTrace();
352 | //column_value = column.getValue();
353 | }
354 | System.out.println("column_value:" + column_value + " : "+getEncoding(column_value)+
355 | ", a:"+ a+" : " +getEncoding(a) +
356 | ", b:"+ b+" : " +getEncoding(b) +
357 | ", c:"+ c+" : " +getEncoding(c));
358 | **/
359 | column_map.put(column_name, column_value);
360 | //System.out.println(column.getName() + " : " + column.getValue() + " update=" + column.getUpdated());
361 | }
362 | return JSON.toJSONString(column_map);
363 | }
364 |
365 | //save data file
366 | private static void save_data_logs(String row_data){
367 | if (canal_print.equals("true")){
368 | System.out.println(row_data);
369 | }
370 |
371 | String ts = "yyyyMMdd";
372 | if (canal_binlog_filename.equals("y")){
373 | ts = "yyyy";
374 | }else if (canal_binlog_filename.equals("m")){
375 | ts = "yyyyMM";
376 | }else if (canal_binlog_filename.equals("d")){
377 | ts = "yyyyMMdd";
378 | }else if (canal_binlog_filename.equals("h")){
379 | ts = "yyyyMMddHH";
380 | }else if (canal_binlog_filename.equals("i")){
381 | ts = "yyyyMMddHHmm";
382 | }else{
383 |
384 | }
385 | SimpleDateFormat df2 = new SimpleDateFormat(ts);
386 | String timeStr2 = df2.format(new Date());
387 | String filename = data_dir + "/binlog_" + timeStr2 + ".log";
388 |
389 | FileWriter writer;
390 | try {
391 | writer = new FileWriter(filename, true);
392 | writer.write(row_data + "\r\n");
393 | writer.flush();
394 | writer.close();
395 | } catch (IOException e) {
396 | e.printStackTrace();
397 | System.out.println("write file error!");
398 | }
399 | }
400 |
401 | /**
402 | // 将信息push 到 rabbitmq
403 | private static void push_rabbitmq(String[] argv) throws java.io.IOException{
404 | ConnectionFactory factory = new ConnectionFactory();
405 | factory.setHost(rabbitmq_host);
406 | factory.setPort(Integer.parseInt(rabbitmq_port));
407 | factory.setUsername(rabbitmq_user);
408 | factory.setPassword(rabbitmq_pass);
409 |
410 | Boolean durable = false;
411 | if(rabbitmq_durable.equals("true")) {durable=true;}
412 |
413 | Connection connection = null;
414 | try {
415 | connection = factory.newConnection();
416 | } catch (TimeoutException e1) {
417 | // TODO Auto-generated catch block
418 | e1.printStackTrace();
419 | System.out.println("connection rabbitmq error!");
420 | }
421 | Channel channel = connection.createChannel();
422 | channel.queueDeclare(rabbitmq_queuename, durable, false, false, null);
423 |
424 | //String message = getMessage(argv);
425 | for(int i=0;i conf, String[] argv) throws java.io.IOException{
32 | String rabbitmq_host = conf.get("rabbitmq_host");
33 | int rabbitmq_port = Integer.parseInt(conf.get("rabbitmq_port"));
34 | String rabbitmq_user = conf.get("rabbitmq_user");
35 | String rabbitmq_pass = conf.get("rabbitmq_pass");
36 | String rabbitmq_queuename = conf.get("rabbitmq_queuename");
37 | String rabbitmq_ack = conf.get("rabbitmq_ack");
38 | String rabbitmq_durable = conf.get("rabbitmq_durable");
39 | Boolean durable = false;
40 | if(rabbitmq_durable.equals("true")) {durable=true;}
41 |
42 | ConnectionFactory factory = new ConnectionFactory();
43 | factory.setHost(rabbitmq_host);
44 | factory.setPort(rabbitmq_port);
45 | factory.setUsername(rabbitmq_user);
46 | factory.setPassword(rabbitmq_pass);
47 |
48 | Connection connection = null;
49 | try {
50 | connection = factory.newConnection();
51 | } catch (TimeoutException e1) {
52 | // TODO Auto-generated catch block
53 | e1.printStackTrace();
54 | System.out.println("connection rabbitmq error!");
55 | }
56 | Channel channel = connection.createChannel();
57 | channel.queueDeclare(rabbitmq_queuename, durable, false, false, null);
58 |
59 | //String message = getMessage(argv);
60 | for(int i=0;i conf, String[] argv) throws java.io.IOException {
29 | String host = conf.get("host");
30 | int port = Integer.parseInt(conf.get("port"));
31 | String user = conf.get("user");
32 | String pass = conf.get("pass");
33 | String queuename = conf.get("queuename");
34 |
35 | Jedis jedis = new Jedis(host, port);
36 | for(int i=0;i db
26 | table => 集合
27 | column=> coll
28 |
29 | body={
30 | "binlog": "mysql-bin.000009:1235",
31 | "db": "test",
32 | "table": "users",
33 | "eventType": "UPDATE",
34 | "before": {
35 | "uid": "8",
36 | "username": "duobao153713223"
37 | },
38 | "after": {
39 | "uid": "8",
40 | "username": "duobao153713223"
41 | },
42 | "time": "2016-08-22 17:47:25"
43 | }
44 | '''
45 | def set_data(body, client=None):
46 |
47 | if not body or body=='':
48 | return False
49 |
50 | try:
51 | # 如果是bytes
52 | body = str(body, encoding = "utf-8")
53 | except:
54 | pass
55 | # 这个位置粗略的处理了下单引号json 实际可以再做处理
56 | # 有可能是单引号json
57 | body = body.replace("'", "\"")
58 |
59 | data = json.loads(body)
60 | if isinstance(data, (dict)) == False:
61 | return False
62 |
63 | if 'eventType' in data and 'after' in data and 'db' in data and 'table' in data:
64 |
65 | if not client:
66 | client = Conn()
67 |
68 | mongo_cache_map = config.mongo_cache_map
69 | db = data.get('db')
70 | table = data.get('table')
71 |
72 | # 指定数据库(db)
73 | # dbc = client.(db)
74 | dbc = client.get_database(db)
75 | # 指定集合(表)
76 | # posts = dbc.(table)
77 | posts = dbc.get_collection(table)
78 |
79 | if not posts:
80 | return False
81 |
82 | if data.get('eventType') in ['UPDATE', 'INSERT', 'DELETE'] and isinstance(data.get('after'), (dict)):
83 |
84 | coll = ''; # 唯一字段名
85 | pid = 0; # 值
86 | # 获取更新条件唯一值
87 | if mongo_cache_map.get(db) and mongo_cache_map.get(db).get(table):
88 | coll = mongo_cache_map.get(db).get(table)
89 | pid = data.get('after').get(coll)
90 |
91 | else:
92 | return False
93 |
94 | if data.get('eventType')=='INSERT':
95 | posts.insert( data.get('after') )
96 | # posts.save(data.get('after'))
97 |
98 | elif data.get('eventType')=='UPDATE':
99 | posts.update( { coll:pid } , {'$set': data.get('after') } )
100 |
101 | elif data.get('eventType')=='DELETE':
102 | posts.remove( { coll:pid } )
103 |
104 | # try:
105 | # redisConn.hmset(key, data['after'])
106 | # except:
107 | # conn_redis()
108 | # redisConn.hmset(key, data['after'])
109 | return True
110 |
111 | return False
112 |
113 | '''
114 | body={
115 | "binlog": "mysql-bin.000009:1235",
116 | "db": "test",
117 | "table": "users",
118 | "eventType": "UPDATE",
119 | "before": {
120 | "uid": "8",
121 | "username": "duobao153713223"
122 | },
123 | "after": {
124 | "uid": "8",
125 | "username": "duobao153713223"
126 | },
127 | "time": "2016-08-22 17:47:25"
128 | }
129 | body = json.dumps(body)
130 | set_data(body)
131 | '''
132 |
133 |
134 |
135 |
136 |
137 |
138 |
--------------------------------------------------------------------------------
/python_sync_nosql/sync_redis.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | # @author: liukelin 314566990@qq.com
4 |
5 | '''
6 |
7 | 消费 数据 写入到redis
8 |
9 | 没用上 conn_redis 重用。。。以后再补了
10 | '''
11 | import os
12 | import config
13 | import redis
14 | import json
15 |
16 |
17 | # redisConn=False
18 | def conn_redis():
19 | redis_password = if config.get('redis_password') else ''
20 | conf = {
21 | "host": config.redis_host,
22 | "port": config.redis_port,
23 | "password": redis_password,
24 | "db": 0
25 | }
26 | # pool = redis.ConnectionPool(**conf)
27 | # redisConn = redis.Redis(ConnectionPool=pool)
28 | redisConn = redis.Redis(**conf)
29 | # print(redisConn, redisConn.ping())
30 | return redisConn
31 |
32 | '''
33 | ·将数据写入到redis
34 | 这里的demo是将表数据映射到 redis hash 结构,key=db:table:primary_id
35 | body={
36 | "binlog": "mysql-bin.000009:1235",
37 | "db": "test",
38 | "table": "users",
39 | "eventType": "UPDATE",
40 | "before": {
41 | "uid": "8",
42 | "username": "duobao153713223"
43 | },
44 | "after": {
45 | "uid": "8",
46 | "username": "duobao153713223"
47 | },
48 | "time": "2016-08-22 17:47:25"
49 | }
50 | '''
51 | def set_data(body, redisConn=None):
52 | if not body or body=='':
53 | return False
54 |
55 | try:
56 | # 如果是bytes
57 | body = str(body, encoding = "utf-8")
58 | except:
59 | pass
60 |
61 | # 这个位置粗略的处理了下单引号json 实际可以再做处理
62 | # 有可能是单引号json
63 | body = body.replace("'", "\"")
64 |
65 | data = json.loads(body)
66 | if isinstance(data, (dict)) == False:
67 | return False
68 | print(data)
69 |
70 | if 'eventType' in data and 'after' in data and 'db' in data and 'table' in data:
71 |
72 | redis_cache_map = config.redis_cache_map
73 | db = data.get('db')
74 | table = data.get('table')
75 |
76 |
77 | if redis_cache_map.get(db) and redis_cache_map.get(db).get(table):
78 | key = "%s_%s_%s" %(db, table, data.get('after').get(redis_cache_map.get(db).get(table)) )
79 | else:
80 | return False
81 |
82 | if data.get('eventType') in ['UPDATE', 'INSERT', 'DELETE'] and data.get('after') and isinstance(data.get('after'), (dict)):
83 |
84 | if not redisConn:
85 | redisConn = conn_redis()
86 |
87 | if data.get('eventType')=='INSERT':
88 |
89 | redisConn.hmset(key, data.get('after'))
90 |
91 | elif data.get('eventType')=='UPDATE':
92 |
93 | redisConn.hmset(key, data.get('after'))
94 |
95 | elif data.get('eventType')=='DELETE':
96 |
97 | redisConn.delete(key)
98 |
99 | # try:
100 | # redisConn.hmset(key, data['after'])
101 | # except:
102 | # conn_redis()
103 | # redisConn.hmset(key, data['after'])
104 | return True
105 |
106 | return False
107 |
108 | if __name__=="__main__":
109 | strs = """{'binlog': 'mysql-bin.000007:5078', 'db': 'zhou', 'table': 'uc_admin', 'eventType': 'UPDATE', 'before': {'create_id': '', 'create_time': '0000-00-00 00:00:00.000', 'id': '9', 'language_code': '', 'password': 'e10adc3949ba59abbe56e057f20f883e', 'status': '1', 'update_id': '', 'update_time': '0000-00-00 00:00:00.000', 'username': 'aa'}, 'after': {'create_id': '', 'create_time': '0000-00-00 00:00:00.000', 'id': '9', 'language_code': '', 'password': 'e10adc3949ba59abbe56e057f20f883e', 'status': '1', 'update_id': '', 'update_time': '0000-00-00 00:00:00.000', 'username': 'aab'}, 'time': '2017-11-07 21:24:56'}"""
110 | set_data(strs)
111 | # conn_redis()
112 |
113 | '''
114 | {
115 | "binlog":"mysql-bin.000027:258",
116 | "db":"test",
117 | "table":"user",
118 | "eventType":"UPDATE",
119 | "before":{"id":"4","name":"user1"},
120 | "after":{"id":"4","name":"user2"},
121 | "time":"2016-09-09 10:43:48"}
122 |
123 | {
124 | "binlog":"mysql-bin.000027:258",
125 | "db":"test",
126 | "table":"user",
127 | "eventType":"INSERT",
128 | "after":{"id":"4","name":"user1"},
129 | "time":"2016-09-09 10:43:48"}
130 |
131 | {
132 | "binlog":"mysql-bin.000027:258",
133 | "db":"test",
134 | "table":"user",
135 | "eventType":"DELETE",
136 | "after":{"id":"4","name":"user2"},
137 | "time":"2016-09-09 10:43:48"}
138 | '''
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
--------------------------------------------------------------------------------