Compare commits
	
		
			3 Commits 
		
	
	
		
			master
			...
			max-bytes-
		
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | 3421daa1e3 | 5 years ago | 
|  | ede2e37d3c | 5 years ago | 
|  | 5390da7ec5 | 5 years ago | 
| @ -1,16 +0,0 @@ | ||||
| FROM java:8 | ||||
| VOLUME /tmp | ||||
| VOLUME /sop | ||||
| 
 | ||||
| # 将所有应用放到一个镜像当中 | ||||
| ADD sop-gateway/target/*.jar sop/sop-gateway/sop-gateway.jar | ||||
| ADD sop-admin/sop-admin-server/target/*.jar sop/sop-admin/sop-admin.jar | ||||
| ADD sop-website/sop-website-server/target/*.jar sop/sop-website/sop-website.jar | ||||
| ADD sop-auth/target/*.jar sop/sop-auth/sop-auth.jar | ||||
| ADD sop-example/sop-story/target/*.jar sop/sop-story/sop-story.jar | ||||
| 
 | ||||
| # 拷贝启动脚本 | ||||
| COPY docker-entrypoint.sh /usr/local/bin/ | ||||
| RUN chmod +x /usr/local/bin/docker-entrypoint.sh | ||||
| 
 | ||||
| ENTRYPOINT ["docker-entrypoint.sh"] | ||||
| @ -1,27 +0,0 @@ | ||||
| /target/ | ||||
| !.mvn/wrapper/maven-wrapper.jar | ||||
| 
 | ||||
| ### STS ### | ||||
| .apt_generated | ||||
| .classpath | ||||
| .factorypath | ||||
| .project | ||||
| .settings | ||||
| .springBeans | ||||
| .sts4-cache | ||||
| 
 | ||||
| ### IntelliJ IDEA ### | ||||
| .idea | ||||
| *.iws | ||||
| *.iml | ||||
| *.ipr | ||||
| *.png | ||||
| 
 | ||||
| ### NetBeans ### | ||||
| /nbproject/private/ | ||||
| /nbbuild/ | ||||
| /dist/ | ||||
| /nbdist/ | ||||
| /.nb-gradle/ | ||||
| /build/ | ||||
| 
 | ||||
| @ -1,3 +1,7 @@ | ||||
| # SOP开发文档 | ||||
| 
 | ||||
| Git地址:[SOP](https://gitee.com/durcframework/SOP) | ||||
| 
 | ||||
| Q群:167643071 | ||||
| 
 | ||||
|  | ||||
|  | ||||
| @ -1,36 +1,39 @@ | ||||
| * [首页](/?t=1616211903021) | ||||
| * [首页](/?t=1595832340981) | ||||
| * 开发文档 | ||||
|   * [快速体验](files/10010_快速体验.md?t=1616211903027) | ||||
|   * [项目接入到SOP](files/10011_项目接入到SOP.md?t=1616211903048) | ||||
|   * [新增接口](files/10020_新增接口.md?t=1616211903048) | ||||
|   * [开发流程](files/10021_开发流程.md?t=1616211903048) | ||||
|   * [用户注册](files/10022_用户注册.md?t=1616211903049) | ||||
|   * [业务参数校验](files/10030_业务参数校验.md?t=1616211903049) | ||||
|   * [错误处理](files/10040_错误处理.md?t=1616211903049) | ||||
|   * [编写文档](files/10041_编写文档.md?t=1616211903049) | ||||
|   * [接口交互详解](files/10050_接口交互详解.md?t=1616211903049) | ||||
|   * [使用签名校验工具](files/10080_使用签名校验工具.md?t=1616211903049) | ||||
|   * [ISV管理](files/10085_ISV管理.md?t=1616211903050) | ||||
|   * [自定义返回结果](files/10087_自定义返回结果.md?t=1616211903050) | ||||
|   * [自定义过滤器](files/10088_自定义过滤器.md?t=1616211903050) | ||||
|   * [自定义校验token](files/10089_自定义校验token.md?t=1616211903050) | ||||
|   * [网关拦截器](files/10090_网关拦截器.md?t=1616211903050) | ||||
|   * [路由授权](files/10091_路由授权.md?t=1616211903050) | ||||
|   * [接口限流](files/10092_接口限流.md?t=1616211903051) | ||||
|   * [路由监控](files/10093_路由监控.md?t=1616211903051) | ||||
|   * [SDK开发](files/10095_SDK开发.md?t=1616211903051) | ||||
|   * [应用授权](files/10097_应用授权.md?t=1616211903051) | ||||
|   * [提供restful接口](files/10100_提供restful接口.md?t=1616211903051) | ||||
|   * [文件上传](files/10104_文件上传.md?t=1616211903051) | ||||
|   * [配置Sleuth链路追踪](files/10109_配置Sleuth链路追踪.md?t=1616211903052) | ||||
|   * [预发布灰度发布](files/10110_预发布灰度发布.md?t=1616211903052) | ||||
|   * [动态修改请求参数](files/10111_动态修改请求参数.md?t=1616211903052) | ||||
|   * [使用eureka](files/10112_使用eureka.md?t=1616211903052) | ||||
|   * [超时设置](files/10113_超时设置.md?t=1616211903052) | ||||
|   * [快速体验](files/10010_快速体验.md?t=1595832340983) | ||||
|   * [项目接入到SOP](files/10011_项目接入到SOP.md?t=1595832341000) | ||||
|   * [新增接口](files/10020_新增接口.md?t=1595832341000) | ||||
|   * [开发流程](files/10021_开发流程.md?t=1595832341001) | ||||
|   * [业务参数校验](files/10030_业务参数校验.md?t=1595832341001) | ||||
|   * [错误处理](files/10040_错误处理.md?t=1595832341001) | ||||
|   * [编写文档](files/10041_编写文档.md?t=1595832341001) | ||||
|   * [接口交互详解](files/10050_接口交互详解.md?t=1595832341001) | ||||
|   * [easyopen支持](files/10070_easyopen支持.md?t=1595832341001) | ||||
|   * [使用签名校验工具](files/10080_使用签名校验工具.md?t=1595832341001) | ||||
|   * [ISV管理](files/10085_ISV管理.md?t=1595832341001) | ||||
|   * [自定义返回结果](files/10087_自定义返回结果.md?t=1595832341001) | ||||
|   * [自定义过滤器](files/10088_自定义过滤器.md?t=1595832341001) | ||||
|   * [自定义校验token](files/10089_自定义校验token.md?t=1595832341002) | ||||
|   * [网关拦截器](files/10090_网关拦截器.md?t=1595832341002) | ||||
|   * [路由授权](files/10090_路由授权.md?t=1595832341002) | ||||
|   * [接口限流](files/10092_接口限流.md?t=1595832341002) | ||||
|   * [路由监控](files/10093_路由监控.md?t=1595832341002) | ||||
|   * [SDK开发](files/10095_SDK开发.md?t=1595832341002) | ||||
|   * [使用SpringCloudGateway](files/10096_使用SpringCloudGateway.md?t=1595832341002) | ||||
|   * [应用授权](files/10097_应用授权.md?t=1595832341003) | ||||
|   * [提供restful接口](files/10100_提供restful接口.md?t=1595832341003) | ||||
|   * [文件上传](files/10104_文件上传.md?t=1595832341003) | ||||
|   * [配置Sleuth链路追踪](files/10109_配置Sleuth链路追踪.md?t=1595832341003) | ||||
|   * [预发布灰度发布](files/10110_预发布灰度发布.md?t=1595832341003) | ||||
|   * [动态修改请求参数](files/10111_动态修改请求参数.md?t=1595832341003) | ||||
|   * [使用eureka](files/10112_使用eureka.md?t=1595832341003) | ||||
|   * [扩展其它注册中心](files/10113_扩展其它注册中心.md?t=1595832341003) | ||||
| * 原理分析 | ||||
|   * [网关性能测试](files/90001_网关性能测试.md?t=1616211903052) | ||||
|   * [原理分析之如何存储路由](files/90011_原理分析之如何存储路由.md?t=1616211903052) | ||||
|   * [原理分析之如何路由](files/90012_原理分析之如何路由.md?t=1616211903052) | ||||
|   * [原理分析之文档归纳](files/90013_原理分析之文档归纳.md?t=1616211903052) | ||||
|   * [原理分析之预发布灰度发布](files/90014_原理分析之预发布灰度发布.md?t=1616211903052) | ||||
|   * [常见问题](files/90100_常见问题.md?t=1616211903053) | ||||
|   * [网关性能测试](files/90001_网关性能测试.md?t=1595832341004) | ||||
|   * [原理分析之@ApiMapping](files/90010_原理分析之@ApiMapping.md?t=1595832341004) | ||||
|   * [原理分析之如何存储路由](files/90011_原理分析之如何存储路由.md?t=1595832341004) | ||||
|   * [原理分析之如何路由](files/90012_原理分析之如何路由.md?t=1595832341004) | ||||
|   * [原理分析之文档归纳](files/90013_原理分析之文档归纳.md?t=1595832341004) | ||||
|   * [原理分析之预发布灰度发布](files/90014_原理分析之预发布灰度发布.md?t=1595832341004) | ||||
|   * [2.x升3.x注意事项](files/90099_2.x升3.x注意事项.md?t=1595832341004) | ||||
|   * [常见问题](files/90100_常见问题.md?t=1595832341004) | ||||
|  | ||||
| @ -1,11 +0,0 @@ | ||||
| # 用户(ISV)注册 | ||||
| 
 | ||||
| 新增ISV有两种方式 | ||||
| 
 | ||||
| - 方式1 | ||||
| 
 | ||||
| 启动sop-admin,在admin后台`ISV管理添加` | ||||
| 
 | ||||
| - 方式2 | ||||
| 
 | ||||
| 启动`sop-website-server`,用户访问自主注册 | ||||
| @ -0,0 +1,78 @@ | ||||
| # easyopen支持 | ||||
| 
 | ||||
| SOP对easyopen项目提供了很好的支持,如果您的服务端使用了easyopen框架,相关配置步骤如下: | ||||
| 
 | ||||
| ## 服务端配置 | ||||
| 
 | ||||
| 首先是服务端相关配置 | ||||
| 
 | ||||
| - pom添加依赖 | ||||
| 
 | ||||
| ```xml | ||||
| <!-- sop接入依赖 --> | ||||
| 		<dependency> | ||||
| 			<groupId>com.gitee.sop</groupId> | ||||
| 			<artifactId>sop-service-common</artifactId> | ||||
| 			<version>最新版本</version> | ||||
| 		</dependency> | ||||
| 
 | ||||
| 		<!-- 使用nacos注册中心 | ||||
|             版本 0.2.x.RELEASE 对应的是 Spring Boot 2.x 版本,版本 0.1.x.RELEASE 对应的是 Spring Boot 1.x 版本。 | ||||
|            https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-alibaba-nacos-discovery | ||||
|         --> | ||||
| 		<dependency> | ||||
| 			<groupId>org.springframework.cloud</groupId> | ||||
| 			<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> | ||||
| 			<version>0.2.2.RELEASE</version> | ||||
| 			<exclusions> | ||||
| 				<exclusion> | ||||
| 					<groupId>com.alibaba.nacos</groupId> | ||||
| 					<artifactId>nacos-client</artifactId> | ||||
| 				</exclusion> | ||||
| 			</exclusions> | ||||
| 		</dependency> | ||||
| 		<dependency> | ||||
| 			<groupId>com.alibaba.nacos</groupId> | ||||
| 			<artifactId>nacos-client</artifactId> | ||||
| 			<version>1.1.0</version> | ||||
| 		</dependency> | ||||
| 
 | ||||
| 		<dependency> | ||||
| 			<groupId>net.oschina.durcframework</groupId> | ||||
| 			<artifactId>easyopen</artifactId> | ||||
| 			<version>1.16.1</version> | ||||
| 		</dependency> | ||||
|         <!-- sop接入依赖 end --> | ||||
| ``` | ||||
| 
 | ||||
| easyopen版本必须升级到1.16.1 | ||||
| 
 | ||||
| - 启动类上面添加注解@EnableDiscoveryClient,将自己注册到注册中心 | ||||
| - 新增一个配置类,继承EasyopenServiceConfiguration,内容为空 | ||||
| 
 | ||||
| ```java | ||||
| @Configuration | ||||
| public class SopConfig extends EasyopenServiceConfiguration { | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 服务端配置完毕,重启服务。 | ||||
| 
 | ||||
| ## 网关端配置 | ||||
| 
 | ||||
| 接下来是网关的配置 | ||||
| 
 | ||||
| - 打开ZuulConfig.java,注释掉原本的@Configuration,新增如下Configuration | ||||
| 
 | ||||
| ```java | ||||
| @Configuration | ||||
| public class ZuulConfig extends EasyopenZuulConfiguration { | ||||
| 
 | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 配置完毕,重启网关服务,可运行测试用例`EasyopenClientPostTest.java`验证 | ||||
| 
 | ||||
| **注:** 配置完成后easyopen签名校验将会关闭,改用网关端来校验;网关对easyopen返回的结果不进行处理,直接返回服务端的结果。 | ||||
| 
 | ||||
| 完整配置可查看sop-example/sop-easyopen项目 | ||||
| @ -1,39 +1,103 @@ | ||||
| # 自定义过滤器 | ||||
| 
 | ||||
| 演示在网关追加一个header | ||||
| ## zuul过滤器 | ||||
| 
 | ||||
| zuul过滤器列表如下: | ||||
| 
 | ||||
| | 类型 | 顺序 | 过滤器               | 功能                       | | ||||
| | ----- | ---- | ----------------------- | ---------------------------- | | ||||
| | pre   | -1000   | PreValidateFilter (SOP自带)  | 校验签名 | | ||||
| | pre   | -998   | PreLimitFilter (SOP自带)  | 限流拦截器 | | ||||
| | pre   | -3   | ServletDetectionFilter  | 标记处理 Servlet 的类型 | | ||||
| | pre   | -2   | Servlet30WrapperFilter  | 包装 HttpServletRequest 请求 | | ||||
| | pre   | -1   | FormBodyWrapperFilter   | 包装请求体       Servlet30WrapperFilter       | | ||||
| | pre | 1    | DebugFilter             | 标记调试标志           | | ||||
| | pre | 5    | PreDecorationFilter     | 决定路由转发过滤器 | | ||||
| | route | 10   | RibbonRoutingFilter     | serviceId 请求转发       | | ||||
| | route | 100  | SimpleHostRoutingFilter | url 请求转发             | | ||||
| | route | 500  | SendForwardFilter       | forward 请求转发         | | ||||
| | post  | 0    | SendErrorFilter         | 处理有错误的请求响应 | | ||||
| | post  | 1000 | SendResponseFilter      | 处理正常的请求响应  | | ||||
| 
 | ||||
| 顺序值小的优先执行,`-3`之前是sop自带的过滤器,`-3`开始是zuul自带的过滤器。 | ||||
| 
 | ||||
| 创建自定义过滤器可以从`-500`开始(-1000 ~ -501留给SOP)。下面是一个自定义过虑器的例子: | ||||
| 
 | ||||
| ```java | ||||
| public class PreXXXFilter extends BaseZuulFilter { | ||||
|     @Override | ||||
|     protected FilterType getFilterType() { | ||||
|         return FilterType.PRE; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected int getFilterOrder() { | ||||
|         return -500; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected Object doRun(RequestContext requestContext) throws ZuulException { | ||||
|         HttpServletRequest request = requestContext.getRequest(); | ||||
|         ApiParam apiParam = ZuulContext.getApiParam(); | ||||
|         String appKey = apiParam.fetchAppKey(); | ||||
|         // ...业务处理 | ||||
|          | ||||
|         // 固定返回null | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 过滤器编写完毕后,在Config中使用: | ||||
| 
 | ||||
| ```java | ||||
| @Configuration | ||||
| public class ZuulConfig extends AlipayZuulConfiguration { | ||||
| 
 | ||||
|     ... | ||||
|      | ||||
|     @Bean | ||||
|     PreXXXFilter preXXXFilter() { | ||||
|         return new PreXXXFilter(); | ||||
|     } | ||||
|     ... | ||||
| 
 | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ## spring cloud gateway | ||||
| 
 | ||||
| 跟zuul同理 | ||||
| 
 | ||||
| ```java | ||||
| public class CustomFilter implements GlobalFilter, Ordered { | ||||
| public class XXXFilter implements GlobalFilter, Ordered { | ||||
| 
 | ||||
|     @Override | ||||
|     public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { | ||||
|         // 演示在网关追加header | ||||
|         ApiParam apiParam = ServerWebExchangeUtil.getApiParam(exchange); | ||||
|         String token = apiParam.fetchAccessToken(); | ||||
|         ServerWebExchange serverWebExchange = ServerWebExchangeUtil.addHeaders(exchange, httpHeaders -> { | ||||
|             httpHeaders.add("token", token); | ||||
|         }); | ||||
|         return chain.filter(serverWebExchange); | ||||
|         ApiParam apiParam = (ApiParam)exchange.getAttribute(SopConstants.CACHE_API_PARAM); | ||||
|         String appKey = apiParam.fetchAppKey(); | ||||
|         // ...业务处理 | ||||
|         ... | ||||
|         return chain.filter(exchange); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int getOrder() { | ||||
|         // 自定义过滤器可以从0开始 | ||||
|         return 0; | ||||
|         return -500; | ||||
|     } | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 使用过滤器,在sop-gateway中找到MyConfig,添加: | ||||
| 使用过滤器: | ||||
| 
 | ||||
| ```java | ||||
| @Configuration | ||||
| public class MyConfig { | ||||
| public class GatewayConfig extends AlipayGatewayConfiguration { | ||||
| 
 | ||||
|     ... | ||||
|     @Bean | ||||
|     CustomFilter customFilter() { | ||||
|         return new CustomFilter(); | ||||
|     XXXFilter xxxFilter() { | ||||
|         return new XXXFilter(); | ||||
|     } | ||||
|     ... | ||||
| } | ||||
|  | ||||
| @ -1,27 +1,22 @@ | ||||
| # 路由监控 | ||||
| 
 | ||||
| 路由监控功能可以查看各个接口的调用情况,监控信息收集采用拦截器实现,前往【服务管理】-【路由监控】查看 | ||||
| 路由监控功能可以查看各个接口的调用情况,监控信息收集采用拦截器实现。 | ||||
| 
 | ||||
| - 后台预览 | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
|  | ||||
| - 统计各个接口的调用次数、耗时等信息 | ||||
| - 错误日志统一在网关负责收集 | ||||
| - 只收集未知类型的错误日志,开发人员主动throw的异常不收集 | ||||
| - 收集的日志存放在内存中,重启网关日志会消失 | ||||
| 
 | ||||
| ## 永久保存日志 | ||||
| 
 | ||||
| - 注意事项 | ||||
| 默认收集的日志存放在内存中,重启网关日志会消失(见:`com.gitee.sop.gatewaycommon.monitor.MonitorManager.java`)。如果要永久保存日志内容,需要自己修改`MonitorManager` | ||||
| 
 | ||||
| 处理完错误后,请及时`标记解决`,一个接口默认保存50条错误信息,采用LRU机制,淘汰老的。标记解决后则会空出一个位置存放新的错误信息。 | ||||
| 
 | ||||
| 重复的错误只会存放一条记录,然后累加错误次数,重复错误定义如下: | ||||
| 
 | ||||
| `instanceId + routeId + errorMsg`,即一个实例 + 路由id + 错误信息确定一个错误 | ||||
| - 后台预览 | ||||
| 
 | ||||
| 可在网关设置`sop.monitor.error-count-capacity=50`参数调整错误容量 | ||||
|  | ||||
| 
 | ||||
| 考虑到数据库压力,网关收到错误信息后并不会立即保存到数据库,而是先保存到内容中,然后定时保存到时间,默认时间隔为30秒 | ||||
| 可通过`sop.monitor.flush-period-seconds=30`调整间隔时间。 | ||||
|  | ||||
| 
 | ||||
| 相关类: | ||||
| 
 | ||||
| - com.gitee.sop.gateway.interceptor.MonitorRouteInterceptor | ||||
| - com.gitee.sop.gatewaycommon.interceptor.MonitorRouteInterceptor | ||||
|  | ||||
| @ -0,0 +1,16 @@ | ||||
| # 使用SpringCloudGateway | ||||
| 
 | ||||
| 修改`sop-gateway/pom.xml`配置,artifactId部分改成`sop-bridge-gateway`即可 | ||||
| 
 | ||||
| ```xml | ||||
| <dependency> | ||||
|     <groupId>com.gitee.sop</groupId> | ||||
|      <!-- 使用zuul作为网关 --> | ||||
|      <!--<artifactId>sop-bridge-zuul</artifactId>--> | ||||
|      <!-- 使用spring cloud gateway作为网关 --> | ||||
|      <artifactId>sop-bridge-gateway</artifactId> | ||||
|     <version>对应版本</version> | ||||
| </dependency> | ||||
| ``` | ||||
| 
 | ||||
| 修改完毕,重启sop-gateway | ||||
| @ -1,3 +1,53 @@ | ||||
| # 使用eureka | ||||
| 
 | ||||
| 切换到`eureka`分支 | ||||
| ## 修改网关 | ||||
| 
 | ||||
| - 打开`/sop-gateway/pom.xml` | ||||
| 
 | ||||
| 注释nacos相关依赖 | ||||
| 
 | ||||
| 添加eureka依赖: | ||||
| 
 | ||||
| ```xml | ||||
| <dependency> | ||||
|     <groupId>org.springframework.cloud</groupId> | ||||
|     <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> | ||||
| </dependency> | ||||
| ``` | ||||
| 
 | ||||
| - 打开sop-gateway下`application-dev.properties` | ||||
| 
 | ||||
| 注释nacos配置 | ||||
| 
 | ||||
| ```properties | ||||
| # nacos cloud配置 | ||||
| #spring.cloud.nacos.discovery.server-addr=${nacos.url} | ||||
| #nacos.config.server-addr=${nacos.url} | ||||
| ``` | ||||
| 
 | ||||
| 添加eureka配置 | ||||
| 
 | ||||
| ```properties | ||||
| # eureka注册中心 | ||||
| eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/ | ||||
| ``` | ||||
| 
 | ||||
| ## 微服务端修改 | ||||
| 
 | ||||
| 步骤同上 | ||||
| 
 | ||||
| ## admin修改 | ||||
| 
 | ||||
| 打开`application-dev.properties`,新增配置 | ||||
| 
 | ||||
| ```properties | ||||
| # 网关地址,多个用逗号隔开 | ||||
| # 在不使用nacos时有用,使用nacos时注释掉 | ||||
| gateway.host=127.0.0.1:8081 | ||||
| 
 | ||||
| # eureka注册中心 | ||||
| eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/ | ||||
| # 如果使用eureka,填eureka,使用nacos,填eureka | ||||
| registry.name=eureka | ||||
| ``` | ||||
| 
 | ||||
|  | ||||
| @ -0,0 +1,86 @@ | ||||
| # 扩展其它注册中心 | ||||
| 
 | ||||
| SOP默认使用的注册中心是[nacos](https://nacos.io/),可以扩展实现其它注册中心,其中`eureka`分支是已经扩展好的,使用eureka注册中心。 | ||||
| 
 | ||||
| 现在以扩展[consul](https://www.consul.io/)为例,说下具体扩展步骤: | ||||
| 
 | ||||
| - 扩展注册中心监听 | ||||
| 
 | ||||
| 在`sop-gateway-common`工程下,找到com.gitee.sop.gatewaycommon.route包,可以看到有两个类 | ||||
| 
 | ||||
| `EurekaRegistryListener`和`NacosRegistryListener` | ||||
| 
 | ||||
| 这两个类的作用是监听注册中心服务注册,从而触发事件,然后获取新注册的服务。 | ||||
| 
 | ||||
| 新建一个类:`ConsulRegistryListener`,继承`BaseRegistryListener` | ||||
| 
 | ||||
| 实现onEvent方法,具体内容可参考`EurekaRegistryListener`类 | ||||
| 
 | ||||
| ```java | ||||
| public class ConsulRegistryListener extends BaseRegistryListener { | ||||
|     /** | ||||
|      * 注册中心触发事件,可以从中获取服务<br> | ||||
|      *      | ||||
|      * 这个方法做的事情有2个:<br> | ||||
|      *  | ||||
|      * 1. 找出新注册的服务,调用pullRoutes方法<br> | ||||
|      * 2. 找出删除的服务,调用removeRoutes方法<br> | ||||
|      *  | ||||
|      * @param applicationEvent 事件体 | ||||
|      */ | ||||
|     @Override | ||||
|     public void onEvent(ApplicationEvent applicationEvent) { | ||||
|      | ||||
|     } | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 配置类中新增: | ||||
| 
 | ||||
| ```java | ||||
| @Bean | ||||
| @ConditionalOnProperty("spring.cloud.consul.host") | ||||
| RegistryListener registryListenerConsul() { | ||||
|     return new ConsulRegistryListener(); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 其中`@ConditionalOnProperty("spring.cloud.consul.host")`的意思是只有配置了`spring.cloud.consul.host`属性,这个Bean才会被Spring注入 | ||||
| 
 | ||||
| `sop-gateway`工程添加`Spring Cloud Consul`相关依赖,配置文件新增consul配置 | ||||
| 
 | ||||
| - 扩展admin实现 | ||||
| 
 | ||||
| 找到`sop-admin-server`工程下com.gitee.sop.adminserver.service包,可以看到有两个类,`RegistryServiceEurekaImpl`和`RegistryServiceNacosImpl` | ||||
| 它们实现了`com.gitee.sop.adminserver.service.RegistryService`接口,因此我们要新建一个consul对应的类 | ||||
| 
 | ||||
| 新建`RegistryServiceConsulImpl`,然后实现RegistryService接口中的方法,具体可参考RegistryServiceEurekaImpl | ||||
| 
 | ||||
| ```java | ||||
| public class RegistryServiceConsulImpl implements RegistryService { | ||||
|      | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 打开`com.gitee.sop.adminserver.config.WebConfig`类 | ||||
| 
 | ||||
| 新增一条配置 | ||||
| 
 | ||||
| ```java | ||||
| /** | ||||
|  * 当配置了registry.name=eureka生效。 | ||||
|  * | ||||
|  * @return | ||||
|  */ | ||||
| @Bean | ||||
| @ConditionalOnProperty(value = "registry.name", havingValue = "consul") | ||||
| RegistryService registryServiceEureka() { | ||||
|     return new RegistryServiceConsulImpl(); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| application配置文件新增一条配置: | ||||
| 
 | ||||
| ```properties | ||||
| registry.name=consul | ||||
| ``` | ||||
| @ -1,14 +0,0 @@ | ||||
| # 超时设置 | ||||
| 
 | ||||
| 当微服务处理业务逻辑时间过长,网关会报超时错误,默认等待时间是5秒。 | ||||
| 
 | ||||
| 可在网关指定`spring.cloud.gateway.httpclient.response-timeout`参数设置超时时间,单位毫秒 | ||||
| 
 | ||||
| ```properties | ||||
| # 设置响应超时10秒 | ||||
| spring.cloud.gateway.httpclient.response-timeout=10000 | ||||
| ``` | ||||
| 
 | ||||
| 更多配置参见:`org.springframework.cloud.gateway.config.HttpClientProperties` | ||||
| 
 | ||||
| 测试用例参见:`com.gitee.sop.test.TimeoutTest` | ||||
| @ -0,0 +1,21 @@ | ||||
| # 原理分析之@ApiMapping注解 | ||||
| 
 | ||||
| @ApiMapping注解的使用方式参考了Spring自带的@PostMapping注解。 | ||||
| 
 | ||||
| 查看org.springframework.web.bind.annotation.PostMapping的类注释,有这么一句话: | ||||
| 
 | ||||
| 
 | ||||
| > Specifically, @PostMapping is a composed annotation that acts as a shortcut for @RequestMapping(method = RequestMethod.POST). | ||||
| 
 | ||||
| 翻译过来就是说,@PostMapping是一个组合模式的注解,可以看成是@RequestMapping(method = RequestMethod.POST)快捷方式。 | ||||
| 
 | ||||
| 如果我们自己定义个Mapping,仿照@PostMapping的方式,然后作用在方法上面会不会成功呢?实践证明是可以的。 | ||||
| 
 | ||||
| @ApiMapping注解正是仿照了@PostMapping注解,然后再添加了几个自己的属性,比如版本号字段。 | ||||
| 
 | ||||
| 那么如何才能通过path + 版本号来确定一个接口呢? | ||||
| 
 | ||||
| springmvc提供了RequestCondition接口来实现这个功能,具体的操作可参考这篇文章:[让SpringMVC支持可版本管理的Restful接口](http://www.cnblogs.com/jcli/p/springmvc_restful_version.html) | ||||
| 
 | ||||
| SOP对应的是`com.gitee.sop.servercommon.mapping.ApiMappingRequestCondition`,这个类在com.gitee.sop.servercommon.mapping下。可以从`ApiMappingHandlerMapping`类开始解读。 | ||||
| 
 | ||||
| @ -1,33 +1,77 @@ | ||||
| # 原理分析之如何路由 | ||||
| 
 | ||||
| Spring Cloud Gateway通过一系列的Filter来进行数据的传输,如下图所示: | ||||
| ## zuul如何路由 | ||||
| 
 | ||||
|  | ||||
| SOP网关默认使用zuul,当然也默认使用了zuul提供的路由功能。zuul默认使用过滤器来实现路由转发, | ||||
| 我们看下zuul中自带的过滤器: | ||||
| 
 | ||||
| SOP网关在此基础上新增了几个Filter用来处理自己的逻辑,如:前置校验、结果返回。 | ||||
| | 类型 | 顺序 | 过滤器               | 功能                       | | ||||
| | ----- | ---- | ----------------------- | ---------------------------- | | ||||
| | pre   | -3   | ServletDetectionFilter  | 标记处理 Servlet 的类型 | | ||||
| | pre   | -2   | Servlet30WrapperFilter  | 包装 HttpServletRequest 请求 | | ||||
| | pre   | -1   | FormBodyWrapperFilter   | 包装请求体              | | ||||
| | pre | 1    | DebugFilter             | 标记调试标志           | | ||||
| | pre | 5    | PreDecorationFilter     | 决定路由转发过滤器 | | ||||
| | route | 10   | RibbonRoutingFilter     | serviceId 请求转发       | | ||||
| | route | 100  | SimpleHostRoutingFilter | url 请求转发             | | ||||
| | route | 500  | SendForwardFilter       | forward 请求转发         | | ||||
| | post  | 0    | SendErrorFilter         | 处理有错误的请求响应 | | ||||
| | post  | 1000 | SendResponseFilter      | 处理正常的请求响应  | | ||||
| 
 | ||||
| | 过滤器  | 类型  | Order  | 功能                       | | ||||
| | -----   | ---- | ----------------------- | ---------------------------- | | ||||
| |IndexFilter| `自定义` | -2147483648 | 入口过滤器,获取参数、签名校验 | | ||||
| |ParameterFormatterFilter | `自定义` | -2147482647 | 格式化参数 | | ||||
| |LimitFilter|`自定义`|-2147482447|限流| | ||||
| |ForwardPathFilter|系统自带|0 |设置转发的path| | ||||
| |RouteToRequestUrlFilter|系统自带|10000|设置转发host| | ||||
| |SopLoadBalancerClientFilter|`自定义`|10100|LoadBalance获取转发实例| | ||||
| |NettyRoutingFilter|系统自带|2147483647|获取httpclient发送请求| | ||||
| |ForwardRoutingFilter|系统自带|2147483647|请求分发| | ||||
| |GatewayModifyResponseGatewayFilter|`自定义`|-2|处理响应结果| | ||||
| 上图就是zuul提供的默认过滤器,可在org.springframework.cloud.netflix.zuul.filters下查看。 | ||||
| 
 | ||||
| 一个完整的请求会自上而下经过这些Filter,下面讲解如何动态设置路由。 | ||||
| zuul的过滤器顺序值小的优先执行,其中的`PreDecorationFilter`是我们重点关注的类,由它来决定路由转发去向。 | ||||
| 
 | ||||
| ## 动态设置路由 | ||||
| 打开PreDecorationFilter类,看到类注释有一句话:`that determines where and how to route based on the supplied` | ||||
| 
 | ||||
| 网关启动后会从注册中心拉取微服务实例,然后请求微服务提供的一个接口(`/sop/routes`),获取开放接口信息(被`@Open`注解的接口)。 | ||||
| 翻译过来就是说,决定从哪里获取路由,然后怎样去路由。 | ||||
| 
 | ||||
| 监听处理类在:`com.gitee.sop.bridge.route.NacosRegistryListener` | ||||
| PreDecorationFilter类的核心方法是run()方法。找到run方法中这一句代码: | ||||
| 
 | ||||
| 获取到路由信息后,将路由信息缓存到本地,并保存到数据库,代码在:`com.gitee.sop.gatewaycommon.gateway.route.GatewayRouteCache.load` | ||||
| `Route route = this.routeLocator.getMatchingRoute(requestURI);` | ||||
| 
 | ||||
| 然后动态设置Gateway路由,代码在:`com.gitee.sop.gatewaycommon.gateway.route.GatewayRouteRepository.refresh` | ||||
| 这句代码很重要,表示路由从哪里获取,如果我们能够重写getMatchingRoute方法那就可以返回自己定义的路由了。 | ||||
| 
 | ||||
| 当有微服务重新启动时,网关会监听到微服务实例有变更,会重复上述步骤,确保网关存有最新的路由。 | ||||
| 接下来找到RouteLocator类的定义,发现是通过构造方法传进来的,那么我们就去找使用构造方法的类。(IDEA下右键构造方法--Find Usage) | ||||
| 
 | ||||
| 在org.springframework.cloud.netflix.zuul.ZuulProxyAutoConfiguration类中找到了定义 | ||||
| 
 | ||||
| ```java | ||||
| // pre filters | ||||
| @Bean | ||||
| @ConditionalOnMissingBean(PreDecorationFilter.class) | ||||
| public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator, ProxyRequestHelper proxyRequestHelper) { | ||||
|     return new PreDecorationFilter(routeLocator, this.server.getServlet().getContextPath(), this.zuulProperties, | ||||
|             proxyRequestHelper); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 方法默认注入了RouteLocator类,默认注入的实现是CompositeRouteLocator类(通过打断点可以查看)。 | ||||
| 
 | ||||
| 同时方法上用了`@ConditionalOnMissingBean`注解,表示如果其它地方没有声明,则默认使用这个。 | ||||
| 
 | ||||
| 因此我们可以自己声明一个PreDecorationFilter,然后注入自定义的RouteLocator就行了。 | ||||
| 
 | ||||
| SOP自定义的RouteLocator为:`com.gitee.sop.gatewaycommon.zuul.route.SopRouteLocator`,可自行前往查看。 | ||||
| 
 | ||||
| 然后再我们的Config中定义: | ||||
| 
 | ||||
| ```java | ||||
| /** | ||||
|  * 选取路由 | ||||
|  * @param zuulRouteRepository | ||||
|  * @param proxyRequestHelper | ||||
|  * @return | ||||
|  */ | ||||
| @Bean | ||||
| public PreDecorationFilter preDecorationFilter(ZuulRouteRepository zuulRouteRepository, ProxyRequestHelper proxyRequestHelper) { | ||||
|     // 自定义路由 | ||||
|     RouteLocator routeLocator = new SopRouteLocator(zuulRouteRepository); | ||||
|     return new PreDecorationFilter(routeLocator, | ||||
|             this.server.getServlet().getContextPath(), | ||||
|             this.zuulProperties, | ||||
|             proxyRequestHelper); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 到此,我们只需要实现RouteLocator接口,就能使用zuul默认的路由功能,非常方便。 | ||||
|  | ||||
| @ -0,0 +1,23 @@ | ||||
| # 2.x升3.x注意事项 | ||||
| 
 | ||||
| 升级到3.x后`本地访问接口`方式会有不同。 | ||||
| 
 | ||||
| ```java | ||||
| @ApiMapping(value = "alipay.story.get") | ||||
| public StoryResult getStory(StoryParam param) { | ||||
|     StoryResult story = new StoryResult(); | ||||
|     story.setId(1L); | ||||
|     story.setName("海底小纵队(alipay.story.get1.0), port:" + environment.getProperty("server.port") + ", param:" + param); | ||||
|     return story; | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| - 2.x版本访问方式: | ||||
| 
 | ||||
| `http://localhost:2222/alipay.story.get/?name=Jim&version=1.0` | ||||
| 
 | ||||
| - 3.x版本访问方式: | ||||
| 
 | ||||
| `http://localhost:2222/alipay.story.get/1.0/?name=Jim` | ||||
| 
 | ||||
| 3.x版本中把版本号融合在了url中,如果这个功能没有用到,可以放心升级。 | ||||
| Before Width: | Height: | Size: 131 KiB | 
| Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 90 KiB | 
| Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 94 KiB | 
| Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 187 KiB | 
| Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 315 KiB | 
| Before Width: | Height: | Size: 12 KiB | 
| @ -1,12 +0,0 @@ | ||||
| #!/bin/sh | ||||
| 
 | ||||
| # 打包 | ||||
| mvn clean package | ||||
| # 创建镜像 | ||||
| docker build -t gitee.com/sop . | ||||
| 
 | ||||
| # 获取镜像id | ||||
| image_id=`docker images gitee.com/sop --format "{{.ID}}" | awk '{print $1}'` | ||||
| 
 | ||||
| # 运行镜像 | ||||
| docker run --name sop -p 8081:8081 -p 8082:8082 -p 8083:8083 -p 8087:8087 -p 2222:2222 -d $image_id | ||||
| @ -1,13 +0,0 @@ | ||||
| #!/bin/sh | ||||
| 
 | ||||
| JAVA_OPTS="-Xms128m -Xmx128m" | ||||
| 
 | ||||
| # mysql, nacos配置 | ||||
| args="--mysql.host=10.1.30.110:3306 --mysql.username=root --mysql.password=root --register.url=10.1.30.110:8848" | ||||
| 
 | ||||
| java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /sop/sop-gateway/sop-gateway.jar $args --logging.file.path=/sop/sop-gateway/log & | ||||
| java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /sop/sop-admin/sop-admin.jar $args --logging.file.path=/sop/sop-admin/log & | ||||
| java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /sop/sop-website/sop-website.jar $args --logging.file.path=/sop/sop-website/log & | ||||
| java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /sop/sop-auth/sop-auth.jar $args --logging.file.path=/sop/sop-auth/log & | ||||
| # 最后一条没有& | ||||
| java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /sop/sop-story/sop-story.jar $args --logging.file.path=/sop/sop-story/log | ||||
| @ -0,0 +1,13 @@ | ||||
| package com.gitee.sop.adminserver.api.config; | ||||
| 
 | ||||
| import com.gitee.easyopen.annotation.ApiService; | ||||
| import com.gitee.easyopen.doc.annotation.ApiDoc; | ||||
| 
 | ||||
| /** | ||||
|  * @author tanghc | ||||
|  */ | ||||
| @ApiService | ||||
| @ApiDoc("配置列表") | ||||
| public class ConfigListApi { | ||||
| 
 | ||||
| } | ||||
| @ -1,80 +0,0 @@ | ||||
| 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); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,54 +0,0 @@ | ||||
| package com.gitee.sop.adminserver.api.service; | ||||
| 
 | ||||
| import com.gitee.easyopen.annotation.Api; | ||||
| import com.gitee.easyopen.annotation.ApiService; | ||||
| import com.gitee.sop.adminserver.api.system.param.IspPageParam; | ||||
| import com.gitee.sop.adminserver.api.system.param.IspResourceParam; | ||||
| import com.gitee.sop.adminserver.common.CopyUtil; | ||||
| import com.gitee.sop.adminserver.entity.IspResource; | ||||
| import com.gitee.sop.adminserver.mapper.IspResourceMapper; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| 
 | ||||
| import javax.validation.constraints.NotNull; | ||||
| import java.util.List; | ||||
| 
 | ||||
| /** | ||||
|  * @author tanghc | ||||
|  */ | ||||
| @ApiService | ||||
| public class ResourceApi { | ||||
| 
 | ||||
|     public static final byte RESOURCE_TYPE_SDK = (byte) 0; | ||||
| 
 | ||||
|     @Autowired | ||||
|     private IspResourceMapper ispResourceMapper; | ||||
| 
 | ||||
| 
 | ||||
|     @Api(name = "isp.sdk.list") | ||||
|     List<IspResource> list(IspPageParam param) { | ||||
|         return ispResourceMapper.list(param.toQuery()); | ||||
|     } | ||||
| 
 | ||||
|     @Api(name = "isp.sdk.add") | ||||
|     void addSdk(IspResourceParam param) { | ||||
|         IspResource ispResource = CopyUtil.copyBean(param, IspResource::new); | ||||
|         ispResource.setType(RESOURCE_TYPE_SDK); | ||||
|         ispResourceMapper.saveIgnoreNull(ispResource); | ||||
|     } | ||||
| 
 | ||||
|     @Api(name = "isp.sdk.update") | ||||
|     void updateSdk(IspResourceParam param) { | ||||
|         IspResource resource = ispResourceMapper.getById(param.getId()); | ||||
|         CopyUtil.copyProperties(param, resource); | ||||
|         ispResourceMapper.update(resource); | ||||
|     } | ||||
| 
 | ||||
|     @Api(name = "isp.sdk.delete") | ||||
|     void deleteSdk(@NotNull Long id) { | ||||
|         IspResource resource = ispResourceMapper.getById(id); | ||||
|         if (resource != null) { | ||||
|             ispResourceMapper.deleteById(id); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,15 +0,0 @@ | ||||
| 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; | ||||
| } | ||||
| @ -1,21 +0,0 @@ | ||||
| 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; | ||||
| } | ||||
| @ -1,19 +0,0 @@ | ||||
| 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; | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @ -1,23 +0,0 @@ | ||||
| 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; | ||||
| } | ||||
| @ -1,32 +0,0 @@ | ||||
| package com.gitee.sop.adminserver.api.service.param; | ||||
| 
 | ||||
| import com.gitee.easyopen.doc.annotation.ApiDocField; | ||||
| import lombok.Getter; | ||||
| import lombok.Setter; | ||||
| import org.hibernate.validator.constraints.Length; | ||||
| 
 | ||||
| import javax.validation.constraints.NotBlank; | ||||
| import javax.validation.constraints.NotNull; | ||||
| 
 | ||||
| /** | ||||
|  * @author tanghc | ||||
|  */ | ||||
| @Getter | ||||
| @Setter | ||||
| public class RouteStatusUpdateParam { | ||||
| 
 | ||||
|     /** | ||||
|      * 路由的Id | ||||
|      */ | ||||
|     @NotBlank(message = "id不能为空") | ||||
|     @ApiDocField(description = "路由id") | ||||
|     private String id; | ||||
| 
 | ||||
|     /** | ||||
|      * 状态 | ||||
|      */ | ||||
|     @NotNull | ||||
|     @ApiDocField(description = "状态,0:审核,1:启用,2:禁用") | ||||
|     private Byte status; | ||||
| 
 | ||||
| } | ||||
| @ -1,26 +0,0 @@ | ||||
| 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; | ||||
| } | ||||
| @ -1,9 +0,0 @@ | ||||
| package com.gitee.sop.adminserver.api.system.param; | ||||
| 
 | ||||
| import com.gitee.fastmybatis.core.query.param.PageParam; | ||||
| 
 | ||||
| /** | ||||
|  * @author tanghc | ||||
|  */ | ||||
| public class IspPageParam extends PageParam { | ||||
| } | ||||
| @ -1,27 +0,0 @@ | ||||
| package com.gitee.sop.adminserver.api.system.param; | ||||
| 
 | ||||
| import lombok.Data; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * 表名:isp_resource | ||||
|  * 备注:ISP资源表 | ||||
|  * | ||||
|  * @author tanghc | ||||
|  */ | ||||
| @Data | ||||
| public class IspResourceParam { | ||||
| 
 | ||||
|     private Long id; | ||||
| 
 | ||||
|     /** 资源名称, 数据库字段:name */ | ||||
|     private String name; | ||||
| 
 | ||||
|     private String version; | ||||
| 
 | ||||
|     /** 资源内容(URL), 数据库字段:content */ | ||||
|     private String content; | ||||
| 
 | ||||
|     private String extContent; | ||||
| 
 | ||||
| } | ||||
| @ -1,27 +0,0 @@ | ||||
| package com.gitee.sop.adminserver.api.system.result; | ||||
| 
 | ||||
| import lombok.Data; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * 表名:isp_resource | ||||
|  * 备注:ISP资源表 | ||||
|  * | ||||
|  * @author tanghc | ||||
|  */ | ||||
| @Data | ||||
| public class IspResourceResult { | ||||
| 
 | ||||
|     private Long id; | ||||
| 
 | ||||
|     /** 资源名称, 数据库字段:name */ | ||||
|     private String name; | ||||
| 
 | ||||
|     private String version; | ||||
| 
 | ||||
|     /** 资源内容(URL), 数据库字段:content */ | ||||
|     private String content; | ||||
| 
 | ||||
|     private String extContent; | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,89 @@ | ||||
| package com.gitee.sop.adminserver.bean; | ||||
| 
 | ||||
| import com.alibaba.fastjson.annotation.JSONField; | ||||
| import com.gitee.easyopen.doc.annotation.ApiDocField; | ||||
| import lombok.Data; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| 
 | ||||
| /** | ||||
|  * @author tanghc | ||||
|  */ | ||||
| @Data | ||||
| public class RouteDefinition { | ||||
|     /** | ||||
|      * 路由的Id | ||||
|      */ | ||||
|     @ApiDocField(description = "路由id") | ||||
|     private String id = ""; | ||||
| 
 | ||||
|     /** | ||||
|      * 接口名 | ||||
|      */ | ||||
|     private String name; | ||||
| 
 | ||||
|     /** | ||||
|      * 版本号 | ||||
|      */ | ||||
|     private String version; | ||||
| 
 | ||||
|     /** | ||||
|      * 路由断言集合配置 | ||||
|      */ | ||||
|     @JSONField(serialize = false) | ||||
|     private List<GatewayPredicateDefinition> predicates = new ArrayList<>(); | ||||
| 
 | ||||
|     /** | ||||
|      * 路由过滤器集合配置 | ||||
|      */ | ||||
|     @JSONField(serialize = false) | ||||
|     private List<GatewayFilterDefinition> filters = new ArrayList<>(); | ||||
| 
 | ||||
|     /** | ||||
|      * 路由规则转发的目标uri | ||||
|      */ | ||||
|     @ApiDocField(description = "uri") | ||||
|     private String uri; | ||||
| 
 | ||||
|     /** | ||||
|      * uri后面跟的path | ||||
|      */ | ||||
|     @ApiDocField(description = "path") | ||||
|     private String path; | ||||
| 
 | ||||
|     /** | ||||
|      * 路由执行的顺序 | ||||
|      */ | ||||
|     private int order = 0; | ||||
| 
 | ||||
|     /** | ||||
|      * 是否忽略验证,业务参数验证除外 | ||||
|      */ | ||||
|     @ApiDocField(description = "是否忽略验证,业务参数验证除外,1:忽略,0:不忽略") | ||||
|     private int ignoreValidate; | ||||
| 
 | ||||
|     /** | ||||
|      * 状态,0:待审核,1:启用,2:禁用 | ||||
|      */ | ||||
|     @ApiDocField(description = "状态,0:待审核,1:启用,2:禁用") | ||||
|     private int status = 1; | ||||
| 
 | ||||
|     /** | ||||
|      * 合并结果,统一格式输出 | ||||
|      */ | ||||
|     @ApiDocField(description = "合并结果,统一格式输出,1:合并,0:不合并") | ||||
|     private int mergeResult = 1; | ||||
| 
 | ||||
|     /** | ||||
|      * 是否需要授权才能访问 | ||||
|      */ | ||||
|     @ApiDocField(description = "是否需要授权才能访问,1:是,0:否") | ||||
|     private int permission; | ||||
| 
 | ||||
|     /** | ||||
|      * 是否是自定义路由 | ||||
|      */ | ||||
|     @ApiDocField(description = "是否是自定义路由,1:是,0:否") | ||||
|     private int custom; | ||||
| } | ||||
| @ -1,13 +0,0 @@ | ||||
| package com.gitee.sop.adminserver.bean; | ||||
| 
 | ||||
| import lombok.Data; | ||||
| 
 | ||||
| /** | ||||
|  * @author tanghc | ||||
|  */ | ||||
| @Data | ||||
| public class RouteErrorCount { | ||||
| 
 | ||||
|     private String routeId; | ||||
|     private Integer count; | ||||
| } | ||||
| @ -1,29 +1,16 @@ | ||||
| package com.gitee.sop.adminserver.bean; | ||||
| 
 | ||||
| import lombok.Data; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| /** | ||||
|  * @author tanghc | ||||
|  */ | ||||
| @Data | ||||
| public class ServiceInfo { | ||||
|     /** 服务名称 */ | ||||
|     private String serviceId; | ||||
|     /** 实例列表 */ | ||||
|     private List<ServiceInstance> instances; | ||||
| 
 | ||||
|     public String getServiceId() { | ||||
|         return serviceId; | ||||
|     } | ||||
| 
 | ||||
|     public void setServiceId(String serviceId) { | ||||
|         this.serviceId = serviceId; | ||||
|     } | ||||
| 
 | ||||
|     public List<ServiceInstance> getInstances() { | ||||
|         return instances; | ||||
|     } | ||||
| 
 | ||||
|     public void setInstances(List<ServiceInstance> instances) { | ||||
|         this.instances = instances; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,26 @@ | ||||
| package com.gitee.sop.adminserver.bean; | ||||
| 
 | ||||
| import lombok.Data; | ||||
| 
 | ||||
| import java.util.Date; | ||||
| import java.util.List; | ||||
| 
 | ||||
| /** | ||||
|  * @author tanghc | ||||
|  */ | ||||
| @Data | ||||
| public class ServiceRouteInfo { | ||||
|     /** 服务名称,对应spring.application.name */ | ||||
|     private String serviceId; | ||||
| 
 | ||||
|     private Date createTime = new Date(); | ||||
| 
 | ||||
|     private Date updateTime = new Date(); | ||||
| 
 | ||||
|     private String description; | ||||
| 
 | ||||
|     private List<RouteDefinition> routeDefinitionList; | ||||
| 
 | ||||
|     /** 是否是自定义服务,1:是,0:否 */ | ||||
|     private int custom; | ||||
| } | ||||
| @ -1,75 +0,0 @@ | ||||
| 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,58 @@ | ||||
| package com.gitee.sop.adminserver.entity; | ||||
| 
 | ||||
| import lombok.Data; | ||||
| 
 | ||||
| import java.util.Date; | ||||
| 
 | ||||
| import javax.persistence.Column; | ||||
| import javax.persistence.GeneratedValue; | ||||
| import javax.persistence.GenerationType; | ||||
| import javax.persistence.Id; | ||||
| import javax.persistence.Table; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * 表名:config_route_limit | ||||
|  * 备注:路由限流配置 | ||||
|  * | ||||
|  * @author tanghc | ||||
|  */ | ||||
| @Table(name = "config_route_limit") | ||||
| @Data | ||||
| public class ConfigRouteLimit { | ||||
|     @Id | ||||
|     @Column(name = "id") | ||||
|     @GeneratedValue(strategy = GenerationType.IDENTITY) | ||||
|     /**  数据库字段:id */ | ||||
|     private Integer id; | ||||
| 
 | ||||
|     /** 路由id, 数据库字段:route_id */ | ||||
|     private String routeId; | ||||
| 
 | ||||
|     /** serviceId, 数据库字段:service_id */ | ||||
|     private String serviceId; | ||||
| 
 | ||||
|     /** 限流策略,1:漏桶策略,2:令牌桶策略, 数据库字段:limit_type */ | ||||
|     private Byte limitType; | ||||
| 
 | ||||
|     /** 每秒可处理请求数, 数据库字段:exec_count_per_second */ | ||||
|     private Integer execCountPerSecond; | ||||
| 
 | ||||
|     /** 返回的错误码, 数据库字段:limit_code */ | ||||
|     private String limitCode; | ||||
| 
 | ||||
|     /** 返回的错误信息, 数据库字段:limit_msg */ | ||||
|     private String limitMsg; | ||||
| 
 | ||||
|     /** 令牌桶容量, 数据库字段:token_bucket_count */ | ||||
|     private Integer tokenBucketCount; | ||||
| 
 | ||||
|     /** 1:开启,0关闭, 数据库字段:limit_status */ | ||||
|     private Byte limitStatus; | ||||
| 
 | ||||
|     /**  数据库字段:gmt_create */ | ||||
|     private Date gmtCreate; | ||||
| 
 | ||||
|     /**  数据库字段:gmt_modified */ | ||||
|     private Date gmtModified; | ||||
| } | ||||
| @ -1,50 +0,0 @@ | ||||
| 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; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * 表名:isp_resource | ||||
|  * 备注:ISP资源表 | ||||
|  * | ||||
|  * @author tanghc | ||||
|  */ | ||||
| @Table(name = "isp_resource") | ||||
| @Data | ||||
| public class IspResource { | ||||
|     /**  数据库字段:id */ | ||||
|     @Id | ||||
|     @Column(name = "id") | ||||
|     @GeneratedValue(strategy = GenerationType.IDENTITY) | ||||
|     private Long id; | ||||
| 
 | ||||
|     /** 资源名称, 数据库字段:name */ | ||||
|     private String name; | ||||
| 
 | ||||
|     private String version; | ||||
| 
 | ||||
|     /** 资源内容(URL), 数据库字段:content */ | ||||
|     private String content; | ||||
| 
 | ||||
|     private String extContent; | ||||
| 
 | ||||
|     /** 资源类型:0:SDK链接, 数据库字段:type */ | ||||
|     private Byte type; | ||||
| 
 | ||||
|     /**  数据库字段:is_deleted */ | ||||
|     @com.gitee.fastmybatis.core.annotation.LogicDelete | ||||
|     private Byte isDeleted; | ||||
| 
 | ||||
|     /**  数据库字段:gmt_create */ | ||||
|     private Date gmtCreate; | ||||
| 
 | ||||
|     /**  数据库字段:gmt_modified */ | ||||
|     private Date gmtModified; | ||||
| } | ||||
| @ -1,66 +0,0 @@ | ||||
| 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; | ||||
| } | ||||
| @ -1,54 +0,0 @@ | ||||
| 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; | ||||
| } | ||||
| @ -1,61 +0,0 @@ | ||||
| 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,12 @@ | ||||
| package com.gitee.sop.adminserver.mapper; | ||||
| 
 | ||||
| import com.gitee.fastmybatis.core.mapper.CrudMapper; | ||||
| 
 | ||||
| import com.gitee.sop.adminserver.entity.ConfigRouteLimit; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * @author tanghc | ||||
|  */ | ||||
| public interface ConfigRouteLimitMapper extends CrudMapper<ConfigRouteLimit, Integer> { | ||||
| } | ||||
| @ -1,11 +0,0 @@ | ||||
| package com.gitee.sop.adminserver.mapper; | ||||
| 
 | ||||
| import com.gitee.fastmybatis.core.mapper.CrudMapper; | ||||
| import com.gitee.sop.adminserver.entity.IspResource; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * @author tanghc | ||||
|  */ | ||||
| public interface IspResourceMapper extends CrudMapper<IspResource, Long> { | ||||
| } | ||||
| @ -1,22 +0,0 @@ | ||||
| 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(); | ||||
| 
 | ||||
| } | ||||
| @ -1,19 +0,0 @@ | ||||
| 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); | ||||
| } | ||||
| @ -1,63 +0,0 @@ | ||||
| <?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> | ||||
| @ -1,10 +0,0 @@ | ||||
| <?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> | ||||
| @ -1 +1 @@ | ||||
| <!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><link rel=icon href=favicon.ico><title>SOP Admin</title><link href=static/css/chunk-elementUI.81cf475c.css rel=stylesheet><link href=static/css/chunk-libs.3dfb7769.css rel=stylesheet><link href=static/css/app.6095bfbf.css rel=stylesheet></head><body><noscript><strong>We're sorry but SOP Admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script>(function(e){function n(n){for(var c,r,a=n[0],f=n[1],i=n[2],d=0,h=[];d<a.length;d++)r=a[d],u[r]&&h.push(u[r][0]),u[r]=0;for(c in f)Object.prototype.hasOwnProperty.call(f,c)&&(e[c]=f[c]);l&&l(n);while(h.length)h.shift()();return o.push.apply(o,i||[]),t()}function t(){for(var e,n=0;n<o.length;n++){for(var t=o[n],c=!0,r=1;r<t.length;r++){var a=t[r];0!==u[a]&&(c=!1)}c&&(o.splice(n--,1),e=f(f.s=t[0]))}return e}var c={},r={runtime:0},u={runtime:0},o=[];function a(e){return f.p+"static/js/"+({}[e]||e)+"."+{"chunk-25908fca":"cc77b01c","chunk-2c1f2e8f":"f092c0a0","chunk-2d0d6219":"18813e69","chunk-2d2085ef":"91d75f3c","chunk-2d221c34":"20057287","chunk-2d238661":"23693be8","chunk-30c6c34f":"b288bbf5","chunk-4de1c2b6":"e74e3d03","chunk-73b2dcec":"60c5d8e9","chunk-9b31c83a":"494fc338","chunk-9f479afe":"2093f9d0","chunk-c3ce42fe":"9517b588"}[e]+".js"}function f(n){if(c[n])return c[n].exports;var t=c[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,f),t.l=!0,t.exports}f.e=function(e){var n=[],t={"chunk-25908fca":1,"chunk-2c1f2e8f":1,"chunk-30c6c34f":1,"chunk-4de1c2b6":1,"chunk-73b2dcec":1,"chunk-9b31c83a":1,"chunk-c3ce42fe":1};r[e]?n.push(r[e]):0!==r[e]&&t[e]&&n.push(r[e]=new Promise(function(n,t){for(var c="static/css/"+({}[e]||e)+"."+{"chunk-25908fca":"a66354ec","chunk-2c1f2e8f":"0314067f","chunk-2d0d6219":"31d6cfe0","chunk-2d2085ef":"31d6cfe0","chunk-2d221c34":"31d6cfe0","chunk-2d238661":"31d6cfe0","chunk-30c6c34f":"3b12267b","chunk-4de1c2b6":"a37cd815","chunk-73b2dcec":"ed391cc5","chunk-9b31c83a":"c4612b4a","chunk-9f479afe":"31d6cfe0","chunk-c3ce42fe":"6b789903"}[e]+".css",u=f.p+c,o=document.getElementsByTagName("link"),a=0;a<o.length;a++){var i=o[a],d=i.getAttribute("data-href")||i.getAttribute("href");if("stylesheet"===i.rel&&(d===c||d===u))return n()}var h=document.getElementsByTagName("style");for(a=0;a<h.length;a++){i=h[a],d=i.getAttribute("data-href");if(d===c||d===u)return n()}var l=document.createElement("link");l.rel="stylesheet",l.type="text/css",l.onload=n,l.onerror=function(n){var c=n&&n.target&&n.target.src||u,o=new Error("Loading CSS chunk "+e+" failed.\n("+c+")");o.code="CSS_CHUNK_LOAD_FAILED",o.request=c,delete r[e],l.parentNode.removeChild(l),t(o)},l.href=u;var s=document.getElementsByTagName("head")[0];s.appendChild(l)}).then(function(){r[e]=0}));var c=u[e];if(0!==c)if(c)n.push(c[2]);else{var o=new Promise(function(n,t){c=u[e]=[n,t]});n.push(c[2]=o);var i,d=document.createElement("script");d.charset="utf-8",d.timeout=120,f.nc&&d.setAttribute("nonce",f.nc),d.src=a(e),i=function(n){d.onerror=d.onload=null,clearTimeout(h);var t=u[e];if(0!==t){if(t){var c=n&&("load"===n.type?"missing":n.type),r=n&&n.target&&n.target.src,o=new Error("Loading chunk "+e+" failed.\n("+c+": "+r+")");o.type=c,o.request=r,t[1](o)}u[e]=void 0}};var h=setTimeout(function(){i({type:"timeout",target:d})},12e4);d.onerror=d.onload=i,document.head.appendChild(d)}return Promise.all(n)},f.m=e,f.c=c,f.d=function(e,n,t){f.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},f.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(e,n){if(1&n&&(e=f(e)),8&n)return e;if(4&n&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var c in e)f.d(t,c,function(n){return e[n]}.bind(null,c));return t},f.n=function(e){var n=e&&e.__esModule?function(){return e["default"]}:function(){return e};return f.d(n,"a",n),n},f.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},f.p="",f.oe=function(e){throw console.error(e),e};var i=window["webpackJsonp"]=window["webpackJsonp"]||[],d=i.push.bind(i);i.push=n,i=i.slice();for(var h=0;h<i.length;h++)n(i[h]);var l=d;t()})([]);</script><script src=static/js/chunk-elementUI.298ac98c.js></script><script src=static/js/chunk-libs.75deb05f.js></script><script src=static/js/app.7297829f.js></script></body></html> | ||||
| <!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><link rel=icon href=favicon.ico><title>SOP Admin</title><link href=static/css/chunk-elementUI.81cf475c.css rel=stylesheet><link href=static/css/chunk-libs.3dfb7769.css rel=stylesheet><link href=static/css/app.c6dfb7ee.css rel=stylesheet></head><body><noscript><strong>We're sorry but SOP Admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script>(function(e){function n(n){for(var r,c,a=n[0],f=n[1],i=n[2],d=0,l=[];d<a.length;d++)c=a[d],u[c]&&l.push(u[c][0]),u[c]=0;for(r in f)Object.prototype.hasOwnProperty.call(f,r)&&(e[r]=f[r]);h&&h(n);while(l.length)l.shift()();return o.push.apply(o,i||[]),t()}function t(){for(var e,n=0;n<o.length;n++){for(var t=o[n],r=!0,c=1;c<t.length;c++){var a=t[c];0!==u[a]&&(r=!1)}r&&(o.splice(n--,1),e=f(f.s=t[0]))}return e}var r={},c={runtime:0},u={runtime:0},o=[];function a(e){return f.p+"static/js/"+({}[e]||e)+"."+{"chunk-25908fca":"66819987","chunk-2c1f2e8f":"f092c0a0","chunk-2d0d32e7":"213708f2","chunk-2d2085ef":"91d75f3c","chunk-2d221c34":"20057287","chunk-4de1c2b6":"e74e3d03","chunk-626b7094":"97d3a892","chunk-73b2dcec":"60c5d8e9","chunk-9b31c83a":"52bc6b2c","chunk-9f479afe":"4609bf8b","chunk-c3ce42fe":"9517b588"}[e]+".js"}function f(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,f),t.l=!0,t.exports}f.e=function(e){var n=[],t={"chunk-25908fca":1,"chunk-2c1f2e8f":1,"chunk-4de1c2b6":1,"chunk-626b7094":1,"chunk-73b2dcec":1,"chunk-9b31c83a":1,"chunk-c3ce42fe":1};c[e]?n.push(c[e]):0!==c[e]&&t[e]&&n.push(c[e]=new Promise(function(n,t){for(var r="static/css/"+({}[e]||e)+"."+{"chunk-25908fca":"a66354ec","chunk-2c1f2e8f":"0314067f","chunk-2d0d32e7":"31d6cfe0","chunk-2d2085ef":"31d6cfe0","chunk-2d221c34":"31d6cfe0","chunk-4de1c2b6":"a37cd815","chunk-626b7094":"e41ad972","chunk-73b2dcec":"ed391cc5","chunk-9b31c83a":"3b12267b","chunk-9f479afe":"31d6cfe0","chunk-c3ce42fe":"6b789903"}[e]+".css",u=f.p+r,o=document.getElementsByTagName("link"),a=0;a<o.length;a++){var i=o[a],d=i.getAttribute("data-href")||i.getAttribute("href");if("stylesheet"===i.rel&&(d===r||d===u))return n()}var l=document.getElementsByTagName("style");for(a=0;a<l.length;a++){i=l[a],d=i.getAttribute("data-href");if(d===r||d===u)return n()}var h=document.createElement("link");h.rel="stylesheet",h.type="text/css",h.onload=n,h.onerror=function(n){var r=n&&n.target&&n.target.src||u,o=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");o.code="CSS_CHUNK_LOAD_FAILED",o.request=r,delete c[e],h.parentNode.removeChild(h),t(o)},h.href=u;var s=document.getElementsByTagName("head")[0];s.appendChild(h)}).then(function(){c[e]=0}));var r=u[e];if(0!==r)if(r)n.push(r[2]);else{var o=new Promise(function(n,t){r=u[e]=[n,t]});n.push(r[2]=o);var i,d=document.createElement("script");d.charset="utf-8",d.timeout=120,f.nc&&d.setAttribute("nonce",f.nc),d.src=a(e),i=function(n){d.onerror=d.onload=null,clearTimeout(l);var t=u[e];if(0!==t){if(t){var r=n&&("load"===n.type?"missing":n.type),c=n&&n.target&&n.target.src,o=new Error("Loading chunk "+e+" failed.\n("+r+": "+c+")");o.type=r,o.request=c,t[1](o)}u[e]=void 0}};var l=setTimeout(function(){i({type:"timeout",target:d})},12e4);d.onerror=d.onload=i,document.head.appendChild(d)}return Promise.all(n)},f.m=e,f.c=r,f.d=function(e,n,t){f.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},f.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(e,n){if(1&n&&(e=f(e)),8&n)return e;if(4&n&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)f.d(t,r,function(n){return e[n]}.bind(null,r));return t},f.n=function(e){var n=e&&e.__esModule?function(){return e["default"]}:function(){return e};return f.d(n,"a",n),n},f.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},f.p="",f.oe=function(e){throw console.error(e),e};var i=window["webpackJsonp"]=window["webpackJsonp"]||[],d=i.push.bind(i);i.push=n,i=i.slice();for(var l=0;l<i.length;l++)n(i[l]);var h=d;t()})([]);</script><script src=static/js/chunk-elementUI.298ac98c.js></script><script src=static/js/chunk-libs.75deb05f.js></script><script src=static/js/app.c6e80241.js></script></body></html> | ||||