mirror of
https://gitee.com/durcframework/SOP.git
synced 2025-08-11 21:57:56 +08:00
重构路由监控
This commit is contained in:
@@ -1,5 +1,11 @@
|
||||
# changelog
|
||||
|
||||
## 4.1.0
|
||||
|
||||
需要执行`sop-upgrade-4.1.0.sql`
|
||||
|
||||
- 重构路由监控功能
|
||||
|
||||
## 4.0.3
|
||||
|
||||
- 可定义业务错误码(见`@Open`注解中的`bizCode`属性)
|
||||
|
@@ -12,9 +12,9 @@
|
||||
默认情况下,接口访问时公开的,ISV都能访问。如果要设置某个接口访问权限,在`@Open`注解中指定permission=true。
|
||||
如:`@Open(value = "permission.story.get", permission = true)`。这样该接口是需要经过授权给ISV才能访问的。
|
||||
|
||||
重启服务后,登录admin,服务管理-路由列表界面中,操作操作列会出现一个授权按钮,点击出现授权窗口,勾选对应的角色即可完成授权。
|
||||
重启服务后,登录admin,服务管理-路由列表界面中,`访问权限`列会出现一个点击授权,点击出现授权窗口,勾选对应的角色即可完成授权。
|
||||
|
||||
- 点击`授权`按钮,进行角色授权
|
||||
- `点击授权`,进行角色授权
|
||||
|
||||

|
||||
|
||||
|
@@ -1,15 +1,6 @@
|
||||
# 路由监控
|
||||
|
||||
路由监控功能可以查看各个接口的调用情况,监控信息收集采用拦截器实现。
|
||||
|
||||
- 统计各个接口的调用次数、耗时等信息
|
||||
- 错误日志统一在网关负责收集
|
||||
- 只收集未知类型的错误日志,开发人员主动throw的异常不收集
|
||||
- 收集的日志存放在内存中,重启网关日志会消失
|
||||
|
||||
## 永久保存日志
|
||||
|
||||
默认收集的日志存放在内存中,重启网关日志会消失(见:`com.gitee.sop.gatewaycommon.monitor.MonitorManager.java`)。如果要永久保存日志内容,需要自己修改`MonitorManager`
|
||||
路由监控功能可以查看各个接口的调用情况,监控信息收集采用拦截器实现,前往【服务管理】-【路由监控】查看
|
||||
|
||||
- 后台预览
|
||||
|
||||
@@ -17,6 +8,20 @@
|
||||
|
||||

|
||||
|
||||
|
||||
- 注意事项
|
||||
|
||||
处理完错误后,请及时`标记解决`,一个接口默认保存50条错误信息,采用LRU机制,淘汰老的。标记解决后则会空出一个位置存放新的错误信息。
|
||||
|
||||
重复的错误只会存放一条记录,然后累加错误次数,重复错误定义如下:
|
||||
|
||||
`instanceId + routeId + errorMsg`,即一个实例 + 路由id + 错误信息确定一个错误
|
||||
|
||||
可在网关设置`sop.monitor.error-count-capacity=50`参数调整错误容量
|
||||
|
||||
考虑到数据库压力,网关收到错误信息后并不会立即保存到数据库,而是先保存到内容中,然后定时保存到时间,默认时间隔为30秒
|
||||
可通过`sop.monitor.flush-period-seconds=30`调整间隔时间。
|
||||
|
||||
相关类:
|
||||
|
||||
- com.gitee.sop.gatewaycommon.interceptor.MonitorRouteInterceptor
|
||||
- com.gitee.sop.gateway.interceptor.MonitorRouteInterceptor
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 84 KiB |
Binary file not shown.
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 68 KiB |
Binary file not shown.
Before Width: | Height: | Size: 187 KiB |
Binary file not shown.
Before Width: | Height: | Size: 315 KiB |
7
pom.xml
7
pom.xml
@@ -68,6 +68,7 @@
|
||||
<springfox-swagger2.version>2.9.2</springfox-swagger2.version>
|
||||
<easyopen.version>1.16.9</easyopen.version>
|
||||
<asm.version>6.2</asm.version>
|
||||
<pagehelper.version>5.2.0</pagehelper.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
@@ -202,6 +203,12 @@
|
||||
<version>${asm.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.pagehelper</groupId>
|
||||
<artifactId>pagehelper</artifactId>
|
||||
<version>${pagehelper.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
|
@@ -21,11 +21,6 @@
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.gitee.sop</groupId>
|
||||
<artifactId>sop-bridge-nacos</artifactId>
|
||||
<version>4.0.3-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<!-- easyopen starter -->
|
||||
<dependency>
|
||||
@@ -64,6 +59,11 @@
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.pagehelper</groupId>
|
||||
<artifactId>pagehelper</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- optional-->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
|
@@ -0,0 +1,79 @@
|
||||
package com.gitee.sop.adminserver.api.service;
|
||||
|
||||
import com.gitee.easyopen.annotation.Api;
|
||||
import com.gitee.easyopen.annotation.ApiService;
|
||||
import com.gitee.fastmybatis.core.query.Query;
|
||||
import com.gitee.fastmybatis.core.query.Sort;
|
||||
import com.gitee.fastmybatis.core.support.PageEasyui;
|
||||
import com.gitee.fastmybatis.core.util.MapperUtil;
|
||||
import com.gitee.sop.adminserver.api.service.param.InstanceMonitorSearchParam;
|
||||
import com.gitee.sop.adminserver.api.service.param.MonitorErrorMsgParam;
|
||||
import com.gitee.sop.adminserver.api.service.param.MonitorInfoErrorSolveParam;
|
||||
import com.gitee.sop.adminserver.api.service.param.MonitorSearchParam;
|
||||
import com.gitee.sop.adminserver.bean.RouteErrorCount;
|
||||
import com.gitee.sop.adminserver.entity.MonitorInfoError;
|
||||
import com.gitee.sop.adminserver.entity.MonitorSummary;
|
||||
import com.gitee.sop.adminserver.mapper.MonitorInfoErrorMapper;
|
||||
import com.gitee.sop.adminserver.mapper.MonitorInfoMapper;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@ApiService
|
||||
public class MonitorNewApi {
|
||||
|
||||
@Autowired
|
||||
private MonitorInfoMapper monitorInfoMapper;
|
||||
|
||||
@Autowired
|
||||
private MonitorInfoErrorMapper monitorInfoErrorMapper;
|
||||
|
||||
@Api(name = "monitornew.data.page")
|
||||
PageInfo<Object> listMonitor(MonitorSearchParam param) {
|
||||
Query query = Query.build(param);
|
||||
query.orderby("errorCount", Sort.DESC)
|
||||
.orderby("avgTime", Sort.DESC);
|
||||
return PageHelper.offsetPage(query.getStart(), query.getLimit())
|
||||
.doSelectPage(() -> monitorInfoMapper.listMonitorSummary(query))
|
||||
.toPageInfo();
|
||||
}
|
||||
|
||||
@Api(name = "monitornew.routeid.data.get")
|
||||
List<MonitorSummary> listInstanceMonitor(InstanceMonitorSearchParam param) {
|
||||
Query query = Query.build(param);
|
||||
query.orderby("errorCount", Sort.DESC)
|
||||
.orderby("avgTime", Sort.DESC);
|
||||
return monitorInfoMapper.listInstanceMonitorInfo(query);
|
||||
}
|
||||
|
||||
private Map<String, Integer> getRouteErrorCount() {
|
||||
List<RouteErrorCount> routeErrorCounts = monitorInfoErrorMapper.listRouteErrorCount();
|
||||
return routeErrorCounts.stream()
|
||||
.collect(Collectors.toMap(RouteErrorCount::getRouteId, RouteErrorCount::getCount));
|
||||
}
|
||||
|
||||
@Api(name = "monitornew.error.page")
|
||||
PageEasyui<MonitorInfoError> listError(MonitorErrorMsgParam param) {
|
||||
Query query = param.toQuery()
|
||||
.orderby("gmt_modified", Sort.DESC);
|
||||
return MapperUtil.queryForEasyuiDatagrid(monitorInfoErrorMapper, query);
|
||||
}
|
||||
|
||||
@Api(name = "monitornew.error.solve")
|
||||
void solve(MonitorInfoErrorSolveParam param) {
|
||||
Query query = Query.build(param);
|
||||
Map<String, Object> set = new HashMap<>(4);
|
||||
set.put("is_deleted", 1);
|
||||
set.put("count", 0);
|
||||
monitorInfoErrorMapper.updateByMap(set, query);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
package com.gitee.sop.adminserver.api.service.param;
|
||||
|
||||
import com.gitee.fastmybatis.core.query.annotation.Condition;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class InstanceMonitorSearchParam {
|
||||
@Condition(column = "t.route_id")
|
||||
private String routeId;
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
package com.gitee.sop.adminserver.api.service.param;
|
||||
|
||||
import com.gitee.fastmybatis.core.query.annotation.Condition;
|
||||
import com.gitee.fastmybatis.core.query.param.PageParam;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class MonitorErrorMsgParam extends PageParam {
|
||||
@NotBlank(message = "routeId不能为空")
|
||||
private String routeId;
|
||||
|
||||
@Condition(ignoreEmptyString = true)
|
||||
private String instanceId;
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package com.gitee.sop.adminserver.api.service.param;
|
||||
|
||||
import com.gitee.fastmybatis.core.query.annotation.Condition;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Data
|
||||
public class MonitorInfoErrorSolveParam {
|
||||
/** 错误id,md5(error_msg), 数据库字段:error_id */
|
||||
@NotBlank
|
||||
@Condition(index = 1)
|
||||
private String errorId;
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
package com.gitee.sop.adminserver.api.service.param;
|
||||
|
||||
import com.gitee.easyopen.doc.annotation.ApiDocField;
|
||||
import com.gitee.fastmybatis.core.query.Operator;
|
||||
import com.gitee.fastmybatis.core.query.annotation.Condition;
|
||||
import com.gitee.fastmybatis.core.query.param.PageParam;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class MonitorSearchParam extends PageParam {
|
||||
@ApiDocField(description = "服务名serviceId")
|
||||
@Condition(column = "service_id", operator = Operator.like, ignoreEmptyString = true)
|
||||
private String serviceId;
|
||||
|
||||
@ApiDocField(description = "路由id")
|
||||
@Condition(column = "route_id", operator = Operator.like, ignoreEmptyString = true)
|
||||
private String routeId;
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
package com.gitee.sop.adminserver.api.service.result;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Data
|
||||
public class MonitorInfoErrorMsgResult {
|
||||
|
||||
private String routeId;
|
||||
|
||||
private String errorId;
|
||||
|
||||
/** 错误信息, 数据库字段:error_msg */
|
||||
private String errorMsg;
|
||||
|
||||
private Integer errorStatus;
|
||||
|
||||
private Integer count;
|
||||
|
||||
/** 数据库字段:gmt_modified */
|
||||
private Date gmtModified;
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
package com.gitee.sop.adminserver.bean;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Data
|
||||
public class RouteErrorCount {
|
||||
|
||||
private String routeId;
|
||||
private Integer count;
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
package com.gitee.sop.adminserver.common;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Slf4j
|
||||
public class CopyUtil {
|
||||
|
||||
public static void copyProperties(Object from, Object to) {
|
||||
BeanUtils.copyProperties(from, to);
|
||||
}
|
||||
|
||||
public static <T> T copyBean(Object from, Supplier<T> supplier) {
|
||||
Objects.requireNonNull(from);
|
||||
T to = supplier.get();
|
||||
BeanUtils.copyProperties(from, to);
|
||||
return to;
|
||||
}
|
||||
|
||||
public static <T> T copyBeanNullable(Object from, Supplier<T> supplier) {
|
||||
if (from == null) {
|
||||
return supplier.get();
|
||||
}
|
||||
T to = supplier.get();
|
||||
BeanUtils.copyProperties(from, to);
|
||||
return to;
|
||||
}
|
||||
|
||||
public static <T> T copyBean(Object from, Supplier<T> supplier, Consumer<T> after) {
|
||||
if (from == null) {
|
||||
return null;
|
||||
}
|
||||
T to = supplier.get();
|
||||
BeanUtils.copyProperties(from, to);
|
||||
after.accept(to);
|
||||
return to;
|
||||
}
|
||||
|
||||
public static <T> List<T> copyList(List<?> fromList, Supplier<T> toElement) {
|
||||
if (fromList == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return fromList.stream()
|
||||
.map(source -> {
|
||||
T target = toElement.get();
|
||||
BeanUtils.copyProperties(source, target);
|
||||
return target;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static <T> List<T> copyList(List<?> fromList, Supplier<T> toElement, Consumer<T> after) {
|
||||
if (fromList == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return fromList.stream()
|
||||
.map(source -> {
|
||||
T target = toElement.get();
|
||||
BeanUtils.copyProperties(source, target);
|
||||
after.accept(target);
|
||||
return target;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
package com.gitee.sop.adminserver.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
/**
|
||||
* 表名:monitor_info
|
||||
* 备注:接口监控信息
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
@Table(name = "monitor_info")
|
||||
@Data
|
||||
public class MonitorInfo {
|
||||
/** 数据库字段:id */
|
||||
@Id
|
||||
@Column(name = "id")
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 路由id, 数据库字段:route_id */
|
||||
private String routeId;
|
||||
|
||||
/** 接口名, 数据库字段:name */
|
||||
private String name;
|
||||
|
||||
/** 版本号, 数据库字段:version */
|
||||
private String version;
|
||||
|
||||
/** 数据库字段:service_id */
|
||||
private String serviceId;
|
||||
|
||||
/** 数据库字段:instance_id */
|
||||
private String instanceId;
|
||||
|
||||
/** 请求耗时最长时间, 数据库字段:max_time */
|
||||
private Integer maxTime;
|
||||
|
||||
/** 请求耗时最小时间, 数据库字段:min_time */
|
||||
private Integer minTime;
|
||||
|
||||
/** 总时长,毫秒, 数据库字段:total_time */
|
||||
private Long totalTime;
|
||||
|
||||
/** 总调用次数, 数据库字段:total_request_count */
|
||||
private Long totalRequestCount;
|
||||
|
||||
/** 成功次数, 数据库字段:success_count */
|
||||
private Long successCount;
|
||||
|
||||
/** 失败次数(业务主动抛出的异常算作成功,如参数校验,未知的错误算失败), 数据库字段:error_count */
|
||||
private Long errorCount;
|
||||
|
||||
/** 数据库字段:gmt_create */
|
||||
private Date gmtCreate;
|
||||
|
||||
/** 数据库字段:gmt_modified */
|
||||
private Date gmtModified;
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
package com.gitee.sop.adminserver.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
/**
|
||||
* 表名:monitor_info_error
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
@Table(name = "monitor_info_error")
|
||||
@Data
|
||||
public class MonitorInfoError {
|
||||
/** 数据库字段:id */
|
||||
@Id
|
||||
@Column(name = "id")
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 错误id,md5Hex(instanceId + routeId + errorMsg), 数据库字段:error_id */
|
||||
private String errorId;
|
||||
|
||||
/** 实例id, 数据库字段:instance_id */
|
||||
private String instanceId;
|
||||
|
||||
/** 数据库字段:route_id */
|
||||
private String routeId;
|
||||
|
||||
/** 数据库字段:error_msg */
|
||||
private String errorMsg;
|
||||
|
||||
/** http status,非200错误, 数据库字段:error_status */
|
||||
private Integer errorStatus;
|
||||
|
||||
/** 错误次数, 数据库字段:count */
|
||||
private Integer count;
|
||||
|
||||
/** 数据库字段:is_deleted */
|
||||
@com.gitee.fastmybatis.core.annotation.LogicDelete
|
||||
private Byte isDeleted;
|
||||
|
||||
/** 数据库字段:gmt_create */
|
||||
private Date gmtCreate;
|
||||
|
||||
/** 数据库字段:gmt_modified */
|
||||
private Date gmtModified;
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
package com.gitee.sop.adminserver.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Data
|
||||
public class MonitorSummary {
|
||||
|
||||
private static final AtomicInteger i = new AtomicInteger();
|
||||
|
||||
private Integer id = i.incrementAndGet();
|
||||
|
||||
private String routeId;
|
||||
|
||||
private String name;
|
||||
private String version;
|
||||
|
||||
/** 数据库字段:service_id */
|
||||
private String serviceId;
|
||||
|
||||
/** 数据库字段:instance_id */
|
||||
private String instanceId;
|
||||
|
||||
/** 请求耗时最长时间, 数据库字段:max_time */
|
||||
private Integer maxTime;
|
||||
|
||||
/** 请求耗时最小时间, 数据库字段:min_time */
|
||||
private Integer minTime;
|
||||
|
||||
/** 总时长,毫秒, 数据库字段:total_time */
|
||||
private Long totalTime;
|
||||
|
||||
/** 总调用次数, 数据库字段:total_request_count */
|
||||
private Long totalRequestCount;
|
||||
|
||||
/** 成功次数, 数据库字段:success_count */
|
||||
private Long successCount;
|
||||
|
||||
/** 失败次数(业务主动抛出的异常算作成功,如参数校验,未知的错误算失败), 数据库字段:error_count */
|
||||
private Long errorCount;
|
||||
|
||||
/** 未解决的错误数量 */
|
||||
private Long unsolvedErrorCount;
|
||||
|
||||
private Float avgTime;
|
||||
|
||||
private Boolean hasChildren;
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
package com.gitee.sop.adminserver.mapper;
|
||||
|
||||
import com.gitee.fastmybatis.core.mapper.CrudMapper;
|
||||
import com.gitee.sop.adminserver.bean.RouteErrorCount;
|
||||
import com.gitee.sop.adminserver.entity.MonitorInfoError;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public interface MonitorInfoErrorMapper extends CrudMapper<MonitorInfoError, Long> {
|
||||
|
||||
|
||||
@Select("SELECT route_id routeId, count(*) `count` FROM monitor_info_error \n" +
|
||||
"WHERE is_deleted=0 \n" +
|
||||
"GROUP BY route_id")
|
||||
List<RouteErrorCount> listRouteErrorCount();
|
||||
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package com.gitee.sop.adminserver.mapper;
|
||||
|
||||
import com.gitee.fastmybatis.core.mapper.CrudMapper;
|
||||
import com.gitee.fastmybatis.core.query.Query;
|
||||
import com.gitee.sop.adminserver.entity.MonitorInfo;
|
||||
import com.gitee.sop.adminserver.entity.MonitorSummary;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public interface MonitorInfoMapper extends CrudMapper<MonitorInfo, Long> {
|
||||
List<MonitorSummary> listMonitorSummary(@Param("query") Query query);
|
||||
|
||||
List<MonitorSummary> listInstanceMonitorInfo(@Param("query") Query query);
|
||||
}
|
@@ -30,6 +30,7 @@ spring.datasource.hikari.pool-name=HikariCP
|
||||
spring.datasource.hikari.max-lifetime=500000
|
||||
|
||||
# 固定不用改
|
||||
mybatis.config-location=classpath:mybatis/mybatisConfig.xml
|
||||
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
|
||||
easyopen.show-doc=false
|
||||
easyopen.ignore-validate=true
|
||||
|
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<!-- 注意:文件名必须跟Dao类名字一致,因为是根据文件名做关联。 -->
|
||||
<mapper namespace="com.gitee.sop.adminserver.mapper.MonitorInfoMapper">
|
||||
|
||||
|
||||
<select id="listMonitorSummary" resultType="com.gitee.sop.adminserver.entity.MonitorSummary">
|
||||
SELECT t.*, IFNULL(t2.unsolvedErrorCount, 0) unsolvedErrorCount
|
||||
FROM (
|
||||
SELECT
|
||||
t.`service_id` serviceId,
|
||||
CONCAT(t.name, t.version) routeId,
|
||||
t.`name` name,
|
||||
t.`version` version,
|
||||
SUM(t.`max_time`) maxTime,
|
||||
SUM(t.`min_time`) minTime,
|
||||
SUM(t.`total_time`) totalTime,
|
||||
SUM(t.`total_request_count`) totalRequestCount,
|
||||
SUM(t.`success_count`) successCount,
|
||||
SUM(t.`error_count`) errorCount,
|
||||
SUM(t.`total_time`)/SUM(t.`total_request_count`) avgTime,
|
||||
1 hasChildren
|
||||
FROM
|
||||
`monitor_info` t
|
||||
<include refid="common.where"/>
|
||||
GROUP BY t.service_id, t.name, t.version
|
||||
<include refid="common.orderBy"/>
|
||||
) t
|
||||
LEFT JOIN
|
||||
(
|
||||
SELECT route_id, count(*) unsolvedErrorCount
|
||||
FROM monitor_info_error WHERE is_deleted=0 GROUP BY route_id
|
||||
) t2 on t.routeId = t2.route_id
|
||||
</select>
|
||||
|
||||
<select id="listInstanceMonitorInfo" resultType="com.gitee.sop.adminserver.entity.MonitorSummary">
|
||||
SELECT
|
||||
t.`service_id` serviceId,
|
||||
t.`instance_id` instanceId,
|
||||
t.route_id routeId,
|
||||
t.`name` name,
|
||||
t.`version` version,
|
||||
t.`max_time` maxTime,
|
||||
t.`min_time` minTime,
|
||||
t.`total_time` totalTime,
|
||||
t.`total_request_count` totalRequestCount,
|
||||
t.`success_count` successCount,
|
||||
t.`error_count` errorCount,
|
||||
IFNULL(t2.unsolvedErrorCount, 0) unsolvedErrorCount,
|
||||
t.`total_time`/t.`total_request_count` avgTime,
|
||||
0 hasChildren
|
||||
FROM
|
||||
`monitor_info` t
|
||||
LEFT JOIN
|
||||
(
|
||||
SELECT instance_id, route_id, count(*) unsolvedErrorCount
|
||||
FROM monitor_info_error WHERE is_deleted=0 GROUP BY instance_id, route_id
|
||||
) t2 on t.route_id = t2.route_id AND t.instance_id = t2.instance_id
|
||||
<include refid="common.where" />
|
||||
<include refid="common.orderBy" />
|
||||
</select>
|
||||
|
||||
</mapper>
|
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
|
||||
<configuration>
|
||||
|
||||
<plugins>
|
||||
<!-- com.github.pagehelper为PageHelper类所在包名 -->
|
||||
<plugin interceptor="com.github.pagehelper.PageInterceptor" />
|
||||
</plugins>
|
||||
|
||||
</configuration>
|
@@ -76,7 +76,7 @@ export const constantRoutes = [
|
||||
{
|
||||
path: 'monitor',
|
||||
name: 'Monitor',
|
||||
component: () => import('@/views/service/monitor'),
|
||||
component: () => import('@/views/service/monitorNew'),
|
||||
meta: { title: '路由监控' }
|
||||
},
|
||||
{
|
||||
|
273
sop-admin/sop-admin-vue/src/views/service/monitorNew.vue
Normal file
273
sop-admin/sop-admin-vue/src/views/service/monitorNew.vue
Normal file
@@ -0,0 +1,273 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :inline="true" :model="searchFormData" class="demo-form-inline" size="mini" @submit.native.prevent>
|
||||
<el-form-item label="接口名">
|
||||
<el-input v-model="searchFormData.routeId" :clearable="true" style="width: 250px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="serviceId">
|
||||
<el-input v-model="searchFormData.serviceId" :clearable="true" style="width: 250px;" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" native-type="submit" @click="loadTable">查询</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table
|
||||
:data="pageInfo.list"
|
||||
row-key="id"
|
||||
lazy
|
||||
empty-text="无数据"
|
||||
:load="loadInstanceMonitorInfo"
|
||||
>
|
||||
<el-table-column
|
||||
fixed
|
||||
prop="instanceId"
|
||||
label="网关实例"
|
||||
width="200"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<span v-if="!scope.row.children">{{ scope.row.instanceId }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
fixed
|
||||
prop="name"
|
||||
label="接口名 (版本号)"
|
||||
width="200"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.name + (scope.row.version ? ' (' + scope.row.version + ')' : '') }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="serviceId"
|
||||
label="serviceId"
|
||||
width="150"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="maxTime"
|
||||
label="最大耗时(ms)"
|
||||
>
|
||||
<template slot="header">
|
||||
最大耗时(ms)
|
||||
<el-tooltip content="耗时计算:签名验证成功后开始,应用返回结果后结束" placement="top">
|
||||
<i class="el-icon-question" style="cursor: pointer"></i>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="minTime"
|
||||
label="最小耗时(ms)"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="avgTime"
|
||||
label="平均耗时(ms)"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.avgTime.toFixed(1) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="totalRequestCount"
|
||||
label="总调用次数"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="successCount"
|
||||
label="成功次数"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="errorCount"
|
||||
label="失败次数"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="unsolvedErrorCount"
|
||||
label="未解决错误"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<el-link
|
||||
v-if="scope.row.unsolvedErrorCount > 0"
|
||||
:underline="false"
|
||||
type="danger"
|
||||
style="text-decoration: underline;"
|
||||
@click="onShowErrorDetail(scope.row)"
|
||||
>
|
||||
{{ scope.row.unsolvedErrorCount }}
|
||||
</el-link>
|
||||
<span v-if="scope.row.unsolvedErrorCount === 0">0</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
background
|
||||
style="margin-top: 5px"
|
||||
:current-page="searchFormData.pageIndex"
|
||||
:page-size="searchFormData.pageSize"
|
||||
:page-sizes="[5, 10, 20, 40]"
|
||||
:total="pageInfo.total"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
@size-change="onSizeChange"
|
||||
@current-change="onPageIndexChange"
|
||||
/>
|
||||
<!-- dialog -->
|
||||
<el-dialog
|
||||
:title="errorMsgData.title"
|
||||
:visible.sync="logDetailVisible"
|
||||
:close-on-click-modal="false"
|
||||
width="70%"
|
||||
@close="onCloseErrorDlg"
|
||||
>
|
||||
<el-alert
|
||||
title="修复错误后请标记解决"
|
||||
:closable="false"
|
||||
class="el-alert-tip"
|
||||
/>
|
||||
<el-table
|
||||
:data="errorMsgData.pageInfo.rows"
|
||||
empty-text="无错误日志"
|
||||
>
|
||||
<el-table-column
|
||||
type="expand"
|
||||
>
|
||||
<template slot-scope="props">
|
||||
<el-input v-model="props.row.errorMsg" type="textarea" :rows="8" readonly />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="errorMsg"
|
||||
label="错误内容"
|
||||
>
|
||||
<template slot-scope="props">
|
||||
<span v-if="props.row.errorMsg.length > 50">{{ props.row.errorMsg.substring(0, 50) }}...</span>
|
||||
<span v-else>{{ props.row.errorMsg }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="instanceId"
|
||||
label="实例ID"
|
||||
width="150px"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="count"
|
||||
label="报错次数"
|
||||
width="80px"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="gmtModified"
|
||||
label="报错时间"
|
||||
width="160px"
|
||||
/>
|
||||
<el-table-column
|
||||
label="操作"
|
||||
width="120"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<el-link type="primary" @click="onSolve(scope.row)">标记解决</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
background
|
||||
style="margin-top: 5px"
|
||||
:current-page="errorMsgFormData.pageIndex"
|
||||
:page-size="errorMsgFormData.pageSize"
|
||||
:page-sizes="[5, 10, 20, 40]"
|
||||
:total="errorMsgData.pageInfo.total"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
@size-change="onSizeChange"
|
||||
@current-change="onPageIndexChange"
|
||||
/>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="logDetailVisible = false">关 闭</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
searchFormData: {
|
||||
routeId: '',
|
||||
serviceId: '',
|
||||
pageIndex: 1,
|
||||
pageSize: 20
|
||||
},
|
||||
pageInfo: {
|
||||
list: [],
|
||||
total: 0
|
||||
},
|
||||
logDetailVisible: false,
|
||||
errorMsgFormData: {
|
||||
routeId: '',
|
||||
instanceId: '',
|
||||
pageIndex: 1,
|
||||
pageSize: 5
|
||||
},
|
||||
errorMsgData: {
|
||||
title: '',
|
||||
name: '',
|
||||
version: '',
|
||||
pageInfo: {
|
||||
rows: [],
|
||||
total: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadTable()
|
||||
},
|
||||
methods: {
|
||||
loadTable: function() {
|
||||
this.post('monitornew.data.page', this.searchFormData, function(resp) {
|
||||
this.pageInfo = resp.data
|
||||
})
|
||||
},
|
||||
loadErrorData: function() {
|
||||
this.post('monitornew.error.page', this.errorMsgFormData, function(resp) {
|
||||
this.errorMsgData.pageInfo = resp.data
|
||||
this.logDetailVisible = true
|
||||
})
|
||||
},
|
||||
loadInstanceMonitorInfo(row, treeNode, resolve) {
|
||||
this.post('monitornew.routeid.data.get', { routeId: row.routeId }, resp => {
|
||||
const children = resp.data
|
||||
row.children = children
|
||||
resolve(children)
|
||||
})
|
||||
},
|
||||
onShowErrorDetail: function(row) {
|
||||
this.errorMsgData.title = `错误日志 ${row.name}(${row.version})`
|
||||
this.errorMsgData.name = row.name
|
||||
this.errorMsgData.version = row.version
|
||||
this.errorMsgFormData.routeId = row.routeId
|
||||
this.errorMsgFormData.instanceId = row.instanceId
|
||||
this.loadErrorData()
|
||||
},
|
||||
onSolve: function(row) {
|
||||
this.confirm('确认标记为已解决吗?', function(done) {
|
||||
this.post('monitornew.error.solve', { routeId: row.routeId, errorId: row.errorId }, function(resp) {
|
||||
done()
|
||||
this.loadErrorData()
|
||||
})
|
||||
})
|
||||
},
|
||||
onCloseErrorDlg: function() {
|
||||
this.loadTable()
|
||||
},
|
||||
onSizeChange: function(size) {
|
||||
this.searchFormData.pageSize = size
|
||||
this.loadTable()
|
||||
},
|
||||
onAdd: function() {
|
||||
this.dialogTitle = '新增IP'
|
||||
this.dialogVisible = true
|
||||
this.dialogFormData.id = 0
|
||||
},
|
||||
onPageIndexChange: function(pageIndex) {
|
||||
this.searchFormData.pageIndex = pageIndex
|
||||
this.loadTable()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@@ -0,0 +1,43 @@
|
||||
package com.gitee.sop.gatewaycommon.bean;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* LRU缓存
|
||||
* LinkedHashMap 本身内部有一个触发条件则自动执行的方法:删除最老元素(最近最少使用的元素)
|
||||
* 由于最近最少使用元素是 LinkedHashMap 内部处理
|
||||
* 故我们不再需要维护 最近访问元素放在链尾,get 时直接访问/ put 时直接存储
|
||||
* created by Ethan-Walker on 2019/2/16
|
||||
*/
|
||||
public class LRUCache<K, V> {
|
||||
private final Map<K, V> map;
|
||||
|
||||
public LRUCache(int capacity) {
|
||||
map = new LinkedHashMap<K, V>(capacity, 0.75f, true) {
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry eldest) {
|
||||
// 容量大于capacity 时就删除
|
||||
return size() > capacity;
|
||||
}
|
||||
};
|
||||
}
|
||||
public V get(K key) {
|
||||
return map.get(key);
|
||||
}
|
||||
|
||||
public V computeIfAbsent(K key,
|
||||
Function<? super K, ? extends V> mappingFunction) {
|
||||
return map.computeIfAbsent(key, mappingFunction);
|
||||
}
|
||||
|
||||
public V put(K key, V value) {
|
||||
return map.put(key, value);
|
||||
}
|
||||
|
||||
public Collection<V> values() {
|
||||
return map.values();
|
||||
}
|
||||
}
|
@@ -5,7 +5,6 @@ import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
||||
import com.gitee.sop.gatewaycommon.bean.BeanInitializer;
|
||||
import com.gitee.sop.gatewaycommon.bean.SpringContext;
|
||||
import com.gitee.sop.gatewaycommon.gateway.loadbalancer.NacosServerIntrospector;
|
||||
import com.gitee.sop.gatewaycommon.interceptor.MonitorRouteInterceptor;
|
||||
import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptor;
|
||||
import com.gitee.sop.gatewaycommon.limit.LimitManager;
|
||||
import com.gitee.sop.gatewaycommon.manager.EnvGrayManager;
|
||||
@@ -17,17 +16,20 @@ import com.gitee.sop.gatewaycommon.manager.LimitConfigManager;
|
||||
import com.gitee.sop.gatewaycommon.manager.RouteConfigManager;
|
||||
import com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext;
|
||||
import com.gitee.sop.gatewaycommon.message.ErrorFactory;
|
||||
import com.gitee.sop.gatewaycommon.monitor.MonitorManager;
|
||||
import com.gitee.sop.gatewaycommon.param.ParameterFormatter;
|
||||
import com.gitee.sop.gatewaycommon.route.RegistryListener;
|
||||
import com.gitee.sop.gatewaycommon.route.ServiceListener;
|
||||
import com.gitee.sop.gatewaycommon.route.ServiceRouteListener;
|
||||
import com.gitee.sop.gatewaycommon.secret.IsvManager;
|
||||
import com.gitee.sop.gatewaycommon.sync.SopAsyncConfigurer;
|
||||
import com.gitee.sop.gatewaycommon.util.RouteInterceptorUtil;
|
||||
import com.gitee.sop.gatewaycommon.validate.SignConfig;
|
||||
import com.gitee.sop.gatewaycommon.validate.Validator;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
@@ -156,6 +158,17 @@ public class AbstractConfiguration implements ApplicationContextAware, Applicati
|
||||
return ApiConfig.getInstance().getParameterFormatter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SopAsyncConfigurer sopAsyncConfigurer(@Value("${sop.monitor-route-interceptor.thread-pool-size:4}")
|
||||
int threadPoolSize) {
|
||||
return new SopAsyncConfigurer("gatewayAsync", threadPoolSize);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public MonitorManager monitorManager() {
|
||||
return new MonitorManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* 跨域过滤器,gateway采用react形式,需要使用reactive包下的UrlBasedCorsConfigurationSource
|
||||
@@ -231,7 +244,6 @@ public class AbstractConfiguration implements ApplicationContextAware, Applicati
|
||||
protected void initRouteInterceptor() {
|
||||
Map<String, RouteInterceptor> routeInterceptorMap = applicationContext.getBeansOfType(RouteInterceptor.class);
|
||||
Collection<RouteInterceptor> routeInterceptors = new ArrayList<>(routeInterceptorMap.values());
|
||||
routeInterceptors.add(new MonitorRouteInterceptor());
|
||||
RouteInterceptorUtil.addInterceptors(routeInterceptors);
|
||||
}
|
||||
|
||||
|
@@ -39,7 +39,7 @@ public class GatewayResultExecutor extends BaseExecutorAdapter<ServerWebExchange
|
||||
List<String> errorCodeList = exchange.getResponse().getHeaders().get(SopConstants.X_SERVICE_ERROR_CODE);
|
||||
if (!CollectionUtils.isEmpty(errorCodeList)) {
|
||||
String errorCode = errorCodeList.get(0);
|
||||
responseStatus = Integer.valueOf(errorCode);
|
||||
responseStatus = Integer.parseInt(errorCode);
|
||||
}
|
||||
return responseStatus;
|
||||
}
|
||||
|
@@ -1,96 +0,0 @@
|
||||
package com.gitee.sop.gatewaycommon.interceptor;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
|
||||
import com.gitee.sop.gatewaycommon.monitor.MonitorInfo;
|
||||
import com.gitee.sop.gatewaycommon.monitor.MonitorManager;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* SOP默认的拦截器,用于收集监控数据
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
public class MonitorRouteInterceptor implements RouteInterceptor {
|
||||
|
||||
private ThreadPoolExecutor threadPoolExecutor;
|
||||
|
||||
public MonitorRouteInterceptor(int threadPoolSize) {
|
||||
threadPoolExecutor = new ThreadPoolExecutor(threadPoolSize, threadPoolSize,
|
||||
0L, TimeUnit.MILLISECONDS,
|
||||
new LinkedBlockingQueue<>());
|
||||
}
|
||||
|
||||
public MonitorRouteInterceptor() {
|
||||
this(4);
|
||||
}
|
||||
|
||||
public MonitorRouteInterceptor(ThreadPoolExecutor threadPoolExecutor) {
|
||||
this.threadPoolExecutor = threadPoolExecutor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRoute(RouteInterceptorContext context) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterRoute(RouteInterceptorContext context) {
|
||||
threadPoolExecutor.execute(() -> this.storeRequestInfo(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录接口调用流量,最大时间,最小时间,总时长,平均时长,调用次数,成功次数,失败次数.
|
||||
* 需要考虑并发情况。
|
||||
*/
|
||||
protected void storeRequestInfo(RouteInterceptorContext context) {
|
||||
MonitorManager monitorManager = ApiConfig.getInstance().getMonitorManager();
|
||||
ApiParam apiParam = context.getApiParam();
|
||||
String routeId = apiParam.fetchNameVersion();
|
||||
long spendTime = context.getFinishTimeMillis() - context.getBeginTimeMillis();
|
||||
// 这步操作是线程安全的,底层调用了ConcurrentHashMap.computeIfAbsent
|
||||
MonitorInfo monitorInfo = monitorManager.getMonitorInfo(routeId, (k) -> this.createMonitorInfo(apiParam));
|
||||
|
||||
monitorInfo.storeMaxTime(spendTime);
|
||||
monitorInfo.storeMinTime(spendTime);
|
||||
monitorInfo.getTotalCount().incrementAndGet();
|
||||
monitorInfo.getTotalTime().addAndGet(spendTime);
|
||||
monitorInfo.getTotalRequestDataSize().addAndGet(context.getRequestDataSize());
|
||||
monitorInfo.getTotalResponseDataSize().addAndGet(context.getResponseDataSize());
|
||||
if (context.isSuccessRequest()) {
|
||||
monitorInfo.getSuccessCount().incrementAndGet();
|
||||
} else {
|
||||
monitorInfo.getErrorCount().incrementAndGet();
|
||||
String errorMsg = context.getServiceErrorMsg();
|
||||
monitorInfo.addErrorMsg(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
private MonitorInfo createMonitorInfo(ApiParam apiParam) {
|
||||
MonitorInfo monitorInfo = new MonitorInfo();
|
||||
monitorInfo.setName(apiParam.fetchName());
|
||||
monitorInfo.setVersion(apiParam.fetchVersion());
|
||||
monitorInfo.setServiceId(apiParam.fetchServiceId());
|
||||
monitorInfo.setTotalRequestDataSize(new AtomicLong());
|
||||
monitorInfo.setTotalResponseDataSize(new AtomicLong());
|
||||
monitorInfo.setTotalTime(new AtomicLong());
|
||||
monitorInfo.setMaxTime(0L);
|
||||
monitorInfo.setMinTime(0L);
|
||||
monitorInfo.setSuccessCount(new AtomicLong());
|
||||
monitorInfo.setTotalCount(new AtomicLong());
|
||||
monitorInfo.setErrorCount(new AtomicLong());
|
||||
monitorInfo.setErrorMsgList(new ArrayList<>(10));
|
||||
return monitorInfo;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return -1000;
|
||||
}
|
||||
|
||||
}
|
@@ -88,6 +88,6 @@ public interface RouteInterceptorContext {
|
||||
*/
|
||||
default boolean isSuccessRequest() {
|
||||
int responseStatus = getResponseStatus();
|
||||
return responseStatus == HttpStatus.OK.value() || responseStatus == SopConstants.BIZ_ERROR_STATUS;
|
||||
return responseStatus == HttpStatus.OK.value();
|
||||
}
|
||||
}
|
||||
|
@@ -33,8 +33,7 @@ public enum EnvironmentKeys {
|
||||
/**
|
||||
* post请求body缓存大小
|
||||
*/
|
||||
MAX_IN_MEMORY_SIZE("spring.codec.max-in-memory-size", "262144")
|
||||
|
||||
MAX_IN_MEMORY_SIZE("spring.codec.max-in-memory-size", "262144"),
|
||||
|
||||
;
|
||||
|
||||
|
@@ -0,0 +1,63 @@
|
||||
package com.gitee.sop.gatewaycommon.monitor;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 每个接口 总调用流量,最大时间,最小时间,总时长,平均时长,调用次数,成功次数,失败次数,错误查看。
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
@Data
|
||||
public class MonitorDTO {
|
||||
|
||||
/**
|
||||
* 路由id
|
||||
*/
|
||||
private String routeId;
|
||||
|
||||
/**
|
||||
* 接口名
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 版本号
|
||||
*/
|
||||
private String version;
|
||||
/**
|
||||
* serviceId
|
||||
*/
|
||||
private String serviceId;
|
||||
|
||||
/**
|
||||
* 实例id
|
||||
*/
|
||||
private String instanceId;
|
||||
|
||||
/** 请求耗时最长时间, 数据库字段:max_time */
|
||||
private Integer maxTime;
|
||||
|
||||
/** 请求耗时最小时间, 数据库字段:min_time */
|
||||
private Integer minTime;
|
||||
|
||||
/**
|
||||
* 总时长
|
||||
*/
|
||||
private Long totalTime;
|
||||
|
||||
/** 总调用次数, 数据库字段:total_request_count */
|
||||
private Long totalRequestCount;
|
||||
|
||||
/** 成功次数, 数据库字段:success_count */
|
||||
private Long successCount;
|
||||
|
||||
/** 失败次数(业务主动抛出的异常算作成功,如参数校验,未知的错误算失败), 数据库字段:error_count */
|
||||
private Long errorCount;
|
||||
|
||||
/**
|
||||
* 错误信息
|
||||
*/
|
||||
private Collection<MonitorErrorMsg> errorMsgList;
|
||||
|
||||
}
|
@@ -0,0 +1,106 @@
|
||||
package com.gitee.sop.gatewaycommon.monitor;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.LRUCache;
|
||||
import lombok.Data;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* 每个接口 总调用流量,最大时间,最小时间,总时长,平均时长,调用次数,成功次数,失败次数,错误查看。
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
@Data
|
||||
public class MonitorData {
|
||||
|
||||
public static final int LIMIT_SIZE = 50;
|
||||
|
||||
|
||||
private String routeId;
|
||||
|
||||
/**
|
||||
* 接口名
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 版本号
|
||||
*/
|
||||
private String version;
|
||||
/**
|
||||
* serviceId
|
||||
*/
|
||||
private String serviceId;
|
||||
|
||||
private String instanceId;
|
||||
|
||||
/**
|
||||
* 请求耗时最长时间
|
||||
*/
|
||||
private Integer maxTime;
|
||||
/**
|
||||
* 请求耗时最小时间
|
||||
*/
|
||||
private Integer minTime;
|
||||
/**
|
||||
* 总时长
|
||||
*/
|
||||
private AtomicInteger totalTime;
|
||||
/**
|
||||
* 总调用次数
|
||||
*/
|
||||
private AtomicInteger totalRequestCount;
|
||||
/**
|
||||
* 成功次数
|
||||
*/
|
||||
private AtomicInteger successCount;
|
||||
/**
|
||||
* 失败次数(业务主动抛出的异常算作成功,如参数校验,未知的错误算失败)
|
||||
*/
|
||||
private AtomicInteger errorCount;
|
||||
|
||||
/**
|
||||
* 错误信息,key: errorId
|
||||
*/
|
||||
private LRUCache<String, MonitorErrorMsg> monitorErrorMsgMap;
|
||||
|
||||
/**
|
||||
* 下一次刷新到数据库的时间
|
||||
*/
|
||||
private LocalDateTime flushTime;
|
||||
|
||||
public synchronized void storeMaxTime(int spendTime) {
|
||||
if (spendTime > maxTime) {
|
||||
maxTime = spendTime;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void storeMinTime(int spendTime) {
|
||||
if (minTime == 0 || spendTime < minTime) {
|
||||
minTime = spendTime;
|
||||
}
|
||||
}
|
||||
|
||||
public void addErrorMsg(String errorMsg, int httpStatus) {
|
||||
if (errorMsg == null || "".equals(errorMsg)) {
|
||||
return;
|
||||
}
|
||||
synchronized (this) {
|
||||
String errorId = DigestUtils.md5Hex(instanceId + routeId + errorMsg);
|
||||
MonitorErrorMsg monitorErrorMsg = monitorErrorMsgMap.computeIfAbsent(errorId, (k) -> {
|
||||
MonitorErrorMsg value = new MonitorErrorMsg();
|
||||
value.setErrorId(errorId);
|
||||
value.setInstanceId(instanceId);
|
||||
value.setRouteId(routeId);
|
||||
value.setErrorMsg(errorMsg);
|
||||
value.setErrorStatus(httpStatus);
|
||||
value.setCount(0);
|
||||
return value;
|
||||
});
|
||||
monitorErrorMsg.setCount(monitorErrorMsg.getCount() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
package com.gitee.sop.gatewaycommon.monitor;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author thc
|
||||
*/
|
||||
@Data
|
||||
public class MonitorErrorMsg {
|
||||
/** 错误id,md5(error_msg), 数据库字段:error_id */
|
||||
private String errorId;
|
||||
|
||||
/** 实例id, 数据库字段:instance_id */
|
||||
private String instanceId;
|
||||
|
||||
/** 数据库字段:route_id */
|
||||
private String routeId;
|
||||
|
||||
/** 数据库字段:isp_id */
|
||||
private Long ispId;
|
||||
|
||||
/** 数据库字段:error_msg */
|
||||
private String errorMsg;
|
||||
|
||||
/** http status,非200错误 */
|
||||
private Integer errorStatus;
|
||||
|
||||
/** 错误次数, 数据库字段:count */
|
||||
private Integer count;
|
||||
}
|
@@ -1,88 +0,0 @@
|
||||
package com.gitee.sop.gatewaycommon.monitor;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* 每个接口 总调用流量,最大时间,最小时间,总时长,平均时长,调用次数,成功次数,失败次数,错误查看。
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
@Data
|
||||
public class MonitorInfo {
|
||||
|
||||
/**
|
||||
* 接口名
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 版本号
|
||||
*/
|
||||
private String version;
|
||||
/**
|
||||
* serviceId
|
||||
*/
|
||||
private String serviceId;
|
||||
/**
|
||||
* 总请求数据量
|
||||
*/
|
||||
private AtomicLong totalRequestDataSize;
|
||||
/**
|
||||
* 总返回数据量
|
||||
*/
|
||||
private AtomicLong totalResponseDataSize;
|
||||
/**
|
||||
* 请求耗时最长时间
|
||||
*/
|
||||
private Long maxTime;
|
||||
/**
|
||||
* 请求耗时最小时间
|
||||
*/
|
||||
private Long minTime;
|
||||
/**
|
||||
* 总时长
|
||||
*/
|
||||
private AtomicLong totalTime;
|
||||
/**
|
||||
* 总调用次数
|
||||
*/
|
||||
private AtomicLong totalCount;
|
||||
/**
|
||||
* 成功次数
|
||||
*/
|
||||
private AtomicLong successCount;
|
||||
/**
|
||||
* 失败次数(业务主动抛出的异常算作成功,如参数校验,未知的错误算失败)
|
||||
*/
|
||||
private AtomicLong errorCount;
|
||||
/**
|
||||
* 错误信息
|
||||
*/
|
||||
private List<String> errorMsgList;
|
||||
|
||||
public synchronized void storeMaxTime(long spendTime) {
|
||||
if (spendTime > maxTime) {
|
||||
maxTime = spendTime;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void storeMinTime(long spendTime) {
|
||||
if (minTime == 0 || spendTime < minTime) {
|
||||
minTime = spendTime;
|
||||
}
|
||||
}
|
||||
|
||||
public void addErrorMsg(String errorMsg) {
|
||||
if (errorMsg == null || "".equals(errorMsg)) {
|
||||
return;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (errorMsgList != null && errorMsgList.size() < 10) {
|
||||
errorMsgList.add(errorMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -9,13 +9,13 @@ import java.util.function.Function;
|
||||
*/
|
||||
public class MonitorManager {
|
||||
|
||||
private static Map<String, MonitorInfo> monitorMap = new ConcurrentHashMap<>(128);
|
||||
private static Map<String, MonitorData> monitorMap = new ConcurrentHashMap<>(128);
|
||||
|
||||
public Map<String, MonitorInfo> getMonitorData() {
|
||||
public Map<String, MonitorData> getMonitorData() {
|
||||
return monitorMap;
|
||||
}
|
||||
|
||||
public MonitorInfo getMonitorInfo(String routeId, Function<String, MonitorInfo> createFun) {
|
||||
public MonitorData getMonitorInfo(String routeId, Function<String, MonitorData> createFun) {
|
||||
return monitorMap.computeIfAbsent(routeId, createFun);
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,13 @@
|
||||
package com.gitee.sop.gatewaycommon.monitor;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Data
|
||||
public class RouteErrorCount {
|
||||
|
||||
private String routeId;
|
||||
private Integer count;
|
||||
}
|
@@ -119,7 +119,7 @@ public abstract class BaseExecutorAdapter<T, R> implements ResultExecutor<T, R>
|
||||
defaultRouteInterceptorContext.setServiceResult(serviceResult);
|
||||
defaultRouteInterceptorContext.setFinishTimeMillis(System.currentTimeMillis());
|
||||
defaultRouteInterceptorContext.setResponseDataSize(serviceResult.length());
|
||||
if (responseStatus != HttpStatus.OK.value() && responseStatus != SopConstants.BIZ_ERROR_STATUS) {
|
||||
if (responseStatus != HttpStatus.OK.value()) {
|
||||
String responseErrorMessage = getResponseErrorMessage(requestContext);
|
||||
if (StringUtils.isEmpty(responseErrorMessage)) {
|
||||
responseErrorMessage = serviceResult;
|
||||
|
@@ -0,0 +1,37 @@
|
||||
package com.gitee.sop.gatewaycommon.sync;
|
||||
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* @author thc
|
||||
*/
|
||||
public class MyNamedThreadFactory implements ThreadFactory {
|
||||
private static final AtomicInteger POOL_NUMBER = new AtomicInteger(1);
|
||||
private final AtomicInteger threadNumber = new AtomicInteger(1);
|
||||
private final ThreadGroup group;
|
||||
private final String namePrefix;
|
||||
|
||||
public MyNamedThreadFactory(String name) {
|
||||
|
||||
SecurityManager s = System.getSecurityManager();
|
||||
group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
|
||||
if (null == name || name.isEmpty()) {
|
||||
name = "pool";
|
||||
}
|
||||
|
||||
namePrefix = name + "-" + POOL_NUMBER.getAndIncrement() + "-thread-";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement());
|
||||
if (t.isDaemon()) {
|
||||
t.setDaemon(false);
|
||||
}
|
||||
if (t.getPriority() != Thread.NORM_PRIORITY) {
|
||||
t.setPriority(Thread.NORM_PRIORITY);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
package com.gitee.sop.gatewaycommon.sync;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
|
||||
import org.springframework.scheduling.annotation.AsyncConfigurer;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 异步执行配置
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
@Slf4j
|
||||
public class SopAsyncConfigurer implements AsyncConfigurer {
|
||||
|
||||
private final ThreadPoolExecutor threadPoolExecutor;
|
||||
|
||||
public SopAsyncConfigurer(String threadName, int poolSize) {
|
||||
threadPoolExecutor = new ThreadPoolExecutor(poolSize, poolSize,
|
||||
0L, TimeUnit.MILLISECONDS,
|
||||
new LinkedBlockingQueue<>(), new MyNamedThreadFactory(threadName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Executor getAsyncExecutor() {
|
||||
return threadPoolExecutor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
|
||||
return (Throwable e, Method method, Object... args) -> {
|
||||
log.error("异步运行方法出错, method:{}, args:{}, message:{}", method, Arrays.deepToString(args), e.getMessage(), e);
|
||||
};
|
||||
}
|
||||
}
|
@@ -0,0 +1,79 @@
|
||||
package com.gitee.sop.gatewaycommon.util;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Slf4j
|
||||
public class CopyUtil {
|
||||
|
||||
public static void copyPropertiesIgnoreNull(Object from, Object to) {
|
||||
MyBeanUtil.copyPropertiesIgnoreNull(from, to);
|
||||
}
|
||||
|
||||
public static void copyProperties(Object from, Object to) {
|
||||
BeanUtils.copyProperties(from, to);
|
||||
}
|
||||
|
||||
public static <T> T copyBean(Object from, Supplier<T> supplier) {
|
||||
Objects.requireNonNull(from);
|
||||
T to = supplier.get();
|
||||
BeanUtils.copyProperties(from, to);
|
||||
return to;
|
||||
}
|
||||
|
||||
public static <T> T copyBeanNullable(Object from, Supplier<T> supplier) {
|
||||
if (from == null) {
|
||||
return supplier.get();
|
||||
}
|
||||
T to = supplier.get();
|
||||
BeanUtils.copyProperties(from, to);
|
||||
return to;
|
||||
}
|
||||
|
||||
public static <T> T copyBean(Object from, Supplier<T> supplier, Consumer<T> after) {
|
||||
if (from == null) {
|
||||
return null;
|
||||
}
|
||||
T to = supplier.get();
|
||||
BeanUtils.copyProperties(from, to);
|
||||
after.accept(to);
|
||||
return to;
|
||||
}
|
||||
|
||||
public static <T> List<T> copyList(List<?> fromList, Supplier<T> toElement) {
|
||||
if (fromList == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return fromList.stream()
|
||||
.map(source -> {
|
||||
T target = toElement.get();
|
||||
BeanUtils.copyProperties(source, target);
|
||||
return target;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static <T> List<T> copyList(List<?> fromList, Supplier<T> toElement, Consumer<T> after) {
|
||||
if (fromList == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return fromList.stream()
|
||||
.map(source -> {
|
||||
T target = toElement.get();
|
||||
BeanUtils.copyProperties(source, target);
|
||||
after.accept(target);
|
||||
return target;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
@@ -72,7 +72,7 @@ public class GlobalExceptionHandler {
|
||||
int lineCount = 5;
|
||||
for (int i = 0; i < stackTrace.length && i < lineCount; i++) {
|
||||
StackTraceElement stackTraceElement = stackTrace[i];
|
||||
msg.append("<br> at ").append(stackTraceElement.toString());
|
||||
msg.append("\n at ").append(stackTraceElement.toString());
|
||||
}
|
||||
response.setHeader("x-service-error-message", UriUtils.encode(msg.toString(), StandardCharsets.UTF_8));
|
||||
return this.processError(request, response, new ServiceException("系统繁忙"));
|
||||
|
@@ -18,6 +18,7 @@ import com.gitee.sop.storyweb.controller.result.TreeResult;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@@ -50,6 +51,9 @@ import java.util.Map;
|
||||
@Api(tags = "故事接口")
|
||||
public class Example1001_BaseController {
|
||||
|
||||
@Value("${server.port}")
|
||||
private int port;
|
||||
|
||||
// http://localhost:2222/stroy/get
|
||||
// 原生的接口,可正常调用
|
||||
@RequestMapping("/get")
|
||||
@@ -71,7 +75,7 @@ public class Example1001_BaseController {
|
||||
public StoryResult get_v1(StoryParam param) {
|
||||
StoryResult story = new StoryResult();
|
||||
story.setId(1L);
|
||||
story.setName("海底小纵队(story.get1.0), " + "param:" + param);
|
||||
story.setName("海底小纵队(story.get1.0), " + "param:" + param + ", port:" + port);
|
||||
return story;
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,67 @@
|
||||
package com.gitee.sop.gateway.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
/**
|
||||
* 表名:monitor_info
|
||||
* 备注:接口监控信息
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
@Table(name = "monitor_info")
|
||||
@Data
|
||||
public class MonitorInfo {
|
||||
/** 数据库字段:id */
|
||||
@Id
|
||||
@Column(name = "id")
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 路由id, 数据库字段:route_id */
|
||||
private String routeId;
|
||||
|
||||
/** 接口名, 数据库字段:name */
|
||||
private String name;
|
||||
|
||||
/** 版本号, 数据库字段:version */
|
||||
private String version;
|
||||
|
||||
/** 数据库字段:service_id */
|
||||
private String serviceId;
|
||||
|
||||
/** 数据库字段:instance_id */
|
||||
private String instanceId;
|
||||
|
||||
/** 请求耗时最长时间, 数据库字段:max_time */
|
||||
private Integer maxTime;
|
||||
|
||||
/** 请求耗时最小时间, 数据库字段:min_time */
|
||||
private Integer minTime;
|
||||
|
||||
/** 总时长,毫秒, 数据库字段:total_time */
|
||||
private Long totalTime;
|
||||
|
||||
/** 总调用次数, 数据库字段:total_request_count */
|
||||
private Long totalRequestCount;
|
||||
|
||||
/** 成功次数, 数据库字段:success_count */
|
||||
private Long successCount;
|
||||
|
||||
/** 失败次数(业务主动抛出的异常算作成功,如参数校验,未知的错误算失败), 数据库字段:error_count */
|
||||
private Long errorCount;
|
||||
|
||||
/** 数据库字段:gmt_create */
|
||||
private Date gmtCreate;
|
||||
|
||||
/** 数据库字段:gmt_modified */
|
||||
private Date gmtModified;
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
package com.gitee.sop.gateway.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
/**
|
||||
* 表名:monitor_info_error
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
@Table(name = "monitor_info_error")
|
||||
@Data
|
||||
public class MonitorInfoError {
|
||||
/** 数据库字段:id */
|
||||
@Id
|
||||
@Column(name = "id")
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 错误id,md5Hex(instanceId + routeId + errorMsg), 数据库字段:error_id */
|
||||
private String errorId;
|
||||
|
||||
/** 实例id, 数据库字段:instance_id */
|
||||
private String instanceId;
|
||||
|
||||
/** 数据库字段:route_id */
|
||||
private String routeId;
|
||||
|
||||
/** 数据库字段:error_msg */
|
||||
private String errorMsg;
|
||||
|
||||
/** http status,非200错误, 数据库字段:error_status */
|
||||
private Integer errorStatus;
|
||||
|
||||
/** 错误次数, 数据库字段:count */
|
||||
private Integer count;
|
||||
|
||||
/** 数据库字段:is_deleted */
|
||||
@com.gitee.fastmybatis.core.annotation.LogicDelete
|
||||
private Byte isDeleted;
|
||||
|
||||
/** 数据库字段:gmt_create */
|
||||
private Date gmtCreate;
|
||||
|
||||
/** 数据库字段:gmt_modified */
|
||||
private Date gmtModified;
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
package com.gitee.sop.gateway.interceptor;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptor;
|
||||
import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptorContext;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
import com.gitee.sop.gatewaycommon.sync.SopAsyncConfigurer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 用于收集监控数据
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class MonitorRouteInterceptor implements RouteInterceptor {
|
||||
|
||||
@Autowired
|
||||
SopAsyncConfigurer sopAsyncConfigurer;
|
||||
|
||||
@Autowired
|
||||
MonitorRouteInterceptorService monitorRouteInterceptorService;
|
||||
|
||||
@Override
|
||||
public void preRoute(RouteInterceptorContext context) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterRoute(RouteInterceptorContext context) {
|
||||
sopAsyncConfigurer.getAsyncExecutor().execute(()-> {
|
||||
monitorRouteInterceptorService.storeRequestInfo(context);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return -1000;
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,164 @@
|
||||
package com.gitee.sop.gateway.interceptor;
|
||||
|
||||
import com.gitee.sop.gateway.mapper.DbMonitorInfoManager;
|
||||
import com.gitee.sop.gatewaycommon.bean.LRUCache;
|
||||
import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptorContext;
|
||||
import com.gitee.sop.gatewaycommon.monitor.MonitorDTO;
|
||||
import com.gitee.sop.gatewaycommon.monitor.MonitorData;
|
||||
import com.gitee.sop.gatewaycommon.monitor.MonitorManager;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
import com.gitee.sop.gatewaycommon.sync.MyNamedThreadFactory;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class MonitorRouteInterceptorService {
|
||||
|
||||
/**
|
||||
* 刷新到数据库时间间隔,秒
|
||||
*/
|
||||
@Value("${sop.monitor.flush-period-seconds:30}")
|
||||
int flushPeriodSeconds;
|
||||
|
||||
/**
|
||||
* 定时任务每n秒,执行一次
|
||||
*/
|
||||
@Value("${sop.monitor.schedule-period-seconds:30}")
|
||||
int schedulePeriodSeconds;
|
||||
|
||||
/**
|
||||
* 错误数量容量
|
||||
*/
|
||||
@Value("${sop.monitor.error-count-capacity:50}")
|
||||
int monitorErrorCapacity;
|
||||
|
||||
@Autowired
|
||||
DbMonitorInfoManager dbMonitorInfoManager;
|
||||
|
||||
@Autowired
|
||||
MonitorManager monitorManager;
|
||||
|
||||
/**
|
||||
* 记录接口调用流量,最大时间,最小时间,总时长,平均时长,调用次数,成功次数,失败次数.
|
||||
* 需要考虑并发情况。
|
||||
*/
|
||||
public synchronized void storeRequestInfo(RouteInterceptorContext context) {
|
||||
ApiParam apiParam = context.getApiParam();
|
||||
ServiceInstance serviceInstance = context.getServiceInstance();
|
||||
String routeId = apiParam.getRouteId();
|
||||
int spendTime = (int)(context.getFinishTimeMillis() - context.getBeginTimeMillis());
|
||||
// 这步操作是线程安全的,底层调用了ConcurrentHashMap.computeIfAbsent
|
||||
String key = getMonitorKey(routeId, serviceInstance.getInstanceId());
|
||||
MonitorData monitorData = monitorManager.getMonitorInfo(key, (k) -> this.createMonitorInfo(apiParam, serviceInstance));
|
||||
monitorData.storeMaxTime(spendTime);
|
||||
monitorData.storeMinTime(spendTime);
|
||||
monitorData.getTotalRequestCount().incrementAndGet();
|
||||
monitorData.getTotalTime().addAndGet(spendTime);
|
||||
if (context.isSuccessRequest()) {
|
||||
monitorData.getSuccessCount().incrementAndGet();
|
||||
} else {
|
||||
monitorData.getErrorCount().incrementAndGet();
|
||||
String errorMsg = context.getServiceErrorMsg();
|
||||
monitorData.addErrorMsg(errorMsg, context.getResponseStatus());
|
||||
}
|
||||
}
|
||||
|
||||
private String getMonitorKey(String routeId, String instanceId) {
|
||||
return routeId + instanceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新到数据库
|
||||
*/
|
||||
private synchronized void flushDb() {
|
||||
Map<String, MonitorData> monitorData = monitorManager.getMonitorData();
|
||||
if (monitorData.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
LocalDateTime checkTime = LocalDateTime.now();
|
||||
List<String> tobeRemoveKeys = new ArrayList<>();
|
||||
List<MonitorDTO> tobeSaveBatch = new ArrayList<>(monitorData.size());
|
||||
monitorData.forEach((key, value) -> {
|
||||
LocalDateTime flushTime = value.getFlushTime();
|
||||
if (flushTime.isEqual(checkTime) || flushTime.isBefore(checkTime)) {
|
||||
log.debug("刷新监控数据到数据库, MonitorData:{}", value);
|
||||
tobeRemoveKeys.add(key);
|
||||
MonitorDTO monitorDTO = getMonitorDTO(value);
|
||||
tobeSaveBatch.add(monitorDTO);
|
||||
}
|
||||
});
|
||||
dbMonitorInfoManager.saveMonitorInfoBatch(tobeSaveBatch);
|
||||
|
||||
for (String key : tobeRemoveKeys) {
|
||||
monitorData.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
private MonitorDTO getMonitorDTO(MonitorData monitorData) {
|
||||
MonitorDTO monitorDTO = new MonitorDTO();
|
||||
monitorDTO.setRouteId(monitorData.getRouteId());
|
||||
monitorDTO.setName(monitorData.getName());
|
||||
monitorDTO.setVersion(monitorData.getVersion());
|
||||
monitorDTO.setServiceId(monitorData.getServiceId());
|
||||
monitorDTO.setInstanceId(monitorData.getInstanceId());
|
||||
monitorDTO.setMaxTime(monitorData.getMaxTime());
|
||||
monitorDTO.setMinTime(monitorData.getMinTime());
|
||||
monitorDTO.setTotalTime(monitorData.getTotalTime().longValue());
|
||||
monitorDTO.setTotalRequestCount(monitorData.getTotalRequestCount().longValue());
|
||||
monitorDTO.setSuccessCount(monitorData.getSuccessCount().longValue());
|
||||
monitorDTO.setErrorCount(monitorData.getErrorCount().longValue());
|
||||
monitorDTO.setErrorMsgList(monitorData.getMonitorErrorMsgMap().values());
|
||||
return monitorDTO;
|
||||
}
|
||||
|
||||
private MonitorData createMonitorInfo(ApiParam apiParam, ServiceInstance serviceInstance) {
|
||||
MonitorData monitorData = new MonitorData();
|
||||
monitorData.setRouteId(apiParam.getRouteId());
|
||||
monitorData.setName(apiParam.fetchName());
|
||||
monitorData.setVersion(apiParam.fetchVersion());
|
||||
monitorData.setServiceId(apiParam.fetchServiceId());
|
||||
monitorData.setInstanceId(serviceInstance.getInstanceId());
|
||||
monitorData.setTotalTime(new AtomicInteger());
|
||||
monitorData.setMaxTime(0);
|
||||
monitorData.setMinTime(0);
|
||||
monitorData.setSuccessCount(new AtomicInteger());
|
||||
monitorData.setTotalRequestCount(new AtomicInteger());
|
||||
monitorData.setErrorCount(new AtomicInteger());
|
||||
monitorData.setFlushTime(getFlushTime());
|
||||
monitorData.setMonitorErrorMsgMap(new LRUCache<>(monitorErrorCapacity));
|
||||
return monitorData;
|
||||
}
|
||||
|
||||
private LocalDateTime getFlushTime() {
|
||||
return LocalDateTime.now()
|
||||
.plusSeconds(flushPeriodSeconds);
|
||||
}
|
||||
|
||||
|
||||
@PostConstruct
|
||||
public void after() {
|
||||
// 每隔schedulePeriodSeconds秒执行一次
|
||||
ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1, new MyNamedThreadFactory("monitorSchedule"));
|
||||
// 延迟执行,随机5~14秒
|
||||
int delay = 5 + new Random().nextInt(10);
|
||||
scheduledThreadPoolExecutor.scheduleWithFixedDelay(this::flushDb, delay, schedulePeriodSeconds, TimeUnit.SECONDS);
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,62 @@
|
||||
package com.gitee.sop.gateway.mapper;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.monitor.MonitorDTO;
|
||||
import com.gitee.sop.gatewaycommon.monitor.MonitorErrorMsg;
|
||||
import com.gitee.sop.gatewaycommon.monitor.RouteErrorCount;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Service
|
||||
public class DbMonitorInfoManager {
|
||||
|
||||
@Autowired
|
||||
private MonitorInfoMapper monitorInfoMapper;
|
||||
|
||||
@Autowired
|
||||
private MonitorInfoErrorMapper monitorInfoErrorMapper;
|
||||
|
||||
@Value("${sop.monitor.error-count-capacity:50}")
|
||||
int limitCount;
|
||||
|
||||
public void saveMonitorInfoBatch(List<MonitorDTO> list) {
|
||||
if (CollectionUtils.isEmpty(list)) {
|
||||
return;
|
||||
}
|
||||
monitorInfoMapper.saveMonitorInfoBatch(list);
|
||||
this.saveMonitorInfoErrorBatch(list);
|
||||
}
|
||||
|
||||
private void saveMonitorInfoErrorBatch(List<MonitorDTO> list) {
|
||||
List<RouteErrorCount> routeErrorCounts = monitorInfoErrorMapper.listRouteErrorCountAll();
|
||||
// 路由id对应的错误次数,key:routeId,value:错误次数
|
||||
Map<String, Integer> routeErrorCountsMap = routeErrorCounts.stream()
|
||||
.collect(Collectors.toMap(RouteErrorCount::getRouteId, RouteErrorCount::getCount));
|
||||
List<MonitorErrorMsg> monitorErrorMsgList = list.stream()
|
||||
.filter(monitorDTO -> CollectionUtils.isNotEmpty(monitorDTO.getErrorMsgList()))
|
||||
.flatMap(monitorDTO -> {
|
||||
int limit = limitCount - routeErrorCountsMap.getOrDefault(monitorDTO.getRouteId(), 0);
|
||||
// 容量已满
|
||||
if (limit <= 0) {
|
||||
return null;
|
||||
}
|
||||
// 截取剩余
|
||||
return monitorDTO.getErrorMsgList().stream().limit(limit);
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
if (CollectionUtils.isNotEmpty(monitorErrorMsgList)) {
|
||||
monitorInfoErrorMapper.saveMonitorInfoErrorBatch(monitorErrorMsgList);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
package com.gitee.sop.gateway.mapper;
|
||||
|
||||
import com.gitee.fastmybatis.core.mapper.CrudMapper;
|
||||
import com.gitee.sop.gateway.entity.MonitorInfoError;
|
||||
import com.gitee.sop.gatewaycommon.monitor.MonitorErrorMsg;
|
||||
import com.gitee.sop.gatewaycommon.monitor.RouteErrorCount;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public interface MonitorInfoErrorMapper extends CrudMapper<MonitorInfoError, Long> {
|
||||
|
||||
@Update("UPDATE monitor_info_error " +
|
||||
"SET is_deleted=0 " +
|
||||
",count=count + 1 " +
|
||||
"WHERE route_id=#{routeId} AND error_id=#{errorId}")
|
||||
int updateError(@Param("routeId") String routeId,@Param("errorId") String errorId);
|
||||
|
||||
int saveMonitorInfoErrorBatch(@Param("list") List<MonitorErrorMsg> list);
|
||||
|
||||
@Select("SELECT route_id routeId, count(*) `count` FROM monitor_info_error \n" +
|
||||
"WHERE is_deleted=0 \n" +
|
||||
"GROUP BY route_id")
|
||||
List<RouteErrorCount> listRouteErrorCount();
|
||||
|
||||
@Select("SELECT route_id routeId, count(*) `count` FROM monitor_info_error \n" +
|
||||
"WHERE is_deleted=0 \n" +
|
||||
"GROUP BY route_id")
|
||||
List<RouteErrorCount> listRouteErrorCountAll();
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
package com.gitee.sop.gateway.mapper;
|
||||
|
||||
import com.gitee.fastmybatis.core.mapper.CrudMapper;
|
||||
import com.gitee.sop.gateway.entity.MonitorInfo;
|
||||
import com.gitee.sop.gatewaycommon.monitor.MonitorDTO;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public interface MonitorInfoMapper extends CrudMapper<MonitorInfo, Long> {
|
||||
|
||||
/**
|
||||
* 更新监控状态
|
||||
*
|
||||
* @return 返回影响行数
|
||||
*/
|
||||
@Update("UPDATE monitor_info " +
|
||||
"set max_time=case when max_time < #{maxTime} then #{maxTime} else max_time end " +
|
||||
",min_time=case when min_time > #{minTime} then #{minTime} else min_time end " +
|
||||
",total_request_count=total_request_count + #{totalRequestCount} " +
|
||||
",total_time=total_time + #{totalTime} " +
|
||||
",success_count=success_count + #{successCount} " +
|
||||
",error_count=error_count + #{errorCount} " +
|
||||
"where route_id=#{routeId}")
|
||||
int updateMonitorInfo(MonitorDTO monitorDTO);
|
||||
|
||||
|
||||
/**
|
||||
* 批量插入监控数据
|
||||
* @param list 监控数据
|
||||
* @return 返回影响行数
|
||||
*/
|
||||
int saveMonitorInfoBatch(@Param("list") List<MonitorDTO> list);
|
||||
}
|
@@ -29,6 +29,7 @@ spring.cloud.gateway.discovery.locator.lower-case-service-id=true
|
||||
spring.cloud.gateway.discovery.locator.enabled=true
|
||||
|
||||
# 不用改
|
||||
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
|
||||
mybatis.fill.com.gitee.fastmybatis.core.support.DateFillInsert=gmt_create
|
||||
mybatis.fill.com.gitee.fastmybatis.core.support.DateFillUpdate=gmt_modified
|
||||
|
||||
|
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<!-- 注意:文件名必须跟Dao类名字一致,因为是根据文件名做关联。 -->
|
||||
<mapper namespace="com.gitee.sop.gateway.mapper.MonitorInfoErrorMapper">
|
||||
|
||||
<insert id="saveMonitorInfoErrorBatch">
|
||||
INSERT INTO `monitor_info_error` (
|
||||
`error_id`,
|
||||
`instance_id`,
|
||||
`route_id`,
|
||||
`error_msg`,
|
||||
`error_status`,
|
||||
`count`)
|
||||
VALUES
|
||||
<foreach collection="list" item="data" separator="," >
|
||||
(#{data.errorId},
|
||||
#{data.instanceId},
|
||||
#{data.routeId},
|
||||
#{data.errorMsg},
|
||||
#{data.errorStatus},
|
||||
#{data.count})
|
||||
</foreach>
|
||||
ON DUPLICATE KEY UPDATE
|
||||
error_msg = VALUES(error_msg)
|
||||
, error_status = VALUES(error_status)
|
||||
, `count`= `count` + VALUES(count)
|
||||
, is_deleted = 0
|
||||
</insert>
|
||||
|
||||
</mapper>
|
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<!-- 注意:文件名必须跟Dao类名字一致,因为是根据文件名做关联。 -->
|
||||
<mapper namespace="com.gitee.sop.gateway.mapper.MonitorInfoMapper">
|
||||
|
||||
<insert id="saveMonitorInfoBatch">
|
||||
INSERT INTO `monitor_info` (
|
||||
`route_id`,
|
||||
`name`,
|
||||
`version`,
|
||||
`service_id`,
|
||||
`instance_id`,
|
||||
`max_time`,
|
||||
`min_time`,
|
||||
`total_time`,
|
||||
`total_request_count`,
|
||||
`success_count`,
|
||||
`error_count`)
|
||||
VALUES
|
||||
<foreach collection="list" item="data" separator="," >
|
||||
(#{data.routeId},
|
||||
#{data.name},
|
||||
#{data.version},
|
||||
#{data.serviceId},
|
||||
#{data.instanceId},
|
||||
#{data.maxTime},
|
||||
#{data.minTime},
|
||||
#{data.totalTime},
|
||||
#{data.totalRequestCount},
|
||||
#{data.successCount},
|
||||
#{data.errorCount})
|
||||
</foreach>
|
||||
<![CDATA[
|
||||
ON DUPLICATE KEY UPDATE
|
||||
max_time = case when max_time < VALUES(max_time) then VALUES(max_time) else max_time end
|
||||
,min_time = case when min_time > VALUES(min_time) then VALUES(min_time) else min_time end
|
||||
,total_time = total_time + VALUES(total_time)
|
||||
,total_request_count = total_request_count + VALUES(total_request_count)
|
||||
,success_count = success_count + VALUES(success_count)
|
||||
,error_count = error_count + VALUES(error_count)
|
||||
]]></insert>
|
||||
|
||||
</mapper>
|
@@ -17,6 +17,8 @@ DROP TABLE IF EXISTS `config_gray`;
|
||||
DROP TABLE IF EXISTS `config_common`;
|
||||
DROP TABLE IF EXISTS `admin_user_info`;
|
||||
DROP TABLE IF EXISTS `config_service_route`;
|
||||
DROP TABLE IF EXISTS `monitor_info`;
|
||||
DROP TABLE IF EXISTS `monitor_info_error`;
|
||||
|
||||
|
||||
CREATE TABLE `admin_user_info` (
|
||||
@@ -208,6 +210,40 @@ CREATE TABLE `config_service_route` (
|
||||
KEY `idx_serviceid` (`service_id`) USING BTREE
|
||||
) ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT='路由配置';
|
||||
|
||||
CREATE TABLE `monitor_info` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`route_id` varchar(128) NOT NULL DEFAULT '' COMMENT '路由id',
|
||||
`name` varchar(128) NOT NULL DEFAULT '' COMMENT '接口名',
|
||||
`version` varchar(64) NOT NULL DEFAULT '' COMMENT '版本号',
|
||||
`service_id` varchar(64) NOT NULL DEFAULT '',
|
||||
`instance_id` varchar(128) NOT NULL DEFAULT '',
|
||||
`max_time` int(11) NOT NULL DEFAULT '0' COMMENT '请求耗时最长时间',
|
||||
`min_time` int(11) NOT NULL DEFAULT '0' COMMENT '请求耗时最小时间',
|
||||
`total_time` bigint(20) NOT NULL DEFAULT '0' COMMENT '总时长,毫秒',
|
||||
`total_request_count` bigint(20) NOT NULL DEFAULT '0' COMMENT '总调用次数',
|
||||
`success_count` bigint(20) NOT NULL DEFAULT '0' COMMENT '成功次数',
|
||||
`error_count` bigint(20) NOT NULL DEFAULT '0' COMMENT '失败次数(业务主动抛出的异常算作成功,如参数校验,未知的错误算失败)',
|
||||
`gmt_create` DATETIME DEFAULT NULL,
|
||||
`gmt_modified` DATETIME DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_routeid` (`route_id`) USING BTREE,
|
||||
KEY `idex_name` (`name`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='接口监控信息';
|
||||
|
||||
CREATE TABLE `monitor_info_error` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`error_id` varchar(64) NOT NULL DEFAULT '' COMMENT '错误id,md5Hex(instanceId + routeId + errorMsg)',
|
||||
`route_id` varchar(128) NOT NULL DEFAULT '',
|
||||
`error_msg` text NOT NULL,
|
||||
`error_status` int(11) NOT NULL DEFAULT '0' COMMENT 'http status,非200错误',
|
||||
`count` int(11) NOT NULL DEFAULT '0' COMMENT '错误次数',
|
||||
`is_deleted` tinyint(4) NOT NULL DEFAULT '0',
|
||||
`gmt_create` DATETIME DEFAULT NULL,
|
||||
`gmt_modified` DATETIME DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_erririd` (`error_id`) USING BTREE,
|
||||
KEY `idx_routeid` (`route_id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
INSERT INTO `admin_user_info` (`id`, `username`, `password`, `status`, `gmt_create`, `gmt_modified`) VALUES
|
||||
(1,'admin','a62cd510fb9a8a557a27ef279569091f',1,'2019-04-02 19:55:26','2019-04-02 19:55:26');
|
||||
|
38
sop-upgrade-4.1.0.sql
Normal file
38
sop-upgrade-4.1.0.sql
Normal file
@@ -0,0 +1,38 @@
|
||||
-- 4.1.0升级脚本
|
||||
|
||||
use sop;
|
||||
|
||||
CREATE TABLE `monitor_info` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`route_id` varchar(128) NOT NULL DEFAULT '' COMMENT '路由id',
|
||||
`name` varchar(128) NOT NULL DEFAULT '' COMMENT '接口名',
|
||||
`version` varchar(64) NOT NULL DEFAULT '' COMMENT '版本号',
|
||||
`service_id` varchar(64) NOT NULL DEFAULT '',
|
||||
`instance_id` varchar(128) NOT NULL DEFAULT '',
|
||||
`max_time` int(11) NOT NULL DEFAULT '0' COMMENT '请求耗时最长时间',
|
||||
`min_time` int(11) NOT NULL DEFAULT '0' COMMENT '请求耗时最小时间',
|
||||
`total_time` bigint(20) NOT NULL DEFAULT '0' COMMENT '总时长,毫秒',
|
||||
`total_request_count` bigint(20) NOT NULL DEFAULT '0' COMMENT '总调用次数',
|
||||
`success_count` bigint(20) NOT NULL DEFAULT '0' COMMENT '成功次数',
|
||||
`error_count` bigint(20) NOT NULL DEFAULT '0' COMMENT '失败次数(业务主动抛出的异常算作成功,如参数校验,未知的错误算失败)',
|
||||
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_routeid` (`route_id`) USING BTREE,
|
||||
KEY `idex_name` (`name`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='接口监控信息';
|
||||
|
||||
CREATE TABLE `monitor_info_error` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`error_id` varchar(64) NOT NULL DEFAULT '' COMMENT '错误id,md5Hex(instanceId + routeId + errorMsg)',
|
||||
`route_id` varchar(128) NOT NULL DEFAULT '',
|
||||
`error_msg` text NOT NULL,
|
||||
`error_status` int(11) NOT NULL DEFAULT '0' COMMENT 'http status,非200错误',
|
||||
`count` int(11) NOT NULL DEFAULT '0' COMMENT '错误次数',
|
||||
`is_deleted` tinyint(4) NOT NULL DEFAULT '0',
|
||||
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_erririd` (`error_id`) USING BTREE,
|
||||
KEY `idx_routeid` (`route_id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
36
sop.sql
36
sop.sql
@@ -19,6 +19,8 @@ DROP TABLE IF EXISTS `config_gray_instance`;
|
||||
DROP TABLE IF EXISTS `config_gray`;
|
||||
DROP TABLE IF EXISTS `config_common`;
|
||||
DROP TABLE IF EXISTS `admin_user_info`;
|
||||
DROP TABLE IF EXISTS `monitor_info`;
|
||||
DROP TABLE IF EXISTS `monitor_info_error`;
|
||||
|
||||
|
||||
CREATE TABLE `admin_user_info` (
|
||||
@@ -210,6 +212,40 @@ CREATE TABLE `user_info` (
|
||||
KEY `idx_unamepwd` (`username`,`password`) USING BTREE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='用户信息表';
|
||||
|
||||
CREATE TABLE `monitor_info` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`route_id` varchar(128) NOT NULL DEFAULT '' COMMENT '路由id',
|
||||
`name` varchar(128) NOT NULL DEFAULT '' COMMENT '接口名',
|
||||
`version` varchar(64) NOT NULL DEFAULT '' COMMENT '版本号',
|
||||
`service_id` varchar(64) NOT NULL DEFAULT '',
|
||||
`instance_id` varchar(128) NOT NULL DEFAULT '',
|
||||
`max_time` int(11) NOT NULL DEFAULT '0' COMMENT '请求耗时最长时间',
|
||||
`min_time` int(11) NOT NULL DEFAULT '0' COMMENT '请求耗时最小时间',
|
||||
`total_time` bigint(20) NOT NULL DEFAULT '0' COMMENT '总时长,毫秒',
|
||||
`total_request_count` bigint(20) NOT NULL DEFAULT '0' COMMENT '总调用次数',
|
||||
`success_count` bigint(20) NOT NULL DEFAULT '0' COMMENT '成功次数',
|
||||
`error_count` bigint(20) NOT NULL DEFAULT '0' COMMENT '失败次数(业务主动抛出的异常算作成功,如参数校验,未知的错误算失败)',
|
||||
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_routeid` (`route_id`) USING BTREE,
|
||||
KEY `idex_name` (`name`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='接口监控信息';
|
||||
|
||||
CREATE TABLE `monitor_info_error` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`error_id` varchar(64) NOT NULL DEFAULT '' COMMENT '错误id,md5Hex(instanceId + routeId + errorMsg)',
|
||||
`route_id` varchar(128) NOT NULL DEFAULT '',
|
||||
`error_msg` text NOT NULL,
|
||||
`error_status` int(11) NOT NULL DEFAULT '0' COMMENT 'http status,非200错误',
|
||||
`count` int(11) NOT NULL DEFAULT '0' COMMENT '错误次数',
|
||||
`is_deleted` tinyint(4) NOT NULL DEFAULT '0',
|
||||
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_erririd` (`error_id`) USING BTREE,
|
||||
KEY `idx_routeid` (`route_id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
INSERT INTO `admin_user_info` (`id`, `username`, `password`, `status`, `gmt_create`, `gmt_modified`) VALUES
|
||||
(1,'admin','a62cd510fb9a8a557a27ef279569091f',1,'2019-04-02 19:55:26','2019-04-02 19:55:26');
|
||||
|
Reference in New Issue
Block a user