Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19cc635151 | ||
|
|
a1206fb094 | ||
|
|
83c018f6a7 | ||
|
|
2729627a80 | ||
|
|
096792beb0 | ||
|
|
fd495351bc | ||
|
|
ecbd9dda6a | ||
|
|
30707e84a7 | ||
|
|
f98cca8727 | ||
|
|
ea95697e88 | ||
|
|
e90268a56e | ||
|
|
fcc315782c | ||
|
|
b79529f607 | ||
|
|
f49e2f7d0c | ||
|
|
4929953acd | ||
|
|
8c946a96ba |
22
README.md
22
README.md
@@ -25,11 +25,11 @@ v1.0.6版本之前,如果kafka集群启用了ACL,但是控制台没看到Acl
|
||||

|
||||
|
||||
## 安装包下载
|
||||
点击下载(v1.0.8版本):[kafka-console-ui.zip](https://github.com/xxd763795151/kafka-console-ui/releases/download/v1.0.8/kafka-console-ui.zip)
|
||||
点击下载(v1.0.10版本):[kafka-console-ui.zip](https://github.com/xxd763795151/kafka-console-ui/releases/download/v1.0.10/kafka-console-ui-1.0.10.zip)
|
||||
|
||||
如果安装包下载的比较慢,可以查看下面的源码打包说明,把代码下载下来,本地快速打包.
|
||||
|
||||
github下载慢也可以试试从gitee下载,点击下载[gitee来源kafka-console-ui.zip](https://gitee.com/xiaodong_xu/kafka-console-ui/releases/download/v1.0.8/kafka-console-ui.zip)
|
||||
github下载慢也可以试试从gitee下载,点击下载[gitee来源kafka-console-ui.zip](https://gitee.com/xiaodong_xu/kafka-console-ui/releases/download/v1.0.10/kafka-console-ui-1.0.10.zip)
|
||||
|
||||
## 快速使用
|
||||
### Windows
|
||||
@@ -99,7 +99,11 @@ auth:
|
||||
## DockerCompose部署
|
||||
感谢@wdkang123 同学分享的部署方式,如果有需要请查看[DockerCompose部署方式](./document/deploy/docker部署.md)
|
||||
## 感谢支持
|
||||
感谢jetbrains的开源支持,如果有朋友愿意一起维护,很欢迎提pr.
|
||||
|
||||
[](https://jb.gg/OpenSourceSupport)
|
||||
|
||||
jetbrains官方地址: https://www.jetbrains.com/
|
||||
## 联系方式
|
||||
+ 微信群
|
||||
|
||||
@@ -107,6 +111,14 @@ auth:
|
||||
|
||||
[//]: # (<img src="https://github.com/xxd763795151/kafka-console-ui/blob/main/document/contact/weixin_contact.jpg" width="40%"/>)
|
||||
|
||||
+ 若联系方式失效, 请联系加一下微信, 说明意图
|
||||
- xxd763795151
|
||||
- wxid_7jy2ezljvebt12
|
||||
抱歉,后面就不再提供新的联系方式加群了。
|
||||
|
||||
在很早之前,有个兄弟提了个建议,可以拉个群,大家可以一起交流,所以成立了一个群,当时我以为没有多少朋友愿意加入。
|
||||
|
||||
可是后来确实有不少朋友进群了,这让我很惶恐,其实这个平台我自己并不觉得有太多技术深度,却有一些朋友愿意来捧场,这个让我觉得很惭愧,所以现在考虑了下,如果真有使用问题,可以留个issue。
|
||||
|
||||
另外,我自己也确实挺忙,对于这个项目的需求处理不够及时,实在是时间和精力上有限,所以有朋友希望新增的一些能力,拖了这么久我也没有下文,实在是抱歉。
|
||||
|
||||
对于一些不太耗时的功能,我还是可以积极处理的。
|
||||
|
||||
另外有些功能,是想要放到后面再加的,所以迟迟没有动手。
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 220 KiB After Width: | Height: | Size: 127 KiB |
@@ -1 +1,13 @@
|
||||
<svg height="180" viewBox="0 0 180 180" width="180" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="32.64" x2="82.77" y1="61.16" y2="85.54"><stop offset=".21" stop-color="#fe2857"/><stop offset="1" stop-color="#293896"/></linearGradient><linearGradient id="b" gradientUnits="userSpaceOnUse" x1="17.38" x2="82.95" y1="69.86" y2="21.23"><stop offset="0" stop-color="#fe2857"/><stop offset=".01" stop-color="#fe2857"/><stop offset=".86" stop-color="#ff318c"/></linearGradient><linearGradient id="c" gradientUnits="userSpaceOnUse" x1="74.17" x2="160.27" y1="21.58" y2="99.76"><stop offset=".02" stop-color="#ff318c"/><stop offset=".21" stop-color="#fe2857"/><stop offset=".86" stop-color="#fdb60d"/></linearGradient><linearGradient id="d" gradientUnits="userSpaceOnUse" x1="155.46" x2="55.07" y1="89.8" y2="158.9"><stop offset=".01" stop-color="#fdb60d"/><stop offset=".86" stop-color="#fcf84a"/></linearGradient><path d="m81.56 83.71-41.35-35a15 15 0 1 0 -14.47 25.7h.15l.39.12 52.16 15.89a3.53 3.53 0 0 0 1.18.21 3.73 3.73 0 0 0 1.93-6.91z" fill="url(#a)"/><path d="m89.85 25.93a10.89 10.89 0 0 0 -16.85-9.18l-50.5 30.66a15 15 0 1 0 17.9 24l45.27-36.89.36-.3a10.93 10.93 0 0 0 3.82-8.29z" fill="url(#b)"/><path d="m163.29 92-76.62-73.79a10.91 10.91 0 1 0 -14.81 16l.14.12 81.4 68.58a7.36 7.36 0 0 0 12.09-5.65 7.39 7.39 0 0 0 -2.2-5.26z" fill="url(#c)"/><path d="m165.5 97.29a7.35 7.35 0 0 0 -11.67-6l-92.71 45.3a15 15 0 1 0 15.48 25.59l85.73-58.84a7.35 7.35 0 0 0 3.17-6.05z" fill="url(#d)"/><path d="m60 60h60v60h-60z"/><g fill="#fff"><path d="m66.53 108.75h22.5v3.75h-22.5z"/><path d="m65.59 75.47 1.67-1.58a1.88 1.88 0 0 0 1.47.87c.64 0 1.06-.45 1.06-1.32v-5.92h2.58v5.94a3.44 3.44 0 0 1 -.92 2.63 3.52 3.52 0 0 1 -2.57 1 3.84 3.84 0 0 1 -3.29-1.62z"/><path d="m73.53 67.52h7.53v2.19h-5v1.43h4.49v2h-4.45v1.49h5v2.2h-7.6z"/><path d="m84.73 69.79h-2.8v-2.27h8.21v2.27h-2.81v7.09h-2.6z"/><path d="m66.63 80.58h4.42a3.47 3.47 0 0 1 2.55.83 2.09 2.09 0 0 1 .61 1.52 2.18 2.18 0 0 1 -1.45 2.09 2.27 2.27 0 0 1 1.86 2.29c0 1.69-1.31 2.69-3.55 2.69h-4.44zm5 2.89c0-.52-.42-.8-1.18-.8h-1.29v1.64h1.25c.78 0 1.24-.27 1.24-.81zm-.9 2.66h-1.57v1.73h1.62c.8 0 1.24-.31 1.24-.86-.02-.53-.4-.87-1.27-.87z"/><path d="m75.45 80.58h4.15a4.14 4.14 0 0 1 3.05 1 2.92 2.92 0 0 1 .83 2.18 3 3 0 0 1 -1.93 2.89l2.24 3.35h-3l-1.89-2.84h-.87v2.84h-2.6zm4 4.5c.87 0 1.4-.43 1.4-1.12 0-.75-.55-1.13-1.41-1.13h-1.39v2.27z"/><path d="m87.09 80.51h2.5l4 9.44h-2.79l-.67-1.69h-3.63l-.67 1.74h-2.71zm2.28 5.73-1.05-2.65-1.06 2.65z"/><path d="m94 80.55h2.6v9.37h-2.6z"/><path d="m97.56 80.55h2.44l3.37 5v-5h2.57v9.37h-2.27l-3.53-5.14v5.14h-2.58z"/><path d="m106.37 88.53 1.44-1.73a4.86 4.86 0 0 0 3 1.13c.71 0 1.08-.25 1.08-.65 0-.41-.3-.61-1.59-.91-2-.46-3.53-1-3.53-2.93 0-1.74 1.38-3 3.63-3a5.88 5.88 0 0 1 3.85 1.25l-1.25 1.78a4.56 4.56 0 0 0 -2.62-.92c-.63 0-.94.25-.94.6 0 .43.32.62 1.63.91 2.15.47 3.48 1.17 3.48 2.92 0 1.91-1.51 3-3.78 3a6.56 6.56 0 0 1 -4.4-1.45z"/></g><path d="m0 0h180v180h-180z" fill="none"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="298" height="64" fill="none" viewBox="0 0 298 64">
|
||||
<defs>
|
||||
<linearGradient id="a" x1=".850001" x2="62.62" y1="62.72" y2="1.81" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FF9419"/>
|
||||
<stop offset=".43" stop-color="#FF021D"/>
|
||||
<stop offset=".99" stop-color="#E600FF"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path fill="#000" d="M86.4844 40.5858c0 .8464-.1792 1.5933-.5377 2.2505-.3585.6573-.8564 1.1651-1.5137 1.5236-.6572.3585-1.3941.5378-2.2406.5378H78v6.1044h5.0787c1.912 0 3.6248-.4282 5.1484-1.2846 1.5236-.8564 2.7186-2.0415 3.585-3.5452.8663-1.5037 1.3045-3.1966 1.3045-5.0886V21.0178h-6.6322v19.568Zm17.8556-1.8224h13.891v-5.6065H104.34v-6.3633h15.355v-5.7758H97.8766v29.9743h22.2464v-5.7757H104.34v-6.453Zm17.865-11.8005h8.882v24.0193h6.633V26.9629h8.842v-5.9451h-24.367v5.9551l.01-.01Zm47.022 9.0022c-.517-.2788-1.085-.4879-1.673-.6472.449-.1295.877-.2888 1.275-.488 1.096-.5676 1.962-1.3643 2.579-2.39.618-1.0257.936-2.2007.936-3.5351 0-1.5237-.418-2.8879-1.244-4.0929-.827-1.195-1.992-2.131-3.486-2.8082-1.494-.6672-3.206-1.0058-5.118-1.0058h-13.315v29.9743h13.574c2.011 0 3.804-.3485 5.387-1.0556 1.573-.707 2.798-1.6829 3.675-2.9476.866-1.2547 1.304-2.6887 1.304-4.302 0-1.4837-.338-2.8082-1.026-3.9833-.687-1.175-1.633-2.0812-2.858-2.7285l-.01.0099Zm-13.603-9.9184h5.886c.816 0 1.533.1494 2.161.4382.627.2888 1.115.707 1.464 1.2547.348.5378.527 1.1751.527 1.9021 0 .7269-.179 1.414-.527 1.9817-.349.5676-.837.9958-1.464 1.3045-.628.3087-1.345.4581-2.161.4581h-5.886v-7.3492.0099Zm10.138 18.134c-.378.5676-.916 1.0058-1.603 1.3145-.697.3087-1.484.4581-2.39.4581h-6.145v-7.6878h6.145c.886 0 1.673.1693 2.37.4979.687.3286 1.235.7867 1.613 1.3842.378.5975.578 1.2747.578 2.0414 0 .7668-.19 1.4241-.568 1.9917Zm29.596-5.3077c1.663-.7967 2.947-1.922 3.864-3.3659.916-1.444 1.374-3.117 1.374-5.0289 0-1.912-.448-3.5253-1.344-4.9592-.897-1.434-2.171-2.5394-3.814-3.3261-1.644-.7867-3.546-1.1751-5.717-1.1751h-13.124v29.9743h6.642V40.0779h4.322l6.084 10.9142h7.578l-6.851-11.7208c.339-.1195.677-.249.996-.3983h-.01Zm-2.151-6.1244c-.369.6274-.896 1.1154-1.583 1.444-.688.3386-1.494.5079-2.42.5079h-5.975v-8.2953h5.975c.926 0 1.732.1693 2.42.4979.687.3287 1.214.8166 1.583 1.434.368.6174.558 1.3544.558 2.1908 0 .8365-.19 1.5734-.558 2.2008v.0199Zm20.594-11.7308-10.706 29.9743h6.742l2.121-6.6122h11.114l2.27 6.6122h6.612L220.99 21.0178h-7.189Zm-.339 18.3431 3.445-10.5756.409-1.922.408 1.922 3.685 10.5756h-7.947Zm20.693 11.6312h6.851V21.0178h-6.851v29.9743Zm31.02-9.6993-12.896-20.275h-6.463v29.9743h6.055V30.7172l12.826 20.2749h6.533V21.0178h-6.055v20.275Zm31.528-3.3559c-.647-1.2448-1.564-2.2904-2.729-3.1369-1.165-.8464-2.509-1.4041-4.023-1.6929l-5.098-1.0456c-.797-.1892-1.434-.5178-1.902-.9958-.469-.478-.708-1.0755-.708-1.7825 0-.6473.17-1.205.518-1.683.339-.478.827-.8464 1.444-1.1153.618-.2689 1.335-.3983 2.151-.3983.817 0 1.554.1394 2.181.4182.627.2788 1.115.6672 1.464 1.1751s.528 1.0755.528 1.7228h6.642c-.04-1.7427-.528-3.2863-1.444-4.6207-.916-1.3443-2.201-2.3899-3.834-3.1468-1.633-.7568-3.505-1.1352-5.597-1.1352-2.091 0-3.943.3884-5.566 1.1751-1.623.7867-2.898 1.8721-3.804 3.2663-.906 1.3941-1.364 2.9775-1.364 4.76 0 1.444.288 2.7485.876 3.9036.587 1.1652 1.414 2.1311 2.479 2.8979 1.076.7668 2.311 1.3045 3.725 1.6033l5.397 1.1153c.886.2091 1.584.5975 2.101 1.1551.518.5577.767 1.2448.767 2.0813 0 .6672-.189 1.2747-.567 1.8025-.379.5277-.907.936-1.584 1.2248-.677.2888-1.474.4282-2.39.4282-.916 0-1.782-.1593-2.529-.478-.747-.3186-1.325-.7767-1.733-1.3742-.418-.5875-.617-1.2747-.617-2.0414h-6.642c.029 1.8721.527 3.5152 1.513 4.9492.976 1.424 2.32 2.5394 4.033 3.336 1.713.7967 3.675 1.195 5.886 1.195 2.21 0 4.202-.4083 5.915-1.2249 1.723-.8165 3.057-1.9418 4.023-3.3758.966-1.434 1.444-3.0572 1.444-4.8696 0-1.4838-.329-2.848-.976-4.1028l.02.01Z"/>
|
||||
<path fill="url(#a)" d="M20.34 3.66 3.66 20.34C1.32 22.68 0 25.86 0 29.18V59c0 2.76 2.24 5 5 5h29.82c3.32 0 6.49-1.32 8.84-3.66l16.68-16.68c2.34-2.34 3.66-5.52 3.66-8.84V5c0-2.76-2.24-5-5-5H29.18c-3.32 0-6.49 1.32-8.84 3.66Z"/>
|
||||
<path fill="#000" d="M48 16H8v40h40V16Z"/>
|
||||
<path fill="#fff" d="M30 47H13v4h17v-4Z"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 4.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 439 KiB After Width: | Height: | Size: 605 KiB |
10
pom.xml
10
pom.xml
@@ -10,7 +10,7 @@
|
||||
</parent>
|
||||
<groupId>com.xuxd</groupId>
|
||||
<artifactId>kafka-console-ui</artifactId>
|
||||
<version>1.0.9</version>
|
||||
<version>1.0.11</version>
|
||||
<name>kafka-console-ui</name>
|
||||
<description>Kafka console manage ui</description>
|
||||
<properties>
|
||||
@@ -21,7 +21,7 @@
|
||||
<ui.path>${project.basedir}/ui</ui.path>
|
||||
<frontend-maven-plugin.version>1.11.0</frontend-maven-plugin.version>
|
||||
<compiler.version>1.8</compiler.version>
|
||||
<kafka.version>3.2.0</kafka.version>
|
||||
<kafka.version>3.5.0</kafka.version>
|
||||
<maven.assembly.plugin.version>3.0.0</maven.assembly.plugin.version>
|
||||
<mybatis-plus-boot-starter.version>3.4.2</mybatis-plus-boot-starter.version>
|
||||
<scala.version>2.13.6</scala.version>
|
||||
@@ -90,6 +90,12 @@
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.apache.kafka</groupId>-->
|
||||
<!-- <artifactId>kafka-tools</artifactId>-->
|
||||
<!-- <version>${kafka.version}</version>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<dependency>
|
||||
<groupId>com.typesafe.scala-logging</groupId>
|
||||
<artifactId>scala-logging_2.13</artifactId>
|
||||
|
||||
@@ -33,4 +33,6 @@ public class QueryMessage {
|
||||
private String headerKey;
|
||||
|
||||
private String headerValue;
|
||||
|
||||
private int filterNumber;
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@ public class QueryMessageDTO {
|
||||
|
||||
private String headerValue;
|
||||
|
||||
private int filterNumber;
|
||||
|
||||
public QueryMessage toQueryMessage() {
|
||||
QueryMessage queryMessage = new QueryMessage();
|
||||
queryMessage.setTopic(topic);
|
||||
@@ -69,6 +71,7 @@ public class QueryMessageDTO {
|
||||
if (StringUtils.isNotBlank(headerValue)) {
|
||||
queryMessage.setHeaderValue(headerValue.trim());
|
||||
}
|
||||
queryMessage.setFilterNumber(filterNumber);
|
||||
|
||||
return queryMessage;
|
||||
}
|
||||
|
||||
@@ -21,4 +21,6 @@ public class BrokerApiVersionVO {
|
||||
private int unSupportNums;
|
||||
|
||||
private List<String> versionInfo;
|
||||
|
||||
private String brokerVersion;
|
||||
}
|
||||
|
||||
@@ -33,4 +33,9 @@ public class AuthController {
|
||||
public ResponseData login(@RequestBody LoginUserDTO userDTO) {
|
||||
return authService.login(userDTO);
|
||||
}
|
||||
|
||||
@GetMapping("/own/data/auth")
|
||||
public boolean ownDataAuthority() {
|
||||
return authService.ownDataAuthority();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,4 +10,6 @@ import com.xuxd.kafka.console.beans.dto.LoginUserDTO;
|
||||
public interface AuthService {
|
||||
|
||||
ResponseData login(LoginUserDTO userDTO);
|
||||
|
||||
boolean ownDataAuthority();
|
||||
}
|
||||
|
||||
@@ -93,4 +93,16 @@ public class AuthServiceImpl implements AuthService {
|
||||
return ResponseData.create().data(loginResult).success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean ownDataAuthority() {
|
||||
if (!authConfig.isEnable()) {
|
||||
return true;
|
||||
}
|
||||
if (!authConfig.isEnableClusterAuthority()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.xuxd.kafka.console.dao.ClusterInfoMapper;
|
||||
import com.xuxd.kafka.console.dao.ClusterRoleRelationMapper;
|
||||
import com.xuxd.kafka.console.filter.CredentialsContext;
|
||||
import com.xuxd.kafka.console.service.ClusterService;
|
||||
import kafka.console.BrokerVersion;
|
||||
import kafka.console.ClusterConsole;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
@@ -188,6 +189,9 @@ public class ClusterServiceImpl implements ClusterService {
|
||||
versionInfo = versionInfo.substring(1, versionInfo.length() - 2);
|
||||
vo.setVersionInfo(Arrays.asList(StringUtils.split(versionInfo, ",")));
|
||||
list.add(vo);
|
||||
// 推测broker版本
|
||||
String vs = BrokerVersion.guessBrokerVersion(versions);
|
||||
vo.setBrokerVersion(vs);
|
||||
}));
|
||||
Collections.sort(list, Comparator.comparingInt(BrokerApiVersionVO::getBrokerId));
|
||||
return ResponseData.create().data(list).success();
|
||||
|
||||
@@ -70,7 +70,7 @@ public class MessageServiceImpl implements MessageService, ApplicationContextAwa
|
||||
|
||||
@Override
|
||||
public ResponseData searchByTime(QueryMessage queryMessage) {
|
||||
int maxNums = 5000;
|
||||
int maxNums = queryMessage.getFilterNumber() <= 0 ? 5000 : queryMessage.getFilterNumber();
|
||||
|
||||
Object searchContent = null;
|
||||
String headerKey = null;
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
CREATE TABLE IF NOT EXISTS T_KAFKA_USER
|
||||
(
|
||||
ID IDENTITY NOT NULL COMMENT '主键ID',
|
||||
USERNAME VARCHAR(50) NOT NULL DEFAULT '' COMMENT '用户名',
|
||||
PASSWORD VARCHAR(50) NOT NULL DEFAULT '' COMMENT '密码',
|
||||
USERNAME VARCHAR(128) NOT NULL DEFAULT '' COMMENT '用户名',
|
||||
PASSWORD VARCHAR(128) NOT NULL DEFAULT '' COMMENT '密码',
|
||||
UPDATE_TIME TIMESTAMP NOT NULL DEFAULT NOW() COMMENT '更新时间',
|
||||
CLUSTER_INFO_ID BIGINT NOT NULL COMMENT '集群信息里的集群ID',
|
||||
PRIMARY KEY (ID),
|
||||
|
||||
@@ -21,7 +21,7 @@ import org.apache.kafka.common.utils.{KafkaThread, LogContext, Time}
|
||||
import org.slf4j.{Logger, LoggerFactory}
|
||||
|
||||
import java.io.IOException
|
||||
import java.util.Properties
|
||||
import java.util.{Collections, Properties}
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.concurrent.{ConcurrentLinkedQueue, TimeUnit}
|
||||
import scala.jdk.CollectionConverters.{ListHasAsScala, MapHasAsJava, PropertiesHasAsScala, SetHasAsScala}
|
||||
@@ -162,7 +162,7 @@ object BrokerApiVersion{
|
||||
|
||||
def listAllBrokerVersionInfo(): Map[Node, Try[NodeApiVersions]] =
|
||||
findAllBrokers().map { broker =>
|
||||
broker -> Try[NodeApiVersions](new NodeApiVersions(getApiVersions(broker)))
|
||||
broker -> Try[NodeApiVersions](new NodeApiVersions(getApiVersions(broker), Collections.emptyList(), false))
|
||||
}.toMap
|
||||
|
||||
def close(): Unit = {
|
||||
|
||||
101
src/main/scala/kafka/console/BrokerVersion.scala
Normal file
101
src/main/scala/kafka/console/BrokerVersion.scala
Normal file
@@ -0,0 +1,101 @@
|
||||
package kafka.console
|
||||
|
||||
import org.apache.kafka.clients.NodeApiVersions
|
||||
import org.apache.kafka.common.message.ApiVersionsResponseData.ApiVersion
|
||||
|
||||
import java.util.Collections
|
||||
import scala.jdk.CollectionConverters.SeqHasAsJava
|
||||
import scala.reflect.runtime.universe._
|
||||
import scala.reflect.runtime.{universe => ru}
|
||||
|
||||
|
||||
/**
|
||||
* broker version with api version.
|
||||
*
|
||||
* @author: xuxd
|
||||
* @since: 2024/8/29 16:12
|
||||
* */
|
||||
class BrokerVersion(val apiVersion: Int, val brokerVersion: String)
|
||||
|
||||
object BrokerVersion {
|
||||
|
||||
val define: List[BrokerVersion] = List(
|
||||
new BrokerVersion(33, "0.11"),
|
||||
new BrokerVersion(37, "1.0"),
|
||||
new BrokerVersion(42, "1.1"),
|
||||
new BrokerVersion(43, "2.2"),
|
||||
new BrokerVersion(44, "2.3"),
|
||||
new BrokerVersion(47, "2.4~2.5"),
|
||||
new BrokerVersion(49, "2.6"),
|
||||
new BrokerVersion(57, "2.7"),
|
||||
new BrokerVersion(64, "2.8"),
|
||||
new BrokerVersion(67, "3.0~3.4"),
|
||||
new BrokerVersion(68, "3.5~3.6"),
|
||||
new BrokerVersion(74, "3.7"),
|
||||
new BrokerVersion(75, "3.8")
|
||||
)
|
||||
|
||||
def getDefineWithJavaList(): java.util.List[BrokerVersion] = {
|
||||
define.toBuffer.asJava
|
||||
}
|
||||
|
||||
def guessBrokerVersion(nodeVersion: NodeApiVersions): String = {
|
||||
// if (nodeVersion.)
|
||||
val unknown = "unknown";
|
||||
var guessVersion = unknown
|
||||
var maxApiKey: Short = -1
|
||||
if (nodeVersion.toString().contains("UNKNOWN")) {
|
||||
val unknownApis = getFieldValueByName(nodeVersion, "unknownApis")
|
||||
unknownApis match {
|
||||
case Some(unknownApis: java.util.List[ApiVersion]) => {
|
||||
if (unknownApis.size > 0) {
|
||||
maxApiKey = unknownApis.get(unknownApis.size() - 1).apiKey()
|
||||
}
|
||||
}
|
||||
case _ => -1
|
||||
}
|
||||
}
|
||||
if (maxApiKey < 0) {
|
||||
val versions = new java.util.ArrayList[ApiVersion](nodeVersion.allSupportedApiVersions().values())
|
||||
Collections.sort(versions, (o1: ApiVersion, o2: ApiVersion) => o2.apiKey() - o1.apiKey)
|
||||
maxApiKey = versions.get(0).apiKey()
|
||||
}
|
||||
if (maxApiKey > 0) {
|
||||
|
||||
if (maxApiKey > define.last.apiVersion) {
|
||||
guessVersion = "> " + define.last.brokerVersion
|
||||
} else if (maxApiKey < define.head.apiVersion) {
|
||||
guessVersion = "< " + define.head.brokerVersion
|
||||
} else {
|
||||
for (i <- define.indices) {
|
||||
if (maxApiKey <= define(i).apiVersion && guessVersion == unknown) {
|
||||
guessVersion = define(i).brokerVersion
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
guessVersion
|
||||
}
|
||||
|
||||
def getFieldValueByName(obj: Object, fieldName: String): Option[Any] = {
|
||||
val runtimeMirror = ru.runtimeMirror(obj.getClass.getClassLoader)
|
||||
val instanceMirror = runtimeMirror.reflect(obj)
|
||||
val typeOfObj = instanceMirror.symbol.toType
|
||||
|
||||
// 查找名为 fieldName 的字段
|
||||
val fieldSymbol = typeOfObj.member(newTermName(fieldName)).asTerm
|
||||
|
||||
// 检查字段是否存在并且不是私有字段
|
||||
if (fieldSymbol.isPrivate || fieldSymbol.isPrivateThis) {
|
||||
// None // 如果字段是私有的,返回 None
|
||||
val fieldMirror = runtimeMirror.reflect(obj).reflectField(fieldSymbol)
|
||||
Some(fieldMirror.get)
|
||||
} else {
|
||||
// 反射获取字段值
|
||||
val fieldMirror = instanceMirror.reflectField(fieldSymbol)
|
||||
Some(fieldMirror.get)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
setPermissions,
|
||||
setToken,
|
||||
setUsername,
|
||||
deleteClusterInfo,
|
||||
} from "@/utils/local-cache";
|
||||
|
||||
Vue.use(Vuex);
|
||||
@@ -38,6 +39,9 @@ export default new Vuex.Store({
|
||||
state.clusterInfo.enableSasl = enableSasl;
|
||||
setClusterInfo(clusterInfo);
|
||||
},
|
||||
[CLUSTER.DELETE]() {
|
||||
deleteClusterInfo();
|
||||
},
|
||||
[AUTH.ENABLE](state, enable) {
|
||||
state.auth.enable = enable;
|
||||
},
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export const CLUSTER = {
|
||||
SWITCH: "switchCluster",
|
||||
DELETE: "deleteClusterInfo",
|
||||
};
|
||||
|
||||
export const AUTH = {
|
||||
|
||||
@@ -365,6 +365,10 @@ export const AuthApi = {
|
||||
url: "/auth/login",
|
||||
method: "post",
|
||||
},
|
||||
ownDataAuthority: {
|
||||
url: "/auth/own/data/auth",
|
||||
method: "get",
|
||||
},
|
||||
};
|
||||
|
||||
export const ClusterRoleRelationApi = {
|
||||
|
||||
@@ -4,6 +4,10 @@ export function setClusterInfo(clusterInfo) {
|
||||
localStorage.setItem(Cache.clusterInfo, JSON.stringify(clusterInfo));
|
||||
}
|
||||
|
||||
export function deleteClusterInfo() {
|
||||
localStorage.removeItem(Cache.clusterInfo);
|
||||
}
|
||||
|
||||
export function getClusterInfo() {
|
||||
const str = localStorage.getItem(Cache.clusterInfo);
|
||||
return str ? JSON.parse(str) : undefined;
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
<p></p>
|
||||
<hr />
|
||||
<h3>kafka API 版本兼容性</h3>
|
||||
<div class="green">
|
||||
broker版本说明:
|
||||
该值并不保证broker实际版本一定是该值(大概是这个版本范围),broker使用不同的模式(如kraft)可能显示不同的值
|
||||
</div>
|
||||
<a-spin :spinning="apiVersionInfoLoading">
|
||||
<a-table
|
||||
:columns="columns"
|
||||
@@ -104,6 +108,11 @@ const columns = [
|
||||
dataIndex: "host",
|
||||
key: "host",
|
||||
},
|
||||
{
|
||||
title: "broker版本",
|
||||
dataIndex: "brokerVersion",
|
||||
key: "brokerVersion",
|
||||
},
|
||||
{
|
||||
title: "支持的api数量",
|
||||
dataIndex: "supportNums",
|
||||
@@ -125,4 +134,7 @@ const columns = [
|
||||
.card-style {
|
||||
width: 100%;
|
||||
}
|
||||
.green {
|
||||
color: green;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<a-input
|
||||
placeholder="username"
|
||||
:allowClear="true"
|
||||
:maxLength="30"
|
||||
:maxLength="100"
|
||||
v-decorator="[
|
||||
'username',
|
||||
{
|
||||
@@ -30,7 +30,7 @@
|
||||
<a-input
|
||||
placeholder="password"
|
||||
:allowClear="true"
|
||||
:maxLength="30"
|
||||
:maxLength="100"
|
||||
v-decorator="[
|
||||
'password',
|
||||
{
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<h3 class="login-title">登录kafka-console-ui</h3>
|
||||
<a-form-item label="账号">
|
||||
<a-input
|
||||
@keyup.enter="handleSubmit"
|
||||
style="width: 200px"
|
||||
allowClear
|
||||
v-decorator="[
|
||||
@@ -20,6 +21,7 @@
|
||||
</a-form-item>
|
||||
<a-form-item label="密码">
|
||||
<a-input-password
|
||||
@keyup.enter="handleSubmit"
|
||||
style="width: 200px"
|
||||
v-decorator="[
|
||||
'password',
|
||||
@@ -28,7 +30,11 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item :wrapper-col="{ span: 16, offset: 5 }">
|
||||
<a-button type="primary" @click="handleSubmit" :loading="loading"
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="handleSubmit"
|
||||
:loading="loading"
|
||||
@keyup.enter="handleSubmit"
|
||||
>登录</a-button
|
||||
>
|
||||
</a-form-item>
|
||||
@@ -40,7 +46,7 @@ import request from "@/utils/request";
|
||||
import { AuthApi } from "@/utils/api";
|
||||
import notification from "ant-design-vue/lib/notification";
|
||||
import { mapMutations } from "vuex";
|
||||
import { AUTH } from "@/store/mutation-types";
|
||||
import { AUTH, CLUSTER } from "@/store/mutation-types";
|
||||
|
||||
export default {
|
||||
name: "Login",
|
||||
@@ -82,8 +88,19 @@ export default {
|
||||
setToken: AUTH.SET_TOKEN,
|
||||
setUsername: AUTH.SET_USERNAME,
|
||||
setPermissions: AUTH.SET_PERMISSIONS,
|
||||
deleteClusterInfo: CLUSTER.DELETE,
|
||||
}),
|
||||
},
|
||||
created() {
|
||||
request({
|
||||
url: AuthApi.ownDataAuthority.url,
|
||||
method: AuthApi.ownDataAuthority.method,
|
||||
}).then((res) => {
|
||||
if (!res) {
|
||||
this.deleteClusterInfo();
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@@ -24,7 +24,11 @@
|
||||
<DeleteMessage :topic-list="topicList"></DeleteMessage>
|
||||
</a-tab-pane>
|
||||
|
||||
<a-tab-pane key="5" tab="发送统计" v-if="isAuthorized('message:send-statistics')">
|
||||
<a-tab-pane
|
||||
key="5"
|
||||
tab="发送统计"
|
||||
v-if="isAuthorized('message:send-statistics')"
|
||||
>
|
||||
<SendStatistics :topic-list="topicList"></SendStatistics>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
|
||||
@@ -60,6 +60,33 @@
|
||||
</a-col>
|
||||
</a-row>
|
||||
<hr class="hr" />
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="24">
|
||||
<a-form-item label="最大检索数">
|
||||
<a-input-number
|
||||
v-decorator="[
|
||||
'filterNumber',
|
||||
{
|
||||
initialValue: 5000,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '输入消息数!',
|
||||
},
|
||||
],
|
||||
},
|
||||
]"
|
||||
:min="1"
|
||||
:max="100000"
|
||||
/>
|
||||
<span
|
||||
>条
|
||||
注意:这里允许最多检索10万条,但是不建议将该值设置过大,这意味着一次查询要在内存里缓存这么多的数据,可能导致内存溢出;并且更大的消息量会导致更长的检索时间</span
|
||||
>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<hr class="hr" />
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="5">
|
||||
<a-form-item label="消息过滤">
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
:mask="false"
|
||||
:destroyOnClose="true"
|
||||
:footer="null"
|
||||
:maskClosable="false"
|
||||
:maskClosable="true"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<div>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
:mask="false"
|
||||
:destroyOnClose="true"
|
||||
:footer="null"
|
||||
:maskClosable="false"
|
||||
:maskClosable="true"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<div>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
:mask="false"
|
||||
:destroyOnClose="true"
|
||||
:footer="null"
|
||||
:maskClosable="false"
|
||||
:maskClosable="true"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
:width="1200"
|
||||
:mask="false"
|
||||
:destroyOnClose="true"
|
||||
:maskClosable="false"
|
||||
:maskClosable="true"
|
||||
@cancel="handleCancel"
|
||||
okText="确认"
|
||||
cancelText="取消"
|
||||
@@ -91,6 +91,7 @@ export default {
|
||||
loading: false,
|
||||
form: this.$form.createForm(this, { name: "coordinated" }),
|
||||
brokerSize: 0,
|
||||
brokerIdList: [],
|
||||
replicaNums: 0,
|
||||
defaultReplicaNums: 0,
|
||||
};
|
||||
@@ -136,6 +137,8 @@ export default {
|
||||
method: KafkaClusterApi.getClusterInfo.method,
|
||||
}).then((res) => {
|
||||
this.brokerSize = res.data.nodes.length;
|
||||
this.brokerIdList = res.data.nodes.map((o) => o.id);
|
||||
this.brokerIdList.sort((a, b) => a - b);
|
||||
});
|
||||
},
|
||||
handleCancel() {
|
||||
@@ -149,9 +152,16 @@ export default {
|
||||
if (this.data.partitions.length > 0) {
|
||||
this.data.partitions.forEach((p) => {
|
||||
if (value > p.replicas.length) {
|
||||
let min = this.brokerIdList[0];
|
||||
let max = this.brokerIdList[this.brokerSize - 1] + 1;
|
||||
let num = p.replicas[p.replicas.length - 1];
|
||||
for (let i = p.replicas.length; i < value; i++) {
|
||||
p.replicas.push(++num % this.brokerSize);
|
||||
++num;
|
||||
if (num < max) {
|
||||
p.replicas.push(num);
|
||||
} else {
|
||||
p.replicas.push((num % max) + min);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (value < p.replicas.length) {
|
||||
|
||||
Reference in New Issue
Block a user