ZooKeeper
ZooKeeper watch的过程(会重试吗,有ack吗)
zookeeper的watch通知不是可靠的,没有进行ack.
public class NIOServerCnxn extends ServerCnxn {
@Override
public void process(WatchedEvent event) {
ReplyHeader h = new ReplyHeader(ClientCnxn.NOTIFICATION_XID, event.getZxid(), 0);
if (LOG.isTraceEnabled()) {
ZooTrace.logTraceMessage(
LOG,
ZooTrace.EVENT_DELIVERY_TRACE_MASK,
"Deliver event " + event + " to 0x" + Long.toHexString(this.sessionId) + " through " + this);
}
// Convert WatchedEvent to a type that can be sent over the wire
WatcherEvent e = event.getWrapper();
// The last parameter OpCode here is used to select the response cache.
// Passing OpCode.error (with a value of -1) means we don't care, as we don't need
// response cache on delivering watcher events.
int responseSize = sendResponse(h, e, "notification", null, null, ZooDefs.OpCode.error);
ServerMetrics.getMetrics().WATCH_BYTES.add(responseSize);
}
@Override
public int sendResponse(ReplyHeader h, Record r, String tag, String cacheKey, Stat stat, int opCode) {
int responseSize = 0;
try {
ByteBuffer[] bb = serialize(h, r, tag, cacheKey, stat, opCode);
responseSize = bb[0].getInt();
bb[0].rewind();
sendBuffer(bb);
decrOutstandingAndCheckThrottle(h);
} catch (Exception e) {
LOG.warn("Unexpected exception. Destruction averted.", e);
}
return responseSize;
}
}
ZooKeeper 怎么保证有序执行
zookeeper采用了全局递增的事务id来标识所有的proposal, zxid 为事务id,是一个64位的数字。高32位为epoch, 用来标识 leader 周期,如果有新的 leader 选举出来,epoch 会自增。低32位用来递增proposal。依赖两阶段提交过程,首先向其他server发出事务执行请求, 如果超过半数机器都执行成功,那么事务才会被提交。
为什么ZooKeeper 重连时要使用老的 session_id,而不是完全重新建立连接
zookeeper的 session 分为 local session 和 global session。
global session 的连接信息保存在所有的 server 节点,所以创建 global session 开销比较大,需要经过共识算法。
在有效期内进行重新连接不需要经过共识算法,开销低还能恢复到断开连接前的工作状态。
只有 global session 支持创建临时节点。
persistent recursive watch 不会触发 NodeChildrenChanged 事件
持久递归监视器会触发 NodeCreated, NodeDeleted, NodeDataChanged 三个事件,同时如果需要递归的话,则是对子节点也触发这三个事件,所以 NodeChildrenChanged 就显得多余了。
zookeeper的监听器设计成一次性的是位性能考虑,如果server端的每次数据更新都需要同步客户端,在并发度高的情况下,性能下降会很明显。 持久递归监听器其实也是一次性的,跟普通的 watcher 一样,当递归监听器的某个子节点事件被触发之后,在对其调用 getData() 之前,不会再次触发事件。
ZooKeeper怎么保证客户端先接收到 watch 事件,然后才能看到数据
a client will never see a change for which it has set a watch until it first sees the watch event. ZooKeeper确保了事件和异步应答的有序分发,具体实现细节是通过synchronized加锁把所有的出站数据进行入队列操作。
public class NIOServerCnxn extends ServerCnxn {
private final Queue<ByteBuffer> outgoingBuffers = new LinkedBlockingQueue<>();
/**
* sendBuffer pushes a byte buffer onto the outgoing buffer queue for
* asynchronous writes.
*/
public void sendBuffer(ByteBuffer... buffers) {
if (LOG.isTraceEnabled()) {
LOG.trace("Add a buffer to outgoingBuffers, sk {} is valid: {}", sk, sk.isValid());
}
synchronized (outgoingBuffers) {
for (ByteBuffer buffer : buffers) {
outgoingBuffers.add(buffer);
}
outgoingBuffers.add(packetSentinel);
}
requestInterestOpsUpdate();
}
}
ZooKeeper的watch维护在local server
Watches are maintained locally at the ZooKeeper server to which the client is connected.
ZooKeeper SSL 为什么需要客户端提供keystore
ZooKeeper的客户端在重连时处理了 "羊群效应" 问题
Only create a new session when you are notified of session expiration (mandatory). 只有在session过期时才创建新的session,其他情况下都使用老的session_id进行连接,这样就不用经过共识算法投票。