From c15276cfcb9bd7c657ba1994df99fa24d889ca1c Mon Sep 17 00:00:00 2001 From: universefeeler Date: Thu, 23 May 2019 02:41:41 +0800 Subject: [PATCH 1/4] [ISSUE #876] feature(console):support console show cluster node state --- console/src/main/resources/nacos-version.txt | 0 .../suggest/browser/media/String_16x.svg | 0 .../browser/media/String_inverse_16x.svg | 0 .../resources/static/console-fe/src/index.js | 2 + .../console-fe/src/layouts/MainLayout.js | 11 +- .../static/console-fe/src/locales/en-US.js | 14 ++ .../static/console-fe/src/locales/zh-CN.js | 14 ++ .../resources/static/console-fe/src/menu.js | 31 +++ .../ClusterNodeList/ClusterNodeList.js | 205 ++++++++++++++++++ .../ClusterNodeList/ClusterNodeList.scss | 29 +++ .../ClusterNodeList/index.js | 16 ++ .../naming/controllers/ClusterController.java | 48 ++++ .../nacos/naming/core/ServiceManager.java | 37 ++++ .../nacos/naming/pojo/ClusterStateView.java | 79 +++++++ 14 files changed, 485 insertions(+), 1 deletion(-) mode change 100755 => 100644 console/src/main/resources/nacos-version.txt mode change 100755 => 100644 console/src/main/resources/static/console-fe/public/js/vs/editor/contrib/suggest/browser/media/String_16x.svg mode change 100755 => 100644 console/src/main/resources/static/console-fe/public/js/vs/editor/contrib/suggest/browser/media/String_inverse_16x.svg create mode 100644 console/src/main/resources/static/console-fe/src/pages/ClusterManagement/ClusterNodeList/ClusterNodeList.js create mode 100644 console/src/main/resources/static/console-fe/src/pages/ClusterManagement/ClusterNodeList/ClusterNodeList.scss create mode 100644 console/src/main/resources/static/console-fe/src/pages/ClusterManagement/ClusterNodeList/index.js create mode 100644 naming/src/main/java/com/alibaba/nacos/naming/pojo/ClusterStateView.java diff --git a/console/src/main/resources/nacos-version.txt b/console/src/main/resources/nacos-version.txt old mode 100755 new mode 100644 diff --git a/console/src/main/resources/static/console-fe/public/js/vs/editor/contrib/suggest/browser/media/String_16x.svg b/console/src/main/resources/static/console-fe/public/js/vs/editor/contrib/suggest/browser/media/String_16x.svg old mode 100755 new mode 100644 diff --git a/console/src/main/resources/static/console-fe/public/js/vs/editor/contrib/suggest/browser/media/String_inverse_16x.svg b/console/src/main/resources/static/console-fe/public/js/vs/editor/contrib/suggest/browser/media/String_inverse_16x.svg old mode 100755 new mode 100644 diff --git a/console/src/main/resources/static/console-fe/src/index.js b/console/src/main/resources/static/console-fe/src/index.js index 07b6752d123..7ef74c562e4 100644 --- a/console/src/main/resources/static/console-fe/src/index.js +++ b/console/src/main/resources/static/console-fe/src/index.js @@ -41,6 +41,7 @@ import ListeningToQuery from './pages/ConfigurationManagement/ListeningToQuery'; import ConfigurationManagement from './pages/ConfigurationManagement/ConfigurationManagement'; import ServiceList from './pages/ServiceManagement/ServiceList'; import ServiceDetail from './pages/ServiceManagement/ServiceDetail'; +import ClusterNodeList from './pages/ClusterManagement/ClusterNodeList'; import reducers from './reducers'; import { changeLanguage } from './reducers/locale'; @@ -81,6 +82,7 @@ const MENU = [ { path: '/configurationManagement', component: ConfigurationManagement }, { path: '/serviceManagement', component: ServiceList }, { path: '/serviceDetail', component: ServiceDetail }, + { path: '/clusterManagement', component: ClusterNodeList }, ]; @connect( diff --git a/console/src/main/resources/static/console-fe/src/layouts/MainLayout.js b/console/src/main/resources/static/console-fe/src/layouts/MainLayout.js index 529b4d223e9..a3f0f4bcb4c 100644 --- a/console/src/main/resources/static/console-fe/src/layouts/MainLayout.js +++ b/console/src/main/resources/static/console-fe/src/layouts/MainLayout.js @@ -391,7 +391,7 @@ class MainLayout extends React.Component { refreshNav() { const { navList } = this.state; const { location, history, functionMode } = this.props; - const [configUrl, serviceUrl] = ['/configurationManagement', '/serviceManagement']; + const [configUrl, serviceUrl, clusterUrl] = ['/configurationManagement', '/serviceManagement', '/clusterManagement']; this.setState( { navList: navList.map(item => { @@ -407,6 +407,12 @@ class MainLayout extends React.Component { ) { item.enable = true; } + if ( + item.serviceName === 'clusterManagementVirtual' && + (functionMode === null || functionMode === 'cluster') + ) { + item.enable = true; + } return item; }), }, @@ -418,6 +424,9 @@ class MainLayout extends React.Component { if (functionMode === 'naming' && location.pathname === configUrl) { history.push(serviceUrl); } + if (functionMode === 'cluster' && location.pathname === clusterUrl) { + history.push(clusterUrl); + } } componentWillReceiveProps() { diff --git a/console/src/main/resources/static/console-fe/src/locales/en-US.js b/console/src/main/resources/static/console-fe/src/locales/en-US.js index 524425aecb0..6ef9a3666e5 100644 --- a/console/src/main/resources/static/console-fe/src/locales/en-US.js +++ b/console/src/main/resources/static/console-fe/src/locales/en-US.js @@ -46,6 +46,8 @@ const I18N_CONF = { serviceManagement: 'Service List', serviceDetail: 'Service Details', namespace: 'Namespace', + clusterManagementVirtual: 'ClusterManagement', + clusterManagement: 'Cluster Node List', }, NameSpace: { namespace: 'Namespaces', @@ -89,6 +91,18 @@ const I18N_CONF = { promptDelete: 'Do you want to delete the service?', create: 'Create Service', }, + ClusterNodeList: { + clusterNodeList: 'Node List', + nodeIp: 'NodeIp', + nodeIpPlaceholder: 'Please enter node Ip', + query: 'Search', + pubNoData: 'No results found.', + nodeState: 'NodeState', + clusterTerm: 'ClusterTerm', + leaderDueMs: 'LeaderDueMs', + heartbeatDueMs: 'HeartbeatDueMs', + voteFor: 'VoteFor', + }, EditClusterDialog: { updateCluster: 'Update Cluster', checkType: 'Check Type', diff --git a/console/src/main/resources/static/console-fe/src/locales/zh-CN.js b/console/src/main/resources/static/console-fe/src/locales/zh-CN.js index 84edb7e22ce..80c704384f5 100644 --- a/console/src/main/resources/static/console-fe/src/locales/zh-CN.js +++ b/console/src/main/resources/static/console-fe/src/locales/zh-CN.js @@ -46,6 +46,8 @@ const I18N_CONF = { serviceManagement: '服务列表', serviceDetail: '服务详情', namespace: '命名空间', + clusterManagementVirtual: '集群管理', + clusterManagement: '节点列表', }, NameSpace: { namespace: '命名空间', @@ -89,6 +91,18 @@ const I18N_CONF = { promptDelete: '确定要删除当前服务吗?', create: '创建服务', }, + ClusterNodeList: { + clusterNodeList: '节点列表', + nodeIp: '节点Ip', + nodeIpPlaceholder: '请输入节点Ip', + query: '查询', + pubNoData: '没有数据', + nodeState: '节点状态', + clusterTerm: '集群任期', + leaderDueMs: 'Leader止时', + heartbeatDueMs: '心跳止时', + voteFor: '投票对象', + }, EditClusterDialog: { updateCluster: '更新集群', checkType: '检查类型', diff --git a/console/src/main/resources/static/console-fe/src/menu.js b/console/src/main/resources/static/console-fe/src/menu.js index c8bc6734731..e1cdbdd5e1a 100644 --- a/console/src/main/resources/static/console-fe/src/menu.js +++ b/console/src/main/resources/static/console-fe/src/menu.js @@ -227,6 +227,37 @@ module.exports = { useRouter: false, id: 'namespace', }, + { + enable: false, + isExtend: true, + name: '集群管理', + title: '集群管理', + isVirtual: true, + projectName: 'nacos', + serviceName: 'clusterManagementVirtual', + link: 'clusterManagementVirtual', + hasFusion: true, + template: '', + registerName: 'com.alibaba.nacos.page.clusterManagementVirtual', + useRouter: false, + id: 'com.alibaba.nacos.page.clusterManagementVirtual', + children: [ + { + isExtend: false, + name: '节点状态', + title: '节点状态', + isVirtual: false, + projectName: 'nacos', + serviceName: 'clusterManagement', + link: 'clusterManagement', + hasFusion: true, + template: '', + registerName: 'com.alibaba.nacos.page.clusterManagement', + useRouter: false, + id: 'clusterManagement', + }, + ], + }, ], defaultKey: 'configurationManagement', projectName: 'nacos', diff --git a/console/src/main/resources/static/console-fe/src/pages/ClusterManagement/ClusterNodeList/ClusterNodeList.js b/console/src/main/resources/static/console-fe/src/pages/ClusterManagement/ClusterNodeList/ClusterNodeList.js new file mode 100644 index 00000000000..5bacc257cda --- /dev/null +++ b/console/src/main/resources/static/console-fe/src/pages/ClusterManagement/ClusterNodeList/ClusterNodeList.js @@ -0,0 +1,205 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * Licensed 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. + */ + +import React from 'react'; +import PropTypes from 'prop-types'; +import { + Button, + Field, + Form, + Grid, + Input, + Loading, + Pagination, + Table, + ConfigProvider, +} from '@alifd/next'; +import { request } from '../../../globalLib'; +import RegionGroup from '../../../components/RegionGroup'; + +import './ClusterNodeList.scss'; + +const FormItem = Form.Item; +const { Row, Col } = Grid; +const { Column } = Table; + +@ConfigProvider.config +class ClusterNodeList extends React.Component { + static displayName = 'ClusterNodeList'; + + static propTypes = { + locale: PropTypes.object, + }; + + constructor(props) { + super(props); + this.state = { + loading: false, + total: 0, + pageSize: 10, + currentPage: 1, + keyword: '', + dataSource: [], + }; + this.field = new Field(this); + } + + openLoading() { + this.setState({ loading: true }); + } + + closeLoading() { + this.setState({ loading: false }); + } + + openEditServiceDialog() { + try { + this.editServiceDialog.current.getInstance().show(this.state.service); + } catch (error) {} + } + + queryClusterStateList() { + const { currentPage, pageSize, keyword, withInstances = false } = this.state; + const parameter = [ + `withInstances=${withInstances}`, + `pageNo=${currentPage}`, + `pageSize=${pageSize}`, + `keyword=${keyword}`, + ]; + request({ + url: `v1/ns/cluster/states?${parameter.join('&')}`, + beforeSend: () => this.openLoading(), + success: ({ count = 0, clusterStateList = [] } = {}) => { + this.setState({ + dataSource: clusterStateList, + total: count, + }); + }, + error: () => + this.setState({ + dataSource: [], + total: 0, + currentPage: 0, + }), + complete: () => this.closeLoading(), + }); + } + + getQueryLater = () => { + setTimeout(() => this.queryClusterStateList()); + }; + + setNowNameSpace = (nowNamespaceName, nowNamespaceId) => + this.setState({ + nowNamespaceName, + nowNamespaceId, + }); + + rowColor = row => ({ className: !row.voteFor ? 'row-bg-red' : '' }); + + render() { + const { locale = {} } = this.props; + const { + pubNoData, + clusterNodeList, + nodeIp, + nodeIpPlaceholder, + query, + } = locale; + const { keyword, nowNamespaceName, nowNamespaceId } = this.state; + const { init, getValue } = this.field; + this.init = init; + this.getValue = getValue; + + return ( +
+ +
+ +
+

+ {clusterNodeList} + | + {nowNamespaceName} + {nowNamespaceId} +

+ + +
+ + this.setState({ keyword })} + onPressEnter={() => + this.setState({ currentPage: 1 }, () => this.queryClusterStateList()) + } + /> + + + + +
+ +
+ + + this.rowColor(row)} + > + + + + + + +
+ +
+ {this.state.total > this.state.pageSize && ( +
+ + this.setState({ currentPage }, () => this.queryClusterStateList()) + } + /> +
+ )} +
+
+ ); + } +} + +export default ClusterNodeList; diff --git a/console/src/main/resources/static/console-fe/src/pages/ClusterManagement/ClusterNodeList/ClusterNodeList.scss b/console/src/main/resources/static/console-fe/src/pages/ClusterManagement/ClusterNodeList/ClusterNodeList.scss new file mode 100644 index 00000000000..1052ee91e3e --- /dev/null +++ b/console/src/main/resources/static/console-fe/src/pages/ClusterManagement/ClusterNodeList/ClusterNodeList.scss @@ -0,0 +1,29 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * Licensed 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. + */ + +.cluster-management { + .page-title { + height: 30px; + width: 100%; + line-height: 30px; + margin: 0 0 20px; + padding: 0 0 0 10px; + border-left: 3px solid #09c; + color: #ccc; + } + .title-item { + font-size: 14px; + color: #000; + margin-right: 8px; + } +} diff --git a/console/src/main/resources/static/console-fe/src/pages/ClusterManagement/ClusterNodeList/index.js b/console/src/main/resources/static/console-fe/src/pages/ClusterManagement/ClusterNodeList/index.js new file mode 100644 index 00000000000..b93591b743d --- /dev/null +++ b/console/src/main/resources/static/console-fe/src/pages/ClusterManagement/ClusterNodeList/index.js @@ -0,0 +1,16 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * Licensed 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. + */ + +import ClusterNodeList from './ClusterNodeList'; + +export default ClusterNodeList; diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/ClusterController.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/ClusterController.java index 714f2d40a55..a2c4163b25e 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/ClusterController.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/ClusterController.java @@ -16,11 +16,14 @@ package com.alibaba.nacos.naming.controllers; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.naming.CommonParams; import com.alibaba.nacos.api.naming.pojo.AbstractHealthChecker; import com.alibaba.nacos.core.utils.WebUtils; +import com.alibaba.nacos.naming.consistency.persistent.raft.RaftPeer; +import com.alibaba.nacos.naming.consistency.persistent.raft.RaftPeerSet; import com.alibaba.nacos.naming.core.Cluster; import com.alibaba.nacos.naming.core.Service; import com.alibaba.nacos.naming.core.ServiceManager; @@ -28,6 +31,8 @@ import com.alibaba.nacos.naming.healthcheck.HealthCheckType; import com.alibaba.nacos.naming.misc.Loggers; import com.alibaba.nacos.naming.misc.UtilsAndCommons; +import com.alibaba.nacos.naming.pojo.ClusterStateView; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; @@ -37,6 +42,9 @@ import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; /** * @author nkorange @@ -48,6 +56,9 @@ public class ClusterController { @Autowired protected ServiceManager serviceManager; + @Autowired + private RaftPeerSet raftPeerSet; + @RequestMapping(value = "", method = RequestMethod.PUT) public String update(HttpServletRequest request) throws Exception { @@ -96,4 +107,41 @@ public String update(HttpServletRequest request) throws Exception { return "ok"; } + @RequestMapping(value = "/states", method = RequestMethod.GET) + public Object listStates(HttpServletRequest request) { + + String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, + Constants.DEFAULT_NAMESPACE_ID); + JSONObject result = new JSONObject(); + int page = Integer.parseInt(WebUtils.required(request, "pageNo")); + int pageSize = Integer.parseInt(WebUtils.required(request, "pageSize")); + String keyword = WebUtils.optional(request, "keyword", StringUtils.EMPTY); + String containedInstance = WebUtils.optional(request, "instance", StringUtils.EMPTY); + + List raftPeerLists = new ArrayList<>(); + + int total = serviceManager.getPagedClusterState(namespaceId, page - 1, pageSize, keyword, containedInstance, raftPeerLists, raftPeerSet); + + if (CollectionUtils.isEmpty(raftPeerLists)) { + result.put("clusterStateList", Collections.emptyList()); + result.put("count", 0); + return result; + } + + JSONArray clusterStateJsonArray = new JSONArray(); + for(RaftPeer raftPeer: raftPeerLists) { + ClusterStateView clusterStateView = new ClusterStateView(); + clusterStateView.setClusterTerm(raftPeer.term.intValue()); + clusterStateView.setNodeIp(raftPeer.ip); + clusterStateView.setNodeState(raftPeer.state.name()); + clusterStateView.setVoteFor(raftPeer.voteFor); + clusterStateView.setHeartbeatDueMs(raftPeer.heartbeatDueMs); + clusterStateView.setLeaderDueMs(raftPeer.leaderDueMs); + clusterStateJsonArray.add(clusterStateView); + } + result.put("clusterStateList", clusterStateJsonArray); + result.put("count", total); + return result; + } + } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceManager.java b/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceManager.java index 50312757d02..fea9087a67a 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceManager.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceManager.java @@ -27,6 +27,8 @@ import com.alibaba.nacos.naming.consistency.Datum; import com.alibaba.nacos.naming.consistency.KeyBuilder; import com.alibaba.nacos.naming.consistency.RecordListener; +import com.alibaba.nacos.naming.consistency.persistent.raft.RaftPeer; +import com.alibaba.nacos.naming.consistency.persistent.raft.RaftPeerSet; import com.alibaba.nacos.naming.misc.*; import com.alibaba.nacos.naming.push.PushService; import org.apache.commons.lang3.ArrayUtils; @@ -649,6 +651,41 @@ public int getPagedService(String namespaceId, int startPage, int pageSize, Stri return matchList.size(); } + public int getPagedClusterState(String namespaceId, int startPage, int pageSize, String keyword, String containedInstance, List raftPeerList, RaftPeerSet raftPeerSet) { + + List matchList = new ArrayList<>(raftPeerSet.allPeers()); + + List tempList = new ArrayList<>(); + if(StringUtils.isNotBlank(keyword)) { + for(RaftPeer raftPeer: matchList) { + String ip = raftPeer.ip.split(":")[0]; + if(keyword.equals(ip)) { + tempList.add(raftPeer); + } + } + matchList = tempList; + } + + if (pageSize >= matchList.size()) { + raftPeerList.addAll(matchList); + return matchList.size(); + } + + for (int i = 0; i < matchList.size(); i++) { + if (i < startPage * pageSize) { + continue; + } + + raftPeerList.add(matchList.get(i)); + + if (raftPeerList.size() >= pageSize) { + break; + } + } + + return matchList.size(); + } + public static class ServiceChecksum { public String namespaceId; diff --git a/naming/src/main/java/com/alibaba/nacos/naming/pojo/ClusterStateView.java b/naming/src/main/java/com/alibaba/nacos/naming/pojo/ClusterStateView.java new file mode 100644 index 00000000000..cf286b2c6b2 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/pojo/ClusterStateView.java @@ -0,0 +1,79 @@ +package com.alibaba.nacos.naming.pojo; + +import com.alibaba.fastjson.JSON; + + +/** + * @author: universefeeler + * @Date: 2019/05/19 15:51 + * @Description: + */ +public class ClusterStateView{ + + private String nodeIp; + + private String nodeState; + + private long clusterTerm; + + private long leaderDueMs; + + private String voteFor; + + private long heartbeatDueMs; + + public long getLeaderDueMs() { + return leaderDueMs; + } + + public void setLeaderDueMs(long leaderDueMs) { + this.leaderDueMs = leaderDueMs; + } + + + public long getHeartbeatDueMs() { + return heartbeatDueMs; + } + + public void setHeartbeatDueMs(long heartbeatDueMs) { + this.heartbeatDueMs = heartbeatDueMs; + } + + + public String getVoteFor() { + return voteFor; + } + + public void setVoteFor(String voteFor) { + this.voteFor = voteFor; + } + + public String getNodeIp() { + return nodeIp; + } + + public void setNodeIp(String nodeIp) { + this.nodeIp = nodeIp; + } + + public String getNodeState() { + return nodeState; + } + + public void setNodeState(String nodeState) { + this.nodeState = nodeState; + } + + public long getClusterTerm() { + return clusterTerm; + } + + public void setClusterTerm(long clusterTerm) { + this.clusterTerm = clusterTerm; + } + + @Override + public String toString() { + return JSON.toJSONString(this); + } +} From 4fe09db8abf48ed9313af02aa2e625b8ef2c36f9 Mon Sep 17 00:00:00 2001 From: universefeeler Date: Wed, 5 Jun 2019 14:56:34 +0800 Subject: [PATCH 2/4] fixed(naming): add licensed for ClusterStateView.java --- .../nacos/naming/pojo/ClusterStateView.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/naming/src/main/java/com/alibaba/nacos/naming/pojo/ClusterStateView.java b/naming/src/main/java/com/alibaba/nacos/naming/pojo/ClusterStateView.java index cf286b2c6b2..ca06da76590 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/pojo/ClusterStateView.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/pojo/ClusterStateView.java @@ -1,3 +1,18 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.nacos.naming.pojo; import com.alibaba.fastjson.JSON; From d08557a725ee27bdb892b4cf2e6233db4b34af8e Mon Sep 17 00:00:00 2001 From: universefeeler Date: Fri, 7 Jun 2019 00:19:25 +0800 Subject: [PATCH 3/4] chore(clusterNode): adjust some code --- .../static/console-fe/src/locales/en-US.js | 4 +- .../static/console-fe/src/locales/zh-CN.js | 4 +- .../ClusterNodeList/ClusterNodeList.js | 2 +- .../naming/controllers/ClusterController.java | 40 ----------- .../controllers/OperatorController.java | 46 ++++++++++++ .../nacos/naming/core/ServiceManager.java | 70 +++++++++---------- 6 files changed, 86 insertions(+), 80 deletions(-) diff --git a/console/src/main/resources/static/console-fe/src/locales/en-US.js b/console/src/main/resources/static/console-fe/src/locales/en-US.js index 6ef9a3666e5..d03e6003971 100644 --- a/console/src/main/resources/static/console-fe/src/locales/en-US.js +++ b/console/src/main/resources/static/console-fe/src/locales/en-US.js @@ -99,8 +99,8 @@ const I18N_CONF = { pubNoData: 'No results found.', nodeState: 'NodeState', clusterTerm: 'ClusterTerm', - leaderDueMs: 'LeaderDueMs', - heartbeatDueMs: 'HeartbeatDueMs', + leaderDueMs: 'LeaderDue(ms)', + heartbeatDueMs: 'HeartbeatDue(ms)', voteFor: 'VoteFor', }, EditClusterDialog: { diff --git a/console/src/main/resources/static/console-fe/src/locales/zh-CN.js b/console/src/main/resources/static/console-fe/src/locales/zh-CN.js index 80c704384f5..eeb36083105 100644 --- a/console/src/main/resources/static/console-fe/src/locales/zh-CN.js +++ b/console/src/main/resources/static/console-fe/src/locales/zh-CN.js @@ -99,8 +99,8 @@ const I18N_CONF = { pubNoData: '没有数据', nodeState: '节点状态', clusterTerm: '集群任期', - leaderDueMs: 'Leader止时', - heartbeatDueMs: '心跳止时', + leaderDueMs: 'Leader止时(ms)', + heartbeatDueMs: '心跳止时(ms)', voteFor: '投票对象', }, EditClusterDialog: { diff --git a/console/src/main/resources/static/console-fe/src/pages/ClusterManagement/ClusterNodeList/ClusterNodeList.js b/console/src/main/resources/static/console-fe/src/pages/ClusterManagement/ClusterNodeList/ClusterNodeList.js index 5bacc257cda..830a51b1ac4 100644 --- a/console/src/main/resources/static/console-fe/src/pages/ClusterManagement/ClusterNodeList/ClusterNodeList.js +++ b/console/src/main/resources/static/console-fe/src/pages/ClusterManagement/ClusterNodeList/ClusterNodeList.js @@ -77,7 +77,7 @@ class ClusterNodeList extends React.Component { `keyword=${keyword}`, ]; request({ - url: `v1/ns/cluster/states?${parameter.join('&')}`, + url: `v1/ns/operator/cluster/states?${parameter.join('&')}`, beforeSend: () => this.openLoading(), success: ({ count = 0, clusterStateList = [] } = {}) => { this.setState({ diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/ClusterController.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/ClusterController.java index a2c4163b25e..b8218ce519f 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/ClusterController.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/ClusterController.java @@ -56,9 +56,6 @@ public class ClusterController { @Autowired protected ServiceManager serviceManager; - @Autowired - private RaftPeerSet raftPeerSet; - @RequestMapping(value = "", method = RequestMethod.PUT) public String update(HttpServletRequest request) throws Exception { @@ -107,41 +104,4 @@ public String update(HttpServletRequest request) throws Exception { return "ok"; } - @RequestMapping(value = "/states", method = RequestMethod.GET) - public Object listStates(HttpServletRequest request) { - - String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, - Constants.DEFAULT_NAMESPACE_ID); - JSONObject result = new JSONObject(); - int page = Integer.parseInt(WebUtils.required(request, "pageNo")); - int pageSize = Integer.parseInt(WebUtils.required(request, "pageSize")); - String keyword = WebUtils.optional(request, "keyword", StringUtils.EMPTY); - String containedInstance = WebUtils.optional(request, "instance", StringUtils.EMPTY); - - List raftPeerLists = new ArrayList<>(); - - int total = serviceManager.getPagedClusterState(namespaceId, page - 1, pageSize, keyword, containedInstance, raftPeerLists, raftPeerSet); - - if (CollectionUtils.isEmpty(raftPeerLists)) { - result.put("clusterStateList", Collections.emptyList()); - result.put("count", 0); - return result; - } - - JSONArray clusterStateJsonArray = new JSONArray(); - for(RaftPeer raftPeer: raftPeerLists) { - ClusterStateView clusterStateView = new ClusterStateView(); - clusterStateView.setClusterTerm(raftPeer.term.intValue()); - clusterStateView.setNodeIp(raftPeer.ip); - clusterStateView.setNodeState(raftPeer.state.name()); - clusterStateView.setVoteFor(raftPeer.voteFor); - clusterStateView.setHeartbeatDueMs(raftPeer.heartbeatDueMs); - clusterStateView.setLeaderDueMs(raftPeer.leaderDueMs); - clusterStateJsonArray.add(clusterStateView); - } - result.put("clusterStateList", clusterStateJsonArray); - result.put("count", total); - return result; - } - } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/OperatorController.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/OperatorController.java index a4b80a61083..f1c968e5026 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/OperatorController.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/OperatorController.java @@ -24,6 +24,8 @@ import com.alibaba.nacos.naming.cluster.ServerListManager; import com.alibaba.nacos.naming.cluster.ServerStatusManager; import com.alibaba.nacos.naming.consistency.persistent.raft.RaftCore; +import com.alibaba.nacos.naming.consistency.persistent.raft.RaftPeer; +import com.alibaba.nacos.naming.consistency.persistent.raft.RaftPeerSet; import com.alibaba.nacos.naming.core.DistroMapper; import com.alibaba.nacos.naming.core.Service; import com.alibaba.nacos.naming.core.ServiceManager; @@ -31,8 +33,10 @@ import com.alibaba.nacos.naming.misc.SwitchEntry; import com.alibaba.nacos.naming.misc.SwitchManager; import com.alibaba.nacos.naming.misc.UtilsAndCommons; +import com.alibaba.nacos.naming.pojo.ClusterStateView; import com.alibaba.nacos.naming.push.PushService; import com.alibaba.nacos.naming.web.NeedAuth; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties; @@ -42,6 +46,8 @@ import javax.servlet.http.HttpServletRequest; import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -77,6 +83,9 @@ public class OperatorController { @Autowired private RaftCore raftCore; + @Autowired + private RaftPeerSet raftPeerSet; + @RequestMapping("/push/state") public JSONObject pushState(HttpServletRequest request) { @@ -215,4 +224,41 @@ public String serverStatus(HttpServletRequest request) { serverListManager.onReceiveServerStatus(serverStatus); return "ok"; } + + @RequestMapping(value = "/cluster/states", method = RequestMethod.GET) + public Object listStates(HttpServletRequest request) { + + String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, + Constants.DEFAULT_NAMESPACE_ID); + JSONObject result = new JSONObject(); + int page = Integer.parseInt(WebUtils.required(request, "pageNo")); + int pageSize = Integer.parseInt(WebUtils.required(request, "pageSize")); + String keyword = WebUtils.optional(request, "keyword", StringUtils.EMPTY); + String containedInstance = WebUtils.optional(request, "instance", StringUtils.EMPTY); + + List raftPeerLists = new ArrayList<>(); + + int total = serviceManager.getPagedClusterState(namespaceId, page - 1, pageSize, keyword, containedInstance, raftPeerLists, raftPeerSet); + + if (CollectionUtils.isEmpty(raftPeerLists)) { + result.put("clusterStateList", Collections.emptyList()); + result.put("count", 0); + return result; + } + + JSONArray clusterStateJsonArray = new JSONArray(); + for(RaftPeer raftPeer: raftPeerLists) { + ClusterStateView clusterStateView = new ClusterStateView(); + clusterStateView.setClusterTerm(raftPeer.term.intValue()); + clusterStateView.setNodeIp(raftPeer.ip); + clusterStateView.setNodeState(raftPeer.state.name()); + clusterStateView.setVoteFor(raftPeer.voteFor); + clusterStateView.setHeartbeatDueMs(raftPeer.heartbeatDueMs); + clusterStateView.setLeaderDueMs(raftPeer.leaderDueMs); + clusterStateJsonArray.add(clusterStateView); + } + result.put("clusterStateList", clusterStateJsonArray); + result.put("count", total); + return result; + } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceManager.java b/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceManager.java index fea9087a67a..3534946edba 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceManager.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceManager.java @@ -229,6 +229,41 @@ public void run() { } } + public int getPagedClusterState(String namespaceId, int startPage, int pageSize, String keyword, String containedInstance, List raftPeerList, RaftPeerSet raftPeerSet) { + + List matchList = new ArrayList<>(raftPeerSet.allPeers()); + + List tempList = new ArrayList<>(); + if(StringUtils.isNotBlank(keyword)) { + for(RaftPeer raftPeer: matchList) { + String ip = raftPeer.ip.split(":")[0]; + if(keyword.equals(ip)) { + tempList.add(raftPeer); + } + } + matchList = tempList; + } + + if (pageSize >= matchList.size()) { + raftPeerList.addAll(matchList); + return matchList.size(); + } + + for (int i = 0; i < matchList.size(); i++) { + if (i < startPage * pageSize) { + continue; + } + + raftPeerList.add(matchList.get(i)); + + if (raftPeerList.size() >= pageSize) { + break; + } + } + + return matchList.size(); + } + public void updatedHealthStatus(String namespaceId, String serviceName, String serverIP) { Message msg = synchronizer.get(serverIP, UtilsAndCommons.assembleFullServiceName(namespaceId, serviceName)); JSONObject serviceJson = JSON.parseObject(msg.getData()); @@ -651,41 +686,6 @@ public int getPagedService(String namespaceId, int startPage, int pageSize, Stri return matchList.size(); } - public int getPagedClusterState(String namespaceId, int startPage, int pageSize, String keyword, String containedInstance, List raftPeerList, RaftPeerSet raftPeerSet) { - - List matchList = new ArrayList<>(raftPeerSet.allPeers()); - - List tempList = new ArrayList<>(); - if(StringUtils.isNotBlank(keyword)) { - for(RaftPeer raftPeer: matchList) { - String ip = raftPeer.ip.split(":")[0]; - if(keyword.equals(ip)) { - tempList.add(raftPeer); - } - } - matchList = tempList; - } - - if (pageSize >= matchList.size()) { - raftPeerList.addAll(matchList); - return matchList.size(); - } - - for (int i = 0; i < matchList.size(); i++) { - if (i < startPage * pageSize) { - continue; - } - - raftPeerList.add(matchList.get(i)); - - if (raftPeerList.size() >= pageSize) { - break; - } - } - - return matchList.size(); - } - public static class ServiceChecksum { public String namespaceId; From 5d9ae1541d0bd9e15df658fec4627d3273aec4e1 Mon Sep 17 00:00:00 2001 From: universefeeler Date: Mon, 10 Jun 2019 13:02:12 +0800 Subject: [PATCH 4/4] chore(console): adjust console show details --- .../pages/ClusterManagement/ClusterNodeList/ClusterNodeList.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/console/src/main/resources/static/console-fe/src/pages/ClusterManagement/ClusterNodeList/ClusterNodeList.js b/console/src/main/resources/static/console-fe/src/pages/ClusterManagement/ClusterNodeList/ClusterNodeList.js index 830a51b1ac4..48bce72cc74 100644 --- a/console/src/main/resources/static/console-fe/src/pages/ClusterManagement/ClusterNodeList/ClusterNodeList.js +++ b/console/src/main/resources/static/console-fe/src/pages/ClusterManagement/ClusterNodeList/ClusterNodeList.js @@ -105,7 +105,7 @@ class ClusterNodeList extends React.Component { nowNamespaceId, }); - rowColor = row => ({ className: !row.voteFor ? 'row-bg-red' : '' }); + rowColor = row => ({ className: !row.voteFor ? '' : '' }); render() { const { locale = {} } = this.props; @@ -180,7 +180,6 @@ class ClusterNodeList extends React.Component { -