From e3fc6059c4598916146cace196b6290ef2793781 Mon Sep 17 00:00:00 2001 From: agapple Date: Mon, 18 Feb 2013 06:34:12 -0800 Subject: [PATCH 01/11] Create gh-pages branch via GitHub --- images/bkg.png | Bin 0 -> 4261 bytes images/blacktocat.png | Bin 0 -> 1266 bytes index.html | 441 +++++++++++++++++++++++++++++++++++ javascripts/main.js | 1 + params.json | 1 + stylesheets/pygment_trac.css | 68 ++++++ stylesheets/stylesheet.css | 247 ++++++++++++++++++++ 7 files changed, 758 insertions(+) create mode 100644 images/bkg.png create mode 100644 images/blacktocat.png create mode 100644 index.html create mode 100644 javascripts/main.js create mode 100644 params.json create mode 100644 stylesheets/pygment_trac.css create mode 100644 stylesheets/stylesheet.css diff --git a/images/bkg.png b/images/bkg.png new file mode 100644 index 0000000000000000000000000000000000000000..fcebb5b22999adb792afee1f74bcf4af91db8441 GIT binary patch literal 4261 zcmai24OG(S8mBsUyM3)~ZEIE-v#qwa1Vm(_WaZcF4$Tzl!!$x9YVt?^Am9ho*2lIQ zTie~ak)qq&rWX>8T6nEuDuzAQq;*m$U_enEjIf_wTdeFYT{JoQD5|@shXlvVcOKfW~DY zHWUE68K|@(8k4bwK`Rgy8yWEk#GD9L_Ez3j^7}+OP!vh??}-!^abRx*B0fRLq0#p- zc&ObBCW}pqaOzqkP^+4>M6iip9W9{ohX*|-IT#4SlFk8;5lDPj+g$04H80)Ze>fQtmU zOf)t&Hr5{_DrzmfV=X9R^Jv1gY;a{DLJ|X{b6Ff73t*%C5ox;tK93XuBb_-y5$ADO zHu$JaaKX?*8V8My#Q2XC*hnUa-da@jcsIz~!gzxBse!?45r=`^!TNt2HYP=5@QE9vlA}}JO%C`b0d#&5gUt*07CiBd4(jWt zLJWhZKp#6>gL!$$qVD|Wzk7)}V zkBQGx@@9q_1yr6q017Ty@?&EzVH&nI8Gv!-^x=W~}{IQz$!k{zA98=Ti? z9cVfF`8lawJL&Vf$95Qs%Nvf1vh7mQMX#$ul8xKfXp?90UsgyjF)tIRCdvlh?KrbH zv$~{=QZbg@;?kDY?!_;sOnP!E$WC_y*L!11kJ$Bv5P=FueEclp=B)kG%NZ+7YOGoP zR-EH7=(Ixzm4*4r!U~*|*f}&ewbAzZg$rV7yy&TpH)pNSQqOg{CC4>}G40e2em2s* zKCkIZr2DpNqSZ@AS6r{w4k}39fVJzZXAy0G=<@V(rsuz5bVoeL zq+fMF{HYOh*>$N+ro3`@%}CQfL>5o_WPYe}`@$iaZYz~6GhYY36SpGGi3vX>Q!if_ zW9q{cdi5&xHzBM2OL>Yxeo@({8F}vGo)?H9zB*<}?_KK~<>Ca;lrUut0s0QfGvOOQ zX|CGP-t&oefY|ZSCUqIdWgV^RD9OM76AmFBk^+y@7AoJF~M91Md>YK-Oz2RcP>?)IpVnVhM_99yGk?`*T+XD zwBp#*t06#hT90_d;P)Bbq_WluNSRZ6dv$X^6I1e|_Ex>3C+}~eO6?xaH@&~Jr03}Q z0ll4KqB|?}$cD=Fj)(0>75P8jfk6WSLIxx#!0NJlW3XW4xh2*vK>a`=aGXCgp^dBC zWbsw8pkoUCTuEOjesHP=HT~O?9Z4p1!un#t@AR82drx@}=8y;C3Sro$7oR@K z@IaRLzRu~|mYn_qwVlpXQ4GdGlX91Qv1*s4X`3a?xBTl+6gjVY8?3u26I5R*7URK1 zE=Ac&mvpCFg3I5*AIz?JhVVgKZ%aQcCA1(@d7`&1KFgm|=7IU)9eu`jukwd}^DxEQ zG;~TIDm9)ab}d=d-5BROfvdYfco-jDRg|~dWgdF4?0Ij6Qg{#XJ0ZZVfM`CND7eoe z%RgC$+LKp`BM6{vjBl?JHK}!kDD{<2amH)l>0X#6e?t7&y3fzGT8is#^2aMbRTCk; zJ(sUXP5}^>+6yzxOEHYVr)^ywMD4BNP?p-pCJgbFKiOU??lf*qwM^j7+rsdqkb`_< zu8XfLVd}?QOz?^->D^mk<}$@nMGhUVuWEj*zsW&W*LTpG#BI`{j3U;d25*I>wQU<%>f=gRb zoB3nzuQZE1VsAI0q9Fu1XeEnWrw61}#&RdiUgeSC)MCg}l56y>n~zJy4~!oRrSk?+ z^|z_7X!7ri<33}WCtfkPM{XaK)*O=Vv)t7yh&CgA{MMuALtvt3t{B)Nx2!U`E3tjf z`teF@drzmAWoULxts_H3G-*w!78UQbn(XfG6=khyzw@p2!)9aEeaHEud<#kD>9RSO zv4-4-Lmar@`!;{Jr^WQv-8GcOR=etVt>w3~j-~BA%P8I!s&Ds3L;i|4t7Purz>$NZ zLdX8z;(M{+ihDk&gD&7dmuocXNUmXK-XYTv4@Mt2?u>v1tjpHoom4}Y)wInK=54oY zKa}v{yU`|fgO;#gLyBgiBOG!z#3@XkTfXu8c}+PK=2_c1mzO*scsQco@tHBs5OfZs z$8NNh_v)za2hh3d!{9GC=2^!u6$x6F3k_b)0HLi@ubw9%xBZ3Ommvims*{jB*0zSC zJ49_LfLb#7dYNlHqdQGMY}V}ji|0&vzYZYi^bR?WnGau-m&DBM#vB>^$Z&(5&gG#x zJ`xV$h>ipCjn@`3+01KQ+utac5>+Ws#n+nJvi`lWegz$?!qujlbLb{{(_)p_l1Z+( zdM>W^kZsfPn#(4ql5bnklzKaaX49VdUT_o$9aG z(}gb@7Cr`Hs63vrG-d~*-Ew3l)s6YthL%Ht*{ ze%1=aHvnw3uQBA!TwFcao}XkUs49=`tQ%O)vYY}elzL*VuTX(=8u_V>j)}r>S4Uh~ zh>GVehE`=Smd4(neDmas=pLEo)|t(XUs77pn;QoN(54u9Jv>@Lm8^V);kZT#8jv3` z&GiE#r51T-G5W5_uCs-YQIy9$Bn^A%)S!H7$u7ib&EeDgKaH>T|KlJnIXy|Wao30c E2cLQQbpQYW literal 0 HcmV?d00001 diff --git a/images/blacktocat.png b/images/blacktocat.png new file mode 100644 index 0000000000000000000000000000000000000000..273d5710a2e0968d77584ad073e4a089fbfd7e68 GIT binary patch literal 1266 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxKsVXI%s|1+P|wiV z#N6CmN5ROz&_Lh7NZ-&%*U;R`*vQJjKmiJrfVLH-q*(>IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8EkR}&8R-I5=oVMzl_XZ^<`pZ$OmImpPA){ffi_eM3D1{oGuTzrd=COM+4n&cLd=IHa;5RX-@T zIKQ+g85kdF$}r8qu)}W=NFmTQR{lkqz(`5Vami0E%}vcK@pQ3O0?O#6WTsdd7+V+^ z8k(CJm>U_GSr{5xm>asenYlPSS~yx7IT;(l%)qAC#mvab6&Utr2Bs#4hOUN|=4MVV zCI+U?PDZXKZWgXEy`Fi+C5d^-sW5vpf%Zc6n&H)JD;QCfH z^og8&1M`mKzE)l;d=qjrb~vvJSy|Mouyo1dg^ni6?thVCs0yAf`Q@5aq}s+K9`V=C z{rP`=v-NB<-^nFOYBRV_`Rx;uVYQy^)9a}ALqMd-xJi4fVcRLrrgF#H^(z_`gmYNg z8`ZC~^QU(&GySnBKw|Qotw~8oCN7?p;&T4i?d&@L$Gl%$8&-dv`S`$=`T36X{>ZDw zT=kLd?0S^caPZI_ua^-qKl~;=EWB<}?CV->TJO$Qp2Pd-+ + + + + + + + + + + Canal by otter-projects + + + + +
+
+

Canal

+

阿里巴巴mysql数据库binlog的增量订阅&消费组件

+ +
+ Download as .zip + Download as .tar.gz + View on GitHub +
+
+
+ +
+
+
+
+

+

背景

+

早期,阿里巴巴B2B公司因为存在杭州和美国双机房部署,存在跨机房同步的业务需求。不过早期的数据库同步业务,主要是基于trigger的方式获取增量变更,不过从2010年开始,阿里系公司开始逐步的尝试基于数据库的日志解析,获取增量变更进行同步,由此衍生出了增量订阅&消费的业务,从此开启了一段新纪元。ps. 目前内部使用的同步,已经支持mysql5.x和oracle部分版本的日志解析

+

+

基于日志增量订阅&消费支持的业务:

+
    +
  1. 数据库镜像
  2. +
  3. 数据库实时备份
  4. +
  5. 多级索引 (卖家和买家各自分库索引)
  6. +
  7. search build
  8. +
  9. 业务cache刷新
  10. +
  11. 价格变化等重要业务消息
  12. +
+

项目介绍

+

名称:canal [kə'næl]

+

译意: 水道/管道/沟渠

+

语言: 纯java开发

+

定位: 基于数据库增量日志解析,提供增量数据订阅&消费,目前主要支持了mysql

+

+

工作原理

+

mysql主备复制实现

+


从上层来看,复制分成三步:

+
    +
  1. master将改变记录到二进制日志(binary log)中(这些记录叫做二进制日志事件,binary log events,可以通过show binlog events进行查看);
  2. +
  3. slave将master的binary log events拷贝到它的中继日志(relay log);
  4. +
  5. slave重做中继日志中的事件,将改变反映它自己的数据。
  6. +
+

canal的工作原理:

+

+

原理相对比较简单:

+
    +
  1. canal模拟mysql slave的交互协议,伪装自己为mysql slave,向mysql master发送dump协议
  2. +
  3. mysql master收到dump请求,开始推送binary log给slave(也就是canal)
  4. +
  5. canal解析binary log对象(原始为byte流)
  6. +
+

架构

+

+

说明:

+
    +
  • server代表一个canal运行实例,对应于一个jvm
  • +
  • instance对应于一个数据队列 (1个server对应1..n个instance)
  • +
+

instance模块:

+
    +
  • eventParser (数据源接入,模拟slave协议和master进行交互,协议解析)
  • +
  • eventSink (Parser和Store链接器,进行数据过滤,加工,分发的工作)
  • +
  • eventStore (数据存储)
  • +
  • metaManager (增量订阅&消费信息管理器)
  • +
+

数据对象格式:EntryProtocol.proto +

+
Entry
+    Header
+        logfileName [binlog文件名]
+        logfileOffset [binlog position]
+        executeTime [发生的变更]
+        schemaName 
+        tableName
+        eventType [insert/update/delete类型]
+    entryType   [事务头BEGIN/事务尾END/数据ROWDATA]
+    storeValue  [byte数据,可展开,对应的类型为RowChange]
+    
+RowChange
+    isDdl       [是否是ddl变更操作,比如create table/drop table]
+    sql     [具体的ddl sql]
+    rowDatas    [具体insert/update/delete的变更数据,可为多条,1个binlog event事件可对应多条变更,比如批处理]
+        beforeColumns [Column类型的数组]
+        afterColumns [Column类型的数组]
+        
+Column 
+    index       
+    sqlType     [jdbc type]
+    name        [column name]
+    isKey       [是否为主键]
+    updated     [是否发生过变更]
+    isNull      [值是否为null]
+    value       [具体的内容,注意为文本]
+

说明:

+
    +
  • 可以提供数据库变更前和变更后的字段内容,针对binlog中没有的name,isKey等信息进行补全
  • +
  • 可以提供ddl的变更语句
  • +
+

QuickStart

+

几点说明:(mysql初始化)

+

a. canal的原理是基于mysql binlog技术,所以这里一定需要开启mysql的binlog写入功能,并且配置binlog模式为row.

+
[mysqld]
+log-bin=mysql-bin #添加这一行就ok
+binlog-format=ROW #选择row模式
+server_id=1 #配置mysql replaction需要定义,不能和canal的slaveId重复
+b. canal的原理是模拟自己为mysql slave,所以这里一定需要做为mysql slave的相关权限.
+
+
CREATE USER canal IDENTIFIED BY 'canal';  
+GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';
+-- GRANT ALL PRIVILEGES ON *.* TO 'canal'@'%' ;
+FLUSH PRIVILEGES;
+

针对已有的账户可通过grants查询权限:

+

启动步骤:

+

1. 下载canal

+

下载部署包

+
wget http://canal4mysql.googlecode.com/files/canal.deployer-1.0.0.tar.gz
+

or

+

自己编译

+
git clone git@github.com:otter-projects/canal.git
+cd canal; 
+mvn clean install -Dmaven.test.skip -Denv=release
+

编译完成后,会在根目录下产生target/canal.deployer-$version.tar.gz

+

+

2. 解压缩

+
mkdir /tmp/canal
+tar zxvf canal.deployer-1.0.0.tar.gz  -C /tmp/canal
+

+

解压完成后,进入/tmp/canal目录,可以看到如下结构:

+

+
drwxr-xr-x 2 jianghang jianghang  136 2013-02-05 21:51 bin
+drwxr-xr-x 4 jianghang jianghang  160 2013-02-05 21:51 conf
+drwxr-xr-x 2 jianghang jianghang 1.3K 2013-02-05 21:51 lib
+drwxr-xr-x 2 jianghang jianghang   48 2013-02-05 21:29 logs
+

+

3. 配置修改

+

+

公用参数:

+
vi conf/canal.properties
+
#################################################
+#########               common argument         ############# 
+#################################################
+canal.id= 1
+canal.address=
+canal.port= 11111
+canal.zkServers=
+# flush data to zk
+canal.zookeeper.flush.period = 1000
+## memory store RingBuffer size, should be Math.pow(2,n)
+canal.instance.memory.buffer.size = 32768
+
+## detecing config
+canal.instance.detecting.enable = false
+canal.instance.detecting.sql = insert into retl.xdual values(1,now()) on duplicate key update x=now()
+canal.instance.detecting.interval.time = 3 
+canal.instance.detecting.retry.threshold = 3 
+canal.instance.detecting.heartbeatHaEnable = false
+
+# support maximum transaction size, more than the size of the transaction will be cut into multiple transactions delivery
+canal.instance.transactionn.size =  1024
+
+# network config
+canal.instance.network.receiveBufferSize = 16384
+canal.instance.network.sendBufferSize = 16384
+canal.instance.network.soTimeout = 30
+
+#################################################
+#########               destinations            ############# 
+#################################################
+canal.destinations= example
+
+canal.instance.global.mode = spring 
+canal.instance.global.lazy = true  ##修改为false,代表立马启动
+#canal.instance.global.manager.address = 127.0.0.1:1099
+canal.instance.global.spring.xml = classpath:spring/memory-instance.xml
+#canal.instance.global.spring.xml = classpath:spring/default-instance.xml
+

+

应用参数:

+
vi conf/example/instance.properties
+
#################################################
+## mysql serverId
+canal.instance.mysql.slaveId = 1234
+
+# position info
+canal.instance.master.address = 127.0.0.1:3306 #改成自己的数据库地址
+canal.instance.master.journal.name = 
+canal.instance.master.position = 
+canal.instance.master.timestamp = 
+
+#canal.instance.standby.address = 
+#canal.instance.standby.journal.name =
+#canal.instance.standby.position = 
+#canal.instance.standby.timestamp = 
+
+# username/password
+canal.instance.dbUsername = retl  #改成自己的数据库信息
+canal.instance.dbPassword = retl  #改成自己的数据库信息
+canal.instance.defaultDatabaseName =   #改成自己的数据库信息
+canal.instance.connectionCharsetNumber = 33  #改成自己的数据库信息
+canal.instance.connectionCharset = UTF-8  #改成自己的数据库信息
+
+# table regex
+canal.instance.filter.regex = .*\\..*
+
+#################################################
+
+

+

+

说明:

+
    +
  • canal.instance.connectionCharset 代表数据库的编码方式对应到java中的编码类型,比如UTF-8,GBK , ISO-8859-1
  • +
  • canal.instance.connectionCharsetNumber 代表数据库的编码方式对应mysql中的唯一id,详细的映射关系可查看:com.mysql.jdbc.CharsetMapping.INDEX_TO_CHARSET
    针对常见的编码:
    utf-8 <=> 33
    gb2312 <=> 24
    gbk <=> 28
  • +
+

4. 准备启动

+

+
sh bin/startup.sh
+

+

5. 查看日志

+
vi logs/canal/canal.log
+
2013-02-05 22:45:27.967 [main] INFO  com.alibaba.otter.canal.deployer.CanalLauncher - ## start the canal server.
+2013-02-05 22:45:28.113 [main] INFO  com.alibaba.otter.canal.deployer.CanalController - ## start the canal server[10.1.29.120:11111]
+2013-02-05 22:45:28.210 [main] INFO  com.alibaba.otter.canal.deployer.CanalLauncher - ## the canal server is running now ......
+

+

具体instance的日志:

+
vi logs/example/example.log
+
2013-02-05 22:50:45.636 [main] INFO  c.a.o.c.i.spring.support.PropertyPlaceholderConfigurer - Loading properties file from class path resource [canal.properties]
+2013-02-05 22:50:45.641 [main] INFO  c.a.o.c.i.spring.support.PropertyPlaceholderConfigurer - Loading properties file from class path resource [example/instance.properties]
+2013-02-05 22:50:45.803 [main] INFO  c.a.otter.canal.instance.spring.CanalInstanceWithSpring - start CannalInstance for 1-example 
+2013-02-05 22:50:45.810 [main] INFO  c.a.otter.canal.instance.spring.CanalInstanceWithSpring - start successful....
+

+

6. 关闭

+
sh bin/stop.sh
+

+

it's over.

+
+ +

ClientExample

+ +

依赖配置:(目前暂未正式发布到mvn仓库,所以需要各位下载canal源码后手工执行下mvn clean install -Dmaven.test.skip)

+ +
<dependency>
+    <groupId>com.alibaba.otter</groupId>
+    <artifactId>canal.client</artifactId>
+    <version>1.0.0</version>
+</dependency>
+ +

+ +

1. 创建mvn标准工程:

+ +
mvn archetype:create -DgroupId=com.alibaba.otter -DartifactId=canal.sample
+ +

+ +

2. 修改pom.xml,添加依赖

+ +

+ +

3. ClientSample代码

+ +
package com.alibaba.otter.canal.sample;
+
+import java.net.InetSocketAddress;
+import java.util.List;
+
+import com.alibaba.otter.canal.common.utils.AddressUtils;
+import com.alibaba.otter.canal.protocol.Message;
+import com.alibaba.otter.canal.protocol.CanalEntry.Column;
+import com.alibaba.otter.canal.protocol.CanalEntry.Entry;
+import com.alibaba.otter.canal.protocol.CanalEntry.EntryType;
+import com.alibaba.otter.canal.protocol.CanalEntry.EventType;
+import com.alibaba.otter.canal.protocol.CanalEntry.RowChange;
+import com.alibaba.otter.canal.protocol.CanalEntry.RowData;
+
+public class SimpleCanalClientExample {
+
+    public static void main(String args[]) {
+        // 创建链接
+        CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress(AddressUtils.getHostIp(),
+                                                                                            11111), "example", "", "");
+        int batchSize = 1000;
+        int emptyCount = 0;
+        try {
+            connector.connect();
+            connector.subscribe(".*\\..*");
+            connector.rollback();
+            int totalEmtryCount = 120;
+            while (emptyCount < totalEmtryCount) {
+                Message message = connector.getWithoutAck(batchSize); // 获取指定数量的数据
+                long batchId = message.getId();
+                int size = message.getEntries().size();
+                if (batchId == -1 || size == 0) {
+                    emptyCount++;
+                    System.out.println("empty count : " + emptyCount);
+                    try {
+                        Thread.sleep(1000);
+                    } catch (InterruptedException e) {
+                    }
+                } else {
+                    emptyCount = 0;
+                    // System.out.printf("message[batchId=%s,size=%s] \n", batchId, size);
+                    printEntry(message.getEntries());
+                }
+
+                connector.ack(batchId); // 提交确认
+                // connector.rollback(batchId); // 处理失败, 回滚数据
+            }
+
+            System.out.println("empty too many times, exit");
+        } finally {
+            connector.disconnect();
+        }
+    }
+
+    private static void printEntry(List<Entry> entrys) {
+        for (Entry entry : entrys) {
+            if (entry.getEntryType() == EntryType.TRANSACTIONBEGIN || entry.getEntryType() == EntryType.TRANSACTIONEND) {
+                continue;
+            }
+
+            RowChange rowChage = null;
+            try {
+                rowChage = RowChange.parseFrom(entry.getStoreValue());
+            } catch (Exception e) {
+                throw new RuntimeException("ERROR ## parser of eromanga-event has an error , data:" + entry.toString(),
+                                           e);
+            }
+
+            EventType eventType = rowChage.getEventType();
+            System.out.println(String.format("================> binlog[%s:%s] , name[%s,%s] , eventType : %s",
+                                             entry.getHeader().getLogfileName(), entry.getHeader().getLogfileOffset(),
+                                             entry.getHeader().getSchemaName(), entry.getHeader().getTableName(),
+                                             eventType));
+
+            for (RowData rowData : rowChage.getRowDatasList()) {
+                if (eventType == EventType.DELETE) {
+                    printColumn(rowData.getBeforeColumnsList());
+                } else if (eventType == EventType.INSERT) {
+                    printColumn(rowData.getAfterColumnsList());
+                } else {
+                    System.out.println("-------> before");
+                    printColumn(rowData.getBeforeColumnsList());
+                    System.out.println("-------> after");
+                    printColumn(rowData.getAfterColumnsList());
+                }
+            }
+        }
+    }
+
+    private static void printColumn(List<Column> columns) {
+        for (Column column : columns) {
+            System.out.println(column.getName() + " : " + column.getValue() + "    update=" + column.getUpdated());
+        }
+    }
+}
+ +

+ +

4. 运行Client

+ +

首先启动Canal Server,可参加QuickStart : http://agapple.iteye.com/blogs/1796070

+ +

启动Canal Client后,可以从控制台从看到类似消息:

+ +
empty count : 1
+empty count : 2
+empty count : 3
+empty count : 4
+ +

此时代表当前数据库无变更数据

+ +

+ +

5. 触发数据库变更

+ +
mysql> use test;
+Database changed
+mysql> CREATE TABLE `xdual` (
+    ->   `ID` int(11) NOT NULL AUTO_INCREMENT,
+    ->   `X` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    ->   PRIMARY KEY (`ID`)
+    -> ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 ;
+Query OK, 0 rows affected (0.06 sec)
+
+mysql> insert into xdual(id,x) values(null,now());Query OK, 1 row affected (0.06 sec)
+ +

+ +

可以从控制台中看到:

+ +
empty count : 1
+empty count : 2
+empty count : 3
+empty count : 4
+================> binlog[mysql-bin.001946:313661577] , name[test,xdual] , eventType : INSERT
+ID : 4    update=true
+X : 2013-02-05 23:29:46    update=true
+ +

+ +

最后:

+ +

整个代码在附件中可以下载,如有问题可及时联系。

+ +

+
+ +

+canal.sample.tar.gz (2.2 KB) +
+
+
+ + + + + + \ No newline at end of file diff --git a/javascripts/main.js b/javascripts/main.js new file mode 100644 index 00000000..d8135d37 --- /dev/null +++ b/javascripts/main.js @@ -0,0 +1 @@ +console.log('This would be the main JS file.'); diff --git a/params.json b/params.json new file mode 100644 index 00000000..943f1d68 --- /dev/null +++ b/params.json @@ -0,0 +1 @@ +{"name":"Canal","tagline":"阿里巴巴mysql数据库binlog的增量订阅&消费组件","body":"
\r\n
\r\n

\r\n

背景

\r\n

早期,阿里巴巴B2B公司因为存在杭州和美国双机房部署,存在跨机房同步的业务需求。不过早期的数据库同步业务,主要是基于trigger的方式获取增量变更,不过从2010年开始,阿里系公司开始逐步的尝试基于数据库的日志解析,获取增量变更进行同步,由此衍生出了增量订阅&消费的业务,从此开启了一段新纪元。ps. 目前内部使用的同步,已经支持mysql5.x和oracle部分版本的日志解析

\r\n

\r\n

基于日志增量订阅&消费支持的业务:

\r\n
    \r\n
  1. 数据库镜像
  2. \r\n
  3. 数据库实时备份
  4. \r\n
  5. 多级索引 (卖家和买家各自分库索引)
  6. \r\n
  7. search build
  8. \r\n
  9. 业务cache刷新
  10. \r\n
  11. 价格变化等重要业务消息
  12. \r\n
\r\n

项目介绍

\r\n

名称:canal [kə'næl]

\r\n

译意: 水道/管道/沟渠

\r\n

语言: 纯java开发

\r\n

定位: 基于数据库增量日志解析,提供增量数据订阅&消费,目前主要支持了mysql

\r\n

\r\n

工作原理

\r\n

mysql主备复制实现

\r\n

\"\"
从上层来看,复制分成三步:

\r\n
    \r\n
  1. master将改变记录到二进制日志(binary log)中(这些记录叫做二进制日志事件,binary log events,可以通过show binlog events进行查看);
  2. \r\n
  3. slave将master的binary log events拷贝到它的中继日志(relay log);
  4. \r\n
  5. slave重做中继日志中的事件,将改变反映它自己的数据。
  6. \r\n
\r\n

canal的工作原理:

\r\n

\"\"

\r\n

原理相对比较简单:

\r\n
    \r\n
  1. canal模拟mysql slave的交互协议,伪装自己为mysql slave,向mysql master发送dump协议
  2. \r\n
  3. mysql master收到dump请求,开始推送binary log给slave(也就是canal)
  4. \r\n
  5. canal解析binary log对象(原始为byte流)
  6. \r\n
\r\n

架构

\r\n

\"\"

\r\n

说明:

\r\n
    \r\n
  • server代表一个canal运行实例,对应于一个jvm
  • \r\n
  • instance对应于一个数据队列 (1个server对应1..n个instance)
  • \r\n
\r\n

instance模块:

\r\n
    \r\n
  • eventParser (数据源接入,模拟slave协议和master进行交互,协议解析)
  • \r\n
  • eventSink (Parser和Store链接器,进行数据过滤,加工,分发的工作)
  • \r\n
  • eventStore (数据存储)
  • \r\n
  • metaManager (增量订阅&消费信息管理器)
  • \r\n
\r\n

数据对象格式:EntryProtocol.proto\r\n

\r\n
Entry\r\n    Header\r\n\t\tlogfileName [binlog文件名]\r\n\t\tlogfileOffset [binlog position]\r\n\t\texecuteTime [发生的变更]\r\n\t\tschemaName \r\n\t\ttableName\r\n\t\teventType [insert/update/delete类型]\r\n\tentryType \t[事务头BEGIN/事务尾END/数据ROWDATA]\r\n\tstoreValue \t[byte数据,可展开,对应的类型为RowChange]\r\n\t\r\nRowChange\r\n\tisDdl\t\t[是否是ddl变更操作,比如create table/drop table]\r\n\tsql\t\t[具体的ddl sql]\r\n\trowDatas\t[具体insert/update/delete的变更数据,可为多条,1个binlog event事件可对应多条变更,比如批处理]\r\n\t\tbeforeColumns [Column类型的数组]\r\n\t\tafterColumns [Column类型的数组]\r\n\t\t\r\nColumn \r\n\tindex\t\t\r\n\tsqlType\t\t[jdbc type]\r\n\tname\t\t[column name]\r\n\tisKey\t\t[是否为主键]\r\n\tupdated\t\t[是否发生过变更]\r\n\tisNull\t\t[值是否为null]\r\n\tvalue\t\t[具体的内容,注意为文本]
\r\n

说明:

\r\n
    \r\n
  • 可以提供数据库变更前和变更后的字段内容,针对binlog中没有的name,isKey等信息进行补全
  • \r\n
  • 可以提供ddl的变更语句
  • \r\n
\r\n

QuickStart

\r\n

几点说明:(mysql初始化)

\r\n

a. canal的原理是基于mysql binlog技术,所以这里一定需要开启mysql的binlog写入功能,并且配置binlog模式为row.

\r\n
[mysqld]\r\nlog-bin=mysql-bin #添加这一行就ok\r\nbinlog-format=ROW #选择row模式\r\nserver_id=1 #配置mysql replaction需要定义,不能和canal的slaveId重复
\r\nb. canal的原理是模拟自己为mysql slave,所以这里一定需要做为mysql slave的相关权限.
\r\n
\r\n
CREATE USER canal IDENTIFIED BY 'canal';  \r\nGRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';\r\n-- GRANT ALL PRIVILEGES ON *.* TO 'canal'@'%' ;\r\nFLUSH PRIVILEGES;
\r\n

针对已有的账户可通过grants查询权限:

\r\n

启动步骤:

\r\n

1. 下载canal

\r\n

下载部署包

\r\n
wget http://canal4mysql.googlecode.com/files/canal.deployer-1.0.0.tar.gz
\r\n

or

\r\n

自己编译

\r\n
git clone git@github.com:otter-projects/canal.git\r\ncd canal; \r\nmvn clean install -Dmaven.test.skip -Denv=release
\r\n

编译完成后,会在根目录下产生target/canal.deployer-$version.tar.gz

\r\n

\r\n

2. 解压缩

\r\n
mkdir /tmp/canal\r\ntar zxvf canal.deployer-1.0.0.tar.gz  -C /tmp/canal
\r\n

\r\n

解压完成后,进入/tmp/canal目录,可以看到如下结构:

\r\n

\r\n
drwxr-xr-x 2 jianghang jianghang  136 2013-02-05 21:51 bin\r\ndrwxr-xr-x 4 jianghang jianghang  160 2013-02-05 21:51 conf\r\ndrwxr-xr-x 2 jianghang jianghang 1.3K 2013-02-05 21:51 lib\r\ndrwxr-xr-x 2 jianghang jianghang   48 2013-02-05 21:29 logs
\r\n

\r\n

3. 配置修改

\r\n

\r\n

公用参数:

\r\n
vi conf/canal.properties
\r\n
#################################################\r\n#########               common argument         ############# \r\n#################################################\r\ncanal.id= 1\r\ncanal.address=\r\ncanal.port= 11111\r\ncanal.zkServers=\r\n# flush data to zk\r\ncanal.zookeeper.flush.period = 1000\r\n## memory store RingBuffer size, should be Math.pow(2,n)\r\ncanal.instance.memory.buffer.size = 32768\r\n\r\n## detecing config\r\ncanal.instance.detecting.enable = false\r\ncanal.instance.detecting.sql = insert into retl.xdual values(1,now()) on duplicate key update x=now()\r\ncanal.instance.detecting.interval.time = 3 \r\ncanal.instance.detecting.retry.threshold = 3 \r\ncanal.instance.detecting.heartbeatHaEnable = false\r\n\r\n# support maximum transaction size, more than the size of the transaction will be cut into multiple transactions delivery\r\ncanal.instance.transactionn.size =  1024\r\n\r\n# network config\r\ncanal.instance.network.receiveBufferSize = 16384\r\ncanal.instance.network.sendBufferSize = 16384\r\ncanal.instance.network.soTimeout = 30\r\n\r\n#################################################\r\n#########               destinations            ############# \r\n#################################################\r\ncanal.destinations= example\r\n\r\ncanal.instance.global.mode = spring \r\ncanal.instance.global.lazy = true  ##修改为false,代表立马启动\r\n#canal.instance.global.manager.address = 127.0.0.1:1099\r\ncanal.instance.global.spring.xml = classpath:spring/memory-instance.xml\r\n#canal.instance.global.spring.xml = classpath:spring/default-instance.xml
\r\n

\r\n

应用参数:

\r\n
vi conf/example/instance.properties
\r\n
#################################################\r\n## mysql serverId\r\ncanal.instance.mysql.slaveId = 1234\r\n\r\n# position info\r\ncanal.instance.master.address = 127.0.0.1:3306 #改成自己的数据库地址\r\ncanal.instance.master.journal.name = \r\ncanal.instance.master.position = \r\ncanal.instance.master.timestamp = \r\n\r\n#canal.instance.standby.address = \r\n#canal.instance.standby.journal.name =\r\n#canal.instance.standby.position = \r\n#canal.instance.standby.timestamp = \r\n\r\n# username/password\r\ncanal.instance.dbUsername = retl  #改成自己的数据库信息\r\ncanal.instance.dbPassword = retl  #改成自己的数据库信息\r\ncanal.instance.defaultDatabaseName =   #改成自己的数据库信息\r\ncanal.instance.connectionCharsetNumber = 33  #改成自己的数据库信息\r\ncanal.instance.connectionCharset = UTF-8  #改成自己的数据库信息\r\n\r\n# table regex\r\ncanal.instance.filter.regex = .*\\\\..*\r\n\r\n#################################################\r\n
\r\n

\r\n

\r\n

说明:

\r\n
    \r\n
  • canal.instance.connectionCharset 代表数据库的编码方式对应到java中的编码类型,比如UTF-8,GBK , ISO-8859-1
  • \r\n
  • canal.instance.connectionCharsetNumber 代表数据库的编码方式对应mysql中的唯一id,详细的映射关系可查看:com.mysql.jdbc.CharsetMapping.INDEX_TO_CHARSET
    针对常见的编码:
    utf-8 <=> 33
    gb2312 <=> 24
    gbk <=> 28
  • \r\n
\r\n

4. 准备启动

\r\n

\r\n
sh bin/startup.sh
\r\n

\r\n

5. 查看日志

\r\n
vi logs/canal/canal.log
\r\n
2013-02-05 22:45:27.967 [main] INFO  com.alibaba.otter.canal.deployer.CanalLauncher - ## start the canal server.\r\n2013-02-05 22:45:28.113 [main] INFO  com.alibaba.otter.canal.deployer.CanalController - ## start the canal server[10.1.29.120:11111]\r\n2013-02-05 22:45:28.210 [main] INFO  com.alibaba.otter.canal.deployer.CanalLauncher - ## the canal server is running now ......
\r\n

\r\n

具体instance的日志:

\r\n
vi logs/example/example.log
\r\n
2013-02-05 22:50:45.636 [main] INFO  c.a.o.c.i.spring.support.PropertyPlaceholderConfigurer - Loading properties file from class path resource [canal.properties]\r\n2013-02-05 22:50:45.641 [main] INFO  c.a.o.c.i.spring.support.PropertyPlaceholderConfigurer - Loading properties file from class path resource [example/instance.properties]\r\n2013-02-05 22:50:45.803 [main] INFO  c.a.otter.canal.instance.spring.CanalInstanceWithSpring - start CannalInstance for 1-example \r\n2013-02-05 22:50:45.810 [main] INFO  c.a.otter.canal.instance.spring.CanalInstanceWithSpring - start successful....
\r\n

\r\n

6. 关闭

\r\n
sh bin/stop.sh
\r\n

\r\n

it's over.

\r\n
\r\n

ClientExample

\r\n

依赖配置:(目前暂未正式发布到mvn仓库,所以需要各位下载canal源码后手工执行下mvn clean install -Dmaven.test.skip)

\r\n
<dependency>\r\n    <groupId>com.alibaba.otter</groupId>\r\n    <artifactId>canal.client</artifactId>\r\n    <version>1.0.0</version>\r\n</dependency>
\r\n

\r\n

1. 创建mvn标准工程:

\r\n
mvn archetype:create -DgroupId=com.alibaba.otter -DartifactId=canal.sample
\r\n

\r\n

2. 修改pom.xml,添加依赖

\r\n

\r\n

3. ClientSample代码

\r\n
package com.alibaba.otter.canal.sample;\r\n\r\nimport java.net.InetSocketAddress;\r\nimport java.util.List;\r\n\r\nimport com.alibaba.otter.canal.common.utils.AddressUtils;\r\nimport com.alibaba.otter.canal.protocol.Message;\r\nimport com.alibaba.otter.canal.protocol.CanalEntry.Column;\r\nimport com.alibaba.otter.canal.protocol.CanalEntry.Entry;\r\nimport com.alibaba.otter.canal.protocol.CanalEntry.EntryType;\r\nimport com.alibaba.otter.canal.protocol.CanalEntry.EventType;\r\nimport com.alibaba.otter.canal.protocol.CanalEntry.RowChange;\r\nimport com.alibaba.otter.canal.protocol.CanalEntry.RowData;\r\n\r\npublic class SimpleCanalClientExample {\r\n\r\n    public static void main(String args[]) {\r\n        // 创建链接\r\n        CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress(AddressUtils.getHostIp(),\r\n                                                                                            11111), \"example\", \"\", \"\");\r\n        int batchSize = 1000;\r\n        int emptyCount = 0;\r\n        try {\r\n            connector.connect();\r\n            connector.subscribe(\".*\\\\..*\");\r\n            connector.rollback();\r\n            int totalEmtryCount = 120;\r\n            while (emptyCount < totalEmtryCount) {\r\n                Message message = connector.getWithoutAck(batchSize); // 获取指定数量的数据\r\n                long batchId = message.getId();\r\n                int size = message.getEntries().size();\r\n                if (batchId == -1 || size == 0) {\r\n                    emptyCount++;\r\n                    System.out.println(\"empty count : \" + emptyCount);\r\n                    try {\r\n                        Thread.sleep(1000);\r\n                    } catch (InterruptedException e) {\r\n                    }\r\n                } else {\r\n                    emptyCount = 0;\r\n                    // System.out.printf(\"message[batchId=%s,size=%s] \\n\", batchId, size);\r\n                    printEntry(message.getEntries());\r\n                }\r\n\r\n                connector.ack(batchId); // 提交确认\r\n                // connector.rollback(batchId); // 处理失败, 回滚数据\r\n            }\r\n\r\n            System.out.println(\"empty too many times, exit\");\r\n        } finally {\r\n            connector.disconnect();\r\n        }\r\n    }\r\n\r\n    private static void printEntry(List<Entry> entrys) {\r\n        for (Entry entry : entrys) {\r\n            if (entry.getEntryType() == EntryType.TRANSACTIONBEGIN || entry.getEntryType() == EntryType.TRANSACTIONEND) {\r\n                continue;\r\n            }\r\n\r\n            RowChange rowChage = null;\r\n            try {\r\n                rowChage = RowChange.parseFrom(entry.getStoreValue());\r\n            } catch (Exception e) {\r\n                throw new RuntimeException(\"ERROR ## parser of eromanga-event has an error , data:\" + entry.toString(),\r\n                                           e);\r\n            }\r\n\r\n            EventType eventType = rowChage.getEventType();\r\n            System.out.println(String.format(\"================> binlog[%s:%s] , name[%s,%s] , eventType : %s\",\r\n                                             entry.getHeader().getLogfileName(), entry.getHeader().getLogfileOffset(),\r\n                                             entry.getHeader().getSchemaName(), entry.getHeader().getTableName(),\r\n                                             eventType));\r\n\r\n            for (RowData rowData : rowChage.getRowDatasList()) {\r\n                if (eventType == EventType.DELETE) {\r\n                    printColumn(rowData.getBeforeColumnsList());\r\n                } else if (eventType == EventType.INSERT) {\r\n                    printColumn(rowData.getAfterColumnsList());\r\n                } else {\r\n                    System.out.println(\"-------> before\");\r\n                    printColumn(rowData.getBeforeColumnsList());\r\n                    System.out.println(\"-------> after\");\r\n                    printColumn(rowData.getAfterColumnsList());\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n    private static void printColumn(List<Column> columns) {\r\n        for (Column column : columns) {\r\n            System.out.println(column.getName() + \" : \" + column.getValue() + \"    update=\" + column.getUpdated());\r\n        }\r\n    }\r\n}
\r\n

\r\n

4. 运行Client

\r\n

首先启动Canal Server,可参加QuickStart : http://agapple.iteye.com/blogs/1796070

\r\n

启动Canal Client后,可以从控制台从看到类似消息:

\r\n
empty count : 1\r\nempty count : 2\r\nempty count : 3\r\nempty count : 4
\r\n

此时代表当前数据库无变更数据

\r\n

\r\n

5. 触发数据库变更

\r\n
mysql> use test;\r\nDatabase changed\r\nmysql> CREATE TABLE `xdual` (\r\n    ->   `ID` int(11) NOT NULL AUTO_INCREMENT,\r\n    ->   `X` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\r\n    ->   PRIMARY KEY (`ID`)\r\n    -> ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 ;\r\nQuery OK, 0 rows affected (0.06 sec)\r\n\r\nmysql> insert into xdual(id,x) values(null,now());Query OK, 1 row affected (0.06 sec)
\r\n

\r\n

可以从控制台中看到:

\r\n
empty count : 1\r\nempty count : 2\r\nempty count : 3\r\nempty count : 4\r\n================> binlog[mysql-bin.001946:313661577] , name[test,xdual] , eventType : INSERT\r\nID : 4    update=true\r\nX : 2013-02-05 23:29:46    update=true
\r\n

\r\n

最后:

\r\n

整个代码在附件中可以下载,如有问题可及时联系。

\r\n
\r\n \r\n
\r\ncanal.sample.tar.gz (2.2 KB)\r\n
\r\n","google":"UA-10379866-5","note":"Don't delete this file! It's used internally to help with page regeneration."} \ No newline at end of file diff --git a/stylesheets/pygment_trac.css b/stylesheets/pygment_trac.css new file mode 100644 index 00000000..d1df6a21 --- /dev/null +++ b/stylesheets/pygment_trac.css @@ -0,0 +1,68 @@ +.highlight .c { color: #999988; font-style: italic } /* Comment */ +.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ +.highlight .k { font-weight: bold } /* Keyword */ +.highlight .o { font-weight: bold } /* Operator */ +.highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */ +.highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ +.highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gr { color: #aa0000 } /* Generic.Error */ +.highlight .gh { color: #999999 } /* Generic.Heading */ +.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ +.highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */ +.highlight .go { color: #888888 } /* Generic.Output */ +.highlight .gp { color: #555555 } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold; } /* Generic.Subheading */ +.highlight .gt { color: #aa0000 } /* Generic.Traceback */ +.highlight .kc { font-weight: bold } /* Keyword.Constant */ +.highlight .kd { font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { font-weight: bold } /* Keyword.Pseudo */ +.highlight .kr { font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */ +.highlight .m { color: #009999 } /* Literal.Number */ +.highlight .s { color: #d14 } /* Literal.String */ +.highlight .na { color: #008080 } /* Name.Attribute */ +.highlight .nb { color: #0086B3 } /* Name.Builtin */ +.highlight .nc { color: #445588; font-weight: bold } /* Name.Class */ +.highlight .no { color: #008080 } /* Name.Constant */ +.highlight .ni { color: #800080 } /* Name.Entity */ +.highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #990000; font-weight: bold } /* Name.Function */ +.highlight .nn { color: #555555 } /* Name.Namespace */ +.highlight .nt { color: #CBDFFF } /* Name.Tag */ +.highlight .nv { color: #008080 } /* Name.Variable */ +.highlight .ow { font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mf { color: #009999 } /* Literal.Number.Float */ +.highlight .mh { color: #009999 } /* Literal.Number.Hex */ +.highlight .mi { color: #009999 } /* Literal.Number.Integer */ +.highlight .mo { color: #009999 } /* Literal.Number.Oct */ +.highlight .sb { color: #d14 } /* Literal.String.Backtick */ +.highlight .sc { color: #d14 } /* Literal.String.Char */ +.highlight .sd { color: #d14 } /* Literal.String.Doc */ +.highlight .s2 { color: #d14 } /* Literal.String.Double */ +.highlight .se { color: #d14 } /* Literal.String.Escape */ +.highlight .sh { color: #d14 } /* Literal.String.Heredoc */ +.highlight .si { color: #d14 } /* Literal.String.Interpol */ +.highlight .sx { color: #d14 } /* Literal.String.Other */ +.highlight .sr { color: #009926 } /* Literal.String.Regex */ +.highlight .s1 { color: #d14 } /* Literal.String.Single */ +.highlight .ss { color: #990073 } /* Literal.String.Symbol */ +.highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #008080 } /* Name.Variable.Class */ +.highlight .vg { color: #008080 } /* Name.Variable.Global */ +.highlight .vi { color: #008080 } /* Name.Variable.Instance */ +.highlight .il { color: #009999 } /* Literal.Number.Integer.Long */ + +.type-csharp .highlight .k { color: #0000FF } +.type-csharp .highlight .kt { color: #0000FF } +.type-csharp .highlight .nf { color: #000000; font-weight: normal } +.type-csharp .highlight .nc { color: #2B91AF } +.type-csharp .highlight .nn { color: #000000 } +.type-csharp .highlight .s { color: #A31515 } +.type-csharp .highlight .sc { color: #A31515 } diff --git a/stylesheets/stylesheet.css b/stylesheets/stylesheet.css new file mode 100644 index 00000000..a54a6397 --- /dev/null +++ b/stylesheets/stylesheet.css @@ -0,0 +1,247 @@ +body { + margin: 0; + padding: 0; + background: #151515 url("../images/bkg.png") 0 0; + color: #eaeaea; + font: 16px; + line-height: 1.5; + font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; +} + +/* General & 'Reset' Stuff */ + +.container { + width: 90%; + max-width: 600px; + margin: 0 auto; +} + +section { + display: block; + margin: 0 0 20px 0; +} + +h1, h2, h3, h4, h5, h6 { + margin: 0 0 20px; +} + +li { + line-height: 1.4 ; +} + +/* Header,
+ header - container + h1 - project name + h2 - project description +*/ + +header { + background: rgba(0, 0, 0, 0.1); + width: 100%; + border-bottom: 1px dashed #b5e853; + padding: 20px 0; + margin: 0 0 40px 0; +} + +header h1 { + font-size: 30px; + line-height: 1.5; + margin: 0 0 0 -40px; + font-weight: bold; + font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; + color: #b5e853; + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1), + 0 0 5px rgba(181, 232, 83, 0.1), + 0 0 10px rgba(181, 232, 83, 0.1); + letter-spacing: -1px; + -webkit-font-smoothing: antialiased; +} + +header h1:before { + content: "./ "; + font-size: 24px; +} + +header h2 { + font-size: 18px; + font-weight: 300; + color: #666; +} + +#downloads .btn { + display: inline-block; + text-align: center; + margin: 0; +} + +/* Main Content +*/ + +#main_content { + width: 100%; + -webkit-font-smoothing: antialiased; +} +section img { + max-width: 100% +} + +h1, h2, h3, h4, h5, h6 { + font-weight: normal; + font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; + color: #b5e853; + letter-spacing: -0.03em; + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1), + 0 0 5px rgba(181, 232, 83, 0.1), + 0 0 10px rgba(181, 232, 83, 0.1); +} + +#main_content h1 { + font-size: 30px; +} + +#main_content h2 { + font-size: 24px; +} + +#main_content h3 { + font-size: 18px; +} + +#main_content h4 { + font-size: 14px; +} + +#main_content h5 { + font-size: 12px; + text-transform: uppercase; + margin: 0 0 5px 0; +} + +#main_content h6 { + font-size: 12px; + text-transform: uppercase; + color: #999; + margin: 0 0 5px 0; +} + +dt { + font-style: italic; + font-weight: bold; +} + +ul li { + list-style: none; +} + +ul li:before { + content: ">>"; + font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; + font-size: 13px; + color: #b5e853; + margin-left: -37px; + margin-right: 21px; + line-height: 16px; +} + +blockquote { + color: #aaa; + padding-left: 10px; + border-left: 1px dotted #666; +} + +pre { + background: rgba(0, 0, 0, 0.9); + border: 1px solid rgba(255, 255, 255, 0.15); + padding: 10px; + font-size: 14px; + color: #b5e853; + border-radius: 2px; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + text-wrap: normal; + overflow: auto; + overflow-y: hidden; +} + +table { + width: 100%; + margin: 0 0 20px 0; +} + +th { + text-align: left; + border-bottom: 1px dashed #b5e853; + padding: 5px 10px; +} + +td { + padding: 5px 10px; +} + +hr { + height: 0; + border: 0; + border-bottom: 1px dashed #b5e853; + color: #b5e853; +} + +/* Buttons +*/ + +.btn { + display: inline-block; + background: -webkit-linear-gradient(top, rgba(40, 40, 40, 0.3), rgba(35, 35, 35, 0.3) 50%, rgba(10, 10, 10, 0.3) 50%, rgba(0, 0, 0, 0.3)); + padding: 8px 18px; + border-radius: 50px; + border: 2px solid rgba(0, 0, 0, 0.7); + border-bottom: 2px solid rgba(0, 0, 0, 0.7); + border-top: 2px solid rgba(0, 0, 0, 1); + color: rgba(255, 255, 255, 0.8); + font-family: Helvetica, Arial, sans-serif; + font-weight: bold; + font-size: 13px; + text-decoration: none; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.75); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05); +} + +.btn:hover { + background: -webkit-linear-gradient(top, rgba(40, 40, 40, 0.6), rgba(35, 35, 35, 0.6) 50%, rgba(10, 10, 10, 0.8) 50%, rgba(0, 0, 0, 0.8)); +} + +.btn .icon { + display: inline-block; + width: 16px; + height: 16px; + margin: 1px 8px 0 0; + float: left; +} + +.btn-github .icon { + opacity: 0.6; + background: url("../images/blacktocat.png") 0 0 no-repeat; +} + +/* Links + a, a:hover, a:visited +*/ + +a { + color: #63c0f5; + text-shadow: 0 0 5px rgba(104, 182, 255, 0.5); +} + +/* Clearfix */ + +.cf:before, .cf:after { + content:""; + display:table; +} + +.cf:after { + clear:both; +} + +.cf { + zoom:1; +} \ No newline at end of file From 18ca8666897c433d0323b8451a22fac65aadf2c2 Mon Sep 17 00:00:00 2001 From: agapple Date: Mon, 18 Feb 2013 06:41:17 -0800 Subject: [PATCH 02/11] Create gh-pages branch via GitHub --- images/bkg.png | Bin 4261 -> 0 bytes images/blacktocat.png | Bin 1266 -> 0 bytes images/code.png | Bin 0 -> 1177 bytes images/pattern.png | Bin 0 -> 13285 bytes images/tar.png | Bin 0 -> 1333 bytes images/top.png | Bin 0 -> 1112 bytes images/zip.png | Bin 0 -> 1312 bytes index.html | 81 ++-- javascripts/main.js | 1 - javascripts/script.js | 52 +++ stylesheets/pygment_trac.css | 3 +- stylesheets/stylesheet.css | 754 +++++++++++++++++++++++++---------- 12 files changed, 651 insertions(+), 240 deletions(-) delete mode 100644 images/bkg.png delete mode 100644 images/blacktocat.png create mode 100644 images/code.png create mode 100644 images/pattern.png create mode 100644 images/tar.png create mode 100644 images/top.png create mode 100644 images/zip.png delete mode 100644 javascripts/main.js create mode 100644 javascripts/script.js diff --git a/images/bkg.png b/images/bkg.png deleted file mode 100644 index fcebb5b22999adb792afee1f74bcf4af91db8441..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4261 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 E2cLQQbpQYW diff --git a/images/blacktocat.png b/images/blacktocat.png deleted file mode 100644 index 273d5710a2e0968d77584ad073e4a089fbfd7e68..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1266 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxKsVXI%s|1+P|wiV z#N6CmN5ROz&_Lh7NZ-&%*U;R`*vQJjKmiJrfVLH-q*(>IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8EkR}&8R-I5=oVMzl_XZ^<`pZ$OmImpPA){ffi_eM3D1{oGuTzrd=COM+4n&cLd=IHa;5RX-@T zIKQ+g85kdF$}r8qu)}W=NFmTQR{lkqz(`5Vami0E%}vcK@pQ3O0?O#6WTsdd7+V+^ z8k(CJm>U_GSr{5xm>asenYlPSS~yx7IT;(l%)qAC#mvab6&Utr2Bs#4hOUN|=4MVV zCI+U?PDZXKZWgXEy`Fi+C5d^-sW5vpf%Zc6n&H)JD;QCfH z^og8&1M`mKzE)l;d=qjrb~vvJSy|Mouyo1dg^ni6?thVCs0yAf`Q@5aq}s+K9`V=C z{rP`=v-NB<-^nFOYBRV_`Rx;uVYQy^)9a}ALqMd-xJi4fVcRLrrgF#H^(z_`gmYNg z8`ZC~^QU(&GySnBKw|Qotw~8oCN7?p;&T4i?d&@L$Gl%$8&-dv`S`$=`T36X{>ZDw zT=kLd?0S^caPZI_ua^-qKl~;=EWB<}?CV->TJO$Qp2Pd-+DSr z1<%~X^wgl##FWaylc_cg49qH-ArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB=XP}#GU}mUbW};_k zVqj@vsiRh+i#(Mch>H3D2mX`VkM*2oZxQ#zd*q`*i_F@!8b8E zGY=#J*5T`G<(XGpl9-pA>gi&u1T;f0Gc(1?#lXVAz{L_6(k5<(hOUN|#^#O|mZrug zrly9DW+sj>y)OC5rManjB{01y2)&Lt^@0*ZZUN9{m(-%nveXo}qWoM1u&1pual6GF zr+HAlDY)HYfm5$O&@uX;s6~oum=G}ifSB-v3*^9)erg^ty%zx!_N6U}nG6h!+@3Cu zAr-gwgfI#oR^VB(o72Hb^+AHi3>K9TCs`jS)et9h*0n8`3-~17ywH6*iD7r%&d0aB z57nO#Zm~Epr+Dx02=#xN^9q=ryy7^pI)0Nux$*2Dw;O`JbIdl}@vMnq{$%*^_6zf+ zN6fdIpDty%Y(4LWnni41g+oVZ>piI-qCp1=BnlU8cTTEdT)33uqfAt|1JBoa9WtUH z%UJg;bzv$#s@q=m-DlpB3O~DTDm!yyRLdCum#gs~j;UK#e)TBxW?pUs$qRGt?W%Y( zf$0RhQbR%2^@OKZ3KRDT&dx75m?LVq=(0~H|G~}*>F>9b9;Iyl6PAC0v8d==$b!sS o>cP@EpPLUx{7ql_NS~37A#Uzopr03c(KRR910 literal 0 HcmV?d00001 diff --git a/images/pattern.png b/images/pattern.png new file mode 100644 index 0000000000000000000000000000000000000000..5cb76aaf96e00e8425c8ce818bffe70a56084d7d GIT binary patch literal 13285 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 literal 0 HcmV?d00001 diff --git a/images/tar.png b/images/tar.png new file mode 100644 index 0000000000000000000000000000000000000000..1cb1bcf586bb0b92a52445180bfe66b26a3fe8f2 GIT binary patch literal 1333 zcmeAS@N?(olHy`uVBq!ia0vp^azHG~!3HD)zsDs4DajJoh?3y^w370~qErUQl>DSr z1<%~X^wgl##FWaylc_cg49qH-ArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB=XP}#GU}mUbW};_k zVqj@vsiRh+i#(Mch>H3D2mX`VkM*2oZxQ#zd*q`*i_F@!8b8E zGY=#J*5T`G<(XGpl9-pA>gi&u1T;f0Gc(1?%*n{u)zsD5#M0Qz(9qS;(%9V5!qU{( z#MIQ#(agjVrq?AuximL5uLPzy1)!Y0xI zY8hr3W(HN3Fa0#^m-AkYeH0P4=Kkkm8Oe|L#7*wF=B|8fQP+_s@abaElDden|4(-$ zuxBqmAF+S_S;M`4YT9Qe^F7|nKSkiUx75QWi=I#ZD*AQPyAAg*7>8vg9By4^o6gy- zDVfb$^}C|+eaNjvCDyAR%r=cF`SW7OpXVG=VXG{^?Rsw%^DSq?LJjXw&YTAx;XXRG z`8UNG?bA)(=;t^V921mZ>9cUlvonh9j&|(Zf|j;T+0yr3{6^D^43WGa8E;Hhi8fk% zy{p>y_I~v>;iFCW)82OQ@l2JP__QNpZt|JpFY_&BBNd7Zeyn}iIOVzI`-$04zE7Lw z9a^Y4+kt1J$NBjl`$KK&F7R0xuylt_U2GfnH+!Sxcl)Od+t*A>{(SgxFYk`ImtIRgIvG2^YLqW4vX1U7M=Tz^ES{&t-mA5qbxag~!&q5WW z)?IvjVe1VCyIqS;s4_0uIUzc=OJKqy!)J#iYtls2uAll9-oVJPe(kL)rMBF4pn}ZP L)z4*}Q$iB}esAKp literal 0 HcmV?d00001 diff --git a/images/top.png b/images/top.png new file mode 100644 index 0000000000000000000000000000000000000000..6ab34e5f7927451f9ff4a74a74f12e4c750b654c GIT binary patch literal 1112 zcmaJ=TSyd97@jnvEF-AsLcw9S9wK)xyF0EkxYf41)<&}~>xPRDWhjaNf~Z>Wn3CKI5om(_xQN5BLcL=tecow_YJA#$8oz-QM?lVIEhwT8%vq%vX>L<|c8 zj$*trQ^A6Y0L2CvKERg)AHy;<>!X>Vmt_PlC@?IT{74+lQksNRw0bfYzJfVu2{hs_t6G5uzx>bsI^#4CB!di8&}$RfOP1&c{SJ zHo*J+EE{8ae=uChbN&FwvEhn9IFF4Qa+?Nql*cO5SYJV`D+bNPk)zO3cSEJxGBhw= zw4fHuB~V?EZxX8%&V?z6rExN}JKDcSoi}0qxW_5s;>(oup^o)#VPWq(_jWCQ6p!Q4 zaM~HXb}Coa$t;NVJeaZKf~T_6Q(5Hc?e+u`$zVg*$jlo#-5gr2rO0PBRr|jdulY20 z?A!c{bJ(j(Mu&3`w+w@I3(3>Yf*ngw-aGEi41XUfmtH299r<;B=**tn*@GoD4TWUX zkNo&H)cpFzhx+%Ur#tt6hfT+RB`(+XJ%7TF_R;;~nM3VA2QLlOj}_f6nnxVDy?x8? SH+#RhZ#^DMM6YaY===j2yL4m# literal 0 HcmV?d00001 diff --git a/images/zip.png b/images/zip.png new file mode 100644 index 0000000000000000000000000000000000000000..08637bb186a442a69b4a21296a23a0ae16b0be32 GIT binary patch literal 1312 zcmeAS@N?(olHy`uVBq!ia0vp^azHG~!3HD)zsDs4DajJoh?3y^w370~qErUQl>DSr z1<%~X^wgl##FWaylc_cg49qH-ArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB=XP}#GU}mUbW};_k zVqj@vsiRh+i#(Mch>H3D2mX`VkM*2oZxQ#zd*q`*i_F@!8b8E zGY=#J*5T`G<(XGpl9-pA>gi&u1T;f0Gc(1?+{x6)z}eK)#M0Q<(9qS;(%9V5!qU{( z#MIQ#(agjVrq?AuximL5uLPzy1)F!N5UF&n+*h5jxgi|xFs|jG0jv+X<8<1;^ScDxXJOxffA-WoVR!rn7_x)UD}`8 z9N78m(&-<$|9`)KW1gAP8MJEAGu5Kg*)qqCs#*dP7XB%!47tF>*pmE)JNakW!MwgR zS;zN8wVp22%=4P{aJQk`qmQp<+@7eH`r9SSc>d`*c`b3BJ^qT4_X~Y|f4ARIyKMWj z>ezP|j(2Iy$%&r7cw?nQf-f3JKWv@ryq8U@eZFA!X#uIpFXpT8?!2wHqhtHM1uPx> zv*cE9+B>=Kf{e2>+qqP!4Lc7e_m}c6*p_4_8xtk7e`9vqq?@e{cO9(LtQY@PxvGBc zPWA73vnR(MZlAfG-zr99>EiWqlh>Ap>b&CLu>BdMk*X!Jxe6%lk_3zy% zFk!Q#e(EW+ c|I1;(z{{R|$Z_$+G*Fr4>FVdQ&MBb@0Dzg;JOBUy literal 0 HcmV?d00001 diff --git a/index.html b/index.html index 48f1034d..df8c12f5 100644 --- a/index.html +++ b/index.html @@ -1,34 +1,42 @@ - + + + - - - + + + - - - + + + + - Canal by otter-projects - + Canal + - + + + + + +
-
-

Canal

-

阿里巴巴mysql数据库binlog的增量订阅&消费组件

- -
- Download as .zip - Download as .tar.gz - View on GitHub -
-
+

Canal

- -
-
-
+
+

阿里巴巴mysql数据库binlog的增量订阅&消费组件

+
+ +
+

背景

@@ -423,10 +431,27 @@ X : 2013-02-05 23:29:46 update=true

-
+ +
+