Curator源码阅读 - ConnectionState的管理与监听
查看Curator
框架 为实现对 连接状态ConnectionState
的管理与监听是怎么构造的。后面我们也可以应用到业务的各种监听中。
Curator2.13实现
接口 Listener
Listener
接口,给用户实现stateChange()
传入新的状态,用户实现对这新的状态要做什么逻辑处理。
public interface ConnectionStateListener
{
/**
* Called when there is a state change in the connection
* @param client the client
* @param newState the new state
*/
public void stateChanged(CuratorFramework client, ConnectionState newState);
}
接口 Listenable
提供一个监听对象容器的接口
// Abstracts a listenable object
public interface Listenable<T>
{
/**
* Add the given listener. The listener will be executed in the containing instance's thread.
*
* @param listener listener to add
*/
public void addListener(T listener);
public void addListener(T listener, Executor executor);
public void removeListener(T listener);
}
ListenerContainer<T> implements Listenable<T>
/**
* Abstracts an object that has listeners 装Listener的容器
* <T> Listener类型
*/
public class ListenerContainer<T> implements Listenable<T>
{
private final Map<T, ListenerEntry<T>> listeners = Maps.newConcurrentMap();
@Override
public void addListener(T listener)
{
addListener(listener, MoreExecutors.sameThreadExecutor());
}
@Override
public void addListener(T listener, Executor executor)
{
listeners.put(listener, new ListenerEntry<T>(listener, executor));
}
/**
* 对 Listener 列表的遍历进行封装
* Utility - apply the given function to each listener.
* @param function function to call for each listener
*/
public void forEach(final Function<T, Void> function)
{
for ( final ListenerEntry<T> entry : listeners.values() )
{
entry.executor.execute
(
new Runnable()
{
@Override
public void run()
{
try
{
function.apply(entry.listener);
}
catch ( Throwable e )
{
ThreadUtils.checkInterrupted(e);
log.error(String.format("Listener (%s) threw an exception", entry.listener), e);
}
}
}
);
}
}
public void clear()
{
listeners.clear();
}
public int size()
{
return listeners.size();
}
}
ConnectionStateManager
// to manage connection state
public class ConnectionStateManager {
// 又是队列? 玩消息什么的都是用队列。现在是存放 ConnectionState
BlockingQueue<ConnectionState> eventQueue = new ArrayBlockingQueue<ConnectionState>(QUEUE_SIZE);
// 持有 ListenerContainer
private final ListenerContainer<ConnectionStateListener> listeners = new ListenerContainer<ConnectionStateListener>();
/**
* Start the manager,起一个线程去执行 processEvents(),要是这线程挂了怎么办?异常怎么处理的?框架怎么处理的。。
*/
public void start()
{
service.submit
(
new Callable<Object>()
{
@Override
public Object call() throws Exception
{
processEvents();
return null;
}
}
);
}
@Override
public void close()
{
if ( state.compareAndSet(State.STARTED, State.CLOSED) )
{
service.shutdownNow();
listeners.clear();
}
}
// 对不断产生的 ConnectionState 进行处理,生产者?
private void processEvents(){
// 当 ConnectionStateManager 启动完成
while ( state.get() == State.STARTED )
{
// 不断从队列拿 Conection 状态
final ConnectionState newState = eventQueue.take();
// 对每个 状态监听接口 应用 Function, 状态监听接口作为 主语
// forEach 是 listeners封装的 遍历所有 listener 的方法而已。。。
listeners.forEach(
new Function<ConnectionStateListener, Void>() {
// ConnectionStateListener是我们自己要实现的接口,stateChanged是要实现的方法
@Override
public Void apply(ConnectionStateListener listener)
{
listener.stateChanged(client, newState);
return null;
}
}
);
/**
上面这段
如果没有封装 Listener 到 ListenerContainer 的话, 所有 Listener 就是个 List列表,就直接调 Listener 的 stateChanged 方法了吧。
for Listener {
listener.stateChanged(client, newState);
}
因为 封装 Listener 到 ListenerContainer了, 上面的 forEach 方法内部就可以有些内部实现,比如 对每个 Listener 都是用对应的 executor 来执行。
**/
}
}
// 上面的方法是处理 ConnectionState 的,那 ConnectionState 是怎么传进来的呢? 生产者?
/**
* Post a state change. If the manager is already in that state the change
* is ignored. Otherwise the change is queued for listeners.
*
* @param newConnectionState new state
* @return true if the state actually changed, false if it was already at that state
*/
public synchronized boolean addStateChange(ConnectionState newConnectionState)
{
// 先判断 ConnectionStateManager 是否已经启动好, state 是内部 Enum
if ( state.get() != State.STARTED )
{
return false;
}
ConnectionState previousState = currentConnectionState;
if ( previousState == newConnectionState )
{
return false;
}
ConnectionState localState = newConnectionState;
// !!!
notifyAll();
while ( !eventQueue.offer(state) )
{
eventQueue.poll();
log.warn("ConnectionStateManager queue full - dropping events to make room");
}
return true;
}
}
调用
启动
// 启动 connectionStateManager,不断检测 connectionState 变化
connectionStateManager.start(); // must be called before client.start()
// 来个匿名默认的 ConnectionStateListener
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);
生产 ConnectionState
,把zk
那里拿到的state
转一下,然后addStateChange
void validateConnection(Watcher.Event.KeeperState state)
{
if ( state == Watcher.Event.KeeperState.Disconnected )
{
suspendConnection();
}
else if ( state == Watcher.Event.KeeperState.Expired )
{
connectionStateManager.addStateChange(ConnectionState.LOST);
}
else if ( state == Watcher.Event.KeeperState.SyncConnected )
{
connectionStateManager.addStateChange(ConnectionState.RECONNECTED);
}
else if ( state == Watcher.Event.KeeperState.ConnectedReadOnly )
{
connectionStateManager.addStateChange(ConnectionState.READ_ONLY);
}
}
复用?
还有其他各种Listener
,都可以放到 ListenerContainer
private final ListenerContainer<CuratorListener> listeners;
private final ListenerContainer<UnhandledErrorListener> unhandledErrorListeners;
/**
* Receives notifications about errors and background events
*/
public interface CuratorListener {
/**
* Called when a background task has completed or a watch has triggered
* @param event the event
* @throws Exception any errors
*/
public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception;
}
public interface UnhandledErrorListener
{
/**
* Called when an exception is caught in a background thread, handler, etc. Before this
* listener is called, the error will have been logged and a {@link ConnectionState#LOST}
* event will have been queued for any {@link ConnectionStateListener}s.
* @param message Source message
* @param e exception
*/
public void unhandledError(String message, Throwable e);
}
总结一下源码技巧
ConnectionState
的监听和管理在类ConnectionStateManager
中, 就是个 生产者消费者模式的代码,特点就是:public addStateChange()
暴露给外部用户生产ConnectionState
,通过队列eventQueue
传递,private processEvents()
在内部对ConnectionState
进行消费。- 直接
new
匿名类,对接口进行默认实现。 - 对
Listener
列表对象进行Container
封装,然后 封装foreach
方法,传入Function
接口 就是foreach
每个元素要执行的业务逻辑,方法体就可以加一些其他福利。
Curator4.2.0实现
curator4.2.0对ConnectionStateListener
进行了一些改进,如下:
1. 装饰ConnectionStateListener,Ccurator-505
当网络断开时,zk会传送很多 connection/disconnection event
,为防止 Curator
因此而不断reseting state
,该issue提供了 一种对监听回路
进行切断或者连接的ConnectionStateListener
, 它会对ConnectionStateListeners
进行装饰代理。当它接受到 ConnectionState.SUSPENDED
,监听回路会切断变成open
状态,这段时间会组织发送state
给所有Listener
,就忽略了对connection state
的改变。时间到期后监听回路会变成close
状态,并且发送当前的connection state
。简而言之,当不断出现up/down/up/down/up/down
的中间状态,使用者将只看到first down
,然后N ms之后连接能够修复的话将只看到重连状态。
Create a circuit breaker style
ConnectionStateListener
. It would proxy any ConnectionStateListeners used by Curator recipe/classes such that when the connection is lost the circuit would open for a period of time and, while open, ignore any changes in state. After the time period expires the circuit would close and send whatever the current connection state is. This way, if the connection is going up/down/up/down/up/down, the application would only see the first down and then N ms later hopefully the connection is repaired and the application would only see the reconnection.
curator-505,结合源码中的testcase更易于理解。
接口ConnectionStateListenerDecorator
该接口用来统一返回是否装饰的ConnectionStateListener
public interface ConnectionStateListenerDecorator
{
ConnectionStateListener decorateListener(CuratorFramework client, ConnectionStateListener actual);
// 不对 ConnectionStateListener 进行装饰
ConnectionStateListenerDecorator standard = (__, actual) -> actual;
/**
* Decorates the listener with circuit breaking behavior
* @param retryPolicy the circuit breaking policy to use
* @return new decorator
*/
static ConnectionStateListenerDecorator circuitBreaking(RetryPolicy retryPolicy) {
return (client, actual) -> new CircuitBreakingConnectionStateListener(client, actual, retryPolicy);
}
/**
* Decorates the listener with circuit breaking behavior
* @param retryPolicy the circuit breaking policy to use
* @param service the scheduler to use
* @return new decorator
*/
static ConnectionStateListenerDecorator circuitBreaking(RetryPolicy retryPolicy, ScheduledExecutorService service)
{
return (client, actual) -> new CircuitBreakingConnectionStateListener(client, actual, retryPolicy, service);
}
}
CircuitBreakingConnectionStateListener
对ConnectionStateListener
进行装饰:正常情况下,decorator
为close
为连接状态,会将state
传给所有Listener
。当decorator
接收到第一个disconnected state
,监听回路变为open
即断开状态,不再将state
传给各Listener
(第一个disconnected state
状态会传),而是decorator
起线程来通过RetryPolicy
进行重试,当重试次数达到一定程度会将close
断路 。
public class CircuitBreakingConnectionStateListener implements ConnectionStateListener
{
// 对该 Listener 进行装饰
private final ConnectionStateListener listener;
private final CircuitBreaker circuitBreaker;
// guarded by sync
private boolean circuitLostHasBeenSent;
private ConnectionState circuitLastState;
private ConnectionState circuitInitialState;
// 同步方法?
@Override
public synchronized void stateChanged(CuratorFramework client, ConnectionState newState)
{
// 断路器被打开了,newState 将不传给 Listener 处理
if ( circuitBreaker.isOpen() ) {
handleOpenStateChange(newState);
} else {
handleClosedStateChange(newState);
}
}
private synchronized void handleOpenStateChange(ConnectionState newState)
{
if ( circuitLostHasBeenSent || (newState != ConnectionState.LOST) )
{
// Circuit is open. Ignoring state change !!状态变化都自己收了。。
circuitLastState = newState;
}
else
{
// Circuit is open. State changed to LOST. Sending to listener (第一次 Lost 信息还是会传给 Listener,也就是用个boolean来判断)
circuitLostHasBeenSent = true;
circuitLastState = circuitInitialState = ConnectionState.LOST;
callListener(ConnectionState.LOST);
}
}
// 正常状态下走这里,将state传给Listener
private synchronized void handleClosedStateChange(ConnectionState newState)
{
// 接收到不是connection状态的就尝试打开断路器
if ( !newState.isConnected() ) {
// checkCloseCircuit 作为 调Runnable的匿名函数
if ( circuitBreaker.tryToOpen(this::checkCloseCircuit) )
{
circuitLastState = circuitInitialState = newState;
circuitLostHasBeenSent = (newState == ConnectionState.LOST);
}
else{
log.debug("Could not open circuit breaker. State: {}", newState);
}
}
callListener(newState);
}
// 通过线程池执行该方法,作为Runnable 匿名函数传入
private synchronized void checkCloseCircuit() {
// 连接上了就关闭断路
if ( (circuitLastState == null) || circuitLastState.isConnected() ) {
closeCircuit();
}
// 没连接上,还在 retry 的时候就 保持断路
else if ( circuitBreaker.tryToRetry(this::checkCloseCircuit) ) {
// Circuit open is continuing due to retry
}
// 经过 retryPolicy 的 不断 retry后还没连上,累了,就关闭断路
else {
// Circuit is closing due to retries exhausted
closeCircuit();
}
}
}
2. 重构 ListenerContainer
ListenerManager
这个ListenerManager
就是从ListenerContainer
演进而来的,PR上是说为了不再依赖Guava
并且支持mappingmapping
。和原来一样也封装了对Listener
列表遍历的forEach(Consumer<V> function)
/**
* Upgraded version of {@link org.apache.curator.framework.listen.ListenerContainer} that
* doesn't leak Guava's internals and also supports mapping/wrapping of listeners
*/
public class MappingListenerManager<K, V> implements ListenerManager<K, V> {
@Override
public void forEach(Consumer<V> function)
{
for ( ListenerEntry<V> entry : listeners.values() )
{
entry.executor.execute(() -> {
try {
function.accept(entry.listener);
}
catch ( Throwable e ) {
ThreadUtils.checkInterrupted(e);
log.error(String.format("Listener (%s) threw an exception", entry.listener), e);
}
});
}
}
}
源码技巧
- 断路器?就是装饰器模式,增加了一些额外的
if-else
逻辑。 - 将原来Guava的
Function
换成了原生的Consumer
,实现传入function
进行处理的逻辑。