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- ...
随机推荐
- kubernetes系列:(一)、kubeadm搭建kubernetes(v1.13.1)单节点集群
kubeadm是Kubernetes官方提供的用于快速部署Kubernetes集群的工具,本篇文章使用kubeadm搭建一个单master节点的k8s集群. 节点部署信息 节点主机名 节点IP 节点角 ...
- 一个登录页面的spring 逻辑过程
1.首先用户访问login.jsp 2.用户在登录页面输入用户名/密码,提交表单到服务器,Spring根据配置调用LoginController控制器响应登录请求(关键) 3.LoginControl ...
- Extjs 常见错误
http://blog.csdn.net/lc448986375/article/details/8082014
- 客户现场调试(连接oracle数据库)
1.System.Data.OracleClient 需要 Oracle 客户端软件 8.1.7 或更高版本 http://blog.csdn.net/yucaoye/article/details/ ...
- Struts2简单环境搭建
一.开篇 Struts2是一个运行于web容器的表示层框架,其核心作用是帮助我们处理Http请求.Struts2处理Http请求(Request),并进行内部处理,再进行Http返回. 下载strut ...
- GIT 常用方法
代码提交顺序: conmmit(提交代码到本地仓库) --->>> pull(将本地仓库代码合并) ---->>> push(将本地合并后的代码提交到 ...
- struts2学习之基础笔记1
第6章 Strusts 2框架 1 引出 Web App àà MVC àà View 视图(jsp,html,JS) | C(Servlet)Filter,Listneer | M(数据bea ...
- Vue.js 2.x Development Build With Hot Reloading For External Server (using Webpack template)
This article assuming you created your project using webpack template. vue init webpack <PROJECT_ ...
- POJ.grids.2980
题目链接:http://bailian.openjudge.cn/practice/2980 解题思路:先将对应位相乘的积累加,最后再来处理进位问题:如 835*49: 先做 835*9: 得到 i ...
- Unity 需不需要再建Assets文件夹
不需要,默认所有文件都是在Assets文件夹下创建的,看不到是因为设置了单栏模式,开启双栏模式就能看到了.