kafka consumer group的删除和offset恢复
来源:互联网 发布:淘宝联盟本月预估收入 编辑:程序博客网 时间:2024/05/05 02:40
运维kafka时,会有时需要删除无效或已下线的consumer group,若设置的offset保存位置是zookeepr,则数据都在zookeeper中,可自行删除,方式就是删除zookeeper相应节点。
kafka的offset若想按照生产时间进行恢复,事实严重不准,具体可参见:关于kafka中的timestamp与offset的对应关系
恢复步骤:
1. 获取该consumer group消费topic的各partition该时间的offset
2. 在zookeeper中修改这些offset
进行操作期间需要暂停该group对topic的消费,恢复offset后再重启,否则修改不生效。
上代码!
ResetOffsetOperator.resetOffset(topic, group, null, whichTime);ZookeeperOperator zookeeperOperator = new ZookeeperOperator();zookeeperOperator.deleteUselessConsumer("test1");
public class ZookeeperOperator implements Watcher { private ZooKeeper zooKeeper = null; private static final Logger log = LoggerFactory .getLogger(ZookeeperOperator.class); public ZookeeperOperator() throws IOException { this(KafkaConf.ZOOKEEPERHOST); } public ZookeeperOperator(String zookeeperHost) throws IOException { zooKeeper = new ZooKeeper(zookeeperHost, 5000, ZookeeperOperator.this); } public void close() { try { zooKeeper.close(); } catch (InterruptedException e) { log.error("Failed to close zookeeper:", e); } } /** * 读取指定节点下孩子节点数目 * * @param path 节点path * @return */ private int readChildren(String path) { try { return zooKeeper.getChildren(path, false, null).size(); } catch (KeeperException e) { log.error("Read error,KeeperException:" + path, e); return 0; } catch (InterruptedException e) { log.error("Read error,InterruptedException:" + path, e); return 0; } } private List<String> getChildrenList(String path) { try { return zooKeeper.getChildren(path, false, null); } catch (KeeperException e) { log.error("Read error,KeeperException:" + path, e); return null; } catch (InterruptedException e) { log.error("Read error,InterruptedException:" + path, e); return null; } } private boolean setData(String path, String data) { try { zooKeeper.setData(path, data.getBytes(), -1); } catch (KeeperException e) { log.error("Set error,KeeperException:" + path + " data:" + data, e); return false; } catch (InterruptedException e) { log.error("Set error,InterruptedException:" + path + " data:" + data, e); return false; } return true; } private boolean deleteData(String path) { try { zooKeeper.delete(path, -1); } catch (InterruptedException e) { log.error("delete error,InterruptedException:" + path, e); return false; } catch (KeeperException e) { log.error("delete error,KeeperException:" + path, e); return false; } return true; } private boolean recursivelyDeleteData(String path) { List<String> childList = getChildrenList(path); if (childList == null) { return false; } else if (childList.isEmpty()) { deleteData(path); } else { for (String childName : childList) { String childPath = path + "/" + childName; List<String> grandChildList = getChildrenList(childPath); if (grandChildList == null) { return false; } else if (grandChildList.isEmpty()) { deleteData(childPath); } else { recursivelyDeleteData(childPath); } } deleteData(path); } return true; } private String getData(String path) { try { return new String(zooKeeper.getData(path, false, null)); } catch (KeeperException e) { log.error("Read error,KeeperException:" + path, e); return ""; } catch (InterruptedException e) { log.error("Read error,InterruptedException:" + path, e); return ""; } } /** * 读取指定节点下孩子节点数目 * * @param topic kafka topic 名称 * @return */ public int readTopicChildren(String topic) { StringBuilder sb = new StringBuilder().append("/brokers/topics/") .append(topic).append("/partitions"); return readChildren(sb.toString()); } public boolean setTopicGroupOffset(String topic, String group, String partition, String data) { StringBuilder sb = new StringBuilder().append("/consumers/").append(group) .append("/offsets/").append(topic).append("/").append(partition); return setData(sb.toString(), data); } public String getTopicGroupOffset(String topic, String group, String partition) { StringBuilder sb = new StringBuilder().append("/consumers/").append(group) .append("/offsets/").append(topic).append("/").append(partition); System.out.println(sb.toString()); return getData(sb.toString()); } public boolean deleteUselessConsumer(String topic, String group) { if (topic.endsWith("-1")) { StringBuilder sb = new StringBuilder().append("/consumers/") .append(group); return recursivelyDeleteData(sb.toString()); } else { StringBuilder sb = new StringBuilder().append("/consumers/").append(group) .append("/offsets/").append(topic); return recursivelyDeleteData(sb.toString()); } } public boolean deleteUselessLikeConsumer(String topic, String group) { String path = "/consumers"; List<String> childList = getChildrenList(path); int success = 0; int count = 0; for (String child : childList) { if (child.startsWith(group)) { count++; if (deleteUselessConsumer(topic, child)) { success++; } } } if (success == count) { return true; } else { return false; } } public boolean deleteUselessLikeConsumer(String group) { return deleteUselessLikeConsumer("-1", group); } public boolean deleteUselessConsumer(String group) { return deleteUselessConsumer("-1", group); } @Override public void process(WatchedEvent event) { log.info("Receive Event:" + event.getState()); }}
public class ResetOffsetOperator { public static boolean resetOffset(final String topic, String group, Properties properties, long whichTime) throws IOException { if (StringUtils.isBlank(topic) || StringUtils.isBlank(group)) { System.err.println("topic or group can not be null or empty!"); System.exit(2); } if (properties == null) { properties = new Properties(); properties.setProperty("zookeeper.connect", KafkaConf.ZOOKEEPERHOST); properties.setProperty("group.id", group); // zookeeper最大超时时间,就是心跳的间隔,若是没有反映,那么认为已经死了,不易过大 properties.setProperty("zookeeper.session.timeout.ms", "10000"); // ZooKeeper集群中leader和follower之间的同步时间 properties.setProperty("zookeeper.sync.time.ms", "2000"); // 自动提交offset到zookeeper的时间间隔 properties.setProperty("auto.commit.interval.ms", "3000"); // 当zookeeper中没有初始的offset时候的处理方式 。smallest :重置为最小值 largest:重置为最大值 anything else:抛出异常 properties.setProperty("auto.offset.reset", "largest"); } ZookeeperOperator zookeeper = new ZookeeperOperator( properties.getProperty("zookeeper.connect")); GroupOperator groupOperator = new GroupOperator(topic, group, whichTime, zookeeper); return groupOperator.retryResetGroupOffset(); }}
public class GroupOperator { private static final Logger LOG = LoggerFactory .getLogger(GroupOperator.class); private List<String> m_replicaBrokers; private ZookeeperOperator zookeeper; private String topic; private List<String> seeds; private int port; private long whichTime; private int partitionNum; private String group; private int retryNum = 5; /** * 初始化 * * @param topic * @param whichTime timestamp(13位)/-1(latest)/-2(earliest) * @throws java.io.IOException */ public GroupOperator(String topic, String group, long whichTime, ZookeeperOperator zookeeper) throws IOException { this.topic = topic; this.group = group; this.whichTime = whichTime; m_replicaBrokers = new ArrayList<String>(); seeds = KafkaConf.getBrokerHost(); port = Integer.parseInt(KafkaConf.BROKERPORT); this.zookeeper = zookeeper; partitionNum = zookeeper.readTopicChildren(topic); } /** * 将zookeeper中该group对应该topic下的所有分区的offset恢复为所希望的时间 */ public boolean resetGroupOffset() { List<Long> offsetList = new ArrayList<Long>(); for (int partition = 0; partition < partitionNum; partition++) { long offset = getOffset(partition); if (offset == -1) { LOG.error("Failed to get offset of " + group + " with partition:" + partition); return false; } else { offsetList.add(offset); } } for (int partition = 0; partition < partitionNum; partition++) { boolean isSuccess = zookeeper .setTopicGroupOffset(topic, group, String.valueOf(partition), String.valueOf(offsetList.get(partition))); if (!isSuccess) { LOG.error("Failed to reset offset of topic:" + topic + " group:" + group + " partition" + partition + " value:" + offsetList.get(partition)); return false; } } return true; } public boolean retryResetGroupOffset() { for (int retry = 0; retry < retryNum; retry++) { if (resetGroupOffset()) { return true; } } return false; } /** * 获取该partition要恢复时间的offset * * @param partition * @return */ public long getOffset(int partition) { // find the meta data about the topic and partition we are interested in PartitionMetadata metadata = findLeader(partition); if (metadata == null) { LOG.error("Can't find metadata for Topic and Partition. Exiting"); return -1; } if (metadata.leader() == null) { LOG.error("Can't find Leader for Topic and Partition. Exiting"); return -1; } String leadBroker = metadata.leader().host(); String clientName = "Client_" + topic + "_" + partition; SimpleConsumer consumer = new SimpleConsumer(leadBroker, port, 100000, 64 * 1024, clientName); long readOffset = getAssignedOffset(consumer, partition, clientName); if (consumer != null) { consumer.close(); } return readOffset; } /** * Finding the Lead Broker for a Topic and Partition * * @param a_partition 分区id,从0开始 * @return */ private PartitionMetadata findLeader(int a_partition) { PartitionMetadata returnMetaData = null; loop: for (String seed : seeds) { SimpleConsumer consumer = null; try { consumer = new SimpleConsumer(seed, port, 100000, 64 * 1024, "leaderLookup"); List<String> topics = Collections.singletonList(topic); TopicMetadataRequest req = new TopicMetadataRequest(topics); kafka.javaapi.TopicMetadataResponse resp = consumer.send(req); List<TopicMetadata> metaData = resp.topicsMetadata(); for (TopicMetadata item : metaData) { for (PartitionMetadata part : item.partitionsMetadata()) { if (part.partitionId() == a_partition) { returnMetaData = part; break loop; } } } } catch (Exception e) { LOG.error("Error communicating with Broker [" + seed + "] to find Leader for [" + topic + ", " + a_partition + "] Reason: " + e); } finally { if (consumer != null) consumer.close(); } } if (returnMetaData != null) { m_replicaBrokers.clear(); for (kafka.cluster.Broker replica : returnMetaData.replicas()) { m_replicaBrokers.add(replica.host()); } } return returnMetaData; } public long getAssignedOffset(SimpleConsumer consumer, int partition, String clientName) { TopicAndPartition topicAndPartition = new TopicAndPartition(topic, partition); Map<TopicAndPartition, PartitionOffsetRequestInfo> requestInfo = new HashMap<TopicAndPartition, PartitionOffsetRequestInfo>(); requestInfo .put(topicAndPartition, new PartitionOffsetRequestInfo(whichTime, 1)); kafka.javaapi.OffsetRequest request = new kafka.javaapi.OffsetRequest( requestInfo, kafka.api.OffsetRequest.CurrentVersion(), clientName); OffsetResponse response = consumer.getOffsetsBefore(request); if (response.hasError()) { System.out.println( "Error fetching data Offset Data the Broker. Reason: " + response .errorCode(topic, partition)); return -1; } long[] offsets = response.offsets(topic, partition); return offsets[0]; }}
0 0
- kafka consumer group的删除和offset恢复
- 八.Kafka Consumer和 offset提交
- kafka consumer group总结
- kafka重置consumer的offset 数据重复消费
- Kafka的 Consumer和Producer
- kafka的partition和offset
- Kafka消费组(consumer group)
- Kafka消费组(consumer group)
- Kafka的Producer和Consumer源码学习
- Kafka实战经验 - Kafka Consumer自定义offset管理
- kafka consumer需要kafka的ip和主机名
- Kafka消费组(consumer group)(转)
- kafka的consumer接口
- 重置kafka的offset
- kafka如何删除group
- 关于springcloud kafka binder的一个关于consumer group设置的一个bug
- Kafka系列4-基本概念及消费者组(Consumer Group)的理解
- kaka-manager和kafka-offset-monitor的安装和使用
- MySQL5.7新特性之Multi-Source多源复制
- Fragment生命周期详解
- HDOJ 1166 敌兵布阵(线段树单点更新求区间和)
- angular模块
- cortex M0 startup代码解析 如有错误欢迎指出
- kafka consumer group的删除和offset恢复
- iOS - 类扩展与分类的区别
- 垃圾回收机制GC知识再总结兼谈如何用好GC
- Ubuntu下开发Android 环境变量设置
- 图文介绍如何在Eclipse统计代码行数
- 研读:TrustOTP: Transforming Smartphones into Secure One-Time Password Tokens
- Spring 学习转载
- Dorothy Lua开发建议
- 2016.3 网易笔试 '9'和'g'看不清,输出所有情况