Merge branch 'master' into eureka

# 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.xml
eureka
tanghc 4 years ago
commit d22cedd308
  1. 7
      changelog.md
  2. 4
      doc/docs/files/10090_路由授权.md
  3. 27
      doc/docs/files/10093_路由监控.md
  4. 6
      doc/docs/files/90001_网关性能测试.md
  5. BIN
      doc/docs/files/images/10090_1.png
  6. BIN
      doc/docs/files/images/10090_2.png
  7. BIN
      doc/docs/files/images/10093_1.png
  8. BIN
      doc/docs/files/images/10093_2.png
  9. 2
      doc/pom.xml
  10. 20
      pom.xml
  11. 4
      sop-admin/pom.xml
  12. 14
      sop-admin/sop-admin-server/pom.xml
  13. 5
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/MonitorApi.java
  14. 80
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/MonitorNewApi.java
  15. 15
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/InstanceMonitorSearchParam.java
  16. 21
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/MonitorErrorMsgParam.java
  17. 19
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/MonitorInfoErrorSolveParam.java
  18. 23
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/MonitorSearchParam.java
  19. 26
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/result/MonitorInfoErrorMsgResult.java
  20. 13
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/bean/RouteErrorCount.java
  21. 75
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/common/CopyUtil.java
  22. 66
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/MonitorInfo.java
  23. 54
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/MonitorInfoError.java
  24. 61
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/MonitorSummary.java
  25. 22
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/mapper/MonitorInfoErrorMapper.java
  26. 19
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/mapper/MonitorInfoMapper.java
  27. 1
      sop-admin/sop-admin-server/src/main/resources/META-INF/sop-admin.properties
  28. 63
      sop-admin/sop-admin-server/src/main/resources/mybatis/mapper/MonitorInfoMapper.xml
  29. 10
      sop-admin/sop-admin-server/src/main/resources/mybatis/mybatisConfig.xml
  30. 2
      sop-admin/sop-admin-vue/src/router/index.js
  31. 273
      sop-admin/sop-admin-vue/src/views/service/monitorNew.vue
  32. 6
      sop-auth/pom.xml
  33. 2
      sop-common/pom.xml
  34. 6
      sop-common/sop-bridge-eureka/pom.xml
  35. 6
      sop-common/sop-bridge-nacos/pom.xml
  36. 4
      sop-common/sop-gateway-common/pom.xml
  37. 43
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/LRUCache.java
  38. 16
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/config/AbstractConfiguration.java
  39. 24
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/BaseGatewayConfiguration.java
  40. 21
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/controller/ErrorLogController.java
  41. 21
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/controller/GatewayMonitorController.java
  42. 2
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/result/GatewayResultExecutor.java
  43. 96
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/interceptor/MonitorRouteInterceptor.java
  44. 2
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/interceptor/RouteInterceptorContext.java
  45. 8
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/loadbalancer/LoadBalanceConfig.java
  46. 2
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/EnvGrayManager.java
  47. 3
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/EnvironmentKeys.java
  48. 63
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/monitor/MonitorDTO.java
  49. 106
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/monitor/MonitorData.java
  50. 30
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/monitor/MonitorErrorMsg.java
  51. 88
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/monitor/MonitorInfo.java
  52. 6
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/monitor/MonitorManager.java
  53. 13
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/monitor/RouteErrorCount.java
  54. 2
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java
  55. 37
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/sync/MyNamedThreadFactory.java
  56. 41
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/sync/SopAsyncConfigurer.java
  57. 79
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/CopyUtil.java
  58. 5
      sop-common/sop-service-common/pom.xml
  59. 2
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/GlobalExceptionHandler.java
  60. 7
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/ServiceRouteController.java
  61. 2
      sop-example/pom.xml
  62. 4
      sop-example/sop-springmvc/pom.xml
  63. 4
      sop-example/sop-story/pom.xml
  64. 6
      sop-example/sop-story/src/main/java/com/gitee/sop/storyweb/controller/Example1001_BaseController.java
  65. 12
      sop-gateway/pom.xml
  66. 2
      sop-gateway/src/main/java/com/gitee/sop/gateway/SopGatewayApplication.java
  67. 67
      sop-gateway/src/main/java/com/gitee/sop/gateway/entity/MonitorInfo.java
  68. 55
      sop-gateway/src/main/java/com/gitee/sop/gateway/entity/MonitorInfoError.java
  69. 43
      sop-gateway/src/main/java/com/gitee/sop/gateway/interceptor/MonitorRouteInterceptor.java
  70. 164
      sop-gateway/src/main/java/com/gitee/sop/gateway/interceptor/MonitorRouteInterceptorService.java
  71. 62
      sop-gateway/src/main/java/com/gitee/sop/gateway/mapper/DbMonitorInfoManager.java
  72. 36
      sop-gateway/src/main/java/com/gitee/sop/gateway/mapper/MonitorInfoErrorMapper.java
  73. 39
      sop-gateway/src/main/java/com/gitee/sop/gateway/mapper/MonitorInfoMapper.java
  74. 1
      sop-gateway/src/main/resources/META-INF/gateway.properties
  75. 30
      sop-gateway/src/main/resources/mybatis/mapper/MonitorInfoErrorMapper.xml
  76. 43
      sop-gateway/src/main/resources/mybatis/mapper/MonitorInfoMapper.xml
  77. 37
      sop-mysql5.6以下版本.sql
  78. 4
      sop-sdk/pom.xml
  79. 4
      sop-sdk/sdk-java/pom.xml
  80. 2
      sop-test/pom.xml
  81. 41
      sop-upgrade-4.1.0.sql
  82. 2
      sop-website/pom.xml
  83. 3
      sop-website/src/main/resources/public/pages/doc/doc.html
  84. 39
      sop.sql

@ -1,5 +1,12 @@
# changelog
## 4.1.0
需要执行`sop-upgrade-4.1.0.sql`
- 重构路由监控功能
- 升级SpringBoot,SpringCloud,SpringCloudAlibaba版本
## 4.0.3
- 可定义业务错误码(见`@Open`注解中的`bizCode`属性)

@ -12,9 +12,9 @@
默认情况下,接口访问时公开的,ISV都能访问。如果要设置某个接口访问权限,在`@Open`注解中指定permission=true。
如:`@Open(value = "permission.story.get", permission = true)`。这样该接口是需要经过授权给ISV才能访问的。
重启服务后,登录admin,服务管理-路由列表界面中,操作操作列会出现一个授权按钮,点击出现授权窗口,勾选对应的角色即可完成授权。
重启服务后,登录admin,服务管理-路由列表界面中,`访问权限`列会出现一个点击授权,点击出现授权窗口,勾选对应的角色即可完成授权。
- 点击`授权`按钮,进行角色授权
- `点击授权`,进行角色授权
![admin预览](images/10090_1.png "10090_1.png")

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

@ -133,8 +133,8 @@ Transfer/sec: 226.50KB
`restart.sh`
```bash
echo "Stopping sop-gateway-4.0.3-SNAPSHOT.jar"
pid=`ps -ef | grep sop-gateway-4.0.3-SNAPSHOT.jar | grep -v grep | awk '{print $2}'`
echo "Stopping sop-gateway-4.1.0-SNAPSHOT.jar"
pid=`ps -ef | grep sop-gateway-4.1.0-SNAPSHOT.jar | grep -v grep | awk '{print $2}'`
if [ -n "$pid" ]
then
echo "kill -9 的id:" $pid
@ -142,7 +142,7 @@ then
fi
nohup java -jar -verbose:gc -XX:+PrintGCDetails -XX:+PrintHeapAtGC -Xloggc:gc.log \
-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m -Xms1024m -Xmx1024m -Xmn256m -Xss256k -XX:SurvivorRatio=8\
-XX:+UseConcMarkSweepGC sop-gateway-4.0.3-SNAPSHOT.jar\
-XX:+UseConcMarkSweepGC sop-gateway-4.1.0-SNAPSHOT.jar\
--spring.profiles.active=dev --server.port=8081 &
tail -f nohup.out

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

@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.gitee.sop</groupId>
<artifactId>doc</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
<properties>
<!-- Generic properties -->

@ -5,14 +5,14 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-parent</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<description>一个开放平台解决方案项目,基于Spring Cloud实现,目标是能够让用户快速得搭建起自己的开放平台</description>
@ -36,17 +36,16 @@
<maven.compiler.target>1.8</maven.compiler.target>
<!-- springboot 版本-->
<spring-boot.version>2.2.5.RELEASE</spring-boot.version>
<spring-boot.version>2.3.4.RELEASE</spring-boot.version>
<!-- spring cloud 版本 -->
<spring-cloud.version>Hoxton.SR3</spring-cloud.version>
<spring-cloud.version>Hoxton.SR8</spring-cloud.version>
<!-- spring cloud alibaba 版本 -->
<!-- 具体版本对应关系见:https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E -->
<spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
<spring-cloud-alibaba.version>2.2.3.RELEASE</spring-cloud-alibaba.version>
<!-- Logging -->
<logback.version>1.2.3</logback.version>
<slf4j.version>1.7.5</slf4j.version>
<!-- Test -->
<junit.version>4.11</junit.version>
@ -62,12 +61,13 @@
<hibernate-validator.version>6.0.13.Final</hibernate-validator.version>
<fastmybatis.version>1.9.1</fastmybatis.version>
<spring-data-redis.version>2.3.0.RELEASE</spring-data-redis.version>
<guava.version>27.1-jre</guava.version>
<guava.version>29.0-jre</guava.version>
<swagger.version>1.5.21</swagger.version>
<springfox-spring-web.version>2.9.2</springfox-spring-web.version>
<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 +202,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>

@ -6,13 +6,13 @@
<parent>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-parent</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sop-admin</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>

@ -5,13 +5,13 @@
<parent>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-parent</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sop-admin-server</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
@ -21,11 +21,6 @@
</properties>
<dependencies>
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-bridge-eureka</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>

@ -29,8 +29,11 @@ import java.util.stream.Collectors;
/**
* @author tanghc
* @deprecated use com.gitee.sop.adminserver.api.service.MonitorNewApi
* @see MonitorNewApi
*/
@ApiService
@Deprecated
//@ApiService
@ApiDoc("服务管理-监控")
@Slf4j
public class MonitorApi {

@ -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);
}

@ -32,6 +32,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: '路由监控' }
},
{

@ -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>

@ -5,7 +5,7 @@
<parent>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-parent</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <!-- lookup parent from repository -->
</parent>
@ -25,7 +25,7 @@
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-service-common</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
</dependency>
<!-- sop相关配置 end-->
@ -37,7 +37,7 @@
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sdk-java</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
</dependency>
<!-- http请求 -->
<dependency>

@ -6,7 +6,7 @@
<parent>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-parent</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <!-- lookup parent from repository -->
</parent>

@ -5,12 +5,12 @@
<parent>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-parent</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
<artifactId>sop-bridge-eureka</artifactId>
@ -18,7 +18,7 @@
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-gateway-common</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
</dependency>
<dependency>

@ -5,12 +5,12 @@
<parent>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-parent</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
<artifactId>sop-bridge-nacos</artifactId>
@ -18,7 +18,7 @@
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-gateway-common</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
</dependency>
<dependency>

@ -5,13 +5,13 @@
<parent>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-parent</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sop-gateway-common</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>

@ -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);
}

@ -1,10 +1,6 @@
package com.gitee.sop.gatewaycommon.gateway.configuration;
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
import com.gitee.sop.gatewaycommon.gateway.controller.ConfigChannelController;
import com.gitee.sop.gatewaycommon.gateway.controller.ErrorLogController;
import com.gitee.sop.gatewaycommon.gateway.controller.GatewayController;
import com.gitee.sop.gatewaycommon.gateway.controller.GatewayMonitorController;
import com.gitee.sop.gatewaycommon.gateway.filter.GatewayModifyResponseGatewayFilter;
import com.gitee.sop.gatewaycommon.gateway.filter.IndexFilter;
import com.gitee.sop.gatewaycommon.gateway.filter.LimitFilter;
@ -50,26 +46,6 @@ public class BaseGatewayConfiguration extends AbstractConfiguration {
return new IndexFilter();
}
@Bean
public GatewayController gatewayErrorController() {
return new GatewayController();
}
@Bean
public ConfigChannelController configChannelController() {
return new ConfigChannelController();
}
@Bean
public ErrorLogController errorLogController() {
return new ErrorLogController();
}
@Bean
public GatewayMonitorController gatewayMonitorController() {
return new GatewayMonitorController();
}
/**
* 自定义异常处理[@@]注册Bean时依赖的Bean会从容器中直接获取所以直接注入即可
*

@ -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);
}
}

@ -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();
}
}

@ -1,8 +0,0 @@
package com.gitee.sop.gatewaycommon.loadbalancer;
/**
* @author tanghc
*/
public class LoadBalanceConfig {
}

@ -8,8 +8,6 @@ import com.gitee.sop.gatewaycommon.loadbalancer.ServiceGrayConfig;
*/
public interface EnvGrayManager extends BeanInitializer {
String ENV_GRAY = "sop.env-gray";
/**
* 保存灰度配置
* @param serviceGrayConfig 灰度配置

@ -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());
}
}

@ -6,13 +6,13 @@
<parent>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-parent</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sop-service-common</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
@ -65,7 +65,6 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.4.RELEASE</version>
<optional>true</optional>
</dependency>
<dependency>

@ -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("系统繁忙"));

@ -2,7 +2,6 @@ package com.gitee.sop.servercommon.route;
import com.gitee.sop.servercommon.bean.ServiceApiInfo;
import com.gitee.sop.servercommon.util.OpenUtil;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
@ -17,12 +16,10 @@ import javax.servlet.http.HttpServletResponse;
* @author tanghc
*/
@Slf4j
@Getter
@RestController
public class ServiceRouteController {
private static final String SECRET = "a3d9sf!1@odl90zd>fkASwq";
private static final String HEADER_RESTFUL = "restful";
private static final ApiMetaBuilder apiMetaBuilder = new ApiMetaBuilder();
@ -42,10 +39,6 @@ public class ServiceRouteController {
return getServiceRouteInfo(request, response);
}
protected ApiMetaBuilder getApiMetaBuilder() {
return new ApiMetaBuilder();
}
protected ServiceRouteInfo getServiceRouteInfo(HttpServletRequest request, HttpServletResponse response) {
String serviceId = environment.getProperty("spring.application.name");
if (serviceId == null) {

@ -5,7 +5,7 @@
<parent>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-parent</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <!-- lookup parent from repository -->
</parent>

@ -5,7 +5,7 @@
<parent>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-parent</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath> <!-- lookup parent from repository -->
</parent>
@ -26,7 +26,7 @@
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-service-common</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
</dependency>
<!-- nacos -->
<dependency>

@ -4,7 +4,7 @@
<parent>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-parent</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath> <!-- lookup parent from repository -->
</parent>
@ -19,7 +19,7 @@
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-service-common</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
</dependency>
<dependency>

@ -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;
}

@ -4,7 +4,7 @@
<parent>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-parent</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <!-- lookup parent from repository -->
</parent>
@ -40,16 +40,6 @@
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--
修复:Spring Cloud Hoxton.SR1 gateway 与reactor-netty 0.9.3.RELEASE不兼容问题
https://blog.csdn.net/sun800711/article/details/104061049
-->
<dependency>
<groupId>io.projectreactor.netty</groupId>
<artifactId>reactor-netty</artifactId>
<version>0.9.4.RELEASE</version>
</dependency>
<dependency>
<groupId>net.oschina.durcframework</groupId>
<artifactId>fastmybatis-spring-boot-starter</artifactId>

@ -3,7 +3,7 @@ package com.gitee.sop.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@SpringBootApplication(scanBasePackages = "com.gitee.sop")
public class SopGatewayApplication {
public static void main(String[] args) {

@ -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,41 @@ 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 DEFAULT NULL,
`gmt_modified` datetime DEFAULT DEFAULT NULL,
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 NULL,
`gmt_modified` DATETIME DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_errorid` (`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');

@ -6,13 +6,13 @@
<parent>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-parent</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sop-sdk</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>

@ -4,13 +4,13 @@
<parent>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-parent</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sdk-java</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
<properties>
<!-- Generic properties -->

@ -5,7 +5,7 @@
<parent>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-parent</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <!-- lookup parent from repository -->
</parent>

@ -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;

@ -5,7 +5,7 @@
<parent>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-parent</artifactId>
<version>4.0.3-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <!-- lookup parent from repository -->
</parent>

@ -388,9 +388,9 @@
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div><!-- layui-main end~ -->
<div class="layui-footer footer footer-doc">
@ -405,6 +405,5 @@
<script src="doc.js" type="text/javascript"></script>
<script src="docEvent.js" type="text/javascript"></script>
</body>
</html>

@ -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,43 @@ 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`,`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;
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');

Loading…
Cancel
Save