/// <summary> /// Create node under given path with given data and settings. /// </summary> /// <param name="path">The path to create node.</param> /// <param name="data">the content of the node.</param> /// <param name="acl">The access control list.</param> /// <param name="type">the znode type.</param> /// <returns>The node path if created.</returns> public async Task <string> CreateAsync(string path, byte[] data, IEnumerable <ZookeeperZnodeAcl> acl, ZookeeperZnodeType type) { await this.EnsureConnectedToZookeeperClusterAsync().ConfigureAwait(false); string result; try { var zacls = new List <ACL>(); if (acl == null || !acl.Any()) { var ids = ZookeeperZnodeAclIdentity.WorldIdentity(); zacls.Add(new ACL((int)ZookeeperZnodeAclRights.All, new ZKId(ids.Schema, ids.Identity))); } else { foreach (var ac in acl) { if (ac.Identity == null) { ac.Identity = ZookeeperZnodeAclIdentity.WorldIdentity(); } zacls.Add(new ACL((int)ac.AclRights, new ZKId(ac.Identity.Schema, ac.Identity.Identity))); } } var zmode = CreateMode.Persistent; if (type == ZookeeperZnodeType.Persistent) { zmode = CreateMode.Persistent; } else if (type == ZookeeperZnodeType.PersistentSequential) { zmode = CreateMode.PersistentSequential; } else if (type == ZookeeperZnodeType.Ephemeral) { zmode = CreateMode.Ephemeral; } else if (type == ZookeeperZnodeType.EphemeralSequential) { zmode = CreateMode.EphemeralSequential; } result = await this.createOrDeleteRequestor.ExecuteAsync(() => Task.Run(() => this.zookeeper.Create(path, data, zacls, zmode))).ConfigureAwait(false); } catch (KeeperException.NodeExistsException) { result = path; } catch (KeeperException.SessionExpiredException) { result = null; } return(result); }
/// <summary> /// Delete a topic by topic name. /// </summary> /// <param name="topic">the name of the topic.</param> /// <returns>Task holds status of deletion. True indicates succeeded. False indicates failed.</returns> public async Task <bool> DeleteKafkaTopicAsync(string topic) { var existing = await this.GetKafkaTopicAsync(topic).ConfigureAwait(false); if (existing == null) { return(false); } // note // when delete a topic from kafka, it actually create a empty node under /admin/delete_topics/{topic} // not deleting the node under /brokers/topics/{topic}, let the kafka controller to handle the real // topic removing. var acl = new ZookeeperZnodeAcl() { AclRights = ZookeeperZnodeAclRights.All, Identity = ZookeeperZnodeAclIdentity.WorldIdentity() }; var path = await this.ZookeeperClient.CreateAsync("/admin/delete_topics/" + topic, Encoding.UTF8.GetBytes(string.Empty), new List <ZookeeperZnodeAcl>() { acl }, ZookeeperZnodeType.Persistent).ConfigureAwait(false); return(!string.IsNullOrWhiteSpace(path)); }
/// <summary> /// Create a topic with creation settings. /// </summary> /// <param name="topicConfiguration">The topic creation configuration.</param> /// <returns>the path to the topic. null is return when unable to create it.</returns> public async Task <string> CreateKafkaTopicAsync(KafkaTopicConfiguration topicConfiguration) { if (topicConfiguration == null || !this.IsTopicNameValid(topicConfiguration.TopicName) || topicConfiguration.NumOfPartitions <= 0 || topicConfiguration.NumOfReplicas <= 0) { return(null); } //// refer to https://github.com/apache/kafka/blob/trunk/core/src/main/scala/kafka/admin/AdminUtils.scala //// the method createTopic // 1 figure how many brokers are available var brokers = await this.ListKakfaBrokersAsync().ConfigureAwait(false); if (brokers == null) { return(null); } // note: // when actual number of active brokers is less than the expected number of brokers, technically you still can // create topic. for example: we expected 5 brokers, but actually there are only 3, when you create below topic: // { "name" :"test", "partitions": "3", "replicas":"3"} // as far as the number of replicas is not bigger than the number of active brokers, you can still assign replicas // onto different brokers. // the only problem with this scenario is the partitions will not put on the another brokers when they are alive. // then need manually move some partitions to the addition brokers to get better balance. // so to make things simple, we just don't do it when this happen. var expected = this.GetExpectedKafkaBrokers(); if (expected == null || brokers.Count() != expected.Count()) { return(null); } var topics = await this.ListKafkaTopicsAsync().ConfigureAwait(false); if (topics != null) { var existing = topics.FirstOrDefault(t => t.Name == topicConfiguration.TopicName); if (existing != null) { return("/brokers/topics/" + topicConfiguration.TopicName); } } // Note : number of replicas can not be greater than number of active brokers, // because replicas of same topic can not be on the same broker // 2 get partitions assignment info onto those brokers with round-robin fashion. var assignments = this.AssignPartitionsAndReplicasToBrokers(topicConfiguration.NumOfPartitions, topicConfiguration.NumOfReplicas, brokers); // 3 convert partition assignment into json format for zookeeper node, and create zookeepr node // the format of json is : {"version":1,"partitions":{"0":[],...}} var serializer = new ZookeeperDataSerializer(); var content = serializer.SerializeKafkaTopicInfo(new KafkaTopicInfo() { Name = topicConfiguration.TopicName, Version = 1, Partitions = assignments.ToArray() }); // 4 create zookpper node // create node at path 'brokers/topics/{name}', with content above var acl = new ZookeeperZnodeAcl() { AclRights = ZookeeperZnodeAclRights.All, Identity = ZookeeperZnodeAclIdentity.WorldIdentity() }; var path = await this.ZookeeperClient.CreateAsync("/brokers/topics/" + topicConfiguration.TopicName, content, new List <ZookeeperZnodeAcl>() { acl }, ZookeeperZnodeType.Persistent).ConfigureAwait(false); return(path); }