/**
*
*/
package com.ganji.as.thrift.protocol.server.nodes.discovery;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.slf4j.Logger;
import com.alibaba.fastjson.JSONObject;
import com.ganji.as.thrift.protocol.builder.ClientBuildingConfig;
import com.netflix.curator.framework.CuratorFramework;
import com.netflix.curator.framework.CuratorFrameworkFactory;
import com.netflix.curator.framework.state.ConnectionState;
import com.netflix.curator.framework.state.ConnectionStateListener;
import com.netflix.curator.retry.ExponentialBackoffRetry;
/**
* @author yikangfeng
* @date 2015年7月21日
*/
public class ServerNodesDiscoveryProvider implements ServerNodesDiscovery {
private final List<ServerNode> serverNodes_ = new ArrayList<>();
private CuratorFramework zkClient_;
static private final String ZK_FULL_PATH_DELIMITER = "!";
static private final String ZK_FULL_PATH_NAMESPACE_SEPERATOR = "/";
static private final Charset ZK_CHAR_SET = Charset.forName("UTF-8");
private String zkHost_;
private String zkNamespace_;
private String zkNodePath_;
final private Logger logger_;
public ServerNodesDiscoveryProvider(
final ClientBuildingConfig clientBuildingConfig) {
this.logger_ = clientBuildingConfig.getLogger();
}
@Override
public List<ServerNode> nodesDiscovery(final String zkFullPath)
throws Throwable {
// TODO Auto-generated method stub
if (zkFullPath == null || zkFullPath.isEmpty())// host mode
return this.nodes();
if (zkClient_ == null) {
synchronized (this) {
if (zkClient_ == null)
buildZKClient(zkFullPath);
}
}
return doNodesDiscovery();
}
private List<ServerNode> doNodesDiscovery() throws Exception {
if (logger_ != null) {
if (logger_.isInfoEnabled())
logger_.info("nodes discovery start...");
}
List<String> serverNodes = registerPathNodeDataChangeWatcher(zkNodePath_);
if (serverNodes == null)
serverNodes = new ArrayList<>();
List<ServerNode> newestServerNodes = new ArrayList<>();
for (final String nodeKey : serverNodes) {
final byte[] nodeValueBytes = zkClient_.getData().forPath(
String.format("%s/%s", zkNodePath_, nodeKey));
if (nodeValueBytes == null || nodeValueBytes.length <= 0)
continue;
final String nodeValueInfo = new String(nodeValueBytes, ZK_CHAR_SET);
final ServerNodeInfo serverNode = JSONObject.parseObject(
nodeValueInfo, ServerNodeInfo.class);
newestServerNodes.add(serverNode);
}
if (serverNodes != null)
serverNodes.clear();
synchronized (this.serverNodes_) {
this.serverNodes_.clear();
this.serverNodes_.addAll(newestServerNodes);
if (logger_ != null) {
if (logger_.isInfoEnabled())
logger_.info(String.format(
"current discovery's server nodes count (%d)",
this.serverNodes_.size()));
}
}
if (logger_ != null) {
if (logger_.isInfoEnabled())
logger_.info("nodes discovery end...");
}
return this.nodes();
}
private Watcher createChildNodeChangeWatcher() {
return new Watcher() {
@Override
public void process(final WatchedEvent event) {
// TODO Auto-generated method stub
if (event == null)
return;
if (event.getState() == Event.KeeperState.SyncConnected) {
switch (event.getType()) {
case NodeChildrenChanged: {
try {
doNodesDiscovery();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
if (logger_ != null) {
if (logger_.isInfoEnabled())
logger_.info(String
.format("To re acquire of active server nodes generate an error, cause:%s",
e));
}
}
break;
}
default:
break;
}
}
}
};
}
private List<String> registerPathNodeDataChangeWatcher(final String path)
throws Exception {
return zkClient_.getChildren()
.usingWatcher(createChildNodeChangeWatcher()).forPath(path);
}
private void buildZKClient(final String zkFullPath) {
final String[] zkFullPathItems = zkFullPath
.split(ZK_FULL_PATH_DELIMITER);
zkHost_ = zkFullPathItems[1];
zkNamespace_ = zkFullPathItems[2].substring(0, zkFullPathItems[2]
.lastIndexOf(ZK_FULL_PATH_NAMESPACE_SEPERATOR));
zkNodePath_ = zkFullPathItems[2].substring(zkFullPathItems[2]
.lastIndexOf(ZK_FULL_PATH_NAMESPACE_SEPERATOR));
if (zkClient_ == null) {
try {
zkClient_ = CuratorFrameworkFactory.builder()
.connectString(zkHost_).namespace(zkNamespace_)
.retryPolicy(new ExponentialBackoffRetry(1000, 60))
.connectionTimeoutMs(2 * 3600 * 1000).build();
zkClient_.getConnectionStateListenable().addListener(
new ConnectionStateListener() {
@Override
public void stateChanged(CuratorFramework client,
ConnectionState newState) {
// TODO Auto-generated method stub
if (newState == ConnectionState.LOST) {
try {
synchronized (this) {
while (!client
.getZookeeperClient()
.blockUntilConnectedOrTimedOut()) {
if (logger_ != null) {
if (logger_.isInfoEnabled())
logger_.info("waitting reconnected...");
}
wait(1000);
}
}
if (logger_ != null) {
if (logger_.isInfoEnabled())
logger_.info("to zookeeper reconnected.");
}
doNodesDiscovery();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
if (logger_ != null) {
if (logger_.isInfoEnabled())
logger_.info(String
.format("To re connect to zookeeper to generate an error, cause:%s",
e));
}
}
}
}
});
zkClient_.start();
} catch (Throwable t) {
zkClient_.close();
throw new IllegalStateException(
"building zookeeper client failed.");
}
}
}
static public ServerNodesDiscoveryFactory FACTORY = new ServerNodesDiscoveryFactory() {
@Override
public ServerNodesDiscovery createServerNodesDiscovery(
final ClientBuildingConfig clientBuildingConfig) {
// TODO Auto-generated method stub
if (clientBuildingConfig == null)
throw new NullPointerException(
"The parameter clientBuildingConfig is not valid.");
return new ServerNodesDiscoveryProvider(clientBuildingConfig);
}
};
@Override
public List<ServerNode> nodes() {
// TODO Auto-generated method stub
synchronized (this.serverNodes_) {
return Collections.unmodifiableList(this.serverNodes_);
}
}
@Override
public void close() throws Exception {
// TODO Auto-generated method stub
if (this.zkClient_ != null)
this.zkClient_.close();
if (this.serverNodes_ != null)
this.serverNodes_.clear();
}
static public void main(String[] args) {
try {
String zkFullPath1 = "zk!bw-kvm-cy-01.dns.ganji.com:2181!/soa/services/as.postlimitservice.thrift!0";
ServerNodesDiscovery nodesDiscovery = ServerNodesDiscoveryProvider.FACTORY
.createServerNodesDiscovery(new ClientBuildingConfig());
System.out.println(nodesDiscovery.nodesDiscovery(zkFullPath1));
while (true) {
System.out.println("current nodes size:"
+ nodesDiscovery.nodes().size());
for (final ServerNode serverNode : nodesDiscovery.nodes())
System.out.println(serverNode);
TimeUnit.SECONDS.sleep(2);
}
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}