Merge pull request #173 from lulu2panpan/master

Canal客户端高可用bug修复
This commit is contained in:
agapple
2016-04-29 11:16:24 +08:00
4 changed files with 27 additions and 8 deletions

View File

@@ -28,7 +28,7 @@ public class CanalConnectors {
*/ */
public static CanalConnector newSingleConnector(SocketAddress address, String destination, String username, public static CanalConnector newSingleConnector(SocketAddress address, String destination, String username,
String password) { String password) {
SimpleCanalConnector canalConnector = new SimpleCanalConnector(address, username, password, destination); SimpleCanalConnector canalConnector = new SimpleCanalConnector(address, username, password, destination, null);
canalConnector.setSoTimeout(30 * 1000); canalConnector.setSoTimeout(30 * 1000);
return canalConnector; return canalConnector;
} }

View File

@@ -45,7 +45,7 @@ public class ClusterCanalConnector implements CanalConnector {
int times = 0; int times = 0;
while (true) { while (true) {
try { try {
currentConnector = new SimpleCanalConnector(nextAddress, username, password, destination); currentConnector = new SimpleCanalConnector(nextAddress, username, password, destination, accessStrategy);
currentConnector.setSoTimeout(soTimeout); currentConnector.setSoTimeout(soTimeout);
if (filter != null) { if (filter != null) {
currentConnector.setFilter(filter); currentConnector.setFilter(filter);
@@ -57,7 +57,7 @@ public class ClusterCanalConnector implements CanalConnector {
currentConnector.connect(); currentConnector.connect();
break; break;
} catch (Exception e) { } catch (Exception e) {
logger.warn("failed to connect to:{} after retry {} times", nextAddress, times); logger.warn("failed to connect to:{} after retry {} times", currentConnector.getAddress(), times);
currentConnector.disconnect(); currentConnector.disconnect();
currentConnector = null; currentConnector = null;
// retry for #retryTimes for each node when trying to // retry for #retryTimes for each node when trying to

View File

@@ -14,6 +14,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.alibaba.otter.canal.client.CanalConnector; import com.alibaba.otter.canal.client.CanalConnector;
import com.alibaba.otter.canal.client.CanalNodeAccessStrategy;
import com.alibaba.otter.canal.client.impl.running.ClientRunningData; import com.alibaba.otter.canal.client.impl.running.ClientRunningData;
import com.alibaba.otter.canal.client.impl.running.ClientRunningListener; import com.alibaba.otter.canal.client.impl.running.ClientRunningListener;
import com.alibaba.otter.canal.client.impl.running.ClientRunningMonitor; import com.alibaba.otter.canal.client.impl.running.ClientRunningMonitor;
@@ -69,18 +70,21 @@ public class SimpleCanalConnector implements CanalConnector {
// 读写数据分别使用不同的锁进行控制,减小锁粒度,读也需要排他锁,并发度容易造成数据包混乱,反序列化失败 // 读写数据分别使用不同的锁进行控制,减小锁粒度,读也需要排他锁,并发度容易造成数据包混乱,反序列化失败
private Object readDataLock = new Object(); private Object readDataLock = new Object();
private Object writeDataLock = new Object(); private Object writeDataLock = new Object();
private CanalNodeAccessStrategy accessStrategy;
public SimpleCanalConnector(SocketAddress address, String username, String password, String destination){ public SimpleCanalConnector(SocketAddress address, String username, String password, String destination,
this(address, username, password, destination, 60000); CanalNodeAccessStrategy accessStrategy){
this(address, username, password, destination, 60000, accessStrategy);
} }
public SimpleCanalConnector(SocketAddress address, String username, String password, String destination, public SimpleCanalConnector(SocketAddress address, String username, String password, String destination,
int soTimeout){ int soTimeout, CanalNodeAccessStrategy accessStrategy){
this.address = address; this.address = address;
this.username = username; this.username = username;
this.password = password; this.password = password;
this.soTimeout = soTimeout; this.soTimeout = soTimeout;
this.clientIdentity = new ClientIdentity(destination, (short) 1001); this.clientIdentity = new ClientIdentity(destination, (short) 1001);
this.accessStrategy = accessStrategy;
} }
public void connect() throws CanalClientException { public void connect() throws CanalClientException {
@@ -123,6 +127,9 @@ public class SimpleCanalConnector implements CanalConnector {
private InetSocketAddress doConnect() throws CanalClientException { private InetSocketAddress doConnect() throws CanalClientException {
try { try {
if (accessStrategy != null) {
address = accessStrategy.nextNode();//实时获取一下最新的running节点,lubiao
}
channel = SocketChannel.open(); channel = SocketChannel.open();
channel.socket().setSoTimeout(soTimeout); channel.socket().setSoTimeout(soTimeout);
channel.connect(address); channel.connect(address);

View File

@@ -1,6 +1,7 @@
package com.alibaba.otter.canal.client.impl.running; package com.alibaba.otter.canal.client.impl.running;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.text.MessageFormat;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@@ -96,7 +97,11 @@ public class ClientRunningMonitor extends AbstractCanalLifeCycle {
releaseRunning(); // 尝试一下release releaseRunning(); // 尝试一下release
} }
public void initRunning() { // 改动记录:
// 1,在方法上加synchronized关键字,保证同步顺序执行;
// 2,判断Zk上已经存在的activeData是否是本机,是的话把mutex重置为true,否则会导致死锁
// 3,增加异常处理,保证出现异常时,running节点能被删除,否则会导致死锁
public synchronized void initRunning() {
if (!isStart()) { if (!isStart()) {
return; return;
} }
@@ -116,12 +121,19 @@ public class ClientRunningMonitor extends AbstractCanalLifeCycle {
initRunning(); initRunning();
} else { } else {
activeData = JsonUtils.unmarshalFromByte(bytes, ClientRunningData.class); activeData = JsonUtils.unmarshalFromByte(bytes, ClientRunningData.class);
if(activeData.getAddress().contains(":") && isMine(activeData.getAddress())){
mutex.set(true);
}
} }
} catch (ZkNoNodeException e) { } catch (ZkNoNodeException e) {
zkClient.createPersistent(ZookeeperPathUtils.getClientIdNodePath(this.destination, clientData.getClientId()), zkClient.createPersistent(ZookeeperPathUtils.getClientIdNodePath(this.destination, clientData.getClientId()),
true); // 尝试创建父节点 true); // 尝试创建父节点
initRunning(); initRunning();
} } catch (Throwable t) {
logger.error(MessageFormat.format("There is an error when execute initRunning method, with destination [{0}].",destination),t);
zkClient.delete(path);
throw new RuntimeException("something goes wrong in initRunning method. ",t);
}
} }
/** /**