KafkaZookeeper2-ZookeeperClient
介绍
ZookeeperClient 是 kafka 新写的客户端,它允许用户流水线式(并行)访问 zookeeper。
为什么放弃了 zkClient?
zkClient 是一个第三方的客户端。
它的优点:
- 在session loss和session expire时自动创建新的ZooKeeper实例进行重连。
- 将一次性watcher包装为持久watcher。后者的具体做法是简单的在watcher回调中,重新读取数据的同时再注册相同的watcher实例。[1]
它的缺点:
- zkClient 在处理请求的时候,只能同步的访问处理。当 kafka 的 partition 个数过多的时候,同时请求 zookeeper 就会造成性能的瓶颈。
因为上述的缺点,ZookeeperClient 在访问的时候采用了异步的访问方式,并且采用了批量处理的方式。
如何批量并行访问
1. 获取消息请求队列。
2. 并行处理每个请求。
3. 将所有的请求结果保存在一个列表中返回。
这里需要考虑几种情况?
多线程并发请求,如何等待所有请求处理完成再返回?
CountDownLatch。
假如一个请求队列中的请求太多了,一次性访问 zookeeper 容易过载。怎么办?
控制同时访问 zookeeper 的请求个数。 使用 Semaphore 来实现。
如何异步访问呢?
org.apache.zookeeper已经为我们实现了。不需要考虑了。
综上,再看 zookeeperClient 的实现:
// 设定同时访问 zookeeper 的最大请求个数
private val inFlightRequests = new Semaphore(maxInFlightRequests)
// 这里 inReadLock(initializationLock) 在某种情况下会产生死锁。4551 修复了这个问题
def handleRequests[Req <: AsyncRequest](requests: Seq[Req]): Seq[Req#Response] = inReadLock(initializationLock) {
if (requests.isEmpty)
Seq.empty
else {
// 设定 CountDownLatch,当前队列的所有请求处理完再返回
val countDownLatch = new CountDownLatch(requests.size)
// 保存处理结果
val responseQueue = new ArrayBlockingQueue[Req#Response](requests.size)
requests.foreach { request =>
// 通过 semaphore 控制多个线程同时访问的最大请求
inFlightRequests.acquire()
try {
// 异步访问
send(request) { response =>
responseQueue.add(response)
inFlightRequests.release()
countDownLatch.countDown()
}
} catch {
case e: Throwable =>
inFlightRequests.release()
throw e
}
}
// 等待所有请求处理完
countDownLatch.await()
// 返回
responseQueue.asScala.toBuffer
}
}
session 如何自动重连?
通过重写 watcher 的 process 函数,在函数中判断当前 zookeeper 对象是否过期,如果过期,就关闭老的,并重新创建一个新的。
// package level visibility for testing only
private[zookeeper] object ZooKeeperClientWatcher extends Watcher {
override def process(event: WatchedEvent): Unit = {
debug(s"Received event: $event")
Option(event.getPath) match {
case None =>
... 发现过期了
} else if (state == KeeperState.Expired) {
inWriteLock(initializationLock) {
info("Session expired.")
// 初始化
initialize()
}
}
... 如果是其他类型的event, 调用相应的handler
}
}
}
private def initialize(): Unit = {
if (!connectionState.isAlive) {
zooKeeper.close()
info(s"Initializing a new session to $connectString.")
// retry forever until ZooKeeper can be instantiated
var connected = false
while (!connected) {
try {
zooKeeper = new ZooKeeper(connectString, sessionTimeoutMs, ZooKeeperClientWatcher)
connected = true
} catch {
case e: Exception =>
info("Error when recreating ZooKeeper, retrying after a short sleep", e)
Thread.sleep(1000)
}
}
}
}
持久 watcher
持久 watcher 就是指在每次请求的时候,都添加相应的 watcher。 kafka 的做法是将所有需要添加 watcher 的路径保存在一个集合中,当请求 zookeeper 的时候, 判断集合中是否包含相应的路径,如果包含就添加 watcher。
1. 保存对应的路径
private val zNodeChangeHandlers = new ConcurrentHashMap[String, ZNodeChangeHandler]().asScala
private val zNodeChildChangeHandlers = new ConcurrentHashMap[String, ZNodeChildChangeHandler]().asScala
2. 添加路径
def registerZNodeChangeHandler(zNodeChangeHandler: ZNodeChangeHandler): Unit = {
zNodeChangeHandlers.put(zNodeChangeHandler.path, zNodeChangeHandler)
}
3. 判断是否存在
private def shouldWatch(request: AsyncRequest): Boolean = request match {
case _: GetChildrenRequest => zNodeChildChangeHandlers.contains(request.path)
case _: ExistsRequest | _: GetDataRequest => zNodeChangeHandlers.contains(request.path)
case _ => throw new IllegalArgumentException(s"Request $request is not watchable")
}
4. 请求的时候做判断
private def send[Req <: AsyncRequest](request: Req)(processResponse: Req#Response => Unit): Unit = {
// Safe to cast as we always create a response of the right type
def callback(response: AsyncResponse): Unit = processResponse(response.asInstanceOf[Req#Response])
def responseMetadata(sendTimeMs: Long) = new ResponseMetadata(sendTimeMs, receivedTimeMs = time.hiResClockMs())
val sendTimeMs = time.hiResClockMs()
request match {
case ExistsRequest(path, ctx) =>
zooKeeper.exists(path, shouldWatch(request), new StatCallback {
override def processResult(rc: Int, path: String, ctx: Any, stat: Stat): Unit =
callback(ExistsResponse(Code.get(rc), path, Option(ctx), stat, responseMetadata(sendTimeMs)))
}, ctx.orNull)
}
}
参考
[1] ZooKeeper(四)-- 第三方客户端 ZkClient的使用
KafkaZookeeper2-ZookeeperClient的更多相关文章
- zookeeperclient代码解读
近期一直在忙WebPageTest(下面简称wpt)开源库的改动工作,当中一项工作须要将zookeeper(下面简称zk)集成到wpt里. zk作为分布式系统的同步工具.实现了写的原子性(要么失败.要 ...
- zookeeperclient设置监听
1.目的 zookeeper是一个分布式服务管理框架.zookeeper提供了对client的通知.即在server端的节点有改动或者删除的时候,能够给client进行通知. 2.server端部署 ...
- dubbo连接zookeeper注册中心因为断网导致线程无限等待问题【转】
最近维护的系统切换了网络环境,由联通换成了电信网络,因为某些过滤规则导致系统连不上zookeeper服务器(应用系统机器在深圳,网络为电信线路,zookeeper服务器在北京,网络为联通线路),因为我 ...
- 支持断线重连、永久watcher、递归操作并且能跨平台(.NET Core)的ZooKeeper异步客户端
在公司内部的微服务架构中有使用到了"ZooKeeper",虽然官方有提供了.NET的SDK,但易用性非常的差,且搜遍github.nuget,没有发现一个可以跨平台且易用的组件,所 ...
- Apache curator-client详解
Apache curator框架中curator-client组件可以作为zookeeper client来使用,它提供了zk实例创建/重连机制等,简单便捷.不过直接使用curator-client并 ...
- 基于ZooKeeper的Dubbo注册中心
SOA服务治理 dubbo_zk 服务总线 感兴趣的M我微信:wonter 微信扫描,人人 CTO 大本营 基于SOA架构的TDD测试驱动开发模式 服务治理要先于SOA 简述我的SOA服务治理 从页面 ...
- .NET Core)的ZooKeeper异步客户端
支持断线重连.永久watcher.递归操作并且能跨平台(.NET Core)的ZooKeeper异步客户端 阅读目录 什么是ZooKeeper? 项目介绍 提供的功能 使用说明 FAQ 在公司内部 ...
- 彻底删除Kafka中的topic
1.删除kafka存储目录(server.properties文件log.dirs配置,默认为"/tmp/kafka-logs")相关topic目录 2.Kafka 删除topic ...
- dubbo作为消费者注册过程分析
请支持原创: http://www.cnblogs.com/donlianli/p/3847676.html 作者当前分析的版本为2.5.x.作者在分析的时候,都是带着疑问去查看代码,debug进 ...
- org.apache.hadoop.hbase.TableExistsException: hbase:namespace
Problem is here : https://community.cloudera.com/t5/Storage-Random-Access-HDFS/HMaster-not-starting- ...
随机推荐
- 粘性固定属性 -- position:sticky
概述 position: sticky,这是一个比较容易忽略的css3 position 新属性,它的作用即为实现粘性布局,它是 relative 与 fixed 的结合. 用法 默认情况下,其表现为 ...
- UINavi中push控制器的时候隐藏TabBar
当一个UITabbarController管理多个UINavigationController的时候,我们又从这每一个UINavigationController中push一个ViewControll ...
- thrift - C#(CSharp)客户端连接池(ConnectionPool)
调用示例: var tran = ThriftPool.Instance().BorrowInstance(); TProtocol protocol = new TBinaryProtoco ...
- Unity脚本中可以引用的类型
Hierarchy(层级视图)面板里的对象,或者 Project(工程视图)里的Prefab.
- nginx学习地址
http://www.cnblogs.com/magicsoar/p/5817734.html
- node——Commonjs
ECMA只规范了js最近的一些能力,并没有规范当要用js写一个庞大的项目如果使用模块化开发. CommonJS规范了当我们想开发大型程序的时候如何模块化来开发,以及模块化的时候,不同文件的通讯. Co ...
- You-Dont-Need-JQuery (你不需要JQuery)
看完这篇文章我才觉得真的要用JQuery ,因为实在是有些地方设计的使用太复杂了, document.querySelector() 和 Document.querySelectorAll 的确是很方 ...
- gcp – 源于CP的高级命令行文件拷贝工具
作者:linux 出处:http://linux.cn/thread/11868/1/1/ gcp – 源于CP的高级命令行文件拷贝工具 几周前,我们讨论了高级拷贝(修改于cp命令,让其可以显示复制进 ...
- node 常用命令行
安装模块命令 npm install moduleName –save npm install moduleName npm install npm start express创建项目目录 expre ...
- vue2.0变化(转载)
原文链接:https://www.cnblogs.com/itbainianmei/p/6062249.html 1.每个组件模板template,不再支持片段代码 之前: <template& ...