# Conflicts: # sop-admin/sop-admin-server/pom.xml # sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/ServiceConfiguration.java # sop-gateway/pom.xml # sop-website/pom.xmleureka
commit
d22cedd308
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 84 KiB |
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 187 KiB |
Before Width: | Height: | Size: 315 KiB |
@ -0,0 +1,80 @@ |
||||
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); |
||||
} |
@ -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> |
@ -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(); |
||||
} |
||||
} |
@ -1,21 +0,0 @@ |
||||
package com.gitee.sop.gatewaycommon.gateway.controller; |
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.BaseErrorLogController; |
||||
import com.gitee.sop.gatewaycommon.param.ApiParam; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
import org.springframework.web.server.ServerWebExchange; |
||||
|
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
@RestController |
||||
public class ErrorLogController extends BaseErrorLogController<ServerWebExchange> { |
||||
|
||||
@Override |
||||
protected ApiParam getApiParam(ServerWebExchange request) { |
||||
Map<String, String> params = request.getRequest().getQueryParams().toSingleValueMap(); |
||||
return ApiParam.build(params); |
||||
} |
||||
} |
@ -1,21 +0,0 @@ |
||||
package com.gitee.sop.gatewaycommon.gateway.controller; |
||||
|
||||
import com.gitee.sop.gatewaycommon.support.BaseMonitorController; |
||||
import com.gitee.sop.gatewaycommon.param.ApiParam; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
import org.springframework.web.server.ServerWebExchange; |
||||
|
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
@RestController |
||||
public class GatewayMonitorController extends BaseMonitorController<ServerWebExchange> { |
||||
|
||||
@Override |
||||
protected ApiParam getApiParam(ServerWebExchange request) { |
||||
Map<String, String> params = request.getRequest().getQueryParams().toSingleValueMap(); |
||||
return ApiParam.build(params); |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
|
||||
} |
@ -1,8 +0,0 @@ |
||||
package com.gitee.sop.gatewaycommon.loadbalancer; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public class LoadBalanceConfig { |
||||
|
||||
} |
@ -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); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
@ -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; |
||||
} |
@ -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()); |
||||
} |
||||
} |
@ -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); |
||||
} |
@ -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> |
@ -0,0 +1,41 @@ |
||||
-- 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`,`instance_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)', |
||||
`instance_id` varchar(128) NOT NULL DEFAULT '' COMMENT '实例id', |
||||
`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_errorid` (`error_id`) USING BTREE, |
||||
KEY `idx_routeid` (`route_id`) USING BTREE |
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8; |
Loading…
Reference in new issue