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- ...
随机推荐
- BZOJ 1500 splay终结版...
GSS系列有一丝丝像- 只不过那个是线段树 这个是splay 翻转 插入 删除啥的就是普通的splay 合在一起了而已 //By SiriusRen #include <cstdio> # ...
- Ubuntu14.04下初步使用MongoDB
不多说,直接上干货! Ubuntu14.04下Mongodb(在线安装方式|apt-get)安装部署步骤(图文详解)(博主推荐) shell命令模式 输入mongo进入shell命令模式,默认连接的数 ...
- PHP日期和时间处理组件-Carbon
https://packagist.org/packages/nesbot/carbon 我们使用PHP时经常需要处理日期和时间,有时会被时间时区搞混淆,而Carbon是PHP中很人性化的时间日期处理 ...
- HDU 1176 免费馅饼【动态规划】
解题思路:用a[i][j]表示在第i秒在地点j的掉落馅饼的数量,设整个馅饼掉落的时间持续为timemax,即为矩阵的高度,一共0到10个地点,为矩阵的长度,如图,即可构成数塔,因为考虑到在地点0的时候 ...
- 如何将App从一个账号迁移到另一个账号?
App迁移(App transfer):将App从一个开发者账号迁移至另一个开发者账号.此文演示了整个迁移过程,为了方便解释,在此过程中,将App转出的开发者账号我们下文将会称之为A账号,接收杭州Ap ...
- BZOJ 4229: 选择 LCT_独创方法_边双
考虑如果两点在一个环中,那么这两点一定可以构出双联通分量. 考虑环和环镶嵌,那么两个环中的点一定都互为双联通分量. 由此,我们想到一个算法: 将删边转为反向加边,用LCT维护图. 当我们连接两个点时, ...
- Jquery JS 全局变量
window["a1"]="abc";window["b1"]=5;
- php 文件加载方式
两种加载文件的方式 include require 使用场景: 动态加载文件的时候,使用include,否则使用require. 示例: # 引入php文件--include方式 inlcude(&q ...
- StringUtils 的填充方法
注意:两个参数的用空格填充,三个参数的用后面的参数填充 第一个参数要填充的字符串,第二个是需要的长度,第三个是以什么填充. 左侧填充: leftPad(): StringUtils.leftPad(S ...
- NOIP2018提高组省一冲奖班模测训练(二)
比赛链接 NOIP2018提高组省一冲奖班模测训练(二) 今天发挥正常,昨天不在状态…… 花了很久A了第一题 第二题打了30分暴力 第三题投机取巧输出test1答案(连暴力都不知道怎么打,太弱了) 2 ...