Curator框架是zookeeper客户端框架,官网有句话说的很简洁:curator对于zookeeper就像Guava对于java。
重复策略,实例化,众多实用的食谱菜单(分布式锁,计数器,队列,栅栏,信号量,路径缓存)。
/** * 创建客户端 * @param connectString zk地址 * @param sessionTimeoutMs session超时 * @param connectionTimeoutMs 路径超时 * @param retryPolicy 重复策略 * @return client */
public static CuratorFramework newClient(String connectString, int sessionTimeoutMs,
int connectionTimeoutMs, RetryPolicy retryPolicy)
{
return builder().
connectString(connectString).
sessionTimeoutMs(sessionTimeoutMs).
connectionTimeoutMs(connectionTimeoutMs).
retryPolicy(retryPolicy).
build();
}
/** * 返回一个用来创建CuratorFramework新的builder * @return new builder */
public static Builder builder()
{
return new Builder();
}
返回的Builder是CuratorFrameworkFactory的内部类,主要用于流式的创建CuratorFramework,里面包含所需参数
private EnsembleProvider ensembleProvider;
private int sessionTimeoutMs = DEFAULT_SESSION_TIMEOUT_MS;
private int connectionTimeoutMs = DEFAULT_CONNECTION_TIMEOUT_MS;
private int maxCloseWaitMs = DEFAULT_CLOSE_WAIT_MS;
private RetryPolicy retryPolicy;
private ThreadFactory threadFactory = null;
private String namespace;
private List<AuthInfo> authInfos = null;
private byte[] defaultData = LOCAL_ADDRESS;
private CompressionProvider compressionProvider = DEFAULT_COMPRESSION_PROVIDER;
private ZookeeperFactory zookeeperFactory = DEFAULT_ZOOKEEPER_FACTORY;
private ACLProvider aclProvider = DEFAULT_ACL_PROVIDER;
private boolean canBeReadOnly = false;
private boolean useContainerParentsIfAvailable = true;
ensembleProvider(配置提供者) 可以通过在Builder里的以下2个方法构造。设置服务器的链接地址,
格式: host:port,host2:port2…..
主要由此提供链接地址,供后期zookeeper里使用。
public Builder connectString(String connectString)
{
ensembleProvider = new FixedEnsembleProvider(connectString);
return this;
}
public Builder ensembleProvider(EnsembleProvider ensembleProvider)
{
this.ensembleProvider = ensembleProvider;
return this;
}
/** * 根据builder里的值创建新的CuratorFramework * @return new CuratorFramework */
public CuratorFramework build()
{
return new CuratorFrameworkImpl(this);
}
CuratorFrameworkImpl为CuratorFramework接口的一个实现,平时主要用到的就是此client。
该构造函数主要还是使用Builder里的默认配置的一些参数,这些参数可以通过CuratorFrameworkFactory李的Builder去流式创建。
设置如下参数,如ZookeeperFactory 工厂,CuratorZookeeperClient【重点,客户端的工作主要靠它】,listeners 监听,backgroundOperations 后台运行行为,namespace 命名空间(用于放置在路径的前缀),
threadFactory CuratorFrameworkImpl的线程工厂,connectionStateManager 链接状态管理器, compressionProvider 压缩器等等。
public CuratorFrameworkImpl(CuratorFrameworkFactory.Builder builder)
{
ZookeeperFactory localZookeeperFactory = makeZookeeperFactory(builder.getZookeeperFactory());
this.client = new CuratorZookeeperClient(localZookeeperFactory, builder.getEnsembleProvider(), builder.getSessionTimeoutMs(), builder.getConnectionTimeoutMs(), new Watcher()
{
@Override
public void process(WatchedEvent watchedEvent)
{
CuratorEvent event = new CuratorEventImpl(CuratorFrameworkImpl.this, CuratorEventType.WATCHED, watchedEvent.getState().getIntValue(), unfixForNamespace(watchedEvent.getPath()),
null, null, null, null, null, watchedEvent, null);
processEvent(event);
}
}, builder.getRetryPolicy(), builder.canBeReadOnly());
listeners = new ListenerContainer<CuratorListener>();
unhandledErrorListeners = new ListenerContainer<UnhandledErrorListener>();
backgroundOperations = new DelayQueue<OperationAndData<?>>();
namespace = new NamespaceImpl(this, builder.getNamespace());
threadFactory = getThreadFactory(builder);
maxCloseWaitMs = builder.getMaxCloseWaitMs();
connectionStateManager = new ConnectionStateManager(this, builder.getThreadFactory());
compressionProvider = builder.getCompressionProvider();
aclProvider = builder.getAclProvider();
state = new AtomicReference<CuratorFrameworkState>(CuratorFrameworkState.LATENT);
useContainerParentsIfAvailable = builder.useContainerParentsIfAvailable();
byte[] builderDefaultData = builder.getDefaultData();
defaultData = (builderDefaultData != null) ? Arrays.copyOf(builderDefaultData, builderDefaultData.length) : new byte[0];
authInfos = buildAuths(builder);
failedDeleteManager = new FailedDeleteManager(this);
namespaceFacadeCache = new NamespaceFacadeCache(this);
}
//1.从builder里拿出ZookeeperFactory
ZookeeperFactory localZookeeperFactory = makeZookeeperFactory(builder.getZookeeperFactory());
//2.builder里的默认ZookeeperFactory
private ZookeeperFactory zookeeperFactory = DEFAULT_ZOOKEEPER_FACTORY;
private static final DefaultZookeeperFactory DEFAULT_ZOOKEEPER_FACTORY = new DefaultZookeeperFactory();
//3.ZookeeperFactory的默认实现类,默认zookeeper工厂
//实际上就是new一个org.apache.zookeeper.ZooKeeper,原生态的Zookeeper。
public class DefaultZookeeperFactory implements ZookeeperFactory {
@Override
public ZooKeeper newZooKeeper(String connectString, int sessionTimeout, Watcher watcher, boolean canBeReadOnly) throws Exception
{
return new ZooKeeper(connectString, sessionTimeout, watcher, canBeReadOnly);
}
}
//4.包装一层,加上鉴权信息
private ZookeeperFactory makeZookeeperFactory(final ZookeeperFactory actualZookeeperFactory)
{
return new ZookeeperFactory()
{
@Override
public ZooKeeper newZooKeeper(String connectString, int sessionTimeout,
Watcher watcher, boolean canBeReadOnly) throws Exception
{
ZooKeeper zooKeeper = actualZookeeperFactory.newZooKeeper(connectString, sessionTimeout, watcher, canBeReadOnly);
for ( AuthInfo auth : authInfos )
{
zooKeeper.addAuthInfo(auth.getScheme(), auth.getAuth());
}
return zooKeeper;
}
};
}
设置其service为单例ExecutorService。主要用在监控链接状态。
public ConnectionStateManager(CuratorFramework client, ThreadFactory threadFactory)
{
this.client = client;
if ( threadFactory == null )
{
threadFactory = ThreadUtils.newThreadFactory("ConnectionStateManager");
}
service = Executors.newSingleThreadExecutor(threadFactory);
}
在CuratorFrameworkImpl初始化的时候构建
1.初始化,需要新建Watcher
this.client = new CuratorZookeeperClient(localZookeeperFactory, builder.getEnsembleProvider(), builder.getSessionTimeoutMs(), builder.getConnectionTimeoutMs(), new Watcher()
{
@Override
public void process(WatchedEvent watchedEvent)
{
CuratorEvent event = new CuratorEventImpl(CuratorFrameworkImpl.this, CuratorEventType.WATCHED, watchedEvent.getState().getIntValue(), unfixForNamespace(watchedEvent.getPath()), null, null, null, null, null, watchedEvent, null);
processEvent(event);//调用本类的方法
}
}, builder.getRetryPolicy(), builder.canBeReadOnly());
2.listeners采用函数式编程,此处的监听执行就是在ConnectionState里的process调用的。
private void processEvent(final CuratorEvent curatorEvent)
{
if ( curatorEvent.getType() == CuratorEventType.WATCHED )
{
validateConnection(curatorEvent.getWatchedEvent().getState());
}
listeners.forEach(new Function<CuratorListener, Void>()
{
@Override
public Void apply(CuratorListener listener)
{
try
{
TimeTrace trace = client.startTracer("EventListener");
listener.eventReceived(CuratorFrameworkImpl.this, curatorEvent);
trace.commit();
}
catch ( Exception e )
{
logError("Event listener threw exception", e);
}
return null;
}
});
}
3.主要有2个字段ConnectionState连接状态,retryPolicy 重复策略就在如下定义。
private final ConnectionState state;
private final AtomicReference<RetryPolicy> retryPolicy = new AtomicReference<RetryPolicy>();
初始化主要做了如下工作,校验重复策略不能为空,校验配置链接地址提供者不能为空,初始化ConnectionState,设置重复策略。
public CuratorZookeeperClient(ZookeeperFactory zookeeperFactory, EnsembleProvider ensembleProvider,
int sessionTimeoutMs, int connectionTimeoutMs, Watcher watcher, RetryPolicy retryPolicy, boolean canBeReadOnly)
{
if ( sessionTimeoutMs < connectionTimeoutMs )
{
log.warn(String.format("session timeout [%d] is less than connection timeout [%d]", sessionTimeoutMs, connectionTimeoutMs));
}
retryPolicy = Preconditions.checkNotNull(retryPolicy, "retryPolicy cannot be null");
ensembleProvider = Preconditions.checkNotNull(ensembleProvider, "ensembleProvider cannot be null");
this.connectionTimeoutMs = connectionTimeoutMs;
state = new ConnectionState(zookeeperFactory, ensembleProvider, sessionTimeoutMs, connectionTimeoutMs, watcher, tracer, canBeReadOnly);
setRetryPolicy(retryPolicy);
}
ConnectionState实现了Watcher和Closeable接口。
设置了链接地址的配置策略,session过期时间,链接超时时间,设置日志追踪驱动器(使用的默认的DefaultTracerDriver),
将传递进来的watcher放入parentWatchers中,最后定义HandleHolder。
private final Queue<Watcher> parentWatchers = new ConcurrentLinkedQueue<Watcher>();
ConnectionState(ZookeeperFactory zookeeperFactory, EnsembleProvider ensembleProvider, int sessionTimeoutMs,
int connectionTimeoutMs, Watcher parentWatcher, AtomicReference<TracerDriver> tracer, boolean canBeReadOnly)
{
this.ensembleProvider = ensembleProvider;
this.sessionTimeoutMs = sessionTimeoutMs;
this.connectionTimeoutMs = connectionTimeoutMs;
this.tracer = tracer;
if ( parentWatcher != null )
{
parentWatchers.offer(parentWatcher);
}
//这个地方的this就是ConnectionState实现Watcher接口的原因。
zooKeeper = new HandleHolder(zookeeperFactory, this, ensembleProvider, sessionTimeoutMs, canBeReadOnly);
}
//主要用来执行CuratorFrameworkImpl里的监听。
//第一、根据当前ConnectionState的isConnected与事件的状态,
//去检查当前的事件的KeeperState,在checkState过程中,
//若为SyncConnected和ConnectedReadOnly表示为链接状态,其他则为断链状态;
//同时若过期Expired,会重新链接zookeeper,
//不过期的会去判断当前链接地址是否发生变化,若发生也会重新链接zookeeper。
//同时若链接状态与之前的不同再修改状态。(详细的在以后讲去了)
//第二、将parentWatchers里的所有watcher全部调用一次。
@Override
public void process(WatchedEvent event)
{
if ( LOG_EVENTS )
{
log.debug("ConnectState watcher: " + event);
}
if ( event.getType() == Watcher.Event.EventType.None )
{
boolean wasConnected = isConnected.get();
boolean newIsConnected = checkState(event.getState(), wasConnected);
if ( newIsConnected != wasConnected )
{
isConnected.set(newIsConnected);
connectionStartMs = System.currentTimeMillis();
}
}
for ( Watcher parentWatcher : parentWatchers )
{
TimeTrace timeTrace = new TimeTrace("connection-state-parent-process", tracer.get());
parentWatcher.process(event);
timeTrace.commit();
}
}
private boolean checkState(Event.KeeperState state, boolean wasConnected)
{
boolean isConnected = wasConnected;
boolean checkNewConnectionString = true;
switch ( state )
{
default:
case Disconnected:
{
isConnected = false;
break;
}
case SyncConnected:
case ConnectedReadOnly:
{
isConnected = true;
break;
}
case AuthFailed:
{
isConnected = false;
log.error("Authentication failed");
break;
}
//若过期Expired,会重新链接zookeeper
case Expired:
{
isConnected = false;
checkNewConnectionString = false;
handleExpiredSession();
break;
}
case SaslAuthenticated:
{
// NOP
break;
}
}
if ( checkNewConnectionString && zooKeeper.hasNewConnectionString() )
{
handleNewConnectionString();
}
return isConnected;
}
简单的设置5个字段
HandleHolder(ZookeeperFactory zookeeperFactory,
Watcher watcher, EnsembleProvider ensembleProvider,
int sessionTimeout, boolean canBeReadOnly)
{
this.zookeeperFactory = zookeeperFactory;
this.watcher = watcher;
this.ensembleProvider = ensembleProvider;
this.sessionTimeout = sessionTimeout;
this.canBeReadOnly = canBeReadOnly;
}
总结
- 主要使用到CuratorFrameworkImpl,CuratorZookeeperClient,ConnectionState,HandleHolder4个类,一步一步往后定义;
- 同时使用ConnectionStateManager进行链接状态监控;
先校验状态是否为LATENT并设置为STARTED。
启动connectionStateManager。
链接状态管理器中增加一个监听器,用于在链接状态时将client的logAsErrorConnectionErrors设为true。
再启动CuratorZookeeperClient。
executorService定义成2个线程的执行器,1个为监听,1个为后台。并执行后台的循环操作。
public void start()
{
log.info("Starting");
if ( !state.compareAndSet(CuratorFrameworkState.LATENT, CuratorFrameworkState.STARTED) )
{
throw new IllegalStateException("Cannot be started more than once");
}
try
{
connectionStateManager.start(); // ordering dependency - must be called before client.start()
final ConnectionStateListener listener = new ConnectionStateListener()
{
@Override
public void stateChanged(CuratorFramework client, ConnectionState newState)
{
if ( ConnectionState.CONNECTED == newState || ConnectionState.RECONNECTED == newState )
{
logAsErrorConnectionErrors.set(true);
}
}
};
this.getConnectionStateListenable().addListener(listener);
client.start();
executorService = Executors.newFixedThreadPool(2, threadFactory); // 1 for listeners, 1 for background ops
executorService.submit(new Callable<Object>()
{
@Override
public Object call() throws Exception
{
backgroundOperationsLoop();
return null;
}
});
}
catch ( Exception e )
{
handleBackgroundOperationException(null, e);
}
}
启动一个ConnectionStateManager的线程执行者,通过listeners的函数式编程去执行监听状态变化。
/** * Start the manager */
public void start()
{
Preconditions.checkState(state.compareAndSet(State.LATENT, State.STARTED), "Cannot be started more than once");
service.submit
(
new Callable<Object>()
{
@Override
public Object call() throws Exception
{
processEvents();
return null;
}
}
);
}
private void processEvents()
{
try
{
while ( !Thread.currentThread().isInterrupted() )
{ //take方法可以阻塞
final ConnectionState newState = eventQueue.take();
if ( listeners.size() == 0 )
{
log.warn("There are no ConnectionStateListeners registered.");
}
listeners.forEach
(
new Function<ConnectionStateListener, Void>()
{
@Override
public Void apply(ConnectionStateListener listener)
{
listener.stateChanged(client, newState);
return null;
}
}
);
}
}
catch ( InterruptedException e )
{
Thread.currentThread().interrupt();
}
}
需要校验CuratorZookeeperClient是否已启动,若启动抛异常。
再去启动ConnectionState。
public void start() throws Exception
{
log.debug("Starting");
if ( !started.compareAndSet(false, true) )
{
IllegalStateException ise = new IllegalStateException("Already started");
throw ise;
}
state.start();
}
先启动链接地址配置器。
再重设。
在重设中,原子性的增加实例次数1次;
同时将链接状态设为false,
再去启动HandleHolder,
同时最重要一步zooKeeper.getZooKeeper()初始化原生态的链接;
void start() throws Exception
{
log.debug("Starting");
ensembleProvider.start();
reset();
}
private synchronized void reset() throws Exception
{
log.debug("reset");
instanceIndex.incrementAndGet();
isConnected.set(false);
connectionStartMs = System.currentTimeMillis();
zooKeeper.closeAndReset();
zooKeeper.getZooKeeper(); // initiate connection
}
可以看出借用内部接口Helper来完成操作的。
Helper接口有2方法,一个获取原生的zookeeper,一个是获取链接地址路径。
private interface Helper
{
ZooKeeper getZooKeeper() throws Exception;
String getConnectionString();
}
重点还是在closeAndReset方法上。
主要还是在helper上的初始化,
当第一次调用getZooKeeper 时用synchronized 包裹,并会对链接地址和zookeeper定义,同时再次定义helper对象,并将此次定义好的两个值作为返回值去实现此前的2个方法。此刻以后每次调用getZooKeeper 时均从子helper里的方法。避免同时有客户端new zookeeper。
void closeAndReset() throws Exception
{
internalClose();
// first helper is synchronized when getZooKeeper is called. Subsequent calls
// are not synchronized.
helper = new Helper()
{
private volatile ZooKeeper zooKeeperHandle = null;
private volatile String connectionString = null;
@Override
public ZooKeeper getZooKeeper() throws Exception
{
synchronized(this)
{
if ( zooKeeperHandle == null )
{
connectionString = ensembleProvider.getConnectionString();
zooKeeperHandle = zookeeperFactory.newZooKeeper(connectionString, sessionTimeout, watcher, canBeReadOnly);
}
helper = new Helper()
{
@Override
public ZooKeeper getZooKeeper() throws Exception
{
return zooKeeperHandle;
}
@Override
public String getConnectionString()
{
return connectionString;
}
};
return zooKeeperHandle;
}
}
@Override
public String getConnectionString()
{
return connectionString;
}
};
}
ZooKeeper getZooKeeper() throws Exception
{
return (helper != null) ? helper.getZooKeeper() : null;
}