Compare commits

...

30 Commits

Author SHA1 Message Date
otto 15aa7016b9 Merge pull request 'qps数值保留两位小数,优化展示效果' (#893) from otto/microservices:dev_monitoring into dev_monitoring 2025-05-29 10:43:11 +08:00
OTTO f96cee3f4a fix(运维监控模块): qps数值保留两位小数,优化展示效果 2025-05-29 10:29:19 +08:00
otto 055b26ee29 Merge pull request '修复集群状态数据获取错误的问题' (#892) from otto/microservices:dev_monitoring into dev_monitoring 2025-05-29 09:45:40 +08:00
OTTO 368a45e8b2 fix(运维监控模块): 修复集群状态数据获取错误的问题
1. 修复获取微服务重启次数时为null的问题(由于重启次数计算的是一天内的重启次数所以数值存在小数,导致转换失败)

2. 修复获取qps失败的问题(指标使用错误、获取范围指标值时属性使用错误)
2025-05-29 09:45:11 +08:00
otto 2c5f0518ca Merge pull request '修改配置文件' (#879) from otto/microservices:dev_monitoring into dev_monitoring 2025-05-22 16:43:44 +08:00
OTTO 06fbbf7208 feat(运维监控模块): 修改配置文件 2025-05-22 16:43:09 +08:00
otto 6613950ff8 Merge pull request '完成运维监控模块开发' (#878) from otto/microservices:dev_monitoring into dev_monitoring 2025-05-22 15:41:30 +08:00
OTTO 8184eb2401 Merge branch 'master' of code.gitlink.org.cn:Gitlink/microservices into dev_monitoring 2025-05-22 15:40:31 +08:00
OTTO 213aee5587 feat(运维监控模块): 新增获取Node状态接口
1. 创建 [K8sNodeStatus] 实例并设置节点IP。
2. 构造PromQL查询语句,调用 Prometheus 接口获取节点CPU使用率:
   - 使用 [PromQLConstant.NODE_CPU_USAGE(nodeIp)] 生成查询语句。
   - 调用 `remoteGatewayService.getPrometheusQueryRes(...)` 发送请求。
   - 解析响应结果,若成功则提取CPU使用率数值并设置到对象中。
3. 同理,查询节点内存使用率并设置到对象中。
4. 返回封装好的 [K8sNodeStatus]对象。
2025-05-22 15:40:14 +08:00
OTTO 274af358dd feat(运维监控模块): 新增获取Pod容器状态接口
根据容器ID查询其在Kubernetes中的Pod的CPU和内存使用情况,并封装为[K8sPodContainerStatus]对象返回。具体逻辑如下:

1. 构造PromQL查询:通过 [PromQLConstant.POD_CPU_USAGE(containerID)] 构造CPU使用率查询语句。
2. 调用Prometheus接口:使用 `remoteGatewayService.getPrometheusQueryRes()` 向Prometheus发起查询请求,携带用户认证信息。
3. 解析CPU结果:若查询成功,从返回结果中提取Pod名称和CPU使用率并设置到返回对象中。
4. 构造内存查询并解析结果:类似步骤1-3,使用 [PromQLConstant.POD_MEMORY_USAGE(containerID)]查询内存使用字节数。
5. 单位转换:将内存由字节转为MiB或GiB,并格式化字符串设置进返回对象。
6. 返回结果:最终返回封装好的 [K8sPodContainerStatus] 对象。
2025-05-22 14:52:38 +08:00
OTTO 58bf8326c5 feat(运维监控模块): 增加 Pod 信息展示并优化应用列表
1. 在 K8sApplication 中添加 podSelector 和 podList 字段
2. 新增 K8sApplicationPod 类用于表示 Pod 信息
3. 修改 getNamespaceDetailList 方法支持获取 Pod 信息
4. 优化应用列表展示,增加 Pod选择器和 Pod 列表
2025-05-22 14:07:10 +08:00
OTTO 5e19e53b94 feat(运维监控模块):新增最近24小时应用接口请求次数查询接口
该方法用于获取 Kubernetes 集群中各个应用的请求次数,并封装成 [ApplicationRequestCountData]列表返回。其主要逻辑如下:

1. 获取 Nginx 服务端口别名映射:调用 Rancher 接口获取指定服务信息,提取各端口的名称(别名)并存入 `portMap`。
2. 查询 Prometheus 请求次数数据:通过 Prometheus 查询表达式 [APPLICATION_REQUEST_COUNT] 获取各应用的请求次数。
3. 组装结果数据:将 Prometheus 返回的端口号替换为对应别名,设置序号和请求次数,最终返回 [ApplicationRequestCountData] 列表。
2025-05-22 11:28:37 +08:00
OTTO 2be36ca12e feat(运维监控模块):新增集群每秒请求率(QPS)查询接口
该方法用于获取指定Kubernetes集群在过去一小时内的节点数量指标数据(QPS趋势),若无数据则填充默认值0,并按时间排序返回结果。具体逻辑如下:
1. 获取当前时间及一小时前的时间戳;
2. 调用Prometheus接口查询集群节点数指标数据;
3. 解析返回结果,封装为MetricCommonData对象列表;
4. 若无返回数据,则构造7个默认值为0的指标数据;
5. 按时间升序排序后返回结果。
2025-05-22 09:35:41 +08:00
OTTO ad405c912e feat(运维监控模块):新增获取集群状态数据接口
调用Prometheus接口查询Pod运行状态,并将相关状态翻译为中文后返回
2025-05-21 19:50:13 +08:00
OTTO 39d43e3d68 feat(运维监控模块):新增获取集群状态数据接口
优化方法命名
2025-05-21 19:07:35 +08:00
OTTO 1eefa65c32 refactor(运维监控模块): 重构监控模块集群状态数据获取方式
1. 将同步调用改为异步调用,使用线程池执行任务(原接口响应时间在2s左右,优化至700ms)
2. 优化了数据获取逻辑,提高了效率
3. 由于网关需要鉴权,调整了网关远程接口参数,使用 token 替代 source
4. 新增了线程池工厂和异步服务接口
2025-05-21 17:22:53 +08:00
OTTO c6b4a1bd80 feat(运维监控模块):新增获取集群状态数据接口
1. 从 Prometheus 查询集群节点总数、不可用节点数、CPU 使用率、内存使用率、Pod 使用率等指标。
2. 获取当天的总请求数、平均请求处理时间、请求成功率等网关性能数据。
3. 获取命名空间列表,并统计微服务应用数量及今日重启次数。
4. 将所有数据封装到 K8sClusterStatus 对象并返回。
2025-05-21 15:19:42 +08:00
OTTO 21ba1ce6bd refactor(运维监控模块):重构Rancher相关远程接口
- 新增 RemoteRancherService 接口,专门处理 Rancher 相关请求
- 移除 RemoteGatewayService 中的 Rancher 相关方法
- 更新 K8sNamespaceServiceImpl 中的远程调用方法,使用新的 RemoteRancherService
2025-05-21 08:42:23 +08:00
OTTO dd870b3cd3 feat(k8s): 添加获取应用 Pod IP 列表功能
该方法用于根据应用类型、命名空间和应用名称获取关联的 Pod 的 IP 地址列表。逻辑如下:

1. 调用 [getK8sApplicationDetail]获取应用详情;
2. 从应用的 relationships 中查找关联到 "pod" 的关系,并提取标签选择器;
3. 若选择器中包含 `app=` 标签,则提取应用名;
4. 调用 [getK8sPodListByNamespace] 获取命名空间下所有 Pod 列表;
5. 筛选出标签中 `app` 匹配的应用名的 Pod;
6. 返回这些 Pod 的 IP 地址列表;
7. 若无匹配数据则返回空列表。
2025-05-20 16:44:17 +08:00
OTTO 417d440549 feat(运维监控模块): 新增获取集群节点列表接口
1. 调用rancher接口获取节点列表
2. 将rancher返回对象转换为java对象
3. 从rancher的节点对象中摘取节点标识、节点名称、节点IP、节点角色以及是否就绪
2025-05-20 15:45:16 +08:00
OTTO 67b2e9ce7c fix(运维监控模块): 新增获取命名空间列表接口
修复StatefulSet类型应用的副本数无法获取的问题
2025-05-20 11:14:07 +08:00
OTTO 32b83749e2 feat(运维监控模块): 新增获取命名空间列表接口
- 修改 IK8sNamespaceService 接口,将返回类型从 GenericsTableDataInfo 改为 List
- 在 K8sApplication 中添加应用别名和命名空间字段
- 更新 K8sNamespaceController,适配新的接口返回类型
- 重构 K8sNamespaceServiceImpl,优化命名空间列表获取逻辑
- 在 RancherApplicationVo 中添加转换为 K8sApplication 的方法
- 更新 RancherNamespaceVo,简化 toK8sNamespace 方法
2025-05-16 16:55:09 +08:00
OTTO 010d5e5354 feat(运维监控模块): 新增获取命名空间列表接口
- 在 RemoteGatewayService 中新增 getK8sDeployList 和 getK8sStatefulList 方法
- 在 K8sNamespaceServiceImpl 中调用新方法获取 deployments 和 statefulsets 列表
- 重构 RancherDaemonsetsVo相关类,改为 RancherApplicationVo 以支持多种应用类型
- 优化命名空间列表获取逻辑,整合 daemonsets、deployments 和 statefulsets 数据
2025-05-16 15:51:02 +08:00
OTTO 94e0be0e71 feat(运维监控模块): 新增获取命名空间列表接口
- 在 RemoteGatewayService 中添加 getK8sDaemonsetsList 方法,调用rancher接口获取集群Daemonsets列表
- 新增Rancher Daemonsets对象以及Daemonsets的state和metadata对象,将Rancher接口返回的对象转换为Java对象
2025-05-16 15:28:02 +08:00
OTTO 2845d19aae feat(运维监控模块): 新增获取命名空间列表接口
- 在获取 K8s 命名空间列表后,增加筛选逻辑
- 遍历命名空间列表,仅保留包含 mon-key 标签的命名空间
2025-05-16 14:34:32 +08:00
OTTO 97b1c9437c feat(运维监控模块): 新增获取命名空间列表接口
- 在 RemoteGatewayService 中添加 getK8sNamespaceList 方法,调用rancher接口获取集群命名空间列表
- 新增Rancher命名空间对象,将Rancher接口返回的对象转换为Java对象
- 新增 K8sNamespace 和 K8sApplication 模型类
-
2025-05-16 14:34:04 +08:00
OTTO b0f9e06e18 feat(运维监控模块): 新增获取命名空间列表接口
- 在 RemoteGatewayService 中添加 getK8sNamespaceList 方法,调用rancher接口获取集群命名空间列表
- 新增Rancher命名空间对象,将Rancher接口返回的对象转换为Java对象
- 新增 K8sNamespace 和 K8sApplication 模型类
-
2025-05-16 14:33:57 +08:00
OTTO f58af4b0b5 feat(网关服务): 集成 Rancher 登录
新增Rancher免登录转发逻辑实现:
1. 构造登录Json对象调用Rancher登录接口获取Token
2. 将Token放入Redis缓存中,缓存失效时间为Rancher Token失效时间减一小时
3. 携带Token转发用户请求
2025-05-15 16:55:21 +08:00
OTTO 78cbd4b0ee Merge branch 'master' of https://gitlink.org.cn/Gitlink/microservices into dev_monitoring
# Conflicts:
#	microservices-modules/pom.xml
2025-05-13 14:52:33 +08:00
OTTO fca8490bdd feat(运维监控模块): 添加运维监控模块并更新项目管理模块描述
- 新增运维监控模块(microservices-modules-mon)
- 更新项目管理模块(microservices-modules-pms)的描述
2025-05-13 14:14:24 +08:00
51 changed files with 1869 additions and 8 deletions

View File

@ -1,15 +1,16 @@
package com.microservices.system.api;
import com.alibaba.fastjson2.JSONObject;
import com.microservices.common.core.constant.SecurityConstants;
import com.microservices.common.core.constant.ServiceNameConstants;
import com.microservices.common.core.constant.TokenConstants;
import com.microservices.system.api.factory.RemoteGatewayFallbackFactory;
import feign.Response;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
/**
@ -28,6 +29,14 @@ public interface RemoteGatewayService {
@PostMapping("/sentinel/auth/login")
Response loginSentinel(@RequestParam("username") String username, @RequestParam("password") String password);
/**
* 登录Rancher
*
* @return 响应
*/
@PostMapping("/rancher/v3-public/localProviders/local?action=login")
Response loginRancher(@RequestBody JSONObject loginBody);
/**
* 登录Nacos
*
@ -43,4 +52,15 @@ public interface RemoteGatewayService {
*/
@PostMapping("/portainer/api/auth")
Response loginPortainer(@RequestBody JSONObject loginBody);
@GetMapping("/prometheus/api/v1/query")
JSONObject getPrometheusQueryRes(@RequestParam("query") String query,
@RequestHeader(TokenConstants.AUTHENTICATION) String token);
@GetMapping("/prometheus/api/v1/query_range")
JSONObject getPrometheusQueryRangeRes(@RequestParam("query") String query,
@RequestParam("step") Long step,
@RequestParam("start") Long start,
@RequestParam("end") Long end,
@RequestHeader(TokenConstants.AUTHENTICATION) String token);
}

View File

@ -0,0 +1,66 @@
package com.microservices.system.api;
import com.alibaba.fastjson2.JSONObject;
import com.microservices.common.core.constant.SecurityConstants;
import com.microservices.common.core.constant.ServiceNameConstants;
import com.microservices.common.core.constant.TokenConstants;
import com.microservices.system.api.factory.RemoteGatewayFallbackFactory;
import com.microservices.system.api.factory.RemoteRancherFallbackFactory;
import feign.Response;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
/**
* 网关
*
* @author microservices
*/
@Component
@FeignClient(contextId = "RemoteRancherService", value = ServiceNameConstants.GATEWAY_SERVICE, fallbackFactory = RemoteRancherFallbackFactory.class)
public interface RemoteRancherService {
/**
* 获取命名空间列表
*
* @return 响应
*/
@GetMapping("/rancher/v3/clusters/{cluster_id}/namespaces")
JSONObject getK8sNamespaceList(@PathVariable("cluster_id") String cluster_id,
@RequestHeader(TokenConstants.AUTHENTICATION) String token);
@GetMapping("/rancher/k8s/clusters/{cluster_id}/v1/apps.daemonsets?exclude=metadata.managedFields")
JSONObject getK8sDaemonsetsList(@PathVariable("cluster_id") String cluster_id,
@RequestHeader(TokenConstants.AUTHENTICATION) String token);
@GetMapping("/rancher/k8s/clusters/{cluster_id}/v1/apps.deployments?exclude=metadata.managedFields")
JSONObject getK8sDeployList(@PathVariable("cluster_id") String cluster_id,
@RequestHeader(TokenConstants.AUTHENTICATION) String token);
@GetMapping("/rancher/k8s/clusters/{cluster_id}/v1/apps.statefulsets?exclude=metadata.managedFields")
JSONObject getK8sStatefulList(@PathVariable("cluster_id") String cluster_id,
@RequestHeader(TokenConstants.AUTHENTICATION) String token);
@GetMapping("/rancher/k8s/clusters/{cluster_id}/v1/nodes?exclude=metadata.managedFields")
JSONObject getK8sClusterNodeList(@PathVariable("cluster_id") String cluster_id,
@RequestHeader(TokenConstants.AUTHENTICATION) String token);
@GetMapping("/rancher/k8s/clusters/{cluster_id}/v1/{fullKind}/{namespace}/{applicationName}")
JSONObject getK8sApplicationDetail(@PathVariable("cluster_id") String cluster_id,
@PathVariable("fullKind") String fullKind,
@PathVariable("namespace") String namespace,
@PathVariable("applicationName") String applicationName,
@RequestHeader(TokenConstants.AUTHENTICATION) String token);
@GetMapping("/rancher/k8s/clusters/{cluster_id}/v1/pods/{namespace}")
JSONObject getK8sPodListByNamespace(@PathVariable("cluster_id") String cluster_id,
@PathVariable("namespace") String namespace,
@RequestHeader(TokenConstants.AUTHENTICATION) String token);
@GetMapping("/rancher/k8s/clusters/{cluster_id}/v1/services/{nginxApplicationId}")
JSONObject getK8sServiceByApplicationId(@PathVariable("cluster_id") String cluster_id,
@PathVariable("nginxApplicationId") String nginxApplicationId,
@RequestHeader(TokenConstants.AUTHENTICATION) String token);
}

View File

@ -25,7 +25,12 @@ public class RemoteGatewayFallbackFactory implements FallbackFactory<RemoteGatew
return new RemoteGatewayService() {
@Override
public Response loginSentinel(String password, String username) {
public Response loginSentinel(String username, String password) {
return null;
}
@Override
public Response loginRancher(JSONObject loginBody) {
return null;
}
@ -36,7 +41,16 @@ public class RemoteGatewayFallbackFactory implements FallbackFactory<RemoteGatew
@Override
public Response loginPortainer(JSONObject loginBody) {
System.out.println(throwable.getMessage());
return null;
}
@Override
public JSONObject getPrometheusQueryRes(String query, String token) {
return null;
}
@Override
public JSONObject getPrometheusQueryRangeRes(String query, Long step, Long start, Long end, String token) {
return null;
}
};

View File

@ -0,0 +1,68 @@
package com.microservices.system.api.factory;
import com.alibaba.fastjson2.JSONObject;
import com.microservices.system.api.RemoteGatewayService;
import com.microservices.system.api.RemoteRancherService;
import feign.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* 用户服务降级处理
*
* @author microservices
*/
@Component
public class RemoteRancherFallbackFactory implements FallbackFactory<RemoteRancherService> {
private static final Logger log = LoggerFactory.getLogger(RemoteRancherFallbackFactory.class);
@Override
public RemoteRancherService create(Throwable throwable) {
log.error("Rancher服务调用失败:{}", throwable.getMessage());
return new RemoteRancherService() {
@Override
public JSONObject getK8sNamespaceList(String cluster_id, String token) {
return null;
}
@Override
public JSONObject getK8sDaemonsetsList(String cluster_id, String token) {
return null;
}
@Override
public JSONObject getK8sDeployList(String cluster_id, String token) {
return null;
}
@Override
public JSONObject getK8sStatefulList(String cluster_id, String token) {
return null;
}
@Override
public JSONObject getK8sClusterNodeList(String cluster_id, String token) {
return null;
}
@Override
public JSONObject getK8sApplicationDetail(String cluster_id, String fullKind, String namespace, String applicationName, String token) {
return null;
}
@Override
public JSONObject getK8sPodListByNamespace(String cluster_id, String namespace, String token) {
return null;
}
@Override
public JSONObject getK8sServiceByApplicationId(String cluster_id, String nginxApplicationId, String token) {
return null;
}
};
}
}

View File

@ -6,3 +6,4 @@ com.microservices.system.api.factory.RemoteCmsFallbackFactory
com.microservices.system.api.factory.RemoteZoneFallbackFactory
com.microservices.system.api.factory.RemotePmsFallbackFactory
com.microservices.system.api.factory.RemoteGatewayFallbackFactory
com.microservices.system.api.factory.RemoteRancherFallbackFactory

View File

@ -213,6 +213,11 @@ public class CacheConstants {
*/
public final static String SENTINEL_TOKEN = "sentinel_token";
/**
* Rancher Token缓存Key
*/
public final static String RANCHER_TOKEN = "rancher_token";
/**
* Nacos Token缓存Key

View File

@ -37,6 +37,11 @@ public class SecurityConstants
*/
public static final String USER_KEY = "user_key";
/**
* 用户Token
*/
public static final String TOKEN = "token";
/**
* 组织ID
*/

View File

@ -22,9 +22,16 @@ public class TokenConstants {
* Sentinel令牌标识
*/
public static final String Sentinel_Token_Key = "sentinel_dashboard_cookie";
/**
* Rancher令牌标识
*/
public static final String Rancher_Cookie_Token_Key = "R_SESS";
/**
* Portainer令牌标识
*/
public static final String Portainer_Token_Key = "portainer_api_key";
/**
* Sentinel令牌标识
* Nacos令牌标识
*/
public static final String Nacos_Token_Key = "Accesstoken";
/**

View File

@ -1,5 +1,6 @@
package com.microservices.common.core.threadPool;
import com.microservices.common.core.constant.TokenConstants;
import com.microservices.common.core.context.SecurityContextHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -37,6 +38,9 @@ public class ThreadPoolExecutorWrap extends ThreadPoolExecutor {
@Override
public void run() {
try {
if(contextValue != null&&contextValue.containsKey("user_key")){
contextValue.put(TokenConstants.AUTHENTICATION, contextValue.get("user_key"));
}
SecurityContextHolder.setLocalMap(contextValue);
// 用户任务逻辑
task.run();

View File

@ -51,7 +51,7 @@ public class GenericsTableDataInfo<T> implements Serializable {
/**
* 面包屑数据
*/
@ApiModelProperty("面包屑数据")
@ApiModelProperty(value = "面包屑数据",hidden = true)
@JsonInclude(JsonInclude.Include.NON_NULL) //为空时隐藏
private List<Breadcrumb> breadcrumb;

View File

@ -42,6 +42,7 @@ public class GlobalResponseFilter implements GlobalFilter, Ordered {
String lowerUrl = request.getURI().getPath().toLowerCase();
ServerHttpResponse originalResponse = exchange.getResponse();
DataBufferFactory bufferFactory = originalResponse.bufferFactory();
// 当请求为Nacos时Nacos返回的所有非JSON响应都会被自动转换为结构化的错误信息同时将HTTP状态码设置为200适合用于规范化微服务架构的响应格式
if (lowerUrl.startsWith("/nacos")) {

View File

@ -53,6 +53,10 @@ public class ThirdPartyToolServiceImpl implements ThirdPartyToolService {
public String portainerUsername;
@Value("${thirdPartyTools.portainer.auth.password:}")
public String portainerPassword;
@Value("${thirdPartyTools.rancher.auth.username:}")
public String rancherUsername;
@Value("${thirdPartyTools.rancher.auth.password:}")
public String rancherPassword;
@Value("${thirdPartyTools.portainer.endpoints:}")
public Integer portainerEndpoints;
ThreadPoolExecutor threadPoolExecutor = CustomExecutorFactory.threadPoolExecutor;
@ -106,6 +110,44 @@ public class ThirdPartyToolServiceImpl implements ThirdPartyToolService {
throw new ServiceException("Sentinel服务获取Token失败");
}
}
// 处理Rancher请求
if (lowerUrl.startsWith("/rancher")) {
String rancherToken = null;
if (redisService.hasKey(CacheConstants.RANCHER_TOKEN)) {
rancherToken = redisService.getCacheObject(CacheConstants.RANCHER_TOKEN);
} else {
JSONObject loginRancherBody = new JSONObject();
loginRancherBody.put("description", "api session");
loginRancherBody.put("responseType", "cookie");
loginRancherBody.put("username", rancherUsername);
loginRancherBody.put("password", rancherPassword);
// 网关采用异步架构所以此处需要通过异步请求获取Sentinel Token
Future<Response> future = threadPoolExecutor.submit(() -> remoteGatewayService.loginRancher(loginRancherBody));
try {
Response response = future.get(1, TimeUnit.SECONDS);
Map<String, Collection<String>> header = response.headers();
if (header != null && header.containsKey("set-cookie")) {
String rancherCookie = header.get("set-cookie").iterator().next();
rancherToken = getValueByCookieKey(rancherCookie,TokenConstants.Rancher_Cookie_Token_Key);
//Rancher登录状态默认15小时失效将Rancher Token缓存过期时间调整为14分钟
redisService.setCacheObject(CacheConstants.RANCHER_TOKEN, rancherToken, 14L, TimeUnit.HOURS);
}
} catch (TimeoutException e) {
log.error("获取Rancher Token超时");
} catch (InterruptedException | ExecutionException e) {
log.error("获取Rancher Token失败{}", e.getMessage());
}
}
if (StringUtils.isNotEmpty(rancherToken)) {
ServletUtils.removeHeader(mutate, TokenConstants.AUTHENTICATION);
mutate.header(TokenConstants.AUTHENTICATION, TokenConstants.PREFIX + rancherToken);
} else {
throw new ServiceException("Rancher服务获取Token失败");
}
}
// 处理Nacos请求
if (lowerUrl.startsWith("/nacos")) {
String nacosToken = null;
@ -229,4 +271,20 @@ public class ThirdPartyToolServiceImpl implements ThirdPartyToolService {
log.error("[第三方工具请求处理异常]请求路径:{},异常信息:{}", exchange.getRequest().getPath(), msg);
return ServletUtils.webFluxResponseWriter(exchange.getResponse(), ExceptionMsgConstants.SYSTEM_EXEC_ERROR, code);
}
/**
* 通过指定Key获取Cookie中指定值
*/
private static String getValueByCookieKey(String cookie, String key) {
if (cookie != null) {
String[] cookies = StringUtils.split(cookie, ';');
for (String cookieItem : cookies) {
if (cookieItem.contains(key)) {
String token = StringUtils.remove(cookieItem, key+"=");
return StringUtils.remove(token, " ");
}
}
}
return null;
}
}

View File

@ -0,0 +1,127 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.microservices</groupId>
<artifactId>microservices-modules</artifactId>
<version>3.6.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>microservices-modules-mon</artifactId>
<description>
microservices-modules-mon运维监控模块
</description>
<dependencies>
<!-- SpringCloud Alibaba Nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- SpringCloud Alibaba Nacos Config -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- SpringCloud Alibaba Sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- SpringBoot Actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Swagger UI -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.fox.version}</version>
</dependency>
<!-- Mysql Connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- Microservices Common Async -->
<dependency>
<groupId>com.microservices</groupId>
<artifactId>microservices-common-async</artifactId>
</dependency>
<!-- Microservices Common DataSource -->
<dependency>
<groupId>com.microservices</groupId>
<artifactId>microservices-common-datasource</artifactId>
</dependency>
<!-- Microservices Common DataScope -->
<dependency>
<groupId>com.microservices</groupId>
<artifactId>microservices-common-datascope</artifactId>
</dependency>
<!-- Microservices Common Log -->
<dependency>
<groupId>com.microservices</groupId>
<artifactId>microservices-common-log</artifactId>
</dependency>
<!-- Microservices Common Swagger -->
<dependency>
<groupId>com.microservices</groupId>
<artifactId>microservices-common-swagger</artifactId>
</dependency>
<dependency>
<groupId>com.microservices</groupId>
<artifactId>microservices-common-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</dependency>
<dependency>
<groupId>com.microservices</groupId>
<artifactId>microservices-common-core</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.microservices</groupId>
<artifactId>microservices-common-httpClient</artifactId>
<version>3.6.2</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,24 @@
package com.microservices.mon;
import com.microservices.common.security.annotation.EnableCustomConfig;
import com.microservices.common.security.annotation.EnableRyFeignClients;
import com.microservices.common.swagger.annotation.EnableCustomSwagger2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* 运维监控模块
*
* @author otto
*/
@EnableCustomConfig
@EnableCustomSwagger2
@EnableRyFeignClients
@SpringBootApplication
public class MicroservicesMonApplication {
public static void main(String[] args) {
SpringApplication.run(MicroservicesMonApplication.class, args);
System.out.println("(♥◠‿◠)ノ゙ 运维监控模块启动成功 ლ(´ڡ`ლ)゙ \n");
}
}

View File

@ -0,0 +1,75 @@
package com.microservices.mon.k8s.controller;
import com.microservices.common.core.web.controller.BaseController;
import com.microservices.common.core.web.domain.GenericsAjaxResult;
import com.microservices.common.core.web.page.GenericsTableDataInfo;
import com.microservices.mon.k8s.domain.*;
import com.microservices.mon.k8s.service.IK8sDashboardService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
@RestController
@RequestMapping("dashboard")
@Api(tags = "微服务大屏")
public class K8sDashboardController extends BaseController {
@Autowired
private IK8sDashboardService k8sNamespaceService;
/**
* 获取命名空间列表
*/
@ApiOperation(value = "获取命名空间列表")
@GetMapping("/namespace/list")
public GenericsTableDataInfo<K8sNamespace> namespaceList() {
return new GenericsTableDataInfo<>(k8sNamespaceService.getNamespaceDetailList(true));
}
@ApiOperation(value = "获取集群节点列表")
@GetMapping("/cluster/Nodelist")
public GenericsTableDataInfo<K8sClusterNode> clusterNodeList() {
return new GenericsTableDataInfo<>(k8sNamespaceService.getClusterNodeList());
}
@ApiOperation(value = "获取应用Pod Ip列表")
@GetMapping("/application/{kind}/{namespace}/{applicationName}/podIdList")
public GenericsTableDataInfo<String> applicationPodIdList(@PathVariable("kind") String kind, @PathVariable("namespace") String namespace, @PathVariable("applicationName") String applicationName) {
return new GenericsTableDataInfo<>(k8sNamespaceService.getApplicationPodIdList(kind, namespace, applicationName));
}
@ApiOperation(value = "获取集群状态数据")
@GetMapping("/cluster/status")
public GenericsAjaxResult<K8sClusterStatus> clusterStatus() {
return GenericsAjaxResult.success(k8sNamespaceService.getClusterStatus());
}
@ApiOperation(value = "集群每秒请求率QPS")
@GetMapping("/cluster/qps")
public GenericsTableDataInfo<MetricCommonData> clusterQps() {
return new GenericsTableDataInfo<>(k8sNamespaceService.getClusterQps());
}
@ApiOperation(value = "最近24小时应用接口请求次数")
@GetMapping("/cluster/applicationRequestCount")
public GenericsTableDataInfo<ApplicationRequestCountData> applicationRequestCount() {
return new GenericsTableDataInfo<>(k8sNamespaceService.getApplicationRequestCount());
}
@ApiOperation(value = "获取Pod容器状态")
@GetMapping("/cluster/podContainerStatus/{containerID}")
public GenericsAjaxResult<K8sPodContainerStatus> podContainerStatus(@PathVariable("containerID") String containerID) {
return GenericsAjaxResult.success(k8sNamespaceService.getPodContainerStatus(containerID));
}
@ApiOperation(value = "获取服务器节点状态")
@GetMapping("/cluster/nodeStatus/{nodeIp}")
public GenericsAjaxResult<K8sNodeStatus> nodeStatus(@PathVariable("nodeIp") String nodeIp) {
return GenericsAjaxResult.success(k8sNamespaceService.getNodeStatus(nodeIp));
}
}

View File

@ -0,0 +1,19 @@
package com.microservices.mon.k8s.domain;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel("应用请求次数对象")
public class ApplicationRequestCountData {
@ApiModelProperty(value = "序号")
private Integer number;
@ApiModelProperty(value = "应用名称")
private String applicationAlias;
@ApiModelProperty(value = "请求次数")
private Long requestCount;
}

View File

@ -0,0 +1,49 @@
package com.microservices.mon.k8s.domain;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
@ApiModel("K8S应用")
public class K8sApplication {
@ApiModelProperty(value = "应用Id")
private String id;
@ApiModelProperty(value = "应用名称")
private String name;
@ApiModelProperty(value = "应用别名")
private String alias;
@ApiModelProperty(value = "应用类型")
private String kind;
@ApiModelProperty(value = "所属命名空间")
private String namespace;
@ApiModelProperty(value = "副本数量")
private Integer replicas;
@ApiModelProperty(value = "有效副本数量(当有效副本数为0时代表应用失败呈现红色当有效副本数大于0但小于副本数时代表应用部分失败呈现橙色当有效副本数等于副本数代表应用正常呈现蓝色)")
private Integer readyReplicas;
@ApiModelProperty(value = "Pod选择器")
private String podSelector;
private List<K8sApplicationPod> podList;
public static String getApplicationFullKind(String kind) {
if("DaemonSet".equals(kind)){
return "apps.daemonsets";
} else if ("StatefulSet".equals(kind)) {
return "apps.statefulsets";
} else if("Deployment".equals(kind)){
return "apps.deployments";
}
return null;
}
}

View File

@ -0,0 +1,23 @@
package com.microservices.mon.k8s.domain;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel("K8S Pod")
public class K8sApplicationPod {
@ApiModelProperty(value = "Pod Id")
private String id;
@ApiModelProperty(value = "Pod名称")
private String name;
@ApiModelProperty(value = "Pod Ip")
private String ip;
@ApiModelProperty(value = "容器id")
private String containerID;
}

View File

@ -0,0 +1,27 @@
package com.microservices.mon.k8s.domain;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
@ApiModel("集群节点")
public class K8sClusterNode {
@ApiModelProperty(value = "节点标识")
private String id;
@ApiModelProperty(value = "节点名称")
private String name;
@ApiModelProperty(value = "节点IP")
private String ip;
@ApiModelProperty(value = "节点角色Master/Worker")
private String role;
@ApiModelProperty(value = "是否就绪")
private Boolean Ready;
}

View File

@ -0,0 +1,123 @@
package com.microservices.mon.k8s.domain;
import com.alibaba.fastjson2.JSONObject;
import com.microservices.mon.k8s.domain.vo.PrometheusResVo;
import com.microservices.mon.k8s.domain.vo.PrometheusResultVo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.HashMap;
import java.util.List;
@Data
@ApiModel("集群状态")
public class K8sClusterStatus {
@ApiModelProperty(value = "集群节点数量")
private Integer nodeCount;
@ApiModelProperty(value = "不可用节点数量")
private Integer unavailableNodeCount;
@ApiModelProperty(value = "Pod使用率")
private Float podUsage;
@ApiModelProperty(value = "CPU使用率")
private Float cpuUsage;
@ApiModelProperty(value = "内存使用率")
private Float memoryUsage;
@ApiModelProperty(value = "最近24小时请求总数")
private Long totalRequestCountToday;
@ApiModelProperty(value = "最近24小时请求平均处理时间(单位ms)")
private Float averageRequestProcessingTimeToday;
@ApiModelProperty(value = "最近24小时请求成功率")
private Float requestSuccessRateToday;
@ApiModelProperty(value = "微服务应用数量")
private Integer microserviceApplicationCount;
@ApiModelProperty(value = "最近24小时微服务重启次数")
private Integer microserviceRestartCountToday;
@ApiModelProperty(value = "微服务Pod运行状态Key代表状态Value代表数量")
private HashMap<String, Integer> podStatus;
public void setNodeCount(PrometheusResVo prometheusResVo) {
this.nodeCount = Integer.parseInt(getPromStringValue(prometheusResVo));
}
public void setUnavailableNodeCount(PrometheusResVo prometheusResVo) {
this.unavailableNodeCount = Integer.parseInt(getPromStringValue(prometheusResVo));
}
public void setCpuUsage(PrometheusResVo prometheusResVo) {
this.cpuUsage = Float.parseFloat(getPromStringValue(prometheusResVo));
}
public void setPodUsage(PrometheusResVo prometheusResVo) {
this.podUsage = Float.parseFloat(getPromStringValue(prometheusResVo));
}
public void setMemoryUsage(PrometheusResVo prometheusResVo) {
this.memoryUsage = Float.parseFloat(getPromStringValue(prometheusResVo));
}
public void setTotalRequestCountToday(PrometheusResVo prometheusResVo) {
this.totalRequestCountToday = Long.parseLong(getPromStringValue(prometheusResVo));
}
public void setAverageRequestProcessingTimeToday(PrometheusResVo prometheusResVo) {
this.averageRequestProcessingTimeToday = Float.parseFloat(getPromStringValue(prometheusResVo));
}
public void setRequestSuccessRateToday(PrometheusResVo prometheusResVo) {
this.requestSuccessRateToday = Float.parseFloat(getPromStringValue(prometheusResVo));
}
public void setMicroserviceApplicationCount(List<K8sNamespace> k8sNamespaceList) {
this.microserviceApplicationCount = k8sNamespaceList.stream().map(x -> x.getApplicationList().size()).reduce(0, Integer::sum);
}
public void setMicroserviceRestartCountToday(PrometheusResVo prometheusResVo) {
this.microserviceRestartCountToday = (int) Float.parseFloat(getPromStringValue(prometheusResVo));
}
public void setPodStatus(PrometheusResVo prometheusResVo) {
HashMap<String, Integer> podStatus = new HashMap<>();
if (prometheusResVo != null && prometheusResVo.isSuccess()) {
List<PrometheusResultVo> resultList = prometheusResVo.getData().getResult();
for (PrometheusResultVo result : resultList) {
Integer value = result.getValue().getIntValue(1);
switch (result.getMetric().getString("phase")) {
case "Running":
podStatus.put("运行中", value);
break;
case "Pending":
podStatus.put("等待中", value);
break;
case "Succeeded":
podStatus.put("运行已完成", value);
break;
case "Failed":
podStatus.put("运行失败", value);
}
}
}
this.podStatus = podStatus;
}
private String getPromStringValue(PrometheusResVo prometheusResVo) {
String value = "";
if (prometheusResVo != null && prometheusResVo.isSuccess()) {
PrometheusResultVo result = prometheusResVo.getData().getResult().get(0);
return result.getValue().getString(1);
}
return value;
}
}

View File

@ -0,0 +1,27 @@
package com.microservices.mon.k8s.domain;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
@ApiModel("命名空间")
public class K8sNamespace {
@ApiModelProperty(value = "命名空间标识")
private String id;
@ApiModelProperty(value = "命名空间键值")
private String key;
@ApiModelProperty(value = "命名空间名称")
private String name;
@ApiModelProperty(value = "命名空间别名(中文)")
private String alias;
@ApiModelProperty(value = "应用列表")
private List<K8sApplication> applicationList;
}

View File

@ -0,0 +1,19 @@
package com.microservices.mon.k8s.domain;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel("K8S Node状态")
public class K8sNodeStatus {
@ApiModelProperty(value = "节点IP")
private String nodeIp;
@ApiModelProperty(value = "CPU使用率")
private Float cpuUsage;
@ApiModelProperty(value = "内存使用率")
private Float memoryUsage;
}

View File

@ -0,0 +1,21 @@
package com.microservices.mon.k8s.domain;
import com.microservices.mon.k8s.domain.vo.PrometheusResVo;
import com.microservices.mon.k8s.domain.vo.PrometheusResultVo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel("K8S Pod状态")
public class K8sPodContainerStatus {
@ApiModelProperty(value = "Pod名称")
private String podName;
@ApiModelProperty(value = "CPU使用率")
private Float cpuUsage;
@ApiModelProperty(value = "内存使用情况")
private String memoryUsage;
}

View File

@ -0,0 +1,21 @@
package com.microservices.mon.k8s.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@Data
@ApiModel("指标通用对象")
public class MetricCommonData {
@ApiModelProperty(value = "时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date date;
@ApiModelProperty(value = "指标值")
private Float value;
}

View File

@ -0,0 +1,13 @@
package com.microservices.mon.k8s.domain.vo;
import lombok.Data;
import java.util.List;
@Data
public class PrometheusDataVo {
// 查询结果类型
String resultType;
// 应用类型
List<PrometheusResultVo> result;
}

View File

@ -0,0 +1,17 @@
package com.microservices.mon.k8s.domain.vo;
import lombok.Data;
@Data
public class PrometheusResVo {
// 状态success代表成功
String status;
// 数据
PrometheusDataVo data;
public boolean isSuccess() {
return "success".equals(status)
&& data != null
&& !data.getResult().isEmpty();
}
}

View File

@ -0,0 +1,15 @@
package com.microservices.mon.k8s.domain.vo;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import lombok.Data;
@Data
public class PrometheusResultVo {
// 指标数据
JSONObject metric;
// 指标值
JSONArray value;
// 范围查询时指标值
JSONArray values;
}

View File

@ -0,0 +1,25 @@
package com.microservices.mon.k8s.domain.vo;
import lombok.Data;
@Data
public class RancherApplicationStateVo {
// Daemonsets-期望数量
Integer desiredNumberScheduled;
// Daemonsets-就绪数量
Integer numberReady;
// Deploy/Stateful-就绪副本数
Integer readyReplicas;
// Stateful-有效副本数
Integer availableReplicas;
// Deploy/Stateful-副本数量
Integer replicas;
}

View File

@ -0,0 +1,54 @@
package com.microservices.mon.k8s.domain.vo;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.microservices.common.core.utils.StringUtils;
import com.microservices.common.core.utils.bean.BeanUtils;
import com.microservices.mon.k8s.domain.K8sApplication;
import lombok.Data;
@Data
public class RancherApplicationVo {
// 标识
String id;
// 应用类型
String kind;
// 元数据
RancherMetadataVo metadata;
// 状态
RancherApplicationStateVo status;
public K8sApplication toK8sApplication() {
K8sApplication target = new K8sApplication();
BeanUtils.copyProperties(this, target);
if (metadata.getAnnotations() != null && metadata.getAnnotations().containsKey("mon-name")) {
target.setAlias(metadata.getAnnotations().getString("mon-name"));
}
target.setName(metadata.getName());
if ("DaemonSet".equals(kind)) {
target.setReplicas(status.getDesiredNumberScheduled());
target.setReadyReplicas(status.getNumberReady());
} else if ("StatefulSet".equals(kind)) {
target.setReplicas(status.getReplicas());
target.setReadyReplicas(status.getAvailableReplicas());
} else {
target.setReplicas(status.getReplicas());
target.setReadyReplicas(status.getReadyReplicas());
}
target.setNamespace(metadata.getNamespace());
JSONArray relationships = metadata.getRelationships();
for (int i = 0; i < relationships.size(); i++) {
JSONObject relationship = relationships.getJSONObject(i);
if (relationship.containsKey("toType") && "pod".equals(relationship.getString("toType"))) {
String selector = relationship.getString("selector");
if (StringUtils.isNotEmpty(selector) && selector.contains("app=")) {
String appName = selector.substring(selector.indexOf("=") + 1);
target.setPodSelector(appName);
}
}
}
return target;
}
}

View File

@ -0,0 +1,22 @@
package com.microservices.mon.k8s.domain.vo;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import lombok.Data;
@Data
public class RancherClusterNodeStateVo {
// ip地址
JSONArray addresses;
// 资源分配
JSONObject allocatable;
// 节点状况
JSONArray conditions;
// 节点信息
JSONObject nodeInfo;
}

View File

@ -0,0 +1,49 @@
package com.microservices.mon.k8s.domain.vo;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.microservices.common.core.utils.bean.BeanUtils;
import com.microservices.mon.k8s.domain.K8sApplication;
import com.microservices.mon.k8s.domain.K8sClusterNode;
import com.microservices.mon.k8s.domain.K8sNamespace;
import lombok.Data;
import java.util.Date;
@Data
public class RancherClusterNodeVo {
// 标识
String id;
// 元数据
RancherMetadataVo metadata;
// 节点状态
RancherClusterNodeStateVo status;
public K8sClusterNode toK8sClusterNode() {
K8sClusterNode target = new K8sClusterNode();
BeanUtils.copyProperties(this, target);
if (metadata.getLabels() != null && metadata.getLabels().containsKey("node-role.kubernetes.io/control-plane")) {
target.setRole("Master");
}else{
target.setRole("Worker");
}
target.setName(metadata.getName());
if(status!=null&&status.getAddresses()!=null){
JSONArray addresses=status.getAddresses();
for (int i = 0; i < addresses.size(); i++) {
JSONObject jsonObject = addresses.getJSONObject(i);
if (jsonObject.getString("type").equals("InternalIP")) {
target.setIp(jsonObject.getString("address"));
}
}
JSONArray conditions=status.getConditions();
for (int i = 0; i < conditions.size(); i++) {
JSONObject jsonObject = conditions.getJSONObject(i);
if (jsonObject.getString("type").equals("Ready")) {
target.setReady(jsonObject.getBoolean("status"));
}
}
}
return target;
}
}

View File

@ -0,0 +1,23 @@
package com.microservices.mon.k8s.domain.vo;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import lombok.Data;
import java.util.Date;
@Data
public class RancherMetadataVo {
// 所属命名空间
String namespace;
// 创建时间
Date creationTimestamp;
// 描述
JSONObject annotations;
// 关联数据
JSONArray relationships;
// 标签
JSONObject labels;
// 名称
String name;
}

View File

@ -0,0 +1,33 @@
package com.microservices.mon.k8s.domain.vo;
import com.alibaba.fastjson2.JSONObject;
import com.microservices.common.core.utils.bean.BeanUtils;
import com.microservices.mon.k8s.domain.K8sNamespace;
import lombok.Data;
import java.util.Date;
import java.util.List;
@Data
public class RancherNamespaceVo {
// 描述
JSONObject annotations;
//创建时间
Date created;
// 标识
String id;
// 标签
JSONObject labels;
// 名称
String name;
// 状态
String state;
public K8sNamespace toK8sNamespace() {
K8sNamespace target = new K8sNamespace();
BeanUtils.copyProperties(this, target);
target.setKey(labels.getString("mon-key"));
target.setAlias(annotations.getString("mon-name"));
return target;
}
}

View File

@ -0,0 +1,20 @@
package com.microservices.mon.k8s.domain.vo;
import com.alibaba.fastjson2.JSONArray;
import lombok.Data;
import java.util.Date;
@Data
public class RancherPodStateVo {
// Pod状况
JSONArray conditions;
// Pod Ip
String podIP;
// 启动时间
Date startTime;
}

View File

@ -0,0 +1,33 @@
package com.microservices.mon.k8s.domain.vo;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.microservices.common.core.utils.StringUtils;
import com.microservices.common.core.utils.bean.BeanUtils;
import com.microservices.mon.k8s.domain.K8sApplication;
import com.microservices.mon.k8s.domain.K8sApplicationPod;
import lombok.Data;
@Data
public class RancherPodVo {
// 标识
String id;
// 元数据
RancherMetadataVo metadata;
// 状态
RancherPodStateVo status;
public K8sApplicationPod toK8sApplicationPod() {
K8sApplicationPod target = new K8sApplicationPod();
BeanUtils.copyProperties(this, target);
target.setIp(status.getPodIP());
if (metadata != null) {
if (metadata.getAnnotations() != null) {
target.setContainerID(metadata.getAnnotations().getString("cni.projectcalico.org/containerID"));
}
target.setName(metadata.getName());
}
return target;
}
}

View File

@ -0,0 +1,23 @@
package com.microservices.mon.k8s.domain.vo;
import lombok.Data;
@Data
public class RancherServicePortsVo {
// 端口名称
String name;
// 映射端口
Integer nodePort;
// 内部端口
Integer port;
// 协议
String protocol;
// 目标端口
Integer targetPort;
}

View File

@ -0,0 +1,12 @@
package com.microservices.mon.k8s.domain.vo;
import lombok.Data;
import java.util.List;
@Data
public class RancherServiceSpecVo {
// 端口映射列表
List<RancherServicePortsVo> ports;
}

View File

@ -0,0 +1,15 @@
package com.microservices.mon.k8s.domain.vo;
import com.microservices.common.core.utils.bean.BeanUtils;
import com.microservices.mon.k8s.domain.K8sApplication;
import lombok.Data;
@Data
public class RancherServiceVo {
// 标识
String id;
// 元数据
RancherMetadataVo metadata;
// 状态
RancherServiceSpecVo spec;
}

View File

@ -0,0 +1,24 @@
package com.microservices.mon.k8s.service;
import com.microservices.mon.k8s.domain.*;
import java.util.HashMap;
import java.util.List;
public interface IK8sDashboardService {
List<K8sNamespace> getNamespaceDetailList(boolean needPod);
List<K8sClusterNode> getClusterNodeList();
List<String> getApplicationPodIdList(String kind, String namespace,String applicationName);
K8sClusterStatus getClusterStatus();
List<MetricCommonData> getClusterQps();
List<ApplicationRequestCountData> getApplicationRequestCount();
K8sPodContainerStatus getPodContainerStatus(String containerID);
K8sNodeStatus getNodeStatus(String nodeIp);
}

View File

@ -0,0 +1,11 @@
package com.microservices.mon.k8s.service;
import com.microservices.mon.k8s.domain.K8sClusterStatus;
/**
* 运维监控Service异步接口
*/
public interface IMonCommonAsyncService {
K8sClusterStatus getClusterStatus(String query);
}

View File

@ -0,0 +1,13 @@
package com.microservices.mon.k8s.service.impl;
import org.springframework.beans.factory.annotation.Value;
import java.util.Map;
public abstract class CommonService {
@Value("${rancher.cluster_id}")
public String cluster_id;
@Value("${rancher.nginxApplicationId}")
public String nginxApplicationId;
}

View File

@ -0,0 +1,258 @@
package com.microservices.mon.k8s.service.impl;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.microservices.common.core.context.SecurityContextHolder;
import com.microservices.common.core.utils.DateUtils;
import com.microservices.common.core.utils.StringUtils;
import com.microservices.mon.k8s.domain.*;
import com.microservices.mon.k8s.domain.vo.*;
import com.microservices.mon.k8s.service.IK8sDashboardService;
import com.microservices.mon.k8s.service.IMonCommonAsyncService;
import com.microservices.mon.k8s.utils.PromQLConstant;
import com.microservices.system.api.RemoteGatewayService;
import com.microservices.system.api.RemoteRancherService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class K8SDashboardServiceImpl extends CommonService implements IK8sDashboardService {
@Autowired
private RemoteRancherService remoteRancherService;
@Autowired
private IMonCommonAsyncService monCommonAsyncService;
@Autowired
private RemoteGatewayService remoteGatewayService;
@Override
public List<K8sNamespace> getNamespaceDetailList(boolean needPod) {
List<RancherApplicationVo> rancherApplicationVoList = new ArrayList<>();
//调用rancher远程接口获取所有daemonsets列表
JSONObject daemonsetsJsonObject = remoteRancherService.getK8sDaemonsetsList(cluster_id, SecurityContextHolder.getUserKey());
if (daemonsetsJsonObject != null && daemonsetsJsonObject.containsKey("data")) {
rancherApplicationVoList.addAll(daemonsetsJsonObject.getJSONArray("data").toList(RancherApplicationVo.class));
}
//调用rancher远程接口获取所有deploy列表
JSONObject deployJsonObject = remoteRancherService.getK8sDeployList(cluster_id, SecurityContextHolder.getUserKey());
if (deployJsonObject != null && deployJsonObject.containsKey("data")) {
rancherApplicationVoList.addAll(deployJsonObject.getJSONArray("data").toList(RancherApplicationVo.class));
}
//调用rancher远程接口获取所有Stateful列表
JSONObject statefulJsonObject = remoteRancherService.getK8sStatefulList(cluster_id, SecurityContextHolder.getUserKey());
if (statefulJsonObject != null && statefulJsonObject.containsKey("data")) {
rancherApplicationVoList.addAll(statefulJsonObject.getJSONArray("data").toList(RancherApplicationVo.class));
}
// 将rancher对象转换为k8s对象
List<K8sApplication> k8sApplicationList = rancherApplicationVoList.stream().map(RancherApplicationVo::toK8sApplication).collect(Collectors.toList());
List<K8sNamespace> k8sNamespaceList = getK8sNamespaceList();
for (int i = 0; i < k8sNamespaceList.size(); i++) {
K8sNamespace k8sNamespace = k8sNamespaceList.get(i);
List<K8sApplication> namespaceApplicationList = k8sApplicationList
.stream()
.filter(k8sApplication -> k8sApplication.getNamespace().equals(k8sNamespace.getName()))
.collect(Collectors.toList());
if (needPod) {
// 获取命名空间下pod列表
JSONObject podListJson = remoteRancherService.getK8sPodListByNamespace(cluster_id, k8sNamespace.getName(), SecurityContextHolder.getUserKey());
List<RancherPodVo> rancherPodVoList;
if (podListJson != null && podListJson.containsKey("data")) {
rancherPodVoList = podListJson.getJSONArray("data").toList(RancherPodVo.class);
for (int j = 0; j < namespaceApplicationList.size(); j++) {
K8sApplication k8sApplication = namespaceApplicationList.get(j);
List<RancherPodVo> applicationToPodList = rancherPodVoList.stream()
.filter(rancherPodVo -> rancherPodVo.getMetadata().getLabels().containsKey("app") && rancherPodVo.getMetadata().getLabels().get("app").equals(k8sApplication.getPodSelector()))
.collect(Collectors.toList());
k8sApplication.setPodList(applicationToPodList.stream().map(RancherPodVo::toK8sApplicationPod).collect(Collectors.toList()));
namespaceApplicationList.set(j, k8sApplication);
}
}
}
k8sNamespace.setApplicationList(namespaceApplicationList);
k8sNamespaceList.set(i, k8sNamespace);
}
return k8sNamespaceList;
}
@Override
public List<K8sClusterNode> getClusterNodeList() {
List<RancherClusterNodeVo> rancherClusterNodeVoList;
//调用rancher远程接口获取集群节点列表
JSONObject clusterNodeJsonObject = remoteRancherService.getK8sClusterNodeList(cluster_id, SecurityContextHolder.getUserKey());
if (clusterNodeJsonObject != null && clusterNodeJsonObject.containsKey("data")) {
rancherClusterNodeVoList = clusterNodeJsonObject.getJSONArray("data").toList(RancherClusterNodeVo.class);
return rancherClusterNodeVoList.stream().map(RancherClusterNodeVo::toK8sClusterNode).collect(Collectors.toList());
}
return Collections.emptyList();
}
@Override
public List<String> getApplicationPodIdList(String kind, String namespace, String applicationName) {
JSONObject applicationJson = remoteRancherService.getK8sApplicationDetail(cluster_id, K8sApplication.getApplicationFullKind(kind), namespace, applicationName, SecurityContextHolder.getUserKey());
RancherApplicationVo rancherApplicationVo = applicationJson.toJavaObject(RancherApplicationVo.class);
if (rancherApplicationVo != null && rancherApplicationVo.getMetadata() != null && rancherApplicationVo.getMetadata().getRelationships() != null) {
JSONArray relationships = rancherApplicationVo.getMetadata().getRelationships();
for (int i = 0; i < relationships.size(); i++) {
JSONObject relationship = relationships.getJSONObject(i);
if (relationship.containsKey("toType") && "pod".equals(relationship.getString("toType"))) {
String selector = relationship.getString("selector");
if (StringUtils.isNotEmpty(selector) && selector.contains("app=")) {
String appName = selector.substring(selector.indexOf("=") + 1);
JSONObject podListJson = remoteRancherService.getK8sPodListByNamespace(cluster_id, namespace, SecurityContextHolder.getUserKey());
if (podListJson != null && podListJson.containsKey("data")) {
List<RancherPodVo> rancherPodVoList = podListJson.getJSONArray("data").toList(RancherPodVo.class);
List<RancherPodVo> applicationToPodList = rancherPodVoList.stream()
.filter(rancherPodVo -> rancherPodVo.getMetadata().getLabels().containsKey("app") && rancherPodVo.getMetadata().getLabels().get("app").equals(appName))
.collect(Collectors.toList());
return applicationToPodList.stream().map(x -> x.getStatus().getPodIP()).collect(Collectors.toList());
}
}
}
}
}
return Collections.emptyList();
}
@Override
public K8sClusterStatus getClusterStatus() {
return monCommonAsyncService.getClusterStatus(PromQLConstant.CLUSTER_NODE_COUNT(cluster_id));
}
@Override
public List<MetricCommonData> getClusterQps() {
List<MetricCommonData> metricCommonDataList = new ArrayList<>();
Date now = DateUtils.getNowDate();
long endTime = now.getTime() / 1000;
Long startTime = now.getTime() / 1000 - 60 * 60;
JSONObject nodeCountJson = remoteGatewayService.getPrometheusQueryRangeRes(PromQLConstant.CLUSTER_QPS, 14L, startTime, endTime, SecurityContextHolder.getUserKey());
if (nodeCountJson != null && nodeCountJson.containsKey("data")) {
PrometheusResVo prometheusResVo = nodeCountJson.toJavaObject(PrometheusResVo.class);
if (prometheusResVo != null && prometheusResVo.isSuccess()) {
PrometheusResultVo prometheusResultVo = prometheusResVo.getData().getResult().get(0);
if (prometheusResultVo.getValues() != null) {
for (int i = 0; i < prometheusResultVo.getValues().size(); i++) {
JSONArray value = prometheusResultVo.getValues().getJSONArray(i);
MetricCommonData metricCommonData = new MetricCommonData();
metricCommonData.setDate(new Date(value.getLong(0) * 1000));
String valueStr=String.format("%.2f",value.getFloatValue(1));
metricCommonData.setValue(Float.parseFloat(valueStr));
metricCommonDataList.add(metricCommonData);
}
}
}
}
if (metricCommonDataList.isEmpty()) {
for (int i = 0; i < 7; i++) {
MetricCommonData metricCommonData = new MetricCommonData();
metricCommonData.setDate(new Date(endTime * 1000));
metricCommonData.setValue(0F);
metricCommonDataList.add(metricCommonData);
endTime -= 60 * 10;
}
}
metricCommonDataList.sort(Comparator.comparing(MetricCommonData::getDate));
return metricCommonDataList;
}
@Override
public List<ApplicationRequestCountData> getApplicationRequestCount() {
List<ApplicationRequestCountData> applicationRequestCountDataList = new ArrayList<>();
HashMap<String, String> portMap = new HashMap<>();
JSONObject nginxSvcJson = remoteRancherService.getK8sServiceByApplicationId(cluster_id, nginxApplicationId, SecurityContextHolder.getUserKey());
if (nginxSvcJson != null) {
RancherServiceVo rancherServiceVo = nginxSvcJson.toJavaObject(RancherServiceVo.class);
for (RancherServicePortsVo rancherServicePortsVo : rancherServiceVo.getSpec().getPorts()) {
// 获取端口描述
String portAlias = rancherServiceVo.getMetadata().getAnnotations().getString(rancherServicePortsVo.getName());
portMap.put(String.valueOf(rancherServicePortsVo.getPort()), portAlias);
}
}
JSONObject applicationRequestCountJson = remoteGatewayService.getPrometheusQueryRes(PromQLConstant.APPLICATION_REQUEST_COUNT, SecurityContextHolder.getUserKey());
if (applicationRequestCountJson != null) {
PrometheusResVo prometheusResVo = applicationRequestCountJson.toJavaObject(PrometheusResVo.class);
if (prometheusResVo != null && prometheusResVo.isSuccess()) {
List<PrometheusResultVo> resultList = prometheusResVo.getData().getResult();
int number = 1;
for (PrometheusResultVo result : resultList) {
ApplicationRequestCountData applicationRequestCountData = new ApplicationRequestCountData();
applicationRequestCountData.setNumber(number++);
JSONObject metric = result.getMetric();
String port = metric.getString("server_port");
applicationRequestCountData.setApplicationAlias(port);
if (portMap.containsKey(port)) {
applicationRequestCountData.setApplicationAlias(portMap.get(port));
}
Long value = result.getValue().getLongValue(1);
applicationRequestCountData.setRequestCount(value);
applicationRequestCountDataList.add(applicationRequestCountData);
}
}
}
return applicationRequestCountDataList;
}
@Override
public K8sPodContainerStatus getPodContainerStatus(String containerID) {
K8sPodContainerStatus k8sPodContainerStatus = new K8sPodContainerStatus();
JSONObject cpuUsageJson = remoteGatewayService.getPrometheusQueryRes(PromQLConstant.POD_CPU_USAGE(containerID), SecurityContextHolder.getUserKey());
PrometheusResVo cpuPrometheusResVo = cpuUsageJson.toJavaObject(PrometheusResVo.class);
if (cpuPrometheusResVo != null && cpuPrometheusResVo.isSuccess()) {
PrometheusResultVo result = cpuPrometheusResVo.getData().getResult().get(0);
k8sPodContainerStatus.setPodName(result.getMetric().getString("container_label_io_kubernetes_pod_name"));
k8sPodContainerStatus.setCpuUsage(result.getValue().getFloatValue(1));
}
JSONObject memoryUsageJson = remoteGatewayService.getPrometheusQueryRes(PromQLConstant.POD_MEMORY_USAGE(containerID), SecurityContextHolder.getUserKey());
PrometheusResVo memoryPrometheusResVo = memoryUsageJson.toJavaObject(PrometheusResVo.class);
if (memoryPrometheusResVo != null && memoryPrometheusResVo.isSuccess()) {
PrometheusResultVo result = memoryPrometheusResVo.getData().getResult().get(0);
Long memoryByte = result.getValue().getLong(1);
float memoryMb = (float) memoryByte / 1024 / 1024;
if (memoryMb > 1024) {
k8sPodContainerStatus.setMemoryUsage(String.format("%.2f GiB", memoryMb / 1024));
} else {
k8sPodContainerStatus.setMemoryUsage(String.format("%.2f mib", memoryMb));
}
}
return k8sPodContainerStatus;
}
@Override
public K8sNodeStatus getNodeStatus(String nodeIp) {
K8sNodeStatus k8sNodeStatus = new K8sNodeStatus();
k8sNodeStatus.setNodeIp(nodeIp);
JSONObject cpuUsageJson = remoteGatewayService.getPrometheusQueryRes(PromQLConstant.NODE_CPU_USAGE(nodeIp), SecurityContextHolder.getUserKey());
PrometheusResVo cpuPrometheusResVo = cpuUsageJson.toJavaObject(PrometheusResVo.class);
if (cpuPrometheusResVo != null && cpuPrometheusResVo.isSuccess()) {
PrometheusResultVo result = cpuPrometheusResVo.getData().getResult().get(0);
k8sNodeStatus.setCpuUsage(result.getValue().getFloatValue(1));
}
JSONObject memoryUsageJson = remoteGatewayService.getPrometheusQueryRes(PromQLConstant.NODE_MEMORY_USAGE(nodeIp), SecurityContextHolder.getUserKey());
PrometheusResVo memoryPrometheusResVo = memoryUsageJson.toJavaObject(PrometheusResVo.class);
if (memoryPrometheusResVo != null && memoryPrometheusResVo.isSuccess()) {
PrometheusResultVo result = memoryPrometheusResVo.getData().getResult().get(0);
k8sNodeStatus.setMemoryUsage(result.getValue().getFloatValue(1));
}
return k8sNodeStatus;
}
private List<K8sNamespace> getK8sNamespaceList() {
List<K8sNamespace> k8sNamespaceList = new ArrayList<>();
List<RancherNamespaceVo> rancherNamespaceVoList;
//调用rancher远程接口获取所有命名空间列表
JSONObject namespaceJsonObject = remoteRancherService.getK8sNamespaceList(cluster_id, SecurityContextHolder.getUserKey());
if (namespaceJsonObject != null && namespaceJsonObject.containsKey("data")) {
rancherNamespaceVoList = namespaceJsonObject.getJSONArray("data").toList(RancherNamespaceVo.class);
for (RancherNamespaceVo rancherNamespaceVo : rancherNamespaceVoList) {
// 仅获取拥有mon-key标签的命名空间
if (rancherNamespaceVo.getLabels().containsKey("mon-key")) {
K8sNamespace k8sNamespace = rancherNamespaceVo.toK8sNamespace();
k8sNamespaceList.add(k8sNamespace);
}
}
}
return k8sNamespaceList;
}
}

View File

@ -0,0 +1,123 @@
package com.microservices.mon.k8s.service.impl;
import com.alibaba.fastjson2.JSONObject;
import com.microservices.common.core.context.SecurityContextHolder;
import com.microservices.common.core.exception.ServiceException;
import com.microservices.mon.k8s.domain.K8sClusterStatus;
import com.microservices.mon.k8s.domain.K8sNamespace;
import com.microservices.mon.k8s.domain.vo.PrometheusResVo;
import com.microservices.mon.k8s.service.IK8sDashboardService;
import com.microservices.mon.k8s.service.IMonCommonAsyncService;
import com.microservices.mon.k8s.utils.CustomExecutorFactory;
import com.microservices.mon.k8s.utils.PromQLConstant;
import com.microservices.system.api.RemoteGatewayService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;
@Slf4j
@Service
public class MonCommonAsyncServiceImpl extends CommonService implements IMonCommonAsyncService {
@Autowired
private RemoteGatewayService remoteGatewayService;
@Autowired
@Lazy
private IK8sDashboardService k8sDashboardService;
@Override
public K8sClusterStatus getClusterStatus(String query) {
K8sClusterStatus k8sClusterStatus = new K8sClusterStatus();
CountDownLatch countDownLatch = new CountDownLatch(9);
CustomExecutorFactory.threadPoolExecutor.execute(() -> {
try {
JSONObject nodeCountJson = remoteGatewayService.getPrometheusQueryRes(PromQLConstant.CLUSTER_NODE_COUNT(cluster_id), SecurityContextHolder.getUserKey());
k8sClusterStatus.setNodeCount(nodeCountJson.toJavaObject(PrometheusResVo.class));
} finally {
countDownLatch.countDown();
}
});
CustomExecutorFactory.threadPoolExecutor.execute(() -> {
try {
JSONObject unavailableNodeCountJson = remoteGatewayService.getPrometheusQueryRes(PromQLConstant.CLUSTER_UNAVAILABLE_NODE_COUNT(cluster_id), SecurityContextHolder.getUserKey());
k8sClusterStatus.setUnavailableNodeCount(unavailableNodeCountJson.toJavaObject(PrometheusResVo.class));
} finally {
countDownLatch.countDown();
}
});
CustomExecutorFactory.threadPoolExecutor.execute(() -> {
try {
JSONObject cpuUsageJson = remoteGatewayService.getPrometheusQueryRes(PromQLConstant.CLUSTER_CPU_USAGE(cluster_id), SecurityContextHolder.getUserKey());
k8sClusterStatus.setCpuUsage(cpuUsageJson.toJavaObject(PrometheusResVo.class));
} finally {
countDownLatch.countDown();
}
});
CustomExecutorFactory.threadPoolExecutor.execute(() -> {
try {
JSONObject podUsageJson = remoteGatewayService.getPrometheusQueryRes(PromQLConstant.CLUSTER_POD_USAGE(cluster_id), SecurityContextHolder.getUserKey());
k8sClusterStatus.setPodUsage(podUsageJson.toJavaObject(PrometheusResVo.class));
} finally {
countDownLatch.countDown();
}
});
CustomExecutorFactory.threadPoolExecutor.execute(() -> {
try {
JSONObject memoryUsageJson = remoteGatewayService.getPrometheusQueryRes(PromQLConstant.CLUSTER_MEMORY_USAGE(cluster_id), SecurityContextHolder.getUserKey());
k8sClusterStatus.setMemoryUsage(memoryUsageJson.toJavaObject(PrometheusResVo.class));
} finally {
countDownLatch.countDown();
}
});
CustomExecutorFactory.threadPoolExecutor.execute(() -> {
try {
JSONObject totalRequestCountTodayJson = remoteGatewayService.getPrometheusQueryRes(PromQLConstant.TOTAL_REQUEST_COUNT_TODAY, SecurityContextHolder.getUserKey());
k8sClusterStatus.setTotalRequestCountToday(totalRequestCountTodayJson.toJavaObject(PrometheusResVo.class));
} finally {
countDownLatch.countDown();
}
});
CustomExecutorFactory.threadPoolExecutor.execute(() -> {
try {
JSONObject averageRequestProcessingTimeTodayJson = remoteGatewayService.getPrometheusQueryRes(PromQLConstant.AVERAGE_REQUEST_PROCESSING_TIME_TODAY, SecurityContextHolder.getUserKey());
k8sClusterStatus.setAverageRequestProcessingTimeToday(averageRequestProcessingTimeTodayJson.toJavaObject(PrometheusResVo.class));
} finally {
countDownLatch.countDown();
}
});
CustomExecutorFactory.threadPoolExecutor.execute(() -> {
try {
JSONObject requestSuccessRateTodayJson = remoteGatewayService.getPrometheusQueryRes(PromQLConstant.REQUEST_SUCCESS_RATE_TODAY, SecurityContextHolder.getUserKey());
k8sClusterStatus.setRequestSuccessRateToday(requestSuccessRateTodayJson.toJavaObject(PrometheusResVo.class));
} finally {
countDownLatch.countDown();
}
});
CustomExecutorFactory.threadPoolExecutor.execute(() -> {
try {
List<K8sNamespace> k8sNamespaceList = k8sDashboardService.getNamespaceDetailList(false);
k8sClusterStatus.setMicroserviceApplicationCount(k8sNamespaceList);
List<String> namespaceNameList = k8sNamespaceList.stream().map(K8sNamespace::getName).collect(Collectors.toList());
JSONObject microserviceRestartCountTodayJson = remoteGatewayService.getPrometheusQueryRes(PromQLConstant.MICROSERVICE_RESTART_COUNT_TODAY(cluster_id,namespaceNameList), SecurityContextHolder.getUserKey());
k8sClusterStatus.setMicroserviceRestartCountToday(microserviceRestartCountTodayJson.toJavaObject(PrometheusResVo.class));
JSONObject podStatusJson = remoteGatewayService.getPrometheusQueryRes(PromQLConstant.POD_STATUS(cluster_id,namespaceNameList), SecurityContextHolder.getUserKey());
k8sClusterStatus.setPodStatus(podStatusJson.toJavaObject(PrometheusResVo.class));
} finally {
countDownLatch.countDown();
}
});
try {
countDownLatch.await();
} catch (InterruptedException e) {
log.error("集群状态获取失败:{0}", e);
throw new ServiceException("集群状态获取失败");
}
return k8sClusterStatus;
}
}

View File

@ -0,0 +1,37 @@
package com.microservices.mon.k8s.utils;
import com.microservices.common.core.exception.ServiceException;
import com.microservices.common.core.threadPool.ThreadPoolExecutorWrap;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 线程池工厂
*
* @author OTTO
*/
public class CustomExecutorFactory {
/**
* 创建线程池
* 存在并发处理的情况设置核心线程为2设置有界队列长度为100最大线程数为10
* 当超出队列已满且达到最大线程数时抛出异常
* Ncpu=CPU数量
* Ucpu=目标CPU的使用率0<=Ucpu<=1
* W/C=任务等待时间与任务计算时间的比率
* Nthreads =Ncpu*Ucpu*(1+W/C)
*/
public static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutorWrap(
4
, 10
, 10
, TimeUnit.SECONDS
, new ArrayBlockingQueue<>(100)
, Executors.defaultThreadFactory()
, (r, executor) -> {
throw new ServiceException("目前处理的人太多了,请稍后再试");
}
);
}

View File

@ -0,0 +1,57 @@
package com.microservices.mon.k8s.utils;
import java.util.List;
/**
* Prometheus查询常量
*/
public class PromQLConstant {
public static String CLUSTER_NODE_COUNT(String clusterId) {
return String.format("sum(kube_node_info{cluster=~\"%s\"})", clusterId);
}
public static String CLUSTER_UNAVAILABLE_NODE_COUNT(String clusterId) {
return String.format("sum(kube_node_spec_unschedulable{cluster=~\"%s\"})", clusterId);
}
public static String CLUSTER_CPU_USAGE(String clusterId) {
return String.format("(sum(kube_pod_container_resource_requests{cluster=~\"%s\",resource=\"cpu\"})/ sum(kube_node_status_allocatable{cluster=~\"%s\",resource=\"cpu\"}))*100", clusterId, clusterId);
}
public static String CLUSTER_POD_USAGE(String clusterId) {
return String.format("(sum(kube_pod_info{cluster=~\"%s\"}) / sum(kube_node_status_allocatable{cluster=~\"%s\",resource=\"pods\"}))*100", clusterId, clusterId);
}
public static String CLUSTER_MEMORY_USAGE(String clusterId) {
return String.format("(sum(kube_pod_container_resource_requests{cluster=~\"%s\",resource=\"memory\"}) / sum(kube_node_status_allocatable{cluster=~\"%s\",resource=\"memory\"}))*100", clusterId, clusterId);
}
public static final String TOTAL_REQUEST_COUNT_TODAY = "round(sum(increase(nginx_http_response_count_total[1d])))";
public static final String AVERAGE_REQUEST_PROCESSING_TIME_TODAY = "sum(rate(nginx_http_response_time_seconds_sum[1d]))*1000";
public static final String REQUEST_SUCCESS_RATE_TODAY = "(sum(nginx_http_response_count_total{status=~\"2.*|3.*\"})/sum(nginx_http_response_count_total))*100";
public static String MICROSERVICE_RESTART_COUNT_TODAY(String clusterId, List<String> namespaceNameList) {
return String.format("sum(increase(kube_pod_container_status_restarts_total{cluster=~\"%s\",namespace=~\"%s\"}[1d]))", clusterId, String.join("|", namespaceNameList));
}
public static String POD_STATUS(String clusterId, List<String> namespaceNameList) {
return String.format("sum(kube_pod_status_phase{cluster=~\"%s\",namespace=~\"%s\", phase!=\"Unknown\"}) by (phase)", clusterId, String.join("|", namespaceNameList));
}
public static String APPLICATION_REQUEST_COUNT = "sort_desc(round(sum(increase(nginx_http_response_count_total[1d])) by (server_port)))";
public static String CLUSTER_QPS = "sum(rate(nginx_http_response_time_seconds_count[10m])) ";
public static String POD_CPU_USAGE(String containerId) {
return String.format("sum without (dc,from,id) (irate(container_cpu_user_seconds_total{container_label_io_kubernetes_sandbox_id=\"%s\"}[5m]) * 100)", containerId);
}
public static String POD_MEMORY_USAGE(String containerId) {
return String.format("sum without (dc,from,id) (container_memory_usage_bytes{container_label_io_kubernetes_sandbox_id=\"%s\"} - container_memory_cache{container_label_io_kubernetes_sandbox_id=\"%s\"})", containerId, containerId);
}
public static String NODE_CPU_USAGE(String nodeIp) {
return String.format("(sum by(instance) (irate(node_cpu_seconds_total{ip=\"%s\",mode!=\"idle\"}[1m])) / on(instance) group_left sum by (instance)((irate(node_cpu_seconds_total{ip=\"%s\"}[1m])))) * 100", nodeIp, nodeIp);
}
public static String NODE_MEMORY_USAGE(String nodeIp) {
return String.format("100 - ((node_memory_MemAvailable_bytes{ip=\"%s\"} * 100) / node_memory_MemTotal_bytes{ip=\"%s\"})", nodeIp, nodeIp);
}
}

View File

@ -0,0 +1,12 @@
Spring Boot Version: ${spring-boot.version}
Spring Application Name: ${spring.application.name}
_
(_)
_ __ _ _ ___ _ _ _ ______ _ __ ___ ___ _ __
| '__| | | |/ _ \| | | | |______| '_ ` _ \ / _ \| '_ \
| | | |_| | (_) | |_| | | | | | | | | (_) | | | |
|_| \__,_|\___/ \__, |_| |_| |_| |_|\___/|_| |_|
__/ |
|___/
# using Big

View File

@ -0,0 +1,34 @@
# Tomcat
server:
port: 9120
# Spring
spring:
application:
# 应用名称
name: microservices-mon
config:
activate:
# 环境配置
on-profile:
prod
cloud:
sentinel:
# 取消控制台懒加载
eager: true
nacos:
discovery:
# 服务注册地址
server-addr: ${nacos_ip}:${nacos_port}
username: ${nacos_username}
password: ${nacos_password}
config:
# 配置中心地址
server-addr: ${nacos_ip}:${nacos_port}
username: ${nacos_username}
password: ${nacos_password}
# 配置文件格式
file-extension: yml
# 共享配置
shared-configs:
- { dataId: "application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}",refresh: true }

View File

@ -0,0 +1,32 @@
# Tomcat
server:
port: 9120
# Spring
spring:
application:
# 应用名称
name: microservices-mon
profiles:
# 环境配置
active: dev
cloud:
sentinel:
# 取消控制台懒加载
eager: true
nacos:
discovery:
# 服务注册地址
server-addr: 172.168.15.201:8848
username: nacos
password: nacos
config:
# 配置中心地址
server-addr: 172.168.15.201:8848
username: nacos
password: nacos
# 配置文件格式
file-extension: yml
# 共享配置
shared-configs:
- { dataId: "application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}",refresh: true }

View File

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- 日志存放路径 -->
<property name="log.path" value="logs/microservices-mon"/>
<!-- 日志输出格式 -->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n"/>
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统日志输出 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/info.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/error.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>ERROR</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 系统模块日志级别控制 -->
<logger name="com.microservices" level="info"/>
<!-- Spring日志级别控制 -->
<logger name="org.springframework" level="warn"/>
<!-- add converter for %tid -->
<conversionRule conversionWord="tid"
converterClass="org.apache.skywalking.apm.toolkit.log.logback.v1.x.LogbackPatternConverter"/>
<!-- add converter for %sw_ctx -->
<conversionRule conversionWord="sw_ctx"
converterClass="org.apache.skywalking.apm.toolkit.log.logback.v1.x.LogbackSkyWalkingContextPatternConverter"/>
<appender name="skywalking_grpc"
class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.mdc.TraceIdMDCPatternLogbackLayout">
<Pattern>
{
"level": "%level",
"tid": "%tid",
"skyWalkingContext": "%sw_ctx",
"thread": "%thread",
"class": "%logger{1.}:%L",
"message": "%message",
"stackTrace": "%exception{10}"
}
</Pattern>
</layout>
</encoder>
</appender>
<root level="info">
<appender-ref ref="skywalking_grpc"/>
<appender-ref ref="console"/>
</root>
<!--系统操作日志-->
<root level="info">
<appender-ref ref="file_info"/>
<appender-ref ref="file_error"/>
</root>
</configuration>

View File

@ -12,7 +12,7 @@
<artifactId>microservices-modules-pms</artifactId>
<description>
microservices-modules-pms特色专区模块
microservices-modules-pms项目管理模块
</description>
<dependencies>

View File

@ -19,6 +19,7 @@
<module>microservices-modules-wiki</module>
<module>microservices-modules-dss</module>
<module>microservices-modules-dms</module>
<module>microservices-modules-mon</module>
</modules>
<artifactId>microservices-modules</artifactId>