变更副本
This commit is contained in:
@@ -16,6 +16,8 @@ public class ReplicaAssignment {
|
|||||||
|
|
||||||
private List<Partition> partitions;
|
private List<Partition> partitions;
|
||||||
|
|
||||||
|
private long interBrokerThrottle = -1;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
static class Partition {
|
static class Partition {
|
||||||
private String topic;
|
private String topic;
|
||||||
|
|||||||
@@ -80,6 +80,6 @@ public class TopicController {
|
|||||||
|
|
||||||
@PostMapping("/replica/assignment")
|
@PostMapping("/replica/assignment")
|
||||||
public Object updateReplicaAssignment(@RequestBody ReplicaAssignment assignment) {
|
public Object updateReplicaAssignment(@RequestBody ReplicaAssignment assignment) {
|
||||||
return "topicService.getCurrentReplicaAssignment(topic)";
|
return topicService.updateReplicaAssignment(assignment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.xuxd.kafka.console.service;
|
package com.xuxd.kafka.console.service;
|
||||||
|
|
||||||
|
import com.xuxd.kafka.console.beans.ReplicaAssignment;
|
||||||
import com.xuxd.kafka.console.beans.ResponseData;
|
import com.xuxd.kafka.console.beans.ResponseData;
|
||||||
import com.xuxd.kafka.console.beans.enums.TopicType;
|
import com.xuxd.kafka.console.beans.enums.TopicType;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -28,4 +29,6 @@ public interface TopicService {
|
|||||||
ResponseData addPartitions(String topic, int addNum, List<List<Integer>> newAssignmentst);
|
ResponseData addPartitions(String topic, int addNum, List<List<Integer>> newAssignmentst);
|
||||||
|
|
||||||
ResponseData getCurrentReplicaAssignment(String topic);
|
ResponseData getCurrentReplicaAssignment(String topic);
|
||||||
|
|
||||||
|
ResponseData updateReplicaAssignment(ReplicaAssignment assignment);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,4 +142,10 @@ public class TopicServiceImpl implements TopicService {
|
|||||||
|
|
||||||
return success ? ResponseData.create().data(gson.fromJson(tuple2._2(), ReplicaAssignment.class)).success() : ResponseData.create().failed(tuple2._2());
|
return success ? ResponseData.create().data(gson.fromJson(tuple2._2(), ReplicaAssignment.class)).success() : ResponseData.create().failed(tuple2._2());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override public ResponseData updateReplicaAssignment(ReplicaAssignment assignment) {
|
||||||
|
Tuple2<Object, String> tuple2 = topicConsole.updateReplicas(gson.toJson(assignment), assignment.getInterBrokerThrottle());
|
||||||
|
boolean success = (boolean) tuple2._1();
|
||||||
|
return success ? ResponseData.create().success() : ResponseData.create().failed(tuple2._2());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package kafka.console
|
package kafka.console
|
||||||
|
|
||||||
import com.xuxd.kafka.console.config.KafkaConfig
|
import com.xuxd.kafka.console.config.KafkaConfig
|
||||||
import kafka.admin.ReassignPartitionsCommand.compareTopicPartitions
|
import kafka.admin.ReassignPartitionsCommand._
|
||||||
import kafka.utils.Json
|
import kafka.utils.Json
|
||||||
import org.apache.kafka.clients.admin._
|
import org.apache.kafka.clients.admin._
|
||||||
import org.apache.kafka.common.errors.UnknownTopicOrPartitionException
|
import org.apache.kafka.common.errors.UnknownTopicOrPartitionException
|
||||||
|
import org.apache.kafka.common.utils.Time
|
||||||
import org.apache.kafka.common.{TopicPartition, TopicPartitionReplica}
|
import org.apache.kafka.common.{TopicPartition, TopicPartitionReplica}
|
||||||
|
|
||||||
import java.util
|
import java.util
|
||||||
@@ -135,6 +136,65 @@ class TopicConsole(config: KafkaConfig) extends KafkaConsole(config: KafkaConfig
|
|||||||
}).asInstanceOf[(Boolean, String)]
|
}).asInstanceOf[(Boolean, String)]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def updateReplicas(reassignmentJson: String, interBrokerThrottle: Long = -1L): (Boolean, String) = {
|
||||||
|
withAdminClientAndCatchError(admin => {
|
||||||
|
executeAssignment(admin, reassignmentJson, interBrokerThrottle)
|
||||||
|
(true, "")
|
||||||
|
}, e => {
|
||||||
|
log.error("executeAssignment error, ", e)
|
||||||
|
(false, e.getMessage)
|
||||||
|
}).asInstanceOf[(Boolean, String)]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy and modify from @{link kafka.admin.ReassignPartitionsCommand#executeAssignment}.
|
||||||
|
*/
|
||||||
|
def executeAssignment(adminClient: Admin,
|
||||||
|
reassignmentJson: String,
|
||||||
|
interBrokerThrottle: Long = -1L,
|
||||||
|
logDirThrottle: Long = -1L,
|
||||||
|
timeoutMs: Long = 30000L,
|
||||||
|
time: Time = Time.SYSTEM): Unit = {
|
||||||
|
val (proposedParts, proposedReplicas) = parseExecuteAssignmentArgs(reassignmentJson)
|
||||||
|
val currentReassignments = adminClient.
|
||||||
|
listPartitionReassignments().reassignments().get().asScala
|
||||||
|
// If there is an existing assignment
|
||||||
|
// This helps avoid surprising users.
|
||||||
|
if (currentReassignments.nonEmpty) {
|
||||||
|
throw new TerseReassignmentFailureException("Cannot execute because there is an existing partition assignment.")
|
||||||
|
}
|
||||||
|
verifyBrokerIds(adminClient, proposedParts.values.flatten.toSet)
|
||||||
|
val currentParts = getReplicaAssignmentForPartitions(adminClient, proposedParts.keySet.toSet)
|
||||||
|
log.info("currentPartitionReplicaAssignment: " + currentPartitionReplicaAssignmentToString(proposedParts, currentParts))
|
||||||
|
log.info(s"newPartitionReplicaAssignment: $reassignmentJson")
|
||||||
|
|
||||||
|
if (interBrokerThrottle >= 0 || logDirThrottle >= 0) {
|
||||||
|
|
||||||
|
if (interBrokerThrottle >= 0) {
|
||||||
|
val moveMap = calculateProposedMoveMap(currentReassignments, proposedParts, currentParts)
|
||||||
|
modifyReassignmentThrottle(adminClient, moveMap, interBrokerThrottle)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logDirThrottle >= 0) {
|
||||||
|
val movingBrokers = calculateMovingBrokers(proposedReplicas.keySet.toSet)
|
||||||
|
modifyLogDirThrottle(adminClient, movingBrokers, logDirThrottle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the partition reassignments.
|
||||||
|
val errors = alterPartitionReassignments(adminClient, proposedParts)
|
||||||
|
if (errors.nonEmpty) {
|
||||||
|
throw new TerseReassignmentFailureException(
|
||||||
|
"Error reassigning partition(s):%n%s".format(
|
||||||
|
errors.keySet.toBuffer.sortWith(compareTopicPartitions).map { part =>
|
||||||
|
s"$part: ${errors(part).getMessage}"
|
||||||
|
}.mkString(System.lineSeparator())))
|
||||||
|
}
|
||||||
|
if (proposedReplicas.nonEmpty) {
|
||||||
|
executeMoves(adminClient, proposedReplicas, timeoutMs, time)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current replica assignments for some topics.
|
* Get the current replica assignments for some topics.
|
||||||
*
|
*
|
||||||
@@ -181,4 +241,13 @@ class TopicConsole(config: KafkaConfig) extends KafkaConsole(config: KafkaConfig
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def modifyReassignmentThrottle(admin: Admin, moveMap: MoveMap, interBrokerThrottle: Long): Unit = {
|
||||||
|
val leaderThrottles = calculateLeaderThrottles(moveMap)
|
||||||
|
val followerThrottles = calculateFollowerThrottles(moveMap)
|
||||||
|
modifyTopicThrottles(admin, leaderThrottles, followerThrottles)
|
||||||
|
|
||||||
|
// val reassigningBrokers = calculateReassigningBrokers(moveMap)
|
||||||
|
// modifyInterBrokerThrottle(admin, reassigningBrokers, interBrokerThrottle)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,21 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
<div class="content-module">
|
||||||
|
<a-card title="Broker管理" style="width: 100%; text-align: left">
|
||||||
|
<p>
|
||||||
|
<a-button type="primary"> 配置限流 </a-button>
|
||||||
|
<label>说明:</label>
|
||||||
|
<span
|
||||||
|
>设置指定broker上的topic的副本之间数据同步占用的带宽,这个设置是broker级别的,但是设置后还要去对应的topic上进行限流配置,指定对这个topic的相关副本进行限制</span
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a-button type="primary"> 解除限流 </a-button>
|
||||||
|
<label>说明:</label>
|
||||||
|
<span>解除指定broker上的topic副本之间数据同步占用的带宽限制</span>
|
||||||
|
</p>
|
||||||
|
</a-card>
|
||||||
|
</div>
|
||||||
<div class="content-module">
|
<div class="content-module">
|
||||||
<a-card title="副本管理" style="width: 100%; text-align: left">
|
<a-card title="副本管理" style="width: 100%; text-align: left">
|
||||||
<p>
|
<p>
|
||||||
@@ -9,6 +25,11 @@
|
|||||||
<label>说明:</label>
|
<label>说明:</label>
|
||||||
<span>将集群中所有分区leader副本设置为首选副本</span>
|
<span>将集群中所有分区leader副本设置为首选副本</span>
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
<a-button type="primary"> 副本变更详情 </a-button>
|
||||||
|
<label>说明:</label>
|
||||||
|
<span>查看正在进行副本变更/重分配的任务,或者将其取消</span>
|
||||||
|
</p>
|
||||||
</a-card>
|
</a-card>
|
||||||
</div>
|
</div>
|
||||||
<div class="content-module">
|
<div class="content-module">
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<a-spin :spinning="loading">
|
<a-spin :spinning="loading">
|
||||||
<div class="replica-box">
|
<div class="replica-box">
|
||||||
<label>副本数:</label
|
<label>设置副本数:</label
|
||||||
><a-input-number
|
><a-input-number
|
||||||
id="inputNumber"
|
id="inputNumber"
|
||||||
v-model="replicaNums"
|
v-model="replicaNums"
|
||||||
@@ -23,6 +23,19 @@
|
|||||||
@change="onChange"
|
@change="onChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="replica-box">
|
||||||
|
<label>是否要限流:</label
|
||||||
|
><a-input-number
|
||||||
|
id="inputNumber"
|
||||||
|
v-model="data.interBrokerThrottle"
|
||||||
|
:min="-1"
|
||||||
|
:max="102400"
|
||||||
|
/>
|
||||||
|
<strong>
|
||||||
|
|说明:broker之间副本同步带宽限制,默认值为-1表示不限制,不是-1表示限制,该值并不表示流速,至于流速配置,在
|
||||||
|
<span style="color: red">运维->配置限流</span> 处进行操作.</strong
|
||||||
|
>
|
||||||
|
</div>
|
||||||
<a-table
|
<a-table
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:data-source="data.partitions"
|
:data-source="data.partitions"
|
||||||
@@ -39,6 +52,11 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</a-table>
|
</a-table>
|
||||||
|
<p>
|
||||||
|
*正在进行即尚未完成的副本变更的任务,可以在
|
||||||
|
<span style="color: red">运维->副本变更详情</span>
|
||||||
|
处查看,也可以在那里将正在进行的任务取消。
|
||||||
|
</p>
|
||||||
</a-spin>
|
</a-spin>
|
||||||
</div>
|
</div>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
@@ -121,6 +139,9 @@ export default {
|
|||||||
this.$emit("closeUpdateReplicaDialog", { refresh: false });
|
this.$emit("closeUpdateReplicaDialog", { refresh: false });
|
||||||
},
|
},
|
||||||
onChange(value) {
|
onChange(value) {
|
||||||
|
if (value < 1 || value > this.brokerSize) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (this.data.partitions.length > 0) {
|
if (this.data.partitions.length > 0) {
|
||||||
this.data.partitions.forEach((p) => {
|
this.data.partitions.forEach((p) => {
|
||||||
if (value > p.replicas.length) {
|
if (value > p.replicas.length) {
|
||||||
@@ -130,7 +151,9 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (value < p.replicas.length) {
|
if (value < p.replicas.length) {
|
||||||
p.replicas.pop();
|
for (let i = p.replicas.length; i > value; i--) {
|
||||||
|
p.replicas.pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -145,6 +168,7 @@ export default {
|
|||||||
this.loading = false;
|
this.loading = false;
|
||||||
if (res.code == 0) {
|
if (res.code == 0) {
|
||||||
this.$message.success(res.msg);
|
this.$message.success(res.msg);
|
||||||
|
this.$emit("closeUpdateReplicaDialog", { refresh: false });
|
||||||
} else {
|
} else {
|
||||||
notification.error({
|
notification.error({
|
||||||
message: "error",
|
message: "error",
|
||||||
|
|||||||
Reference in New Issue
Block a user