This commit is contained in:
zzl
2024-11-26 11:08:48 +08:00
committed by GitHub
9 changed files with 166 additions and 7 deletions

View File

@@ -117,4 +117,11 @@ public class ConsumerController {
@RequestParam boolean jstack) {
return consumerService.getConsumerRunningInfo(consumerGroup, clientId, jstack);
}
@RequestMapping(value = "/consumerStack.query")
@ResponseBody
public Object getConsumerStack(@RequestParam String consumerGroup, @RequestParam String clientId,
@RequestParam boolean jstack) {
return consumerService.getConsumerStack(consumerGroup, clientId, jstack);
}
}

View File

@@ -56,7 +56,6 @@ public class OpsController {
opsService.addNameSvrAddr(newNamesrvAddr);
return true;
}
@RequestMapping(value = "/updateIsVIPChannel.do", method = RequestMethod.POST)
@ResponseBody
public Object updateIsVIPChannel(@RequestParam String useVIPChannel) {

View File

@@ -0,0 +1,32 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.rocketmq.dashboard.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import java.util.Map;
@Builder
@Data
@AllArgsConstructor
public class StackResult {
private Map<String, String> stackMap;
}

View File

@@ -17,6 +17,7 @@
package org.apache.rocketmq.dashboard.service;
import org.apache.rocketmq.dashboard.model.StackResult;
import org.apache.rocketmq.remoting.protocol.body.ConsumerConnection;
import org.apache.rocketmq.remoting.protocol.body.ConsumerRunningInfo;
import org.apache.rocketmq.dashboard.model.ConsumerGroupRollBackStat;
@@ -55,4 +56,6 @@ public interface ConsumerService {
ConsumerConnection getConsumerConnection(String consumerGroup, String address);
ConsumerRunningInfo getConsumerRunningInfo(String consumerGroup, String clientId, boolean jstack);
StackResult getConsumerStack(String consumerGroup, String clientId, boolean jstack);
}

View File

@@ -18,11 +18,14 @@
package org.apache.rocketmq.dashboard.service.impl;
import com.google.common.base.Predicate;
import com.google.common.base.Splitter;
import com.google.common.base.Throwables;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -42,10 +45,16 @@ import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.MQVersion;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.dashboard.model.ConsumerGroupRollBackStat;
import org.apache.rocketmq.dashboard.model.GroupConsumeInfo;
import org.apache.rocketmq.dashboard.model.QueueStatInfo;
import org.apache.rocketmq.dashboard.model.StackResult;
import org.apache.rocketmq.dashboard.model.TopicConsumerInfo;
import org.apache.rocketmq.dashboard.service.client.ProxyAdmin;
import org.apache.rocketmq.remoting.protocol.admin.ConsumeStats;
import org.apache.rocketmq.remoting.protocol.admin.RollbackStats;
@@ -61,10 +70,6 @@ import org.apache.rocketmq.remoting.protocol.route.BrokerData;
import org.apache.rocketmq.remoting.protocol.subscription.SubscriptionGroupConfig;
import org.apache.rocketmq.common.utils.ThreadUtils;
import org.apache.rocketmq.dashboard.config.RMQConfigure;
import org.apache.rocketmq.dashboard.model.ConsumerGroupRollBackStat;
import org.apache.rocketmq.dashboard.model.GroupConsumeInfo;
import org.apache.rocketmq.dashboard.model.QueueStatInfo;
import org.apache.rocketmq.dashboard.model.TopicConsumerInfo;
import org.apache.rocketmq.dashboard.model.request.ConsumerConfigInfo;
import org.apache.rocketmq.dashboard.model.request.DeleteSubGroupRequest;
import org.apache.rocketmq.dashboard.model.request.ResetOffsetRequest;
@@ -482,4 +487,59 @@ public class ConsumerServiceImpl extends AbstractCommonService implements Consum
throw new RuntimeException(e);
}
}
@Override
public StackResult getConsumerStack(String consumerGroup, String clientId, boolean jstack) {
ConsumerRunningInfo consumerRunningInfo = getConsumerRunningInfo(consumerGroup, clientId, jstack);
Map<String, String> stackMap = new HashMap<>();
Map<String, List<String>> map = formatThreadStack(consumerRunningInfo.getJstack());
if (MapUtils.isNotEmpty(map)) {
Set<String> threads = map.keySet();
for (String thread : threads) {
StringBuilder result = new StringBuilder();
map.get(thread).forEach(s -> result.append(s).append("\n"));
stackMap.put(thread, result.toString());
}
}
return new StackResult(stackMap);
}
private Map<String, List<String>> formatThreadStack(String stack) {
Map<String, List<String>> threadStackMap = new HashMap<>();
List<String> stackList = Splitter.on("\n\n").splitToList(stack);
for (String threadStack : stackList) {
List<String> stacks = Splitter.on("\n").splitToList(threadStack);
if (CollectionUtils.isNotEmpty(stacks)) {
List<String> elements = new ArrayList<>();
String threadName = null;
for (String s : stacks) {
List<String> stackItem = Splitter.on(" ")
.omitEmptyStrings()
.trimResults()
.splitToList(s);
if (stackItem.size() == 1) {
String stackStr = stackItem.get(0);
if (threadName == null) {
int index = stackStr.indexOf("TID");
if (index != -1) {
threadName = stackStr.substring(0, index);
}
} else {
elements.add(stackStr.substring(threadName.length(), stackStr.length()));
}
}
if (stackItem.size() == 2) {
if (threadName == null) {
threadName = stackItem.get(0);
}
elements.add(stackItem.get(stackItem.size() - 1));
}
}
if (threadName != null) {
threadStackMap.put(threadName, elements);
}
}
}
return threadStackMap;
}
}

View File

@@ -273,7 +273,7 @@ module.controller('consumerController', ['$scope', 'ngDialog', '$http', 'Notific
console.log(resp);
ngDialog.open({
template: 'clientInfoDialog',
// controller: 'addTopicDialogController',
controller: 'consumerStackDialogController',
data: {data: resp.data, consumerGroupName: consumerGroupName}
});
} else {
@@ -421,4 +421,33 @@ module.controller('consumerTopicViewDialogController', ['$scope', 'ngDialog', '$
});
};
}]
);
module.controller('consumerStackDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) {
$scope.consumerStack = function (consumerGroup, clientId, jstack) {
$http({
method: "GET",
url: "consumer/consumerStack.query",
params: {
consumerGroup: consumerGroup
,
clientId: clientId,
jstack: jstack
}
}).success(function (resp) {
if (resp.status == 0) {
console.log(resp);
ngDialog.open({
template: 'consumerStackDialog',
data: {
consumerStack: resp.data,
clientId: clientId
}
});
} else {
Notification.error({message: resp.errMsg, delay: 2000});
}
});
};
}]
);

View File

@@ -135,4 +135,5 @@ var en = {
"MESSAGE_TYPE_FIFO": "FIFO",
"MESSAGE_TYPE_DELAY": "DELAY",
"MESSAGE_TYPE_TRANSACTION": "TRANSACTION",
"STACK": "STACK"
}

View File

@@ -136,4 +136,5 @@ var zh = {
"MESSAGE_TYPE_FIFO": "顺序消息",
"MESSAGE_TYPE_DELAY": "定时/延时消息",
"MESSAGE_TYPE_TRANSACTION": "事务消息",
"STACK": "堆栈"
}

View File

@@ -160,7 +160,7 @@
<div class="modal-header">
<h4 class="modal-title">[{{ngDialogData.consumerGroupName}}]{{'CLIENT'|translate}}</h4>
</div>
<div class="modal-body ">
<div class="modal-body " style="overflow: auto">
<form class="form-horizontal" novalidate>
<table class="table table-bordered">
<tr>
@@ -168,12 +168,15 @@
<th class="text-center">ClientAddr</th>
<th class="text-center">Language</th>
<th class="text-center">Version</th>
<th class="text-center">Operation</th>
</tr>
<tr ng-repeat="conn in ngDialogData.data.connectionSet">
<td class="text-center">{{conn.clientId}}</td>
<td class="text-center">{{conn.clientAddr}}</td>
<td class="text-center">{{conn.language}}</td>
<td class="text-center">{{conn.versionDesc}}</td>
<td class="text-center"><a
ng-click="consumerStack(ngDialogData.consumerGroupName, conn.clientId, true)">{{'STACK' | translate}}</a></td>
</tr>
</table>
<p>Below is subscription:</p>
@@ -568,4 +571,28 @@
</div>
</div>
</div>
</script>
<!--consumer stack-->
<script type="text/ng-template" id="consumerStackDialog">
<div class="modal-dialog modal-lg">
<div class="modal-header">
<h4 class="modal-title">{{ngDialogData.clientId}}</h4>
</div>
<div class="modal-body ">
<div ng-repeat="(thread, stackItem) in ngDialogData.consumerStack.stackMap">
<p style="white-space: pre-line;">
<label style="color: #0da6e3;">Thread: {{thread}}</label>
{{stackItem}}
</p>
</div>
</div>
<div class="modal-footer">
<div class="ngdialog-buttons">
<button type="button" class="ngdialog-button ngdialog-button-secondary"
ng-click="closeThisDialog('Cancel')">{{ 'CLOSE' | translate }}
</button>
</div>
</div>
</div>
</script>