第0章:簡介
(1)下面是http://code.google.com中的binlog事件分析結構圖:
(2)獲取開源包的maven座標
<!-- MySQL JAVA Slave Client --> <dependency> <groupId>com.google.code</groupId> <artifactId>open-replicator</artifactId> <version>1.0.5</version> </dependency> |
(3)參考網站
開源項目首頁:http://code.google.com/p/open-replicator/
開源中國:http://www.oschina.net/p/open-replicator
(4)札記
1)Open Replicator是一個用Java編寫的MySQL binlog分析程序。Open Replicator 首先連接到MySQL(就像一個普通的MySQL Slave一樣),然後接收和分析binlog,最終將分析得出的binlog events以回調的方式通知應用。
2)Open Replicator可以被應用到MySQL數據變化的實時推送,多Master到單Slave的數據同步等多種應用場景。
3)在程序結構上,最主要的設計原則是高性能,低內存佔用。Open Replicator目前只支持MySQL5.0及以上版本。
第1章:實踐
(1)包裝Open Replicator類(AutoOpenReplicator.java)
package com.mcc.core.openReplicator;
import com.google.code.or.OpenReplicator;
import com.google.code.or.common.glossary.column.StringColumn;
import com.google.code.or.net.Packet;
import com.google.code.or.net.Transport;
import com.google.code.or.net.impl.packet.EOFPacket;
import com.google.code.or.net.impl.packet.ResultSetRowPacket;
import com.google.code.or.net.impl.packet.command.ComQuery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* MySQL binlog分析程序 ,用到open-replicator包
* 增加加自動配置binlog位置及重連機制
*
* @author <a href="mailto:[email protected]">menergy</a>
* DateTime: 13-12-26 下午2:22
*/
public class AutoOpenReplicator extends OpenReplicator {
// members
private static Logger logger = LoggerFactory.getLogger(AutoOpenReplicator.class);
private boolean autoReconnect = true;
// timeout auto reconnect , default 30 second
private int delayReconnect = 30;
// default timeout is 60 second, after timeout will be reconnect!
private int defaultTimeout = 120 * 1000;
// COM Query Transport
private Transport comQueryTransport;
// static block
// constructors
// properties
/**
* 是否自動重連
*
* @return 自動重連
*/
public boolean isAutoReconnect() {
return autoReconnect;
}
/**
* 設置自動重連
*
* @param autoReconnect 自動重連
*/
public void setAutoReconnect(boolean autoReconnect) {
this.autoReconnect = autoReconnect;
}
/**
* 斷開多少秒後進行自動重連
*
* @param delayReconnect 斷開後多少秒
*/
public void setDelayReconnect(int delayReconnect) {
this.delayReconnect = delayReconnect;
}
/**
* 斷開多少秒後進行自動重連
*
* @return 斷開後多少秒
*/
public int getDelayReconnect() {
return delayReconnect;
}
// public methods
// protected methods
@Override
public void start() {
do {
try {
long current = System.currentTimeMillis();
if (!this.isRunning()) {
if (this.getBinlogFileName() == null) updatePosition();
logger.info("Try to startup dump binlog from mysql master[{}, {}] ...", this.binlogFileName, this.binlogPosition);
this.reset();
super.start();
logger.info("Startup successed! After {} second if nothing event fire will be reconnect ...", defaultTimeout / 1000);
} else {
if (current - this.lastAlive >= this.defaultTimeout) {
this.stopQuietly(0, TimeUnit.SECONDS);
}
}
TimeUnit.SECONDS.sleep(this.getDelayReconnect());
} catch (Exception e) {
if (logger.isErrorEnabled()) {
logger.error("connect mysql failure!", e);
}
// reconnect failure, reget last binlog & position from master node and update cache!
//LoadCenter.loadAll(); // just update all cache, not flush!
updatePosition();
try {
TimeUnit.SECONDS.sleep(this.getDelayReconnect());
} catch (InterruptedException ignore) {
// NOP
}
}
} while (this.autoReconnect);
}
@Override
public void stopQuietly(long timeout, TimeUnit unit) {
super.stopQuietly(timeout, unit);
if (this.getBinlogParser() != null) {
// 重置, 當MySQL服務器進行restart/stop操作時進入該流程
this.binlogParser.setParserListeners(null); // 這句比較關鍵,不然會死循環
}
}
// friendly methods
// private methods
/**
* 自動配置binlog位置
*/
private void updatePosition() {
// 配置binlog位置
try {
ResultSetRowPacket binlogPacket = query("show master status");
if (binlogPacket != null) {
List<StringColumn> values = binlogPacket.getColumns();
this.setBinlogFileName(values.get(0).toString());
this.setBinlogPosition(Long.valueOf(values.get(1).toString()));
}
} catch (Exception e) {
if (logger.isErrorEnabled()) {
logger.error("update binlog position failure!", e);
}
}
}
/**
* ComQuery 查詢
*
* @param sql 查詢語句
* @return
*/
private ResultSetRowPacket query(String sql) throws Exception {
ResultSetRowPacket row = null;
final ComQuery command = new ComQuery();
command.setSql(StringColumn.valueOf(sql.getBytes()));
if (this.comQueryTransport == null) this.comQueryTransport = getDefaultTransport();
this.comQueryTransport.connect(this.host, this.port);
this.comQueryTransport.getOutputStream().writePacket(command);
this.comQueryTransport.getOutputStream().flush();
// step 1
this.comQueryTransport.getInputStream().readPacket();
//
Packet packet;
// step 2
while (true) {
packet = comQueryTransport.getInputStream().readPacket();
if (packet.getPacketBody()[0] == EOFPacket.PACKET_MARKER) {
break;
}
}
// step 3
while (true) {
packet = comQueryTransport.getInputStream().readPacket();
if (packet.getPacketBody()[0] == EOFPacket.PACKET_MARKER) {
break;
} else {
row = ResultSetRowPacket.valueOf(packet);
}
}
this.comQueryTransport.disconnect();
return row;
}
private void reset() {
this.transport = null;
this.binlogParser = null;
}
// inner class
// test main
}
(2)事件監聽器模板類(NotificationListener.java)
package com.mcc.core.openReplicator;
import com.google.code.or.binlog.BinlogEventListener;
import com.google.code.or.binlog.BinlogEventV4;
import com.google.code.or.binlog.impl.event.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Binlog事件監聽器模板
*
* @author <a href="mailto:[email protected]">menergy</a>
* DateTime: 13-12-26 下午2:34
*/
public class NotificationListener implements BinlogEventListener {
private static Logger logger = LoggerFactory.getLogger(NotificationListener.class);
private String eventDatabase;
/**
* 這裏只是實現例子,該方法可以自由處理邏輯
* @param event
*/
@Override
public void onEvents(BinlogEventV4 event) {
Class<?> eventType = event.getClass();
// 事務開始
if (eventType == QueryEvent.class) {
QueryEvent actualEvent = (QueryEvent) event;
this.eventDatabase = actualEvent.getDatabaseName().toString();
//TODO,這裏可以獲取事件數據庫信息,可做其它邏輯處理
logger.info("事件數據庫名:{}",eventDatabase);
return;
}
// 只監控指定數據庫
if (eventDatabase != null && !"".equals(eventDatabase.trim())) {
if (eventType == TableMapEvent.class) {
TableMapEvent actualEvent = (TableMapEvent) event;
long tableId = actualEvent.getTableId();
String tableName = actualEvent.getTableName().toString();
//TODO,這裏可以獲取事件表信息,可做其它邏輯處理
logger.info("事件數據表ID:{}, 事件數據庫表名稱:{}",tableId, tableName);
} else if (eventType == WriteRowsEvent.class) { // 插入事件
WriteRowsEvent actualEvent = (WriteRowsEvent) event;
long tableId = actualEvent.getTableId();
//TODO,這裏可以獲取寫行事件信息,可做其它邏輯處理
logger.info("寫行事件ID:{}",tableId);
} else if (eventType == UpdateRowsEvent.class) { // 更新事件
UpdateRowsEvent actualEvent = (UpdateRowsEvent) event;
long tableId = actualEvent.getTableId();
//TODO,這裏可以獲取更新事件信息,可做其它邏輯處理
logger.info("更新事件ID:{}",tableId);
} else if (eventType == DeleteRowsEvent.class) {// 刪除事件
DeleteRowsEvent actualEvent = (DeleteRowsEvent) event;
long tableId = actualEvent.getTableId();
//TODO,這裏可以獲取刪除事件信息,可做其它邏輯處理
logger.info("刪除事件ID:{}",tableId);
} else if (eventType == XidEvent.class) {// 結束事務
XidEvent actualEvent = (XidEvent) event;
long xId = actualEvent.getXid();
//TODO,這裏可以獲取結束事件信息,可做其它邏輯處理
logger.info("結束事件ID:{}",xId);
}
}
}
}
(3)MySQL binlog分析程序測試類(OpenReplicatorTest.java)
package com.mcc.core.test;
import com.mcc.core.openReplicator.AutoOpenReplicator;
import com.mcc.core.openReplicator.NotificationListener;
/**
* MySQL binlog分析程序測試
*
* @author <a href="mailto:[email protected]">menergy</a>
* DateTime: 13-12-26 下午2:26
*/
public class OpenReplicatorTest {
public static void main(String args[]){
// 配置從MySQL Master進行復制
final AutoOpenReplicator aor = new AutoOpenReplicator();
aor.setServerId(100001);
aor.setHost("192.168.1.1");
aor.setUser("admin");
aor.setPassword("123456");
aor.setAutoReconnect(true);
aor.setDelayReconnect(5);
aor.setBinlogEventListener(new NotificationListener());
aor.start();
}
}