From 2efcb5a275d98411f98755a896978ff27c977a75 Mon Sep 17 00:00:00 2001 From: akun <957746831@qq.com> Date: Tue, 28 May 2024 17:48:52 +0800 Subject: [PATCH] =?UTF-8?q?Vert.x=E7=BD=91=E5=85=B3=E6=94=B9=E9=80=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zt-vertx/pom.xml | 71 ++ zt-vertx/zt-vertx-api/pom.xml | 41 + .../vertx/api/pojo/AddressRetryStrategy.java | 13 + .../com/sf/vertx/api/pojo/AdvancedConfig.java | 16 + .../java/com/sf/vertx/api/pojo/ApiConfig.java | 19 + .../java/com/sf/vertx/api/pojo/AppConfig.java | 20 + .../com/sf/vertx/api/pojo/DataSecurity.java | 13 + .../sf/vertx/api/pojo/EnvironmentConfig.java | 13 + .../api/pojo/HttpClientOptionsConfig.java | 15 + .../sf/vertx/api/pojo/MockExpectation.java | 28 + .../sf/vertx/api/pojo/MockMatchCondition.java | 35 + .../com/sf/vertx/api/pojo/MockResponse.java | 27 + .../main/java/com/sf/vertx/api/pojo/Node.java | 101 +++ .../com/sf/vertx/api/pojo/RouteContent.java | 16 + .../java/com/sf/vertx/api/pojo/Router.java | 14 + .../com/sf/vertx/api/pojo/SacService.java | 17 + .../com/sf/vertx/api/pojo/ServerAddress.java | 16 + .../java/com/sf/vertx/api/pojo/Strategy.java | 27 + .../com/sf/vertx/api/pojo/VertxConfig.java | 18 + .../sf/vertx/api/pojo/VertxOptionsConfig.java | 27 + .../roundRobin/SacLoadBalancing.java | 7 + .../roundRobin/WeightedRoundRobin.java | 108 +++ .../java/com/sf/vertx/enums/GatewayError.java | 119 +++ .../com/sf/vertx/enums/GatewayRouteType.java | 38 + .../sf/vertx/enums/GatewayServiceModel.java | 38 + .../sf/vertx/enums/GatewayServiceType.java | 38 + .../java/com/sf/vertx/enums/MatchType.java | 49 ++ .../com/sf/vertx/enums/RequestMethod.java | 44 ++ zt-vertx/zt-vertx-service/Dockerfile | 8 + zt-vertx/zt-vertx-service/pom.xml | 242 ++++++ .../main/java/com/sf/vertx/VertxStart.java | 21 + .../config/FastJson2JsonRedisSerializer.java | 52 ++ .../java/com/sf/vertx/config/RedisConfig.java | 75 ++ .../com/sf/vertx/constans/RedisKeyConfig.java | 30 + .../com/sf/vertx/constans/SACConstants.java | 21 + .../vertx/controller/AppConfigController.java | 107 +++ .../sf/vertx/exception/HttpMockException.java | 39 + .../com/sf/vertx/exception/MockException.java | 39 + .../sf/vertx/exception/ServiceException.java | 39 + .../com/sf/vertx/handle/ApiMockHandler.java | 23 + .../sf/vertx/handle/ApiMockHandlerImpl.java | 32 + .../sf/vertx/handle/ApiRateLimitHandler.java | 30 + .../vertx/handle/ApiRateLimitHandlerImpl.java | 53 ++ .../com/sf/vertx/handle/AppConfigHandler.java | 718 ++++++++++++++++++ .../sf/vertx/handle/AppRateLimitHandler.java | 28 + .../vertx/handle/AppRateLimitHandlerImpl.java | 48 ++ .../java/com/sf/vertx/handle/BodyHandler.java | 178 +++++ .../com/sf/vertx/handle/BodyHandlerImpl.java | 389 ++++++++++ .../com/sf/vertx/handle/ConsulHandler.java | 111 +++ .../handle/OpenParameterCheckHandlerImpl.java | 52 ++ .../vertx/handle/ParameterCheckHandler.java | 33 + .../com/sf/vertx/handle/ProxyHandler.java | 27 + .../com/sf/vertx/handle/ProxyHandlerImpl.java | 35 + .../vertx/handle/RestfulFailureHandler.java | 12 + .../handle/RestfulFailureHandlerImpl.java | 68 ++ .../handle/SACParameterCheckHandlerImpl.java | 52 ++ .../com/sf/vertx/init/DynamicBuildServer.java | 68 ++ .../sf/vertx/init/GatewayConfigVerticle.java | 188 +++++ .../com/sf/vertx/init/GatewayRPCVerticle.java | 79 ++ .../java/com/sf/vertx/init/RedisManager.java | 62 ++ .../com/sf/vertx/init/RedisProperties.java | 26 + .../com/sf/vertx/init/SacVertxConfig.java | 46 ++ .../com/sf/vertx/init/VertxProperties.java | 26 + .../com/sf/vertx/pojo/ClusterEventMsg.java | 13 + .../com/sf/vertx/pojo/SacCurrentLimiting.java | 13 + .../java/com/sf/vertx/security/AesUtils.java | 84 ++ .../com/sf/vertx/security/Base64Utils.java | 31 + .../java/com/sf/vertx/security/HexUtils.java | 53 ++ .../com/sf/vertx/security/MainSecurity.java | 84 ++ .../com/sf/vertx/security/MessageDigest.java | 47 ++ .../java/com/sf/vertx/security/RSA2Utils.java | 276 +++++++ .../java/com/sf/vertx/security/RSAUtil.java | 154 ++++ .../com/sf/vertx/security/StringUtils.java | 53 ++ .../sf/vertx/service/AppConfigService.java | 13 + .../service/impl/AppConfigServiceImpl.java | 82 ++ .../java/com/sf/vertx/utils/AppUtils.java | 269 +++++++ .../main/java/com/sf/vertx/utils/Filter.java | 91 +++ .../java/com/sf/vertx/utils/ProxyTool.java | 82 ++ .../src/main/resources/META-INF/MANIFEST.MF | 2 + .../io.vertx/vertx-core/reflect-config.json | 38 + .../io.vertx.core.spi.launcher.CommandFactory | 18 + .../META-INF/vertx/vertx-version.txt | 1 + .../src/main/resources/application.yaml | 39 + .../src/main/resources/cacerts.jks | Bin 0 -> 1286 bytes .../src/main/resources/cluster.xml | 30 + .../main/resources/i18n/messages.properties | 38 + .../src/main/resources/keystore.jks | Bin 0 -> 2756 bytes .../src/main/resources/log4j2.xml | 123 +++ .../src/main/resources/server.cer | Bin 0 -> 896 bytes 89 files changed, 5679 insertions(+) create mode 100644 zt-vertx/pom.xml create mode 100644 zt-vertx/zt-vertx-api/pom.xml create mode 100644 zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/AddressRetryStrategy.java create mode 100644 zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/AdvancedConfig.java create mode 100644 zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/ApiConfig.java create mode 100644 zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/AppConfig.java create mode 100644 zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/DataSecurity.java create mode 100644 zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/EnvironmentConfig.java create mode 100644 zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/HttpClientOptionsConfig.java create mode 100644 zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/MockExpectation.java create mode 100644 zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/MockMatchCondition.java create mode 100644 zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/MockResponse.java create mode 100644 zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/Node.java create mode 100644 zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/RouteContent.java create mode 100644 zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/Router.java create mode 100644 zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/SacService.java create mode 100644 zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/ServerAddress.java create mode 100644 zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/Strategy.java create mode 100644 zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/VertxConfig.java create mode 100644 zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/VertxOptionsConfig.java create mode 100644 zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/arithmetic/roundRobin/SacLoadBalancing.java create mode 100644 zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/arithmetic/roundRobin/WeightedRoundRobin.java create mode 100644 zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/enums/GatewayError.java create mode 100644 zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/enums/GatewayRouteType.java create mode 100644 zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/enums/GatewayServiceModel.java create mode 100644 zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/enums/GatewayServiceType.java create mode 100644 zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/enums/MatchType.java create mode 100644 zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/enums/RequestMethod.java create mode 100644 zt-vertx/zt-vertx-service/Dockerfile create mode 100644 zt-vertx/zt-vertx-service/pom.xml create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/VertxStart.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/config/FastJson2JsonRedisSerializer.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/config/RedisConfig.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/constans/RedisKeyConfig.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/constans/SACConstants.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/controller/AppConfigController.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/exception/HttpMockException.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/exception/MockException.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/exception/ServiceException.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ApiMockHandler.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ApiMockHandlerImpl.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ApiRateLimitHandler.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ApiRateLimitHandlerImpl.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/AppConfigHandler.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/AppRateLimitHandler.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/AppRateLimitHandlerImpl.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/BodyHandler.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/BodyHandlerImpl.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ConsulHandler.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/OpenParameterCheckHandlerImpl.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ParameterCheckHandler.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ProxyHandler.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ProxyHandlerImpl.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/RestfulFailureHandler.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/RestfulFailureHandlerImpl.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/SACParameterCheckHandlerImpl.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/DynamicBuildServer.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/GatewayConfigVerticle.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/GatewayRPCVerticle.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/RedisManager.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/RedisProperties.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/SacVertxConfig.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/VertxProperties.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/pojo/ClusterEventMsg.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/pojo/SacCurrentLimiting.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/AesUtils.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/Base64Utils.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/HexUtils.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/MainSecurity.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/MessageDigest.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/RSA2Utils.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/RSAUtil.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/StringUtils.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/service/AppConfigService.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/service/impl/AppConfigServiceImpl.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/utils/AppUtils.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/utils/Filter.java create mode 100644 zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/utils/ProxyTool.java create mode 100644 zt-vertx/zt-vertx-service/src/main/resources/META-INF/MANIFEST.MF create mode 100644 zt-vertx/zt-vertx-service/src/main/resources/META-INF/native-image/io.vertx/vertx-core/reflect-config.json create mode 100644 zt-vertx/zt-vertx-service/src/main/resources/META-INF/services/io.vertx.core.spi.launcher.CommandFactory create mode 100644 zt-vertx/zt-vertx-service/src/main/resources/META-INF/vertx/vertx-version.txt create mode 100644 zt-vertx/zt-vertx-service/src/main/resources/application.yaml create mode 100644 zt-vertx/zt-vertx-service/src/main/resources/cacerts.jks create mode 100644 zt-vertx/zt-vertx-service/src/main/resources/cluster.xml create mode 100644 zt-vertx/zt-vertx-service/src/main/resources/i18n/messages.properties create mode 100644 zt-vertx/zt-vertx-service/src/main/resources/keystore.jks create mode 100644 zt-vertx/zt-vertx-service/src/main/resources/log4j2.xml create mode 100644 zt-vertx/zt-vertx-service/src/main/resources/server.cer diff --git a/zt-vertx/pom.xml b/zt-vertx/pom.xml new file mode 100644 index 0000000..bd2a4dd --- /dev/null +++ b/zt-vertx/pom.xml @@ -0,0 +1,71 @@ + + + 4.0.0 + + com.smarterFramework + zt-vertx + 1.0.0 + + zt-vertx + 中天vertx网关 + + + 17 + 1.0.0 + UTF-8 + UTF-8 + + 2.0.34 + 5.8.22 + 1.18.26 + + + + + + + + com.alibaba.fastjson2 + fastjson2 + ${fastjson.version} + + + com.smarterFramework + zt-vertx-api + ${zt-vertx.version} + + + cn.hutool + hutool-core + ${hutool.version} + + + org.projectlombok + lombok + ${lombok.version} + + + + + + zt-vertx-service + zt-vertx-api + + pom + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + + + \ No newline at end of file diff --git a/zt-vertx/zt-vertx-api/pom.xml b/zt-vertx/zt-vertx-api/pom.xml new file mode 100644 index 0000000..3c1308d --- /dev/null +++ b/zt-vertx/zt-vertx-api/pom.xml @@ -0,0 +1,41 @@ + + + + com.smarterFramework + zt-vertx + 1.0.0 + + 4.0.0 + jar + zt-vertx-api + + + gateway网关 + + + + + org.projectlombok + lombok + provided + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + \ No newline at end of file diff --git a/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/AddressRetryStrategy.java b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/AddressRetryStrategy.java new file mode 100644 index 0000000..30e3a53 --- /dev/null +++ b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/AddressRetryStrategy.java @@ -0,0 +1,13 @@ +package com.sf.vertx.api.pojo; + +import java.io.Serializable; + +import lombok.Data; + +@Data +public class AddressRetryStrategy implements Serializable { + private static final long serialVersionUID = -336914981251646244L; + private Integer threshold = 3; // 2, // 失败次数 + private Integer timeWindow = 20; //1, //时间窗口,单位s + private Integer periodicTime = 3; // vertx定时任务循环执行时间间隔 +} diff --git a/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/AdvancedConfig.java b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/AdvancedConfig.java new file mode 100644 index 0000000..b5d75a8 --- /dev/null +++ b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/AdvancedConfig.java @@ -0,0 +1,16 @@ +package com.sf.vertx.api.pojo; + +import java.io.Serializable; + +import lombok.Data; + +@Data +public class AdvancedConfig implements Serializable { + private static final long serialVersionUID = -6935223493505821401L; + private Integer retryStrategy; // 请求失败重试次数 + private Integer timeout; // 请求超时时间 + private String cacheConfig; // 响应数据的缓存策略 + private String zipConfig;// 响应压缩 + private String monitorCconfig;// 监控指标 + +} diff --git a/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/ApiConfig.java b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/ApiConfig.java new file mode 100644 index 0000000..3121eb4 --- /dev/null +++ b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/ApiConfig.java @@ -0,0 +1,19 @@ +package com.sf.vertx.api.pojo; + +import java.io.Serializable; +import java.util.List; + +import lombok.Data; + +@Data +public class ApiConfig implements Serializable { + private static final long serialVersionUID = 5774283776114726263L; + private String apiCode; + private String uri; + private String method; // 大写 + private Long timeout = 3000L; // 超时时间,单位毫秒 + private Integer mockDefaultHttpStatus; + private String mockDefaultResponse; + private List strategy; // 策略 + private List mockExpectations; // mock期望 +} diff --git a/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/AppConfig.java b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/AppConfig.java new file mode 100644 index 0000000..08dc8d3 --- /dev/null +++ b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/AppConfig.java @@ -0,0 +1,20 @@ +package com.sf.vertx.api.pojo; + +import java.io.Serializable; +import java.util.List; + +import lombok.Data; + +@Data +public class AppConfig implements Serializable { + private static final long serialVersionUID = 1518165296680157119L; + private String appCode; // 应用唯一码, app访问uri添加前缀,用于网关区分多应用 + private boolean exclusiveService; // 预留字段, 独立端口 + private Integer exclusiveGatewayCode; // 预留字段, 独享网关配置编号 + //private EnvironmentConfig environmentConfig; // 环境配置 + private List service; // 服务 + private DataSecurity dataSecurity; // 数据加解密 + private Strategy apiCurrentLimitingConfig; // 接口限流配置 + private Strategy appCurrentLimitingConfig; // APP限流配置 + private AdvancedConfig advancedConfig; // 高级配置 +} diff --git a/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/DataSecurity.java b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/DataSecurity.java new file mode 100644 index 0000000..7cef0ad --- /dev/null +++ b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/DataSecurity.java @@ -0,0 +1,13 @@ +package com.sf.vertx.api.pojo; + +import java.io.Serializable; + +import lombok.Data; + +@Data +public class DataSecurity implements Serializable { + private static final long serialVersionUID = 5034274428665340830L; + private String algorithm; // 加密算法(ECC、RSA 和国密(SM2)) + private String publicKey; // 公钥 + private String privateKey; // 私钥 (当加密算法为 ECC 或国密SM2时,填写私钥内容。当加密算法为 RSA 时,分别填写公私钥内容。) +} diff --git a/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/EnvironmentConfig.java b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/EnvironmentConfig.java new file mode 100644 index 0000000..c0e5e0b --- /dev/null +++ b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/EnvironmentConfig.java @@ -0,0 +1,13 @@ +package com.sf.vertx.api.pojo; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +import lombok.Data; + +@Data +public class EnvironmentConfig implements Serializable { + private static final long serialVersionUID = -3952046909425019869L; + +} diff --git a/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/HttpClientOptionsConfig.java b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/HttpClientOptionsConfig.java new file mode 100644 index 0000000..e16fac5 --- /dev/null +++ b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/HttpClientOptionsConfig.java @@ -0,0 +1,15 @@ +package com.sf.vertx.api.pojo; + +import java.io.Serializable; + +import lombok.Data; + +@Data +public class HttpClientOptionsConfig implements Serializable { + private static final long serialVersionUID = -6302301564759941097L; + private Integer maxPoolSize; + private Integer connectTimeout; + private Integer http2KeepAliveTimeout; + private Integer idleTimeout; + private Integer timeout; +} diff --git a/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/MockExpectation.java b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/MockExpectation.java new file mode 100644 index 0000000..c3d2169 --- /dev/null +++ b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/MockExpectation.java @@ -0,0 +1,28 @@ +package com.sf.vertx.api.pojo; + +import lombok.Data; + +import java.util.List; + +/** +* 网关服务-接口Mock期望 +*/ +@Data +public class MockExpectation { + + /** + * http状态码 + */ + private Integer httpStatus; + + /** + * Mock响应,JSON字符串 + */ + private String mockResponse; + + /** + * 匹配条件 + */ + private List matchConditions; + +} diff --git a/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/MockMatchCondition.java b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/MockMatchCondition.java new file mode 100644 index 0000000..be735f0 --- /dev/null +++ b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/MockMatchCondition.java @@ -0,0 +1,35 @@ +package com.sf.vertx.api.pojo; + +import lombok.Data; + +import java.util.List; + +/** +* 接口Mock匹配条件 +*/ +@Data +public class MockMatchCondition { + + /** + * 参数位置,header、query、body + */ + private String parameterPosition; + + /** + * 参数键值 + */ + private String parameterKey; + + /** + * 参数值,包含、不包含类型允许多个值,所以使用数组,其余类型都只有一个值。 + */ + private List parameterValue; + + /** + * 匹配类型 + */ + private String matchType; + + + +} diff --git a/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/MockResponse.java b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/MockResponse.java new file mode 100644 index 0000000..0ebf4b9 --- /dev/null +++ b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/MockResponse.java @@ -0,0 +1,27 @@ +package com.sf.vertx.api.pojo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** +* 网关服务-接口Mock +*/ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class MockResponse { + + /** + * http状态码 + */ + private Integer httpStatus; + + /** + * Mock响应,JSON字符串 + */ + private String mockResponse; + +} diff --git a/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/Node.java b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/Node.java new file mode 100644 index 0000000..664c7d9 --- /dev/null +++ b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/Node.java @@ -0,0 +1,101 @@ +package com.sf.vertx.api.pojo; + +import java.io.Serializable; + +import com.alibaba.fastjson2.annotation.JSONField; + +import lombok.Data; + +/** + * String ip:负载IP
+ * final Integer weight:权重,保存配置的权重
+ * Integer effectiveWeight:有效权重,轮询的过程权重可能变化
+ * Integer currentWeight:当前权重,比对该值大小获取节点
+ * 第一次加权轮询时:currentWeight = weight = effectiveWeight
+ * 后面每次加权轮询时:currentWeight 的值都会不断变化,其他权重不变
+ */ +public class Node implements Comparable, Serializable { + private static final long serialVersionUID = -2846988871213226377L; + private String ip; + private Integer port; + private Integer weight; + private Integer effectiveWeight; + private Integer currentWeight; + private String protocol; // 协议 + + public Node() { + } + + public Node(String ip, Integer weight) { + this.ip = ip; + this.weight = weight; + this.effectiveWeight = weight; + this.currentWeight = weight; + } + + public Node(String ip, Integer weight, Integer effectiveWeight, Integer currentWeight) { + this.ip = ip; + this.weight = weight; + this.effectiveWeight = effectiveWeight; + this.currentWeight = currentWeight; + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + public Integer getWeight() { + return weight; + } + + public void setWeight(Integer weight) { + this.weight = weight; + } + + public Integer getEffectiveWeight() { + return effectiveWeight; + } + + public void setEffectiveWeight(Integer effectiveWeight) { + this.effectiveWeight = effectiveWeight; + } + + public Integer getCurrentWeight() { + return currentWeight; + } + + public void setCurrentWeight(Integer currentWeight) { + this.currentWeight = currentWeight; + } + + @Override + public int compareTo(Node node) { + return currentWeight > node.currentWeight ? 1 : (currentWeight.equals(node.currentWeight) ? 0 : -1); + } + + @Override + public String toString() { + return "{ip='" + ip + "', weight=" + weight + ", effectiveWeight=" + effectiveWeight + ", currentWeight=" + + currentWeight + "}"; + } +} diff --git a/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/RouteContent.java b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/RouteContent.java new file mode 100644 index 0000000..6af99d8 --- /dev/null +++ b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/RouteContent.java @@ -0,0 +1,16 @@ +package com.sf.vertx.api.pojo; + +import java.io.Serializable; +import java.util.List; + +import lombok.Data; + +@Data +public class RouteContent implements Serializable { + private static final long serialVersionUID = -4405058529530680618L; + private ServerAddress serverAddress; // 服务地址 + private Integer weight; //权重值 + private String headerKey; //请求头 + private List headerValues; // ["v1","v2"], + private String matchType; // 匹配类型,EQ,IN +} diff --git a/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/Router.java b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/Router.java new file mode 100644 index 0000000..3c67134 --- /dev/null +++ b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/Router.java @@ -0,0 +1,14 @@ +package com.sf.vertx.api.pojo; + +import java.io.Serializable; +import java.util.List; + +import lombok.Data; + +@Data +public class Router implements Serializable { + private static final long serialVersionUID = -4471811880134025210L; + private String routeType; // 路由类型 WEIGHT_ROUTE ,HEADER_ROUTE + private List routeContent; // 路由的配置信息 + +} diff --git a/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/SacService.java b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/SacService.java new file mode 100644 index 0000000..2adfb26 --- /dev/null +++ b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/SacService.java @@ -0,0 +1,17 @@ +package com.sf.vertx.api.pojo; + +import java.io.Serializable; +import java.util.List; + +import lombok.Data; + +@Data +public class SacService implements Serializable { + private static final long serialVersionUID = -5171112142954536813L; + private String serviceName; // 服务名 + private String serviceType; // 服务类型,SAC=SAC规范服务,OPEN=开放服务 + private String serviceModel; // 模式, NORMAL, ROUTE + private ServerAddress serverAddress; // NORMAL模式的服务地址 + private List apiConfig; // request set header sacApiCode + private Router routeConfig; // 路由 +} diff --git a/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/ServerAddress.java b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/ServerAddress.java new file mode 100644 index 0000000..caddaec --- /dev/null +++ b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/ServerAddress.java @@ -0,0 +1,16 @@ +package com.sf.vertx.api.pojo; + +import java.io.Serializable; + +import lombok.Data; + +@Data +public class ServerAddress implements Serializable { + private static final long serialVersionUID = 7446602403871080553L; + private String address; + private String protocol; // "http" + private String host; + private int port; + private String path; // 前缀 + +} diff --git a/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/Strategy.java b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/Strategy.java new file mode 100644 index 0000000..adeaee5 --- /dev/null +++ b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/Strategy.java @@ -0,0 +1,27 @@ +package com.sf.vertx.api.pojo; + +import java.io.Serializable; + +import lombok.Data; + +/*** + * 策略 + * @author xy + * + */ +@Data +public class Strategy implements Serializable { + private static final long serialVersionUID = -8831406773224882471L; + // 限流熔断 + private String type;// CURRENT_LIMITING 限流策略, 熔断策略, CIRCUIT_BREAKER + private Integer threshold; // 2, // 限流阈值(APP总和) + private Integer timeWindow; //1, //时间窗口,单位s + private Integer recovery_interval; //熔断后的恢复时间间隔,单位s + private String defaultResponse; +// ": "{ +// \"msg\": \"接口繁忙请重试\", +// \"code\": 501, +// \"data\": \"到达限流阈值\", +// }" // 默认限流响应,JSON字符串。 + +} diff --git a/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/VertxConfig.java b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/VertxConfig.java new file mode 100644 index 0000000..f71aa38 --- /dev/null +++ b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/VertxConfig.java @@ -0,0 +1,18 @@ +package com.sf.vertx.api.pojo; + +import java.io.Serializable; + +import lombok.Data; + +@Data +public class VertxConfig implements Serializable { + private static final long serialVersionUID = -1706421732809219829L; + private Integer port; // 启动端口 + private String appCodeHeaderKey = "sacAppCode"; + private String apiCodeHeaderKey = "sacApiCode"; + private String rateLimitModel = "local"; // local,redis 负载均衡模式 + private VertxOptionsConfig vertxOptionsConfig; + private HttpClientOptionsConfig httpClientOptionsConfig; // 配置Vert端口连接池 + private AddressRetryStrategy addressRetryStrategy; + +} diff --git a/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/VertxOptionsConfig.java b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/VertxOptionsConfig.java new file mode 100644 index 0000000..dc9f92d --- /dev/null +++ b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/api/pojo/VertxOptionsConfig.java @@ -0,0 +1,27 @@ +package com.sf.vertx.api.pojo; + +import java.util.concurrent.TimeUnit; + +import lombok.Data; + +@Data +public class VertxOptionsConfig { + private int eventLoopPoolSize; + private int workerPoolSize ; + private int internalBlockingPoolSize; + private long blockedThreadCheckInterval; // 3000000000 打印thread wait日志 + private long maxEventLoopExecuteTime; + private long maxWorkerExecuteTime; + //private ClusterManager clusterManager; + private boolean haEnabled; + private int quorumSize; + private String haGroup; + private long warningExceptionTime; + private boolean preferNativeTransport; + private TimeUnit maxEventLoopExecuteTimeUnit; + private TimeUnit maxWorkerExecuteTimeUnit; + private TimeUnit warningExceptionTimeUnit; + private TimeUnit blockedThreadCheckIntervalUnit; + private boolean disableTCCL; + private Boolean useDaemonThread; +} diff --git a/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/arithmetic/roundRobin/SacLoadBalancing.java b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/arithmetic/roundRobin/SacLoadBalancing.java new file mode 100644 index 0000000..e8fe95e --- /dev/null +++ b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/arithmetic/roundRobin/SacLoadBalancing.java @@ -0,0 +1,7 @@ +package com.sf.vertx.arithmetic.roundRobin; + +import com.sf.vertx.api.pojo.Node; + +public interface SacLoadBalancing { + Node selectNode(); +} diff --git a/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/arithmetic/roundRobin/WeightedRoundRobin.java b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/arithmetic/roundRobin/WeightedRoundRobin.java new file mode 100644 index 0000000..8aa0ee5 --- /dev/null +++ b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/arithmetic/roundRobin/WeightedRoundRobin.java @@ -0,0 +1,108 @@ +package com.sf.vertx.arithmetic.roundRobin; + +import java.util.ArrayList; +import java.util.List; + +import com.sf.vertx.api.pojo.Node; + +/** + * 加权轮询算法 + * https://www.cnblogs.com/dennyLee2025/p/16128477.html + */ +public class WeightedRoundRobin implements SacLoadBalancing { + + private List nodes = new ArrayList<>(); + // 权重之和 + public Integer totalWeight = 0; + // 准备模拟数据 +// static { +// nodes.add(new Node("192.168.1.101", 1)); +// nodes.add(new Node("192.168.1.102", 3)); +// nodes.add(new Node("192.168.1.103", 2)); +// nodes.forEach(node -> totalWeight += node.getEffectiveWeight()); +// } + + public void init(List serverAddressList) { + nodes = serverAddressList; + nodes.forEach(node -> totalWeight += node.getEffectiveWeight()); + } + + /** + * 按照当前权重(currentWeight)最大值获取IP + * + * @return Node + */ + public Node selectNode() { + if (nodes == null || nodes.size() <= 0) + return null; + if (nodes.size() == 1) + return nodes.get(0); + + Node nodeOfMaxWeight = null; // 保存轮询选中的节点信息 +// 之前写错的代码 +// synchronized (nodes){ + synchronized (WeightedRoundRobin.class) { + // 打印信息对象:避免并发时打印出来的信息太乱,不利于观看结果 + StringBuffer sb = new StringBuffer(); + sb.append(Thread.currentThread().getName() + "==加权轮询--[当前权重]值的变化:" + printCurrentWeight(nodes)); + + // 选出当前权重最大的节点 + Node tempNodeOfMaxWeight = null; + for (Node node : nodes) { + if (tempNodeOfMaxWeight == null) + tempNodeOfMaxWeight = node; + else + tempNodeOfMaxWeight = tempNodeOfMaxWeight.compareTo(node) > 0 ? tempNodeOfMaxWeight : node; + } + // 必须new个新的节点实例来保存信息,否则引用指向同一个堆实例,后面的set操作将会修改节点信息 + nodeOfMaxWeight = new Node(tempNodeOfMaxWeight.getIp(), tempNodeOfMaxWeight.getWeight(), + tempNodeOfMaxWeight.getEffectiveWeight(), tempNodeOfMaxWeight.getCurrentWeight()); + nodeOfMaxWeight.setProtocol(tempNodeOfMaxWeight.getProtocol()); + nodeOfMaxWeight.setPort(tempNodeOfMaxWeight.getPort()); + // 调整当前权重比:按权重(effectiveWeight)的比例进行调整,确保请求分发合理。 + tempNodeOfMaxWeight.setCurrentWeight(tempNodeOfMaxWeight.getCurrentWeight() - totalWeight); + sb.append(" -> " + printCurrentWeight(nodes)); + + nodes.forEach(node -> node.setCurrentWeight(node.getCurrentWeight() + node.getEffectiveWeight())); + + sb.append(" -> " + printCurrentWeight(nodes)); + System.out.println(sb); // 打印权重变化过程 + } + return nodeOfMaxWeight; + } + + // 格式化打印信息 + private String printCurrentWeight(List nodes) { + StringBuffer stringBuffer = new StringBuffer("["); + nodes.forEach(node -> stringBuffer.append(node.getCurrentWeight() + ",")); + return stringBuffer.substring(0, stringBuffer.length() - 1) + "]"; + } + + // 并发测试:两个线程循环获取节点 + public static void main(String[] args) { + List serverAddressList = new ArrayList<>(); + Node node1 = new Node("192.168.1.101", 1); + serverAddressList.add(node1); + Node node2 = new Node("192.168.1.102", 3); + serverAddressList.add(node2); + + Node node3 = new Node("192.168.1.103", 2); + serverAddressList.add(node3); + Thread thread = new Thread(() -> { + WeightedRoundRobin weightedRoundRobin1 = new WeightedRoundRobin(); + weightedRoundRobin1.init(serverAddressList); + for (int i = 1; i <= weightedRoundRobin1.totalWeight; i++) { + Node node = weightedRoundRobin1.selectNode(); + System.out.println(Thread.currentThread().getName() + "==第" + i + "次轮询选中[当前权重最大]的节点:" + node + "\n"); + } + }); + thread.start(); + WeightedRoundRobin weightedRoundRobin2 = new WeightedRoundRobin(); + weightedRoundRobin2.init(serverAddressList); + for (int i = 1; i <= weightedRoundRobin2.totalWeight; i++) { + Node node = weightedRoundRobin2.selectNode(); + System.out.println(Thread.currentThread().getName() + "==第" + i + "次轮询选中[当前权重最大]的节点:" + node + "\n"); + } + + } +} \ No newline at end of file diff --git a/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/enums/GatewayError.java b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/enums/GatewayError.java new file mode 100644 index 0000000..5bc8914 --- /dev/null +++ b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/enums/GatewayError.java @@ -0,0 +1,119 @@ +package com.sf.vertx.enums; + +/** + * VertX网关错误 + * + * @author zoukun + */ +public enum GatewayError { + // CLIENT ERROR + BAD_REQUEST(400, "Bad Request", ErrorType.CLIENT_ERROR), + UNAUTHORIZED(401, "Unauthorized", ErrorType.CLIENT_ERROR), + FORBIDDEN(403, "Forbidden", ErrorType.CLIENT_ERROR), + NOT_FOUND(404, "Not Found", ErrorType.CLIENT_ERROR), + METHOD_NOT_ALLOWED(405, "Method Not Allowed", ErrorType.CLIENT_ERROR), + NOT_ACCEPTABLE(406, "Not Acceptable", ErrorType.CLIENT_ERROR), + PROXY_AUTHENTICATION_REQUIRED(407, "Proxy Authentication Required", ErrorType.CLIENT_ERROR), + REQUEST_TIMEOUT(408, "Request Timeout", ErrorType.CLIENT_ERROR), + CONFLICT(409, "Conflict", ErrorType.CLIENT_ERROR), + GONE(410, "Gone", ErrorType.CLIENT_ERROR), + LENGTH_REQUIRED(411, "Length Required", ErrorType.CLIENT_ERROR), + PRECONDITION_FAILED(412, "Precondition Failed", ErrorType.CLIENT_ERROR), + PAYLOAD_TOO_LARGE(413, "Payload Too Large", ErrorType.CLIENT_ERROR), + URI_TOO_LONG(414, "URI Too Long", ErrorType.CLIENT_ERROR), + UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type", ErrorType.CLIENT_ERROR), + REQUESTED_RANGE_NOT_SATISFIABLE(416, "Requested range not satisfiable", ErrorType.CLIENT_ERROR), + EXPECTATION_FAILED(417, "Expectation Failed", ErrorType.CLIENT_ERROR), + UNPROCESSABLE_ENTITY(422, "Unprocessable Entity", ErrorType.CLIENT_ERROR), + LOCKED(423, "Locked", ErrorType.CLIENT_ERROR), + FAILED_DEPENDENCY(424, "Failed Dependency", ErrorType.CLIENT_ERROR), + TOO_EARLY(425, "Too Early", ErrorType.CLIENT_ERROR), + UPGRADE_REQUIRED(426, "Upgrade Required", ErrorType.CLIENT_ERROR), + PRECONDITION_REQUIRED(428, "Precondition Required", ErrorType.CLIENT_ERROR), + TOO_MANY_REQUESTS(429, "Too Many Requests", ErrorType.CLIENT_ERROR), + REQUEST_HEADER_FIELDS_TOO_LARGE(431, "Request Header Fields Too Large", ErrorType.CLIENT_ERROR), + UNAVAILABLE_FOR_LEGAL_REASONS(451, "Unavailable For Legal Reasons", ErrorType.CLIENT_ERROR), + + // SERVER_ERROR + INTERNAL_SERVER_ERROR(500, "Internal Server Error", ErrorType.SERVER_ERROR), + NOT_IMPLEMENTED(501, "Not Implemented", ErrorType.SERVER_ERROR), + BAD_GATEWAY(502, "Bad Gateway", ErrorType.SERVER_ERROR), + SERVICE_UNAVAILABLE(503, "Service Unavailable", ErrorType.SERVER_ERROR), + GATEWAY_TIMEOUT(504, "Gateway Timeout", ErrorType.SERVER_ERROR), + HTTP_VERSION_NOT_SUPPORTED(505, "HTTP Version not supported", ErrorType.SERVER_ERROR), + INSUFFICIENT_STORAGE(507, "Insufficient Storage", ErrorType.SERVER_ERROR), + NOT_EXTENDED(510, "Not Extended", ErrorType.SERVER_ERROR), + NETWORK_AUTHENTICATION_REQUIRED(511, "Network Authentication Required", ErrorType.SERVER_ERROR), + + // SERVICE_ERROR + DEFAULT_SERVICE_ERROR(10000, "Service request failed, please try again later", ErrorType.SERVICE_ERROR), // 默认业务错误提示 + APP_ACCESS_PROHIBITED(10001, "应用禁止访问,请联系管理员", ErrorType.SERVICE_ERROR), + APP_SERVICE_NOT_FOUND(10002, "APP service not found", ErrorType.SERVICE_ERROR), + API_SERVICE_NOT_FOUND(10003, "API service not found", ErrorType.SERVICE_ERROR), + PARAMETER_TRANSFER_ERROR(10010, "参数传递错误", ErrorType.SERVICE_ERROR), + UNABLE_TO_FIND_ROUTING_ADDRESS(10011, "无法找到路由地址", ErrorType.SERVICE_ERROR), + UNABLE_TO_FIND_MATCHING_ENCRYPTION_ALGORITHM(10012, "无法找到匹配的加解密算法", ErrorType.SERVICE_ERROR), + UNABLE_TO_FIND_MATCHING_CIRCUIT_BREAKER_STRATEGY(10013, "无法找到匹配的熔断策略", ErrorType.SERVICE_ERROR), + REVERSE_PROXY_EXECUTION_ERROR(10014, "反向代理执行错误", ErrorType.SERVICE_ERROR), + REQUEST_URL_RESTRICTED_BY_FLOW(10015, "请求url被限流", ErrorType.SERVICE_ERROR), + REQUEST_URL_IS_BROKEN(10016, "请求url被熔断", ErrorType.SERVICE_ERROR), + APP_REQUEST_URL_RESTRICTED_BY_FLOW(10017, "应用请求url被限流", ErrorType.SERVICE_ERROR), + ; + + private final int code; + private final String reasonPhrase; + + private final ErrorType errorType; + + GatewayError(int code, String reasonPhrase, ErrorType errorType) { + this.code = code; + this.reasonPhrase = reasonPhrase; + this.errorType = errorType; + } + + public int getCode() { + return code; + } + + public ErrorType getErrorType() { + return errorType; + } + + public String getReasonPhrase() { + return reasonPhrase; + } + + public static GatewayError getByCode(int code) { + for (GatewayError value : GatewayError.values()) { + if (value.code == code) { + return value; + } + } + return null; + } + + /** + * 网关错误类型 + */ + public enum ErrorType { + + CLIENT_ERROR(4), + SERVER_ERROR(5), + /** + * 业务错误,建议业务错误返回HTTP状态码为200,业务码放在返回信息当中。 + */ + SERVICE_ERROR(10), + ; + + private final int value; + + ErrorType(int value) { + this.value = value; + } + + public int value() { + return this.value; + } + + } +} diff --git a/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/enums/GatewayRouteType.java b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/enums/GatewayRouteType.java new file mode 100644 index 0000000..cf39442 --- /dev/null +++ b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/enums/GatewayRouteType.java @@ -0,0 +1,38 @@ +package com.sf.vertx.enums; + + +/** + * 网关路由类型 + * + * @author zoukun + */ +public enum GatewayRouteType { + WEIGHT_ROUTE("WEIGHT_ROUTE", "权重路由策略"), + HEADER_ROUTE("HEADER_ROUTE", "请求头路由策略"), + ; + + private final String code; + private final String info; + + GatewayRouteType(String code, String info) { + this.code = code; + this.info = info; + } + + public String getCode() { + return code; + } + + public String getInfo() { + return info; + } + + public static GatewayRouteType getByCode(String code) { + for (GatewayRouteType value : GatewayRouteType.values()) { + if (value.code.equals(code)) { + return value; + } + } + return null; + } +} diff --git a/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/enums/GatewayServiceModel.java b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/enums/GatewayServiceModel.java new file mode 100644 index 0000000..473bad6 --- /dev/null +++ b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/enums/GatewayServiceModel.java @@ -0,0 +1,38 @@ +package com.sf.vertx.enums; + + +/** + * 网关服务模式 + * + * @author zoukun + */ +public enum GatewayServiceModel { + NORMAL("NORMAL", "普通模式"), + ROUTE("ROUTE", "路由模式"), + ; + + private final String code; + private final String info; + + GatewayServiceModel(String code, String info) { + this.code = code; + this.info = info; + } + + public String getCode() { + return code; + } + + public String getInfo() { + return info; + } + + public static GatewayServiceModel getByCode(String code) { + for (GatewayServiceModel value : GatewayServiceModel.values()) { + if (value.code.equals(code)) { + return value; + } + } + return null; + } +} diff --git a/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/enums/GatewayServiceType.java b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/enums/GatewayServiceType.java new file mode 100644 index 0000000..62dc635 --- /dev/null +++ b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/enums/GatewayServiceType.java @@ -0,0 +1,38 @@ +package com.sf.vertx.enums; + + +/** + * 网关服务类型 + * + * @author zoukun + */ +public enum GatewayServiceType { + SAC("SAC", "SAC规范服务"), + OPEN("OPEN", "开放服务"), + ; + + private final String code; + private final String info; + + GatewayServiceType(String code, String info) { + this.code = code; + this.info = info; + } + + public String getCode() { + return code; + } + + public String getInfo() { + return info; + } + + public static GatewayServiceType getByCode(String code) { + for (GatewayServiceType value : GatewayServiceType.values()) { + if (value.code.equals(code)) { + return value; + } + } + return null; + } +} diff --git a/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/enums/MatchType.java b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/enums/MatchType.java new file mode 100644 index 0000000..3b93cc7 --- /dev/null +++ b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/enums/MatchType.java @@ -0,0 +1,49 @@ +package com.sf.vertx.enums; + +/** + * 匹配方式 + * + * @author zoukun + */ +public enum MatchType +{ + EQ("EQ", "等于"), + NOT_EQ("NOT_EQ", "不等于"), + GT("GT", "大于"), + GE("GE", "大于或等于"), + LT("LT", "小于"), + LE("LE", "小于或等于"), + IN("IN", "包含"), + NOT_IN("NOT_IN", "不包含"), + IS_NULL("IS_NULL", "等于空"), + NOT_NULL("NOT_NULL", "不等于空"), + ; + + private final String code; + private final String info; + + MatchType(String code, String info) + { + this.code = code; + this.info = info; + } + + public String getCode() + { + return code; + } + + public String getInfo() + { + return info; + } + + public static MatchType getByCode(String code){ + for (MatchType value : MatchType.values()) { + if (value.code.equalsIgnoreCase(code)){ + return value; + } + } + return null; + } +} diff --git a/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/enums/RequestMethod.java b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/enums/RequestMethod.java new file mode 100644 index 0000000..af89e30 --- /dev/null +++ b/zt-vertx/zt-vertx-api/src/main/java/com/sf/vertx/enums/RequestMethod.java @@ -0,0 +1,44 @@ +package com.sf.vertx.enums; + +/** + * 支持的请求方法 + * + * @author zoukun + */ +public enum RequestMethod +{ + GET("GET", "GET"), + POST("POST", "POST"), + PUT("PUT", "PUT"), + DELETE("DELETE", "DELETE"), + HEAD("HEAD", "HEAD"), + ; + + private final String code; + private final String info; + + RequestMethod(String code, String info) + { + this.code = code; + this.info = info; + } + + public String getCode() + { + return code; + } + + public String getInfo() + { + return info; + } + + public static RequestMethod getByCode(String code){ + for (RequestMethod value : RequestMethod.values()) { + if (value.code.equalsIgnoreCase(code)){ + return value; + } + } + return null; + } +} diff --git a/zt-vertx/zt-vertx-service/Dockerfile b/zt-vertx/zt-vertx-service/Dockerfile new file mode 100644 index 0000000..802293b --- /dev/null +++ b/zt-vertx/zt-vertx-service/Dockerfile @@ -0,0 +1,8 @@ +FROM openjdk:17 +# 复制jar文件到路径 +COPY sf-vertx/target/sf-vertx.jar /usr/local/sf-vertx.jar +RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone +# 指定路径 +WORKDIR /usr/local + +ENTRYPOINT ["java","-jar","sf-vertx.jar"] \ No newline at end of file diff --git a/zt-vertx/zt-vertx-service/pom.xml b/zt-vertx/zt-vertx-service/pom.xml new file mode 100644 index 0000000..f5f5ff6 --- /dev/null +++ b/zt-vertx/zt-vertx-service/pom.xml @@ -0,0 +1,242 @@ + + + + com.smarterFramework + zt-vertx + 1.0.0 + + 4.0.0 + jar + zt-vertx-service + + + gateway网关 + + + 17 + UTF-8 + UTF-8 + 4.5.7 + 1.5.2 + 2.2.0 + 2.0.34 + 3.2.2 + 5.8.22 + 1.18.26 + 2.17.2 + 1.7.25 + + + + + + io.github.resilience4j + resilience4j-bom + ${resilience4j.version} + pom + import + + + io.vertx + vertx-stack-depchain + ${vertx.version} + pom + import + + + + + + + + io.vertx + vertx-core + + + io.vertx + vertx-web + + + io.vertx + vertx-web-proxy + + + io.vertx + vertx-web-client + + + + io.vertx + vertx-config-yaml + + + io.vertx + vertx-config + + + + io.vertx + vertx-redis-client + + + + com.smarterFramework + sf-vertx-api + 1.0.0 + + + cn.hutool + hutool-core + ${hutool.version} + + + + + io.vertx + vertx-codegen + true + + + + + + + + org.projectlombok + lombok + ${lombok.version} + provided + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + org.apache.logging.log4j + log4j-core + ${log4j.version} + compile + + + org.apache.logging.log4j + log4j-slf4j-impl + ${log4j.version} + compile + + + + io.github.resilience4j + resilience4j-ratelimiter + + + io.github.resilience4j + resilience4j-bulkhead + + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + ${project.artifactId} + + + \ No newline at end of file diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/VertxStart.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/VertxStart.java new file mode 100644 index 0000000..c166f36 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/VertxStart.java @@ -0,0 +1,21 @@ +package com.sf.vertx; + + +import com.sf.vertx.init.GatewayConfigVerticle; +import com.sf.vertx.init.GatewayRPCVerticle; +import io.vertx.core.Vertx; + +/** + * 启动程序 + * + * @author ztzh + */ +public class VertxStart { + + public static void main(String[] args) { + System.setProperty("vertx.logger-delegate-factory-class-name","io.vertx.core.logging.Log4j2LogDelegateFactory"); + Vertx vertx = Vertx.vertx(); + vertx.deployVerticle(GatewayConfigVerticle.class.getName()); + vertx.deployVerticle(GatewayRPCVerticle.class.getName()); + } +} diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/config/FastJson2JsonRedisSerializer.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/config/FastJson2JsonRedisSerializer.java new file mode 100644 index 0000000..3c1d997 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/config/FastJson2JsonRedisSerializer.java @@ -0,0 +1,52 @@ +/* +package com.sf.vertx.config; + +import java.nio.charset.Charset; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.SerializationException; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONReader; +import com.alibaba.fastjson2.JSONWriter; + +*/ +/** + * Redis使用FastJson序列化 + * + * @author ztzh + *//* + +public class FastJson2JsonRedisSerializer implements RedisSerializer +{ + public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + + private Class clazz; + + public FastJson2JsonRedisSerializer(Class clazz) + { + super(); + this.clazz = clazz; + } + + @Override + public byte[] serialize(T t) throws SerializationException + { + if (t == null) + { + return new byte[0]; + } + return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET); + } + + @Override + public T deserialize(byte[] bytes) throws SerializationException + { + if (bytes == null || bytes.length <= 0) + { + return null; + } + String str = new String(bytes, DEFAULT_CHARSET); + + return JSON.parseObject(str, clazz, JSONReader.Feature.SupportAutoType); + } +} +*/ diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/config/RedisConfig.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/config/RedisConfig.java new file mode 100644 index 0000000..1774c42 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/config/RedisConfig.java @@ -0,0 +1,75 @@ +/* +package com.sf.vertx.config; + +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +*/ +/** + * redis配置 + * + * @author ztzh + *//* + +@Configuration +@EnableCaching +public class RedisConfig extends CachingConfigurerSupport +{ + @Bean(name = "redisTemplate") + @SuppressWarnings(value = { "unchecked", "rawtypes" }) + public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) + { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + + FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); + + // 使用StringRedisSerializer来序列化和反序列化redis的key值 + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(serializer); + + // Hash的key也采用StringRedisSerializer的序列化方式 + template.setHashKeySerializer(new StringRedisSerializer()); + template.setHashValueSerializer(serializer); + + template.afterPropertiesSet(); + return template; + } + + @Bean + public DefaultRedisScript limitScript() + { + DefaultRedisScript redisScript = new DefaultRedisScript<>(); + redisScript.setScriptText(limitScriptText()); + redisScript.setResultType(Long.class); + return redisScript; + } + + */ +/** + * 限流脚本 + *//* + + private String limitScriptText() + { + return "local key = KEYS[1]\n" + + "local count = tonumber(ARGV[1])\n" + + "local time = tonumber(ARGV[2])\n" + + "local current = redis.call('get', key);\n" + + "if current and tonumber(current) > count then\n" + + " return tonumber(current);\n" + + "end\n" + + "current = redis.call('incr', key)\n" + + "if tonumber(current) == 1 then\n" + + " redis.call('expire', key, time)\n" + + "end\n" + + "return tonumber(current);"; + } +} +*/ diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/constans/RedisKeyConfig.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/constans/RedisKeyConfig.java new file mode 100644 index 0000000..ba5cb8d --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/constans/RedisKeyConfig.java @@ -0,0 +1,30 @@ +/* +package com.sf.vertx.constans; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class RedisKeyConfig { + + @Value("${server.vertx.environment}") + private String vertxEnvironment; + + public static final String BASE_REDIS_KEY = "vertx:config:"; + public static String APP_CONFIG_PREFIX_KEY = null; + public static String APP_CONFIG_SET_KEY = null; + public static String APP_CURRENT_LIMITING_CONFIG_KEY = null; + public static String VERTX_CONFIG_STRING_KEY = null; + public static String VERTX_ADDRESS_RETRY_STRATEGY_SET_KEY = null; + public static String VERTX_ADDRESS_RETRY_STRATEGY_KEY = null; + + public void init() { + APP_CONFIG_PREFIX_KEY = BASE_REDIS_KEY + vertxEnvironment; + APP_CONFIG_SET_KEY = APP_CONFIG_PREFIX_KEY + ":set"; + APP_CURRENT_LIMITING_CONFIG_KEY = APP_CONFIG_PREFIX_KEY + ":app:limiting"; + VERTX_CONFIG_STRING_KEY = APP_CONFIG_PREFIX_KEY + ":vertx"; + VERTX_ADDRESS_RETRY_STRATEGY_KEY = APP_CONFIG_PREFIX_KEY + ":addAddressRetryStrategy"; + VERTX_ADDRESS_RETRY_STRATEGY_SET_KEY = VERTX_ADDRESS_RETRY_STRATEGY_KEY + ":set"; + } +} +*/ diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/constans/SACConstants.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/constans/SACConstants.java new file mode 100644 index 0000000..4616207 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/constans/SACConstants.java @@ -0,0 +1,21 @@ +package com.sf.vertx.constans; + +public class SACConstants { + + public static final String CACHE_KEY_CONNECTOR = ":"; + + public static final String APP_CONFIG = "appConfig"; + + public static final String API_CONFIG = "apiConfig"; + + public static final String API_SERVICE_TYPE = "apiServiceType"; + + public static final String CIRCUIT_BREAKER = "CIRCUIT_BREAKER"; + + /** + * 业务码 key + */ + public static final String GATEWAY_SERVICE_CODE = "serviceCode"; + + +} diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/controller/AppConfigController.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/controller/AppConfigController.java new file mode 100644 index 0000000..d0bfddd --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/controller/AppConfigController.java @@ -0,0 +1,107 @@ +/* +package com.sf.vertx.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.alibaba.fastjson2.JSONObject; +import com.sf.vertx.api.pojo.AppConfig; +import com.sf.vertx.api.pojo.VertxConfig; +import com.sf.vertx.handle.AppConfigHandler; +import com.sf.vertx.service.AppConfigService; + +*/ +/*** + * 测试redis + * + * @author xy + * + *//* + +@RestController +@RequestMapping("/vertx") +public class AppConfigController { + @Autowired + private AppConfigService appConfigService; + + @PostMapping("/app/config") + public JSONObject addAppConfig(@RequestBody AppConfig appConfig) { + JSONObject json = new JSONObject(); + try { + appConfigService.saveAppConfig(appConfig); + json.put("code", 200); + json.put("msg", "success"); + } catch (Exception e) { + e.printStackTrace(); + json.put("code", 500); + json.put("msg", e.getMessage()); + } + return json; + } + + @DeleteMapping("/app/config") + public JSONObject deleteAppConfig(@RequestBody AppConfig appConfig) { + JSONObject json = new JSONObject(); + try { + appConfigService.deleteAppConfig(appConfig); + json.put("code", 200); + json.put("msg", "success"); + } catch (Exception e) { + e.printStackTrace(); + json.put("code", 500); + json.put("msg", e.getMessage()); + } + return json; + } + + @PostMapping("/app/disable/appCode") + public JSONObject addDisabledAppcode(@RequestBody AppConfig appConfig) { + JSONObject json = new JSONObject(); + try { + AppConfigHandler.addDisabledAppcode(appConfig.getAppCode()); + json.put("code", 200); + json.put("msg", "success"); + } catch (Exception e) { + e.printStackTrace(); + json.put("code", 500); + json.put("msg", e.getMessage()); + } + return json; + } + + @DeleteMapping("/app/disable/appCode") + public JSONObject removeDisabledAppcode(@RequestBody AppConfig appConfig) { + JSONObject json = new JSONObject(); + try { + AppConfigHandler.removeDisabledAppcode(appConfig.getAppCode()); + json.put("code", 200); + json.put("msg", "success"); + } catch (Exception e) { + e.printStackTrace(); + json.put("code", 500); + json.put("msg", e.getMessage()); + } + return json; + } + + @PostMapping("/vertx/config") + public JSONObject saveVertxConfig(@RequestBody VertxConfig vertxConfig) { + JSONObject json = new JSONObject(); + try { + appConfigService.saveVertxConfig(vertxConfig); + json.put("code", 200); + json.put("msg", "success"); + } catch (Exception e) { + e.printStackTrace(); + json.put("code", 500); + json.put("msg", e.getMessage()); + } + return json; + } + +} +*/ diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/exception/HttpMockException.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/exception/HttpMockException.java new file mode 100644 index 0000000..42d7af3 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/exception/HttpMockException.java @@ -0,0 +1,39 @@ +package com.sf.vertx.exception; + +import io.netty.handler.codec.http.HttpResponseStatus; + +public final class HttpMockException extends RuntimeException { + private static final long serialVersionUID = -6984329893540102440L; + private final int statusCode; + private final String payload; + + public HttpMockException() { + this(500, null, null); + } + + public HttpMockException(int statusCode) { + this(statusCode, null, null); + } + + public HttpMockException(int statusCode, Throwable cause) { + this(statusCode, null, cause); + } + + public HttpMockException(int statusCode, String payload) { + this(statusCode, payload, null); + } + + public HttpMockException(int statusCode, String payload, Throwable cause) { + super(HttpResponseStatus.valueOf(statusCode).reasonPhrase(), cause, false, false); + this.statusCode = statusCode; + this.payload = payload; + } + + public int getStatusCode() { + return statusCode; + } + + public String getPayload() { + return payload; + } + } diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/exception/MockException.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/exception/MockException.java new file mode 100644 index 0000000..f41ee90 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/exception/MockException.java @@ -0,0 +1,39 @@ +package com.sf.vertx.exception; + +import io.netty.handler.codec.http.HttpResponseStatus; + +public class MockException extends RuntimeException { + private static final long serialVersionUID = 7975954645547803571L; + private final int statusCode; + private final String payload; + + public MockException() { + this(500, null, null); + } + + public MockException(int statusCode) { + this(statusCode, null, null); + } + + public MockException(int statusCode, Throwable cause) { + this(statusCode, null, cause); + } + + public MockException(int statusCode, String payload) { + this(statusCode, payload, null); + } + + public MockException(int statusCode, String payload, Throwable cause) { + super(HttpResponseStatus.valueOf(statusCode).reasonPhrase(), cause, false, false); + this.statusCode = statusCode; + this.payload = payload; + } + + public int getStatusCode() { + return statusCode; + } + + public String getPayload() { + return payload; + } +} \ No newline at end of file diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/exception/ServiceException.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/exception/ServiceException.java new file mode 100644 index 0000000..e3d4db6 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/exception/ServiceException.java @@ -0,0 +1,39 @@ +package com.sf.vertx.exception; + +import com.sf.vertx.enums.GatewayError; +import io.netty.handler.codec.http.HttpResponseStatus; + +/** + * 业务异常 + */ +public class ServiceException extends RuntimeException { + private static final long serialVersionUID = 7975954645547803572L; + private final int statusCode; + private final String payload; + + public ServiceException() { + this(GatewayError.DEFAULT_SERVICE_ERROR); + } + + public ServiceException(GatewayError gatewayError, String payload) { + this(gatewayError.getCode(), payload, null); + } + + public ServiceException(GatewayError gatewayError) { + this(gatewayError.getCode(), gatewayError.getReasonPhrase(), null); + } + + private ServiceException(int statusCode, String payload, Throwable cause) { + super("(" + statusCode + ")" + payload, cause, false, false); + this.statusCode = statusCode; + this.payload = payload; + } + + public int getStatusCode() { + return statusCode; + } + + public String getPayload() { + return payload; + } +} \ No newline at end of file diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ApiMockHandler.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ApiMockHandler.java new file mode 100644 index 0000000..5eed117 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ApiMockHandler.java @@ -0,0 +1,23 @@ +/* +package com.sf.vertx.handle; + +import io.vertx.codegen.annotations.VertxGen; +import io.vertx.core.Handler; +import io.vertx.ext.web.RoutingContext; + +*/ +/*** + * 接口mock处理 + * + * @author zk + * + *//* + +@VertxGen +public interface ApiMockHandler extends Handler { + + static ApiMockHandler create() { + return new ApiMockHandlerImpl(); + } +} +*/ diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ApiMockHandlerImpl.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ApiMockHandlerImpl.java new file mode 100644 index 0000000..937c475 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ApiMockHandlerImpl.java @@ -0,0 +1,32 @@ +/* +package com.sf.vertx.handle; + +import com.sf.vertx.api.pojo.MockResponse; +import com.sf.vertx.enums.GatewayError; +import com.sf.vertx.exception.MockException; +import com.sf.vertx.exception.ServiceException; +import com.sf.vertx.utils.AppUtils; +import io.vertx.ext.web.RoutingContext; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class ApiMockHandlerImpl implements ApiMockHandler { + + @Override + public void handle(RoutingContext rc) { + try { + // mock + MockResponse mockResponse = AppUtils.mock(rc); + if (mockResponse != null) { + rc.fail(new MockException(mockResponse.getHttpStatus(), mockResponse.getMockResponse())); + return; + } + } catch (Exception e) { + log.error("ApiMockHandlerImpl:",e); + rc.fail(new ServiceException(GatewayError.DEFAULT_SERVICE_ERROR)); + return; + } + rc.next(); + } +} +*/ diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ApiRateLimitHandler.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ApiRateLimitHandler.java new file mode 100644 index 0000000..461867d --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ApiRateLimitHandler.java @@ -0,0 +1,30 @@ +/* +package com.sf.vertx.handle; + +import io.vertx.codegen.annotations.VertxGen; +import io.vertx.core.Handler; +import io.vertx.ext.web.RoutingContext; + +*/ +/*** + * 限流熔断, redis存储 + * @author xy + * + *//* + +@VertxGen +public interface ApiRateLimitHandler extends Handler { + + static ApiRateLimitHandler create(String instance) { + switch (instance) { + case "redis": + //RedisRateLimiter redisRateLimiter = new RedisRateLimiter(); + //return new RateLimitHandlerRedisImpl(redisRateLimiter); + default: + // 本地缓存 + return new ApiRateLimitHandlerImpl(); + } + + } +} +*/ diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ApiRateLimitHandlerImpl.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ApiRateLimitHandlerImpl.java new file mode 100644 index 0000000..5cfb154 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ApiRateLimitHandlerImpl.java @@ -0,0 +1,53 @@ +/* +package com.sf.vertx.handle; + +import com.sf.vertx.constans.RedisKeyConfig; +import com.sf.vertx.enums.GatewayError; +import com.sf.vertx.exception.ServiceException; +import com.sf.vertx.pojo.SacCurrentLimiting; + +import io.github.resilience4j.core.functions.CheckedRunnable; +import io.github.resilience4j.ratelimiter.RateLimiter; +import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.handler.HttpException; +import lombok.extern.slf4j.Slf4j; + +import static com.sf.vertx.constans.SACConstants.CACHE_KEY_CONNECTOR; + +*/ +/*** + * 内存存储 + * + * @author xy + * + *//* + +@Slf4j +public class ApiRateLimitHandlerImpl implements ApiRateLimitHandler { + + @Override + public void handle(RoutingContext rc) { + log.info("Enter ApiRateLimitHandlerImpl.handle()"); + String appCode = rc.request().headers().get(AppConfigHandler.getAppCodeHeaderKey()); + String apiCode = rc.request().headers().get(AppConfigHandler.getApiCodeHeaderKey()); + + SacCurrentLimiting currentLimiting = AppConfigHandler.getApiCurrentLimiting(appCode, apiCode); + + if(currentLimiting != null) { + String key = RedisKeyConfig.APP_CURRENT_LIMITING_CONFIG_KEY + CACHE_KEY_CONNECTOR + appCode + CACHE_KEY_CONNECTOR + apiCode + CACHE_KEY_CONNECTOR + rc.request().uri() + + CACHE_KEY_CONNECTOR + rc.request().method(); + RateLimiter rateLimiter = currentLimiting.getRegistry().rateLimiter(key); + CheckedRunnable restrictedCall = RateLimiter.decorateCheckedRunnable(rateLimiter, rc::next); + try { + restrictedCall.run(); + } catch (Throwable t) { + //t.printStackTrace(); + log.info("api ratelimit:{}", key); + rc.fail(new ServiceException(GatewayError.REQUEST_URL_RESTRICTED_BY_FLOW, currentLimiting.getStrategy().getDefaultResponse())); + } + } else { + rc.next(); + } + } +} +*/ diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/AppConfigHandler.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/AppConfigHandler.java new file mode 100644 index 0000000..c46ae1f --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/AppConfigHandler.java @@ -0,0 +1,718 @@ +/* +package com.sf.vertx.handle; + +import java.math.BigDecimal; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.NumberUtil; +import com.sf.vertx.api.pojo.*; +import com.sf.vertx.enums.GatewayServiceType; +import com.sf.vertx.enums.MatchType; +import com.sf.vertx.enums.RequestMethod; +import com.sf.vertx.security.MainSecurity; +import com.sf.vertx.utils.AppUtils; +import io.vertx.core.http.*; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.handler.CorsHandler; +import io.vertx.ext.web.handler.HttpException; +import io.vertx.httpproxy.*; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.redis.core.RedisTemplate; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.TypeReference; +import com.hazelcast.config.Config; +import com.hazelcast.config.JoinConfig; +import com.hazelcast.config.NetworkConfig; +import com.hazelcast.config.TcpIpConfig; +import com.sf.vertx.arithmetic.roundRobin.SacLoadBalancing; +import com.sf.vertx.constans.RedisKeyConfig; +import com.sf.vertx.init.SacVertxConfig; +import com.sf.vertx.pojo.ClusterEventMsg; +import com.sf.vertx.pojo.SacCurrentLimiting; +import com.sf.vertx.utils.ProxyTool; + +import cn.hutool.core.collection.ConcurrentHashSet; +import io.github.resilience4j.ratelimiter.RateLimiterConfig; +import io.github.resilience4j.ratelimiter.RateLimiterRegistry; +import io.vertx.circuitbreaker.CircuitBreaker; +import io.vertx.circuitbreaker.CircuitBreakerOptions; +import io.vertx.core.Future; +import io.vertx.core.Vertx; +import io.vertx.core.VertxOptions; +import io.vertx.core.net.JksOptions; +import io.vertx.core.spi.cluster.ClusterManager; +import io.vertx.ext.web.Route; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.client.WebClient; +import io.vertx.spi.cluster.hazelcast.HazelcastClusterManager; +import lombok.extern.slf4j.Slf4j; + +import static com.sf.vertx.constans.SACConstants.API_CONFIG; +import static com.sf.vertx.constans.SACConstants.APP_CONFIG; + + +*/ +/*** + * vertx配置维护 + * + * @author xy + * + *//* + +@Slf4j +public class AppConfigHandler { + private static VertxConfig VERTX_CONFIG = new VertxConfig(); + public static Vertx VERTX; + private static SacVertxConfig sacVertxConfig; + private static RedisTemplate redisTemplate; + public static CircuitBreaker CONNECTION_CIRCUIT_BREAKER; + // global cache app config + private static final ConcurrentHashMap CACHE_APP_CONFIG_MAP = new ConcurrentHashMap<>(); + // global api config appCode - RateLimiterRegistry + private static final ConcurrentHashMap GLOBAL_API_CURRENT_LIMITING_MAP = new ConcurrentHashMap<>(); + // global app config appCode - Strategy + private static final ConcurrentHashMap GLOBAL_APP_CURRENT_LIMITING_MAP = new ConcurrentHashMap<>(); + // appCode:apiCode:SacLoadBalancing + private static ConcurrentHashMap LOADBALANCING_MAP = new ConcurrentHashMap<>(); + // appCode:apiCode - ApiConfig + private static ConcurrentHashMap APICODE_CONFIG_MAP = new ConcurrentHashMap<>(); + // apiCode限流配置 appCode:apiCode - RateLimiterRegistry + private static ConcurrentHashMap APICODE_CONFIG_CURRENT_LIMITING_MAP = new ConcurrentHashMap<>(); + // 服务类型, apiCode限流配置 appCode:apiCode - 服务类型,SAC=SAC规范服务,OPEN=开放服务 + private static ConcurrentHashMap APICODE_CONFIG_SERVICE_TYPE_MAP = new ConcurrentHashMap<>(); + + // 负载均衡路由类型 appCode:apiCode - routerType + // 执行流程 routerType=
+ // 1、serviceNodel="NORMAL", serviceNodel="ROUTE" and RouteType = "WEIGHT_ROUTE" + //
+ // return LOADBALANCING_MAP + // 2、serviceNodel="ROUTE", RouteType = "HEADER_ROUTE"
+ // return APICODE_CONFIG_ROUTERCONENT_MAP + private static ConcurrentHashMap APICODE_CONFIG_ROUTERTYPE_MAP = new ConcurrentHashMap<>(); + + private static ConcurrentHashMap> APICODE_CONFIG_HEADER_ROUTERCONENT_MAP = new ConcurrentHashMap<>(); + // apiCode熔断配置 appCode:apiCode - CircuitBreaker + private static ConcurrentHashMap APICODE_CONFIG_CIRCUIT_BREAKER_MAP = new ConcurrentHashMap<>(); + + // apicode uri = * - appConfig + private static ConcurrentHashMap APICODE_APPCONFIG_MAP = new ConcurrentHashMap<>(); + + // 禁用appCode + private static ConcurrentHashSet DISABLED_APPCODE = new ConcurrentHashSet(); + + public static AppConfig getAppConfigByDomain(String domain) { + return APICODE_APPCONFIG_MAP.get(domain); + } + + public static Integer routerType(String key) { + return APICODE_CONFIG_ROUTERTYPE_MAP.get(key) != null ? APICODE_CONFIG_ROUTERTYPE_MAP.get(key) : 1; + } + + public static List routerHeaderConentList(String key) { + return APICODE_CONFIG_HEADER_ROUTERCONENT_MAP.get(key); + } + + public static Boolean isServiceTypeOpen(String key) { + return APICODE_CONFIG_SERVICE_TYPE_MAP.get(key) != null + && StringUtils.equals(APICODE_CONFIG_SERVICE_TYPE_MAP.get(key), "OPEN"); + } + + public static Boolean isServiceTypeSac(String key) { + return APICODE_CONFIG_SERVICE_TYPE_MAP.get(key) != null + && StringUtils.equals(APICODE_CONFIG_SERVICE_TYPE_MAP.get(key), "SAC"); + } + + public static String getServiceTypeOpen(String key) { + return APICODE_CONFIG_SERVICE_TYPE_MAP.get(key); + } + + public static String sacResponseHeaderKey() { + return sacVertxConfig.getSacResponseHeaderKey(); + } + + public static String rpcUri() { + return sacVertxConfig.getRpcUri(); + } + + public static void removeDisabledAppcode(String appCode) { + DISABLED_APPCODE.remove(appCode); + } + + public static void addDisabledAppcode(String appCode) { + DISABLED_APPCODE.add(appCode); + } + + public static boolean isDisabledAppcode(String appCode) { + return DISABLED_APPCODE.contains(appCode); + } + + public static SacCurrentLimiting getGlobalAppCurrentLimitingConfig(String appCode) { + return GLOBAL_APP_CURRENT_LIMITING_MAP.get(appCode); + } + + public static AppConfig getAppConfig(String appCode) { + return CACHE_APP_CONFIG_MAP.get(appCode); + } + + public static void init(RedisTemplate _redisTemplate, SacVertxConfig _sacVertxConfig) { + redisTemplate = _redisTemplate; + sacVertxConfig = _sacVertxConfig; + } + + public static boolean isDataSecurity(String appCode) { + return CACHE_APP_CONFIG_MAP.get(appCode) != null && CACHE_APP_CONFIG_MAP.get(appCode).getDataSecurity() != null; + } + + public static boolean isApiCodeCircuitBreaker(String key) { + return APICODE_CONFIG_CIRCUIT_BREAKER_MAP.get(key) != null; + } + + public static CircuitBreaker getApiCodeCircuitBreaker(String key) { + return APICODE_CONFIG_CIRCUIT_BREAKER_MAP.get(key); + } + + */ +/*** + * 是否解析, 走独立请求 + * + * @return + *//* + + public static boolean isAnalysisBody(String appCode, String apiCode, String contentType) { + String apiCodeCacheKey = appCode + ":" + apiCode; + // Mock解析body + if (APICODE_CONFIG_MAP.get(apiCodeCacheKey) != null && APICODE_CONFIG_MAP.get(apiCodeCacheKey).getMockDefaultHttpStatus() != null) { + return true; + } + ApiConfig apicodeConfig = AppConfigHandler.getApicodeConfig(apiCodeCacheKey); + + // SAC请求方式GET,DELETE,HEAD请求需要解析body,SAC请求方式参数统一使用body传递,可能需要二次处理 + if (isServiceTypeSac(apiCodeCacheKey)) { + RequestMethod requestMethod = RequestMethod.getByCode(apicodeConfig.getMethod()); + if (RequestMethod.GET.equals(requestMethod) + || RequestMethod.DELETE.equals(requestMethod) + || RequestMethod.HEAD.equals(requestMethod)) { + return true; + } + } + if (StringUtils.startsWith(contentType, "multipart")) { + return false; + } + String keyCircuitBreaker = apiCodeCacheKey + ":" + "CIRCUIT_BREAKER"; + CircuitBreaker circuitBreaker = AppConfigHandler.getApiCodeCircuitBreaker(keyCircuitBreaker); + boolean isDataSecurity = AppConfigHandler.isDataSecurity(appCode); + // 文件上传不走加解密 + return (isDataSecurity || circuitBreaker != null); + } + + */ +/*** + * 优先apicode配置限流、无法找到匹配全局限流 + * + * @param appCode + * @param apiCode + * @return + *//* + + public static SacCurrentLimiting getApiCurrentLimiting(String appCode, String apiCode) { + String key = appCode + ":" + apiCode; + SacCurrentLimiting sacCurrentLimiting = APICODE_CONFIG_CURRENT_LIMITING_MAP.get(key) != null + ? APICODE_CONFIG_CURRENT_LIMITING_MAP.get(key) + : null; + sacCurrentLimiting = sacCurrentLimiting != null ? sacCurrentLimiting + : (GLOBAL_API_CURRENT_LIMITING_MAP.get(appCode) != null ? GLOBAL_API_CURRENT_LIMITING_MAP.get(appCode) + : null); + return sacCurrentLimiting; + } + + public static VertxConfig getVertxConfig() { + return VERTX_CONFIG; + } + + public static String getAppCodeHeaderKey() { + return VERTX_CONFIG.getAppCodeHeaderKey(); + } + + public static String getApiCodeHeaderKey() { + return VERTX_CONFIG.getApiCodeHeaderKey(); + } + + public static SacLoadBalancing getLoadBalancing(String key) { + return LOADBALANCING_MAP.get(key); + } + + public static ApiConfig getApicodeConfig(String key) { + return APICODE_CONFIG_MAP.get(key); + } + + public static Long getApicodeConfigTimeOut(String key) { + return APICODE_CONFIG_MAP.get(key) != null ? APICODE_CONFIG_MAP.get(key).getTimeout() : 3000L; + } + + + private static RateLimiterRegistry createRateLimiter(Strategy strategy) { + RateLimiterConfig config = RateLimiterConfig.custom() + .limitRefreshPeriod(Duration.ofSeconds(strategy.getTimeWindow())) + .limitForPeriod(strategy.getThreshold()).timeoutDuration(Duration.ofMillis(0)).build(); + RateLimiterRegistry registry = RateLimiterRegistry.of(config); + return registry; + } + + private static void initRateLimiter(String appCode, Strategy strategy, + ConcurrentHashMap map) { + RateLimiterRegistry registry = createRateLimiter(strategy); + SacCurrentLimiting sacCurrentLimiting = new SacCurrentLimiting(); + sacCurrentLimiting.setStrategy(strategy); + sacCurrentLimiting.setRegistry(registry); + map.put(appCode, sacCurrentLimiting); + } + + */ +/*** + * 从redis加载数据 + * + * @throws Exception + *//* + + public static void initAllAppConfig() { + Set set = redisTemplate.opsForZSet().range(RedisKeyConfig.APP_CONFIG_SET_KEY, 0, -1); + for (String appCode : set) { + AppConfigHandler.initAppConfig(appCode, false); + } + } + + */ +/*** + * 加载vertx配置 + *//* + + public static void initVertxConfig() { + String vertxConfigKey = RedisKeyConfig.VERTX_CONFIG_STRING_KEY; + String vertxConfigValue = redisTemplate.opsForValue().get(vertxConfigKey); + if (StringUtils.isNotBlank(vertxConfigValue)) { + VERTX_CONFIG = JSONObject.parseObject(vertxConfigValue, VertxConfig.class); + } + } + + private static void delAppConfigCache(String appCode) { + AppConfig appConfig = CACHE_APP_CONFIG_MAP.get(appCode); + if (appConfig != null) { + // app、api默认限流 + GLOBAL_API_CURRENT_LIMITING_MAP.remove(appCode); + GLOBAL_APP_CURRENT_LIMITING_MAP.remove(appCode); + for (SacService sacService : appConfig.getService()) { + if (sacService.getApiConfig() != null && sacService.getApiConfig().size() > 0) { + for (ApiConfig apiConfig : sacService.getApiConfig()) { + String key = appCode + ":" + apiConfig.getApiCode(); + APICODE_CONFIG_MAP.remove(key); + LOADBALANCING_MAP.remove(key); + APICODE_CONFIG_SERVICE_TYPE_MAP.remove(key); + APICODE_CONFIG_CURRENT_LIMITING_MAP.remove(key); + APICODE_APPCONFIG_MAP.remove(apiConfig.getApiCode()); + String keyCircuitBreaker = key + ":" + "CIRCUIT_BREAKER"; + CircuitBreaker circuitBreaker = APICODE_CONFIG_CIRCUIT_BREAKER_MAP.get(keyCircuitBreaker); + if (circuitBreaker != null) { + circuitBreaker.close(); + APICODE_CONFIG_CIRCUIT_BREAKER_MAP.remove(keyCircuitBreaker); + } + } + } + } + // 应用配置 + CACHE_APP_CONFIG_MAP.remove(appCode); + } + } + + public static void initAppConfig(String appCode, boolean isDelLocalCache) { + // 是否需要先删除 + if (isDelLocalCache) { + delAppConfigCache(appCode); + } + + String appCodeKey = RedisKeyConfig.APP_CONFIG_PREFIX_KEY + ":" + appCode; + String appCodeValue = redisTemplate.opsForValue().get(appCodeKey); + if (StringUtils.isNotBlank(appCodeValue)) { + AppConfig appConfig = JSON.parseObject(appCodeValue, new TypeReference() { + }); + CACHE_APP_CONFIG_MAP.put(appCode, appConfig); + + // app、api默认限流 + if (appConfig.getApiCurrentLimitingConfig() != null) { + initRateLimiter(appCode, appConfig.getApiCurrentLimitingConfig(), GLOBAL_API_CURRENT_LIMITING_MAP); + } + + if (appConfig.getAppCurrentLimitingConfig() != null) { + initRateLimiter(appCode, appConfig.getAppCurrentLimitingConfig(), GLOBAL_APP_CURRENT_LIMITING_MAP); + } + + // app router负载均衡 + List routeContentList = null; + for (SacService sacService : appConfig.getService()) { + int routerType = 1; + List nodeList = new ArrayList<>(); + // 获取service模式 + if (StringUtils.equals(sacService.getServiceModel(), "NORMAL")) { + Node node = new Node(); + node.setIp(sacService.getServerAddress().getHost()); + node.setPort(sacService.getServerAddress().getPort()); + node.setWeight(0); + node.setProtocol(sacService.getServerAddress().getProtocol()); + nodeList.add(node); + } else if (StringUtils.equals(sacService.getServiceModel(), "ROUTE")) { + if (sacService.getRouteConfig() != null + && StringUtils.equals(sacService.getRouteConfig().getRouteType(), "WEIGHT_ROUTE")) { + for (RouteContent routeContent : sacService.getRouteConfig().getRouteContent()) { + Node node = new Node(); + node.setIp(routeContent.getServerAddress().getHost()); + node.setPort(routeContent.getServerAddress().getPort()); + node.setWeight(routeContent.getWeight() != null && routeContent.getWeight() > 0 + ? routeContent.getWeight() + : 1); + node.setProtocol(routeContent.getServerAddress().getProtocol()); + nodeList.add(node); + } + } else if (sacService.getRouteConfig() != null + && StringUtils.equals(sacService.getRouteConfig().getRouteType(), "HEADER_ROUTE")) { + routerType = 2; + routeContentList = sacService.getRouteConfig().getRouteContent(); + } + } + + // 初始化apiConfig + if (sacService.getApiConfig() != null && sacService.getApiConfig().size() > 0) { + for (ApiConfig apiConfig : sacService.getApiConfig()) { + String key = appCode + ":" + apiConfig.getApiCode(); + APICODE_CONFIG_MAP.put(key, apiConfig); + if (sacService.getServiceType() != null) { + APICODE_CONFIG_SERVICE_TYPE_MAP.put(key, sacService.getServiceType()); + } + + // OPEN模式, 域名映射 + if (isServiceTypeOpen(key) && StringUtils.equals(apiConfig.getUri(), "*")) { + APICODE_APPCONFIG_MAP.put(apiConfig.getApiCode(), appConfig); + } + + // 负载均衡模式 + APICODE_CONFIG_ROUTERTYPE_MAP.put(key, routerType); + switch (routerType) { + case 1: + if (nodeList.size() > 0) { + // 初始化负载均衡算法 + SacLoadBalancing sacLoadBalancing = ProxyTool.roundRobin(nodeList); + LOADBALANCING_MAP.put(key, sacLoadBalancing); + } + break; + case 2: + APICODE_CONFIG_HEADER_ROUTERCONENT_MAP.put(key, routeContentList); + default: + break; + } + + if (apiConfig.getStrategy() != null && apiConfig.getStrategy().size() > 0) { + for (Strategy strategy : apiConfig.getStrategy()) { + if (StringUtils.equals(strategy.getType(), "CURRENT_LIMITING")) { + RateLimiterRegistry registry = createRateLimiter(strategy); + SacCurrentLimiting sacCurrentLimiting = new SacCurrentLimiting(); + sacCurrentLimiting.setStrategy(strategy); + sacCurrentLimiting.setRegistry(registry); + APICODE_CONFIG_CURRENT_LIMITING_MAP.put(key, sacCurrentLimiting); + } else if (StringUtils.equals(strategy.getType(), "CIRCUIT_BREAKER")) { + String keyCircuitBreaker = key + ":" + "CIRCUIT_BREAKER"; +// interfaceBreaker = CircuitBreaker.create("interfaceBreaker", VERTX, +// new CircuitBreakerOptions().setMaxFailures(3).setMaxRetries(5).setTimeout(2000) +// .setFallbackOnFailure(true) +// ).openHandler(v -> { +// log.info("Circuit opened"); +// }).closeHandler(v -> { +// log.info("Circuit closed"); +// });//.retryPolicy(retryCount -> retryCount * 100L); + + // apiCode熔断 + CircuitBreaker circuitBreaker = CircuitBreaker + .create(keyCircuitBreaker + "-circuit-breaker", VERTX, + new CircuitBreakerOptions().setMaxFailures(strategy.getThreshold()) // 最大失败数 + .setFailuresRollingWindow(strategy.getTimeWindow() * 1000) // 毫秒 + //.setTimeout(apiConfig.getTimeout()) // 超时时间,不要开启, 配置会设置接口超时, 这个参数有bug,半开超时会卡死 + .setFallbackOnFailure(true) // 失败后是否调用回退函数(fallback) + .setResetTimeout(strategy.getRecovery_interval() * 1000) // 在开启状态下,尝试重试之前所需时间 + ).openHandler(v -> { + log.info(keyCircuitBreaker + " Circuit open"); + }).halfOpenHandler(v -> { + log.info(keyCircuitBreaker + "Circuit halfOpen"); + }).closeHandler(v -> { + log.info(keyCircuitBreaker + "Circuit close"); + }); + APICODE_CONFIG_CIRCUIT_BREAKER_MAP.put(keyCircuitBreaker, circuitBreaker); + } + } + } + + } + } + + } + } + } + + public static void createVertx() { + // TODO 编解码线程池,后面优化协程等方式 + VertxOptions vertxOptions = new VertxOptions(); + loadVertxOptions(vertxOptions); + VERTX = Vertx.vertx(vertxOptions); + + initConnectionCircuitBreaker(); + createVertxRouter(); + consumerClusterEventMsg(); + } + + private static Config hazelcastConfig(SacVertxConfig sacVertxConfig) { + // 集群 + Config hazelcastConfig = new Config(); + hazelcastConfig.setClusterName(sacVertxConfig.getClusterName()); // 集群名字 + NetworkConfig networkConfig = new NetworkConfig(); + networkConfig.setPort(sacVertxConfig.getNetworkPort()); + networkConfig.setPortAutoIncrement(sacVertxConfig.isPortAutoIncrement()); + + JoinConfig join = new JoinConfig(); + TcpIpConfig tcpIpConfig = new TcpIpConfig(); + tcpIpConfig.setEnabled(true); + String[] clusterIps = sacVertxConfig.getClusterIp().split(","); + List members = Arrays.asList(clusterIps); + tcpIpConfig.setMembers(members); + join.setTcpIpConfig(tcpIpConfig); + networkConfig.setJoin(join); + hazelcastConfig.setNetworkConfig(networkConfig); + + // TODO 还有问题,不会使用 +// ManagementCenterConfig managementCenterConfig = new ManagementCenterConfig(); +// Set interfaces = new HashSet<>(); +// interfaces.add("http://192.168.1.68:8080/mancenter"); +// managementCenterConfig.setTrustedInterfaces(interfaces); +// hazelcastConfig.setManagementCenterConfig(managementCenterConfig); + return hazelcastConfig; + } + + public static void createHazelcastClusterVertx() { + Config hazelcastConfig = hazelcastConfig(sacVertxConfig); + ClusterManager hazelcastClusterManager = new HazelcastClusterManager(hazelcastConfig); + // TODO 编解码线程池,后面优化协程等方式 + VertxOptions vertxOptions = new VertxOptions(); + loadVertxOptions(vertxOptions); + vertxOptions.setClusterManager(hazelcastClusterManager); + Vertx.clusteredVertx(vertxOptions, res -> { + if (res.succeeded()) { + VERTX = res.result(); + log.info("hazelcastClusterManager create success"); + initConnectionCircuitBreaker(); + createVertxRouter(); + consumerClusterEventMsg(); + } else { + res.cause().printStackTrace(); + log.info("hazelcastClusterManager create failure"); + } + }); + } + + private static void consumerClusterEventMsg() { + // 订阅消息 + VERTX.eventBus().consumer("sac_cluster_event", message -> { + if (message.body() != null) { + ClusterEventMsg msg = JSONObject.parseObject(message.body().toString(), ClusterEventMsg.class); + log.info("Received message: {}", msg); + // message.reply("我是返回数据===" + message.body()); + if (msg.getType() == 1) { + if (msg.getOperation() == 1) { + // 初始化AppConfig本地缓存 + AppConfigHandler.initAppConfig(msg.getAppCode(), true); + } else if (msg.getOperation() == 3) { + // 删除本地缓存 + delAppConfigCache(msg.getAppCode()); + } else if (msg.getOperation() == 4) { + // 禁用app + addDisabledAppcode(msg.getAppCode()); + } else if (msg.getOperation() == 5) { + // 启用app + removeDisabledAppcode(msg.getAppCode()); + } + } + } + }); + } + + */ +/*** + * 发布消息,订阅消息 + * + * @param msg + *//* + + public static void publishClusterEventMsg(ClusterEventMsg msg) { + VERTX.eventBus().publish("sac_cluster_event", JSONObject.toJSONString(msg)); + } + + private static void createVertxRouter() { + // consul初始化 + // ConsulHandler.init(vertx); + + // 从redis同步app配置 + initAllAppConfig(); + + VertxConfig vertxConfig = AppConfigHandler.getVertxConfig(); + // 创建HTTP监听 + // 所有ip都能访问 + HttpServerOptions httpServerOptions = new HttpServerOptions().setHost("0.0.0.0"); + if (sacVertxConfig.isSSLs()) { + httpServerOptions.setSsl(true) + .setKeyStoreOptions(new JksOptions().setPassword("changeit").setPath("keystore.jks")); + } + HttpServer server = VERTX.createHttpServer(httpServerOptions); + Router mainHttpRouter = Router.router(VERTX); + Integer serverPort = vertxConfig.getPort() == null ? sacVertxConfig.getPort() : vertxConfig.getPort(); + log.info("serverPort:{}", serverPort); + server.requestHandler(mainHttpRouter).listen(serverPort, h -> { + if (h.succeeded()) { + log.info("HTTP端口监听成功:{}", serverPort); + } else { + log.error("HTTP端口监听失败:{}", serverPort); + } + }); + +// HttpClientOptions clientOptions = new HttpClientOptions(); +// clientOptions.setMaxPoolSize(20); // 最大连接池大小 +// clientOptions.setConnectTimeout(2000); // 连接超时 毫秒 +// clientOptions.setHttp2KeepAliveTimeout(1); +// clientOptions.setIdleTimeout(1000); // 连接空闲超时 毫秒 +// HttpClient proxyClient = VERTX.createHttpClient(clientOptions); + HttpClient proxyClient = VERTX.createHttpClient(); + HttpProxy proxy = HttpProxy.reverseProxy(proxyClient); + proxy.originSelector(request -> Future.succeededFuture(ProxyTool.resolveOriginAddress(request))); + proxy.addInterceptor(new ProxyInterceptor() { + @Override + public Future handleProxyRequest(ProxyContext context) { +// if(StringUtils.equals(sacAppHeaderKey, "dsafdsfadafhappC")) { +// // 会跳转到 RestfulFailureHandlerImpl +// throw new HttpException(10003); +// } + String appCode = context.request().headers().get(getAppCodeHeaderKey()); + String apiCode = context.request().headers().get(getApiCodeHeaderKey()); + String key = appCode + ":" + apiCode; + if (isServiceTypeSac(key)) { + String uri = APICODE_CONFIG_MAP.get(key).getUri(); + String method = APICODE_CONFIG_MAP.get(key).getMethod(); + context.request().setURI(uri).setMethod(HttpMethod.valueOf(method)); + } + return context.sendRequest(); + } + + @Override + public Future handleProxyResponse(ProxyContext context) { + // 调试代码,获取reponse body +// Filter filter = new Filter(); +// ProxyResponse proxyResponse = context.response(); +// Body body = proxyResponse.getBody(); +// proxyResponse.setBody(Body.body(filter.init(context.request().getURI(), body.stream(), false))); + // 继续拦截链 + return context.sendResponse(); + } + }); + WebClient mainWebClient = WebClient.create(VERTX); + String rateLimitModel = vertxConfig.getRateLimitModel(); + Route routeSac = mainHttpRouter.post(rpcUri()); + routeSac.handler(CorsHandler.create().addRelativeOrigin(".*")) + .handler(ParameterCheckHandler.create(GatewayServiceType.SAC)) + .handler(AppRateLimitHandler.create(rateLimitModel)) + .handler(ApiRateLimitHandler.create(rateLimitModel)) + .handler(BodyHandler.create().setHandleFileUploads(false)) + .handler(ApiMockHandler.create()) + .handler(ProxyHandler.create(mainWebClient, proxy)) + .failureHandler(RestfulFailureHandler.create()); +// mainHttpRouter.route().handler(ProxyHandler.create(mainWebClient, proxy)); + + Route routeOpen = mainHttpRouter.route(); + routeOpen.handler(CorsHandler.create().addRelativeOrigin(".*")) + .handler(ParameterCheckHandler.create(GatewayServiceType.OPEN)) + .handler(AppRateLimitHandler.create(rateLimitModel)) + .handler(ApiRateLimitHandler.create(rateLimitModel)) + .handler(BodyHandler.create().setHandleFileUploads(false)) + .handler(ApiMockHandler.create()) + .handler(ProxyHandler.create(mainWebClient, proxy)) + .failureHandler(RestfulFailureHandler.create()); + } + + */ +/*** + * 初始化connection Breaker + *//* + + private static void initConnectionCircuitBreaker() { + CONNECTION_CIRCUIT_BREAKER = CircuitBreaker.create("connectionCircuitBreaker-circuit-breaker", VERTX, + new CircuitBreakerOptions().setMaxFailures(3) // 最大失败数 + .setTimeout(2000) // 超时时间 + .setFallbackOnFailure(true) // 失败后是否调用回退函数(fallback) + .setResetTimeout(10000) // 在开启状态下,尝试重试之前所需时间 + ).openHandler(v -> { + log.info("connectionCircuitBreaker Circuit open"); + }).halfOpenHandler(v -> { + log.info("connectionCircuitBreaker Circuit halfOpen"); + }).closeHandler(v -> { + log.info("connectionCircuitBreaker Circuit close"); + }); + } + + private static void loadVertxOptions(VertxOptions vertxOptions) { + long blockedThreadCheckInterval = VERTX_CONFIG.getVertxOptionsConfig() == null ? -1 + : VERTX_CONFIG.getVertxOptionsConfig().getBlockedThreadCheckInterval(); + int workerPoolSize = VERTX_CONFIG == null || VERTX_CONFIG.getVertxOptionsConfig() == null ? -1 + : VERTX_CONFIG.getVertxOptionsConfig().getWorkerPoolSize(); + if (workerPoolSize != -1) { + vertxOptions.setWorkerPoolSize(workerPoolSize); + } + + // TODO + blockedThreadCheckInterval = 1000000L; + if (blockedThreadCheckInterval != -1) { + vertxOptions.setBlockedThreadCheckInterval(blockedThreadCheckInterval); // 不打印Thread blocked 阻塞日志 + } + } + + + public static String bodyEncrypt(String body, String appCode) { + DataSecurity dataSecurity = getAppConfig(appCode).getDataSecurity(); + switch (dataSecurity.getAlgorithm()) { + case "AES": + return MainSecurity.aesEncrypt(body, dataSecurity.getPrivateKey()); + case "RSA": + return MainSecurity.rsaEncrypt(body, dataSecurity.getPublicKey()); + default: + break; + } + log.info(" appCode:{}, encrypt key config is error.", appCode); + throw new HttpException(10011); + } + + public static String bodyDecrypt(String body, String appCode) { + DataSecurity dataSecurity = getAppConfig(appCode).getDataSecurity(); + switch (dataSecurity.getAlgorithm()) { + case "AES": + return MainSecurity.aesDecrypt(body, dataSecurity.getPrivateKey()); + case "RSA": + return MainSecurity.rsaDecrypt(body, dataSecurity.getPrivateKey()); + default: + break; + } + log.info(" appCode:{}, decrypt key config is error.", appCode); + throw new HttpException(10011); + } + + +} +*/ diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/AppRateLimitHandler.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/AppRateLimitHandler.java new file mode 100644 index 0000000..c41f24d --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/AppRateLimitHandler.java @@ -0,0 +1,28 @@ +/* +package com.sf.vertx.handle; + +import io.vertx.codegen.annotations.VertxGen; +import io.vertx.core.Handler; +import io.vertx.ext.web.RoutingContext; + +*/ +/*** + * 限流熔断, redis存储 + * @author xy + * + *//* + +@VertxGen +public interface AppRateLimitHandler extends Handler { + + static AppRateLimitHandler create(String instance) { + switch (instance) { + case "redis": + default: + // 本地缓存 + return new AppRateLimitHandlerImpl(); + } + + } +} +*/ diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/AppRateLimitHandlerImpl.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/AppRateLimitHandlerImpl.java new file mode 100644 index 0000000..4487e53 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/AppRateLimitHandlerImpl.java @@ -0,0 +1,48 @@ +/* +package com.sf.vertx.handle; + +import com.sf.vertx.constans.RedisKeyConfig; +import com.sf.vertx.enums.GatewayError; +import com.sf.vertx.exception.ServiceException; +import com.sf.vertx.pojo.SacCurrentLimiting; + +import io.github.resilience4j.core.functions.CheckedRunnable; +import io.github.resilience4j.ratelimiter.RateLimiter; +import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.handler.HttpException; +import lombok.extern.slf4j.Slf4j; + +*/ +/*** + * 内存存储 + * + * @author xy + * + *//* + +@Slf4j +public class AppRateLimitHandlerImpl implements AppRateLimitHandler { + + @Override + public void handle(RoutingContext rc) { + log.info("Enter AppRateLimitHandlerImpl.handle()"); + String appCode = rc.request().headers().get(AppConfigHandler.getAppCodeHeaderKey()); + SacCurrentLimiting currentLimiting = AppConfigHandler.getGlobalAppCurrentLimitingConfig(appCode); + + if (currentLimiting != null) { + String key = RedisKeyConfig.APP_CURRENT_LIMITING_CONFIG_KEY + ":" + appCode; + RateLimiter rateLimiter = currentLimiting.getRegistry().rateLimiter(key); + CheckedRunnable restrictedCall = RateLimiter.decorateCheckedRunnable(rateLimiter, rc::next); + try { + restrictedCall.run(); + } catch (Throwable t) { + //t.printStackTrace(); + log.info("app ratelimit:{}", key); + rc.fail(new ServiceException(GatewayError.REQUEST_URL_RESTRICTED_BY_FLOW, currentLimiting.getStrategy().getDefaultResponse())); + } + } else { + rc.next(); + } + } +} +*/ diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/BodyHandler.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/BodyHandler.java new file mode 100644 index 0000000..34b1d5b --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/BodyHandler.java @@ -0,0 +1,178 @@ +/* +package com.sf.vertx.handle; + +*/ +/* + * Copyright 2014 Red Hat, Inc. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * The Apache License v2.0 is available at + * http://www.opensource.org/licenses/apache2.0.php + * + * You may elect to redistribute this code under either of these licenses. + *//* + + +import io.vertx.codegen.annotations.Fluent; +import io.vertx.codegen.annotations.VertxGen; +import io.vertx.core.Handler; +import io.vertx.ext.web.RoutingContext; + +*/ +/** + * A handler which gathers the entire request body and sets it on the {@link RoutingContext}. + *

+ * It also handles HTTP file uploads and can be used to limit body sizes. + * + * @author Tim Fox + *//* + +@VertxGen +public interface BodyHandler extends Handler { + + */ +/** + * Default max size for a request body in bytes = {@code 10485760}, i.e. 10 megabytes + *//* + + long DEFAULT_BODY_LIMIT = 10 * 1024 * 1024; + + */ +/** + * Default uploads directory on server for file uploads + *//* + + String DEFAULT_UPLOADS_DIRECTORY = "file-uploads"; + + */ +/** + * Default value of whether form attributes should be merged into request params + *//* + + boolean DEFAULT_MERGE_FORM_ATTRIBUTES = true; + + */ +/** + * Default value of whether uploaded files should be removed after handling the request + *//* + + boolean DEFAULT_DELETE_UPLOADED_FILES_ON_END = false; + + */ +/** + * Default value of whether to pre-allocate the body buffer size according to the content-length HTTP request header + *//* + + boolean DEFAULT_PREALLOCATE_BODY_BUFFER = false; + + */ +/** + * Create a body handler with defaults. + * + * @return the body handler + *//* + + static BodyHandler create() { + return new BodyHandlerImpl(); + } + + */ +/** + * Create a body handler setting if it should handle file uploads. + * + * @param handleFileUploads true if files upload should be handled + * @return the body handler + *//* + + static BodyHandler create(boolean handleFileUploads) { + return new BodyHandlerImpl(handleFileUploads); + } + + */ +/** + * Create a body handler and use the given upload directory. + * + * @param uploadDirectory the uploads directory + * @return the body handler + *//* + + static BodyHandler create(String uploadDirectory) { + return new BodyHandlerImpl(uploadDirectory); + } + + */ +/** + * Set whether file uploads will be handled. + * + * @param handleFileUploads true if they should be handled + * @return reference to this for fluency + *//* + + @Fluent + BodyHandler setHandleFileUploads(boolean handleFileUploads); + + */ +/** + * Set the maximum body size in bytes, {@code -1} means no limit. + * + * @param bodyLimit the max size in bytes + * @return reference to this for fluency + *//* + + @Fluent + BodyHandler setBodyLimit(long bodyLimit); + + */ +/** + * Set the uploads directory to use. + * + * @param uploadsDirectory the uploads directory + * @return reference to this for fluency + *//* + + @Fluent + BodyHandler setUploadsDirectory(String uploadsDirectory); + + */ +/** + * Set whether form attributes will be added to the request parameters. + * + * @param mergeFormAttributes true if they should be merged + * @return reference to this for fluency + *//* + + @Fluent + BodyHandler setMergeFormAttributes(boolean mergeFormAttributes); + + */ +/** + * Set whether uploaded files should be removed after handling the request. + * + * @param deleteUploadedFilesOnEnd true if uploaded files should be removed after handling the request + * @return reference to this for fluency + *//* + + @Fluent + BodyHandler setDeleteUploadedFilesOnEnd(boolean deleteUploadedFilesOnEnd); + + */ +/** + * Pre-allocate the body buffer according to the value parsed from content-length header. + * The buffer is capped at 64KB + * @param isPreallocateBodyBuffer {@code true} if body buffer is pre-allocated according to the size + * read from content-length Header. + * {code false} if body buffer is pre-allocated to 1KB, and is resized dynamically + * @return reference to this for fluency + *//* + + @Fluent + BodyHandler setPreallocateBodyBuffer(boolean isPreallocateBodyBuffer); + +} +*/ diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/BodyHandlerImpl.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/BodyHandlerImpl.java new file mode 100644 index 0000000..bd0becd --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/BodyHandlerImpl.java @@ -0,0 +1,389 @@ +/* +package com.sf.vertx.handle; + +import java.io.File; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; + +*/ +/* + * Copyright 2014 Red Hat, Inc. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * The Apache License v2.0 is available at + * http://www.opensource.org/licenses/apache2.0.php + * + * You may elect to redistribute this code under either of these licenses. + *//* + + +import com.sf.vertx.api.pojo.ApiConfig; +import com.sf.vertx.api.pojo.AppConfig; +import com.sf.vertx.enums.RequestMethod; +import com.sf.vertx.utils.AppUtils; +import io.netty.handler.codec.DecoderException; +import io.netty.handler.codec.http.HttpHeaderValues; +import io.vertx.core.Future; +import io.vertx.core.Handler; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.file.FileSystem; +import io.vertx.core.http.HttpHeaders; +import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.http.HttpServerResponse; +import io.vertx.core.http.HttpVersion; +import io.vertx.core.impl.logging.Logger; +import io.vertx.core.impl.logging.LoggerFactory; +import io.vertx.ext.web.FileUpload; +import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.impl.FileUploadImpl; +import io.vertx.ext.web.impl.RoutingContextInternal; + +import static com.sf.vertx.constans.SACConstants.*; + +*/ +/** + * @author Tim Fox + *//* + +public class BodyHandlerImpl implements BodyHandler { + + private static final Logger LOG = LoggerFactory.getLogger(BodyHandlerImpl.class); + + private long bodyLimit = DEFAULT_BODY_LIMIT; + private boolean handleFileUploads; + private String uploadsDir; + private boolean mergeFormAttributes = DEFAULT_MERGE_FORM_ATTRIBUTES; + private boolean deleteUploadedFilesOnEnd = DEFAULT_DELETE_UPLOADED_FILES_ON_END; + private boolean isPreallocateBodyBuffer = DEFAULT_PREALLOCATE_BODY_BUFFER; + private static final int DEFAULT_INITIAL_BODY_BUFFER_SIZE = 1024; // bytes + + public BodyHandlerImpl() { + this(true, DEFAULT_UPLOADS_DIRECTORY); + } + + public BodyHandlerImpl(boolean handleFileUploads) { + this(handleFileUploads, DEFAULT_UPLOADS_DIRECTORY); + } + + public BodyHandlerImpl(String uploadDirectory) { + this(true, uploadDirectory); + } + + private BodyHandlerImpl(boolean handleFileUploads, String uploadDirectory) { + this.handleFileUploads = handleFileUploads; + setUploadsDirectory(uploadDirectory); + } + + @Override + public void handle(RoutingContext context) { + // =======源码流程 + final HttpServerRequest request = context.request(); + final HttpServerResponse response = context.response(); + // + // we need to keep state since we can be called again on reroute + if (!((RoutingContextInternal) context).seenHandler(RoutingContextInternal.BODY_HANDLER)) { + ((RoutingContextInternal) context).visitHandler(RoutingContextInternal.BODY_HANDLER); + + // Check if a request has a request body. + // A request with a body __must__ either have `transfer-encoding` + // or `content-length` headers set. + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.3 + final long parsedContentLength = parseContentLengthHeader(request); + // http2 never transmits a `transfer-encoding` as frames are chunks. + final boolean hasTransferEncoding = request.version() == HttpVersion.HTTP_2 + || request.headers().contains(HttpHeaders.TRANSFER_ENCODING); + + if (!hasTransferEncoding && parsedContentLength == -1) { + // there is no "body", so we can skip this handler + context.next(); + return; + } + + // before parsing the body we can already discard a bad request just by + // inspecting the content-length against + // the body limit, this will reduce load, on the server by totally skipping + // parsing the request body + if (bodyLimit != -1 && parsedContentLength != -1) { + if (parsedContentLength > bodyLimit) { + context.fail(413); + return; + } + } + + // handle expectations + // https://httpwg.org/specs/rfc7231.html#header.expect + final String expect = request.getHeader(HttpHeaders.EXPECT); + if (expect != null) { + // requirements validation + if (expect.equalsIgnoreCase("100-continue")) { + // A server that receives a 100-continue expectation in an HTTP/1.0 request MUST + // ignore that expectation. + if (request.version() != HttpVersion.HTTP_1_0) { + // signal the client to continue + response.writeContinue(); + } + } else { + // the server cannot meet the expectation, we only know about 100-continue + context.fail(417); + return; + } + } + + // TODO 改造了这个地方 在真正解析body之前验证是否需要继续解析 + AppConfig appConfig = AppUtils.getAppConfigFromRoutingContext(context); + ApiConfig apiConfig = AppUtils.getApiConfigFromRoutingContext(context); + String apiServiceType = context.get(API_SERVICE_TYPE); + if (!AppUtils.isAnalysisBody(appConfig,apiConfig,apiServiceType)){ + context.next(); + return; + } + + final BHandler handler = new BHandler(context, isPreallocateBodyBuffer ? parsedContentLength : -1); + boolean ended = request.isEnded(); + if (!ended) { + request + // resume the request (if paused) + .handler(handler).endHandler(handler::end).resume(); + } + } else { + // on reroute we need to re-merge the form params if that was desired + if (mergeFormAttributes && request.isExpectMultipart()) { + request.params().addAll(request.formAttributes()); + } + context.next(); + } + } + + @Override + public BodyHandler setHandleFileUploads(boolean handleFileUploads) { + this.handleFileUploads = handleFileUploads; + return this; + } + + @Override + public BodyHandler setBodyLimit(long bodyLimit) { + this.bodyLimit = bodyLimit; + return this; + } + + @Override + public BodyHandler setUploadsDirectory(String uploadsDirectory) { + this.uploadsDir = uploadsDirectory; + return this; + } + + @Override + public BodyHandler setMergeFormAttributes(boolean mergeFormAttributes) { + this.mergeFormAttributes = mergeFormAttributes; + return this; + } + + @Override + public BodyHandler setDeleteUploadedFilesOnEnd(boolean deleteUploadedFilesOnEnd) { + this.deleteUploadedFilesOnEnd = deleteUploadedFilesOnEnd; + return this; + } + + @Override + public BodyHandler setPreallocateBodyBuffer(boolean isPreallocateBodyBuffer) { + this.isPreallocateBodyBuffer = isPreallocateBodyBuffer; + return this; + } + + private long parseContentLengthHeader(HttpServerRequest request) { + String contentLength = request.getHeader(HttpHeaders.CONTENT_LENGTH); + if (contentLength == null || contentLength.isEmpty()) { + return -1; + } + try { + long parsedContentLength = Long.parseLong(contentLength); + return parsedContentLength < 0 ? -1 : parsedContentLength; + } catch (NumberFormatException ex) { + return -1; + } + } + + private class BHandler implements Handler { + private static final int MAX_PREALLOCATED_BODY_BUFFER_BYTES = 65535; + + final RoutingContext context; + final long contentLength; + Buffer body; + boolean failed; + final AtomicInteger uploadCount = new AtomicInteger(); + boolean ended; + long uploadSize = 0L; + final boolean isMultipart; + final boolean isUrlEncoded; + + public BHandler(RoutingContext context, long contentLength) { + this.context = context; + this.contentLength = contentLength; + // the request clearly states that there should + // be a body, so we respect the client and ensure + // that the body will not be null + if (contentLength != -1) { + initBodyBuffer(); + } + + List fileUploads = context.fileUploads(); + + final String contentType = context.request().getHeader(HttpHeaders.CONTENT_TYPE); + if (contentType == null) { + isMultipart = false; + isUrlEncoded = false; + } else { + final String lowerCaseContentType = contentType.toLowerCase(); + isMultipart = lowerCaseContentType.startsWith(HttpHeaderValues.MULTIPART_FORM_DATA.toString()); + isUrlEncoded = lowerCaseContentType + .startsWith(HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.toString()); + } + + if (isMultipart || isUrlEncoded) { + context.request().setExpectMultipart(true); + if (handleFileUploads) { + makeUploadDir(context.vertx().fileSystem()); + } + context.request().uploadHandler(upload -> { + if (bodyLimit != -1 && upload.isSizeAvailable()) { + // we can try to abort even before the upload starts + long size = uploadSize + upload.size(); + if (size > bodyLimit) { + failed = true; + context.cancelAndCleanupFileUploads(); + context.fail(413); + return; + } + } + if (handleFileUploads) { + // we actually upload to a file with a generated filename + uploadCount.incrementAndGet(); + String uploadedFileName = new File(uploadsDir, UUID.randomUUID().toString()).getPath(); + FileUploadImpl fileUpload = new FileUploadImpl(context.vertx().fileSystem(), uploadedFileName, + upload); + fileUploads.add(fileUpload); + Future fut = upload.streamToFileSystem(uploadedFileName); + fut.onComplete(ar -> { + if (fut.succeeded()) { + uploadEnded(); + } else { + context.cancelAndCleanupFileUploads(); + context.fail(ar.cause()); + } + }); + } + }); + } + + context.request().exceptionHandler(t -> { + context.cancelAndCleanupFileUploads(); + int sc = 200; + if (t instanceof DecoderException) { + // bad request + sc = 400; + if (t.getCause() != null) { + t = t.getCause(); + } + } + context.fail(sc, t); + }); + } + + private void initBodyBuffer() { + int initialBodyBufferSize; + if (contentLength < 0) { + initialBodyBufferSize = DEFAULT_INITIAL_BODY_BUFFER_SIZE; + } else if (contentLength > MAX_PREALLOCATED_BODY_BUFFER_BYTES) { + initialBodyBufferSize = MAX_PREALLOCATED_BODY_BUFFER_BYTES; + } else { + initialBodyBufferSize = (int) contentLength; + } + + if (bodyLimit != -1) { + initialBodyBufferSize = (int) Math.min(initialBodyBufferSize, bodyLimit); + } + + this.body = Buffer.buffer(initialBodyBufferSize); + } + + private void makeUploadDir(FileSystem fileSystem) { + if (!fileSystem.existsBlocking(uploadsDir)) { + fileSystem.mkdirsBlocking(uploadsDir); + } + } + + @Override + public void handle(Buffer buff) { + if (failed) { + return; + } + uploadSize += buff.length(); + if (bodyLimit != -1 && uploadSize > bodyLimit) { + failed = true; + context.cancelAndCleanupFileUploads(); + context.fail(413); + } else { + // multipart requests will not end up in the request body + // url encoded should also not, however jQuery by default + // post in urlencoded even if the payload is something else + if (!isMultipart */ +/* && !isUrlEncoded *//* +) { + if (body == null) { + initBodyBuffer(); + } + body.appendBuffer(buff); + } + } + } + + void uploadEnded() { + int count = uploadCount.decrementAndGet(); + // only if parsing is done and count is 0 then all files have been processed + if (ended && count == 0) { + doEnd(); + } + } + + void end(Void v) { + // this marks the end of body parsing, calling doEnd should + // only be possible from this moment onwards + ended = true; + + // only if parsing is done and count is 0 then all files have been processed + if (uploadCount.get() == 0) { + doEnd(); + } + } + + void doEnd() { + + if (failed || context.failed()) { + context.cancelAndCleanupFileUploads(); + return; + } + + if (deleteUploadedFilesOnEnd) { + context.addBodyEndHandler(x -> context.cancelAndCleanupFileUploads()); + } + + HttpServerRequest req = context.request(); + if (mergeFormAttributes && req.isExpectMultipart()) { + req.params().addAll(req.formAttributes()); + } + ((RoutingContextInternal) context).setBody(body); + // release body as it may take lots of memory + body = null; + + context.next(); + } + } +} +*/ diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ConsulHandler.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ConsulHandler.java new file mode 100644 index 0000000..20f8f31 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ConsulHandler.java @@ -0,0 +1,111 @@ +/* +package com.sf.vertx.handle; + +import com.alibaba.fastjson2.JSONObject; + +import io.vertx.core.Vertx; +import io.vertx.ext.consul.ConsulClientOptions; +import io.vertx.ext.consul.KeyValue; +import io.vertx.ext.consul.KeyValueList; +import io.vertx.ext.consul.Watch; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class ConsulHandler { + + public static void init(Vertx vertx) { + ConsulClientOptions options = new ConsulClientOptions().setHost("127.0.0.1").setPort(8500); + + Watch.keyPrefix("apiCode_", vertx, options) + .setHandler(res -> { + if (res.succeeded()) { + // + // 删除 + boolean isDel = (res.nextResult() == null || res.nextResult().getList() == null) && (res.prevResult() != null && res.prevResult().getList() != null); +// if(isDel) { +// log.info("isDel"); +// KeyValueList keyValueList = res.prevResult(); +// if(keyValueList != null && keyValueList.getList() != null) { +// for(KeyValue keyValue : keyValueList.getList()) { +// log.info("keyValue:{}", JSONObject.toJSONString(keyValue)); +// } +// } +// } +// // 新增 +// boolean isAdd = (res.nextResult() != null && res.nextResult().getList() != null) && (res.prevResult() == null || res.prevResult().getList() == null); +// if(isAdd) { +// log.info("isAdd"); +// KeyValueList keyValueList = res.nextResult(); +// if(keyValueList != null && keyValueList.getList() != null) { +// for(KeyValue keyValue : keyValueList.getList()) { +// log.info("keyValue:{}", JSONObject.toJSONString(keyValue)); +// } +// } +// } +// +// // 修改 +// boolean isModify = (res.nextResult() != null && res.nextResult().getList() != null) && (res.prevResult() != null && res.prevResult().getList() != null); +// if(isModify) { +// log.info("isModify"); +// KeyValueList keyValueList = res.nextResult(); +// if(keyValueList != null && keyValueList.getList() != null) { +// for(KeyValue keyValue : keyValueList.getList()) { +// log.info("keyValue:{}", JSONObject.toJSONString(keyValue)); +// } +// } +// } + } else { + res.cause().printStackTrace(); + } + }) + .start(); + } + + public static void init1(Vertx vertx) { + ConsulClientOptions options = new ConsulClientOptions().setHost("127.0.0.1").setPort(8500); + + Watch.keyPrefix("apiCode_", vertx, options) + .setHandler(res -> { + if (res.succeeded()) { + // 删除 + boolean isDel = (res.nextResult() == null || res.nextResult().getList() == null) && (res.prevResult() != null && res.prevResult().getList() != null); + if(isDel) { + log.info("isDel"); + KeyValueList keyValueList = res.prevResult(); + if(keyValueList != null && keyValueList.getList() != null) { + for(KeyValue keyValue : keyValueList.getList()) { + log.info("keyValue:{}", JSONObject.toJSONString(keyValue)); + } + } + } + // 新增 + boolean isAdd = (res.nextResult() != null && res.nextResult().getList() != null) && (res.prevResult() == null || res.prevResult().getList() == null); + if(isAdd) { + log.info("isAdd"); + KeyValueList keyValueList = res.nextResult(); + if(keyValueList != null && keyValueList.getList() != null) { + for(KeyValue keyValue : keyValueList.getList()) { + log.info("keyValue:{}", JSONObject.toJSONString(keyValue)); + } + } + } + + // 修改 + boolean isModify = (res.nextResult() != null && res.nextResult().getList() != null) && (res.prevResult() != null && res.prevResult().getList() != null); + if(isModify) { + log.info("isModify"); + KeyValueList keyValueList = res.nextResult(); + if(keyValueList != null && keyValueList.getList() != null) { + for(KeyValue keyValue : keyValueList.getList()) { + log.info("keyValue:{}", JSONObject.toJSONString(keyValue)); + } + } + } + } else { + res.cause().printStackTrace(); + } + }) + .start(); + } +} +*/ diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/OpenParameterCheckHandlerImpl.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/OpenParameterCheckHandlerImpl.java new file mode 100644 index 0000000..3195ee2 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/OpenParameterCheckHandlerImpl.java @@ -0,0 +1,52 @@ +/* +package com.sf.vertx.handle; + +import com.sf.vertx.api.pojo.ApiConfig; +import com.sf.vertx.enums.GatewayError; +import com.sf.vertx.exception.ServiceException; + +import com.sf.vertx.api.pojo.AppConfig; + +import com.sf.vertx.utils.AppUtils; +import io.vertx.ext.web.RoutingContext; +import lombok.extern.slf4j.Slf4j; + +import static com.sf.vertx.constans.SACConstants.*; + +@Slf4j +public class OpenParameterCheckHandlerImpl implements ParameterCheckHandler { + + @Override + public void handle(RoutingContext rc) { + try { + log.info("Enter OPEN Route"); + // 判断OPEN模式, header不传递参数, 走域名映射 + String domain = rc.request().authority().host(); + log.info("request domain:{}", domain); + AppConfig appConfig = AppConfigHandler.getAppConfigByDomain(domain); + if (appConfig == null) { + rc.fail(new ServiceException(GatewayError.APP_SERVICE_NOT_FOUND)); + return; + } + String apiCacheKey = appConfig.getAppCode() + CACHE_KEY_CONNECTOR + domain; + ApiConfig apiConfig = AppConfigHandler.getApicodeConfig(apiCacheKey); + if (apiConfig == null) { + rc.fail(new ServiceException(GatewayError.API_SERVICE_NOT_FOUND)); + return; + } + // 设置应用配置、接口配置到上下文 + AppUtils.setAppConfigToRoutingContext(appConfig,rc); + AppUtils.setApiConfigIntoRoutingContext(apiConfig,rc); + rc.put(API_SERVICE_TYPE,AppConfigHandler.getServiceTypeOpen(apiCacheKey)); + // 将appcode和apicode设置到请求头,兼容原有逻辑 + rc.request().headers().add(AppConfigHandler.getAppCodeHeaderKey(), appConfig.getAppCode()); + rc.request().headers().add(AppConfigHandler.getApiCodeHeaderKey(), apiConfig.getApiCode()); + } catch (Exception e) { + log.error("OpenParameterCheckHandlerImpl Error:",e); + rc.fail(new ServiceException(GatewayError.DEFAULT_SERVICE_ERROR)); + return; + } + rc.next(); + } +} +*/ diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ParameterCheckHandler.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ParameterCheckHandler.java new file mode 100644 index 0000000..a4f5610 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ParameterCheckHandler.java @@ -0,0 +1,33 @@ +/* +package com.sf.vertx.handle; + +import com.sf.vertx.enums.GatewayServiceType; +import com.sf.vertx.enums.GatewayError; +import com.sf.vertx.exception.ServiceException; +import io.vertx.codegen.annotations.VertxGen; +import io.vertx.core.Handler; +import io.vertx.ext.web.RoutingContext; + +*/ +/*** + * 入口参数校验 + * + * @author xy + * + *//* + +@VertxGen +public interface ParameterCheckHandler extends Handler { + + static ParameterCheckHandler create(GatewayServiceType serviceType) { + switch (serviceType) { + case SAC: + return new SACParameterCheckHandlerImpl(); + case OPEN: + return new OpenParameterCheckHandlerImpl(); + default: + throw new ServiceException(GatewayError.INTERNAL_SERVER_ERROR); + } + } +} +*/ diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ProxyHandler.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ProxyHandler.java new file mode 100644 index 0000000..44cd8a5 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ProxyHandler.java @@ -0,0 +1,27 @@ +package com.sf.vertx.handle; + +import io.vertx.codegen.annotations.VertxGen; +import io.vertx.core.Handler; +import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.client.WebClient; +import io.vertx.httpproxy.HttpProxy; + +/** + * @author Emad Alblueshi + */ + +@VertxGen +public interface ProxyHandler extends Handler { + + static ProxyHandler create(WebClient mainWebClient, HttpProxy httpProxy) { + return new ProxyHandlerImpl(mainWebClient, httpProxy); + } + + static ProxyHandler create(HttpProxy httpProxy) { + return new ProxyHandlerImpl(httpProxy); + } + + static ProxyHandler create(HttpProxy httpProxy, int port, String host) { + return new ProxyHandlerImpl(httpProxy, port, host); + } +} diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ProxyHandlerImpl.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ProxyHandlerImpl.java new file mode 100644 index 0000000..ba1b9d4 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ProxyHandlerImpl.java @@ -0,0 +1,35 @@ +package com.sf.vertx.handle; + +import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.client.WebClient; +import io.vertx.httpproxy.HttpProxy; + +/** + * @author Emad Alblueshi + */ +public class ProxyHandlerImpl implements ProxyHandler { + + private final HttpProxy httpProxy; + private WebClient mainWebClient; + + public ProxyHandlerImpl(WebClient mainWebClient, HttpProxy httpProxy) { + this.httpProxy = httpProxy; + this.mainWebClient = mainWebClient; + } + + public ProxyHandlerImpl(HttpProxy httpProxy) { + this.httpProxy = httpProxy; + } + + public ProxyHandlerImpl(HttpProxy httpProxy, int port, String host) { + this.httpProxy = httpProxy.origin(port, host); + } + + @Override + public void handle(RoutingContext ctx) { + // TODO 改造了这个地方 + // httpProxy.handle(mainWebClient, ctx); + // 原始代码只有如下一句 + httpProxy.handle(ctx.request()); + } +} \ No newline at end of file diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/RestfulFailureHandler.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/RestfulFailureHandler.java new file mode 100644 index 0000000..df8e15b --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/RestfulFailureHandler.java @@ -0,0 +1,12 @@ +/* +package com.sf.vertx.handle; + +import io.vertx.core.Handler; +import io.vertx.ext.web.RoutingContext; + +public interface RestfulFailureHandler extends Handler { + static RestfulFailureHandlerImpl create() { + return new RestfulFailureHandlerImpl(); + } +} +*/ diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/RestfulFailureHandlerImpl.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/RestfulFailureHandlerImpl.java new file mode 100644 index 0000000..202f12d --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/RestfulFailureHandlerImpl.java @@ -0,0 +1,68 @@ +/* +package com.sf.vertx.handle; + +import cn.hutool.core.util.StrUtil; +import com.sf.vertx.enums.GatewayError; +import com.sf.vertx.exception.MockException; +import com.sf.vertx.exception.ServiceException; +import com.sf.vertx.utils.AppUtils; + +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.handler.HttpException; +import lombok.extern.slf4j.Slf4j; + + +@Slf4j +public class RestfulFailureHandlerImpl implements RestfulFailureHandler { + + @Override + public void handle(RoutingContext frc) { + int statusCode = 500; + JsonObject errorJson; + // 网关的业务码 + int gatewayServiceCode = GatewayError.DEFAULT_SERVICE_ERROR.getCode(); + try { + Throwable failure = frc.failure(); + log.info("failure:", failure); + if (failure instanceof HttpException) { + HttpException httpException = (HttpException) failure; + gatewayServiceCode = httpException.getStatusCode(); + errorJson = AppUtils.getResponseJsonByGatewayError(GatewayError.getByCode(statusCode)); + if (StrUtil.isNotBlank(httpException.getPayload())){ + errorJson.put("msg",httpException.getPayload()); + } + } else if (failure instanceof MockException) { + MockException mockException = (MockException) failure; + gatewayServiceCode = mockException.getStatusCode(); + statusCode = mockException.getStatusCode(); + errorJson = new JsonObject(mockException.getPayload()); + }else if (failure instanceof ServiceException) { + ServiceException serviceException = (ServiceException) failure; + gatewayServiceCode = serviceException.getStatusCode(); + // 业务异常,为网关业务错误,响应码设置为500, + GatewayError gatewayError = GatewayError.getByCode(serviceException.getStatusCode()); + // 如果是被限流或熔断,直接返回限流或熔断响应 + if (GatewayError.REQUEST_URL_RESTRICTED_BY_FLOW.equals(gatewayError) + || GatewayError.REQUEST_URL_IS_BROKEN.equals(gatewayError)){ + errorJson = new JsonObject(serviceException.getPayload()); + }else { + errorJson = AppUtils.getResponseJsonByGatewayError(gatewayError); + } + } else { + errorJson = AppUtils.getResponseJsonByGatewayError(GatewayError.getByCode(statusCode)); + } + + } catch (Exception e) { + log.error("RestfulFailureHandlerImpl.handle Error:",e); + errorJson = AppUtils.getResponseJsonByGatewayError(GatewayError.DEFAULT_SERVICE_ERROR); + } + frc.response().setChunked(true).setStatusCode(statusCode).putHeader("Content-Type", "application/json") + .putHeader(AppConfigHandler.sacResponseHeaderKey(), String.valueOf(gatewayServiceCode)) + .end(errorJson.toBuffer()); + } + + + +} +*/ diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/SACParameterCheckHandlerImpl.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/SACParameterCheckHandlerImpl.java new file mode 100644 index 0000000..22d9714 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/SACParameterCheckHandlerImpl.java @@ -0,0 +1,52 @@ +/* +package com.sf.vertx.handle; + +import cn.hutool.core.util.StrUtil; +import com.sf.vertx.api.pojo.ApiConfig; +import com.sf.vertx.api.pojo.AppConfig; +import com.sf.vertx.enums.GatewayError; +import com.sf.vertx.exception.ServiceException; +import com.sf.vertx.utils.AppUtils; +import io.vertx.ext.web.RoutingContext; +import lombok.extern.slf4j.Slf4j; + +import static com.sf.vertx.constans.SACConstants.*; + +@Slf4j +public class SACParameterCheckHandlerImpl implements ParameterCheckHandler { + + @Override + public void handle(RoutingContext rc) { + try { + log.info("Enter SAC Route"); + // SAC规范要求Header必须要同时包含appCode和apiCode + String appCode = rc.request().headers().get(AppConfigHandler.getAppCodeHeaderKey()); + String apiCode = rc.request().headers().get(AppConfigHandler.getApiCodeHeaderKey()); + if (StrUtil.isBlank(appCode) || StrUtil.isBlank(apiCode)) { + rc.fail(new ServiceException(GatewayError.PARAMETER_TRANSFER_ERROR)); + return; + } + AppConfig appConfig = AppConfigHandler.getAppConfig(appCode); + if (appConfig == null) { + rc.fail(new ServiceException(GatewayError.APP_SERVICE_NOT_FOUND)); + return; + } + String apiCacheKey = appConfig.getAppCode() + CACHE_KEY_CONNECTOR + apiCode; + ApiConfig apiConfig = AppConfigHandler.getApicodeConfig(apiCacheKey); + if (apiConfig == null) { + rc.fail(new ServiceException(GatewayError.API_SERVICE_NOT_FOUND)); + return; + } + // 设置应用配置、接口配置到上下文 + AppUtils.setAppConfigToRoutingContext(appConfig,rc); + AppUtils.setApiConfigIntoRoutingContext(apiConfig,rc); + rc.put(API_SERVICE_TYPE,AppConfigHandler.getServiceTypeOpen(apiCacheKey)); + } catch (Exception e) { + log.error("SACParameterCheckHandlerImpl Error:",e); + rc.fail(new ServiceException(GatewayError.DEFAULT_SERVICE_ERROR)); + return; + } + rc.next(); + } +} +*/ diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/DynamicBuildServer.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/DynamicBuildServer.java new file mode 100644 index 0000000..9d0cf2b --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/DynamicBuildServer.java @@ -0,0 +1,68 @@ +/* +package com.sf.vertx.init; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.core.annotation.Order; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import com.sf.vertx.constans.RedisKeyConfig; +import com.sf.vertx.handle.AppConfigHandler; + +import lombok.extern.slf4j.Slf4j; + +*/ +/*** + * 动态构建vertx服务 + * + * @author xy + * + *//* + +@Slf4j +@Order(value = 10) +@Component +public class DynamicBuildServer implements ApplicationRunner { + // TODO 后面可以和app挂钩 + @Autowired + private SacVertxConfig sacVertxConfig; + + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private RedisKeyConfig redisKeyConfig; + + @Override + public void run(ApplicationArguments args) throws Exception { + // 初始化redis key + redisKeyConfig.init(); + AppConfigHandler.init(redisTemplate, sacVertxConfig); + // 从redis同步vertx配置 + AppConfigHandler.initVertxConfig(); + + // 加载vertx、应用配置 + startVertxService(); + } + + */ +/*** + * 应用启动, 从redis读取配置,初始化vertx服务 + * + * @throws Exception + *//* + + private void startVertxService() throws Exception { + if(sacVertxConfig.getDeploymentMode() == 2) { + // 集群 + AppConfigHandler.createHazelcastClusterVertx(); + } else { + // 单机 + AppConfigHandler.createVertx(); + } + } + +} +*/ diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/GatewayConfigVerticle.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/GatewayConfigVerticle.java new file mode 100644 index 0000000..6ee7d3f --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/GatewayConfigVerticle.java @@ -0,0 +1,188 @@ +package com.sf.vertx.init; + +import com.alibaba.fastjson2.JSONObject; +import com.sf.vertx.enums.GatewayError; +import io.vertx.config.ConfigRetriever; +import io.vertx.config.ConfigRetrieverOptions; +import io.vertx.config.ConfigStoreOptions; +import io.vertx.core.AbstractVerticle; +import io.vertx.core.AsyncResult; +import io.vertx.core.Future; +import io.vertx.core.Handler; +import io.vertx.core.http.HttpHeaders; +import io.vertx.core.http.HttpServerOptions; +import io.vertx.core.http.HttpServerResponse; +import io.vertx.core.json.JsonObject; +import io.vertx.core.net.JksOptions; +import io.vertx.ext.web.Route; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.handler.BodyHandler; +import io.vertx.ext.web.handler.CorsHandler; +import io.vertx.ext.web.handler.SessionHandler; +import io.vertx.ext.web.sstore.ClusteredSessionStore; +import io.vertx.ext.web.sstore.LocalSessionStore; +import io.vertx.ext.web.sstore.SessionStore; +import lombok.extern.slf4j.Slf4j; + +import java.text.MessageFormat; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + * 网关配置同步与管理 + */ +@Slf4j +public class GatewayConfigVerticle extends AbstractVerticle { + + /** + * HTTP的路由服务 + */ + private static Router httpRouter = null; + + private static VertxProperties vertxProperties; + + /** + * 全局IP黑名单 + */ + private Set blackIpSet = new LinkedHashSet<>(); + + public void start() { + ConfigStoreOptions store = new ConfigStoreOptions() + .setType("file") + .setFormat("yaml") + .setConfig(new JsonObject() + .put("path", "application.yaml") + ); + + ConfigRetriever retriever = ConfigRetriever.create(vertx, + new ConfigRetrieverOptions().addStore(store)); + retriever.getConfig(json -> { + if (json.succeeded()) { + doStart(json); + } else { + stop(); + } + }); + + + } + + + private void doStart(AsyncResult json) { + JsonObject config = json.result(); + log.info("配置读取成功"); + // 读取vertx配置 + JsonObject vertxConfig = config.getJsonObject("vertx"); + // 读取redis配置 + JsonObject redisConfig = config.getJsonObject("redis"); + + //先初始化再发布Http服务 + vertx.executeBlocking(() -> { + //顺序不能乱 + try { + //初始化Redis + RedisManager.initRedisConfig(vertx, redisConfig, redisConnectionAsyncResult -> { + if (redisConnectionAsyncResult.succeeded()) { + log.info("GatewayConfig redis初始化成功"); + } else { + log.error("GatewayConfig redis初始化失败", redisConnectionAsyncResult.cause()); + stop(); + } + }); + } catch (Exception e) { + log.error("GatewayConfig redis初始化失败:", e); + stop(); + } + return true; + }).onComplete(ar -> { + // 启动服务 + if (ar.succeeded()) { + vertxProperties = vertxConfig.mapTo(VertxProperties.class); + blackIpSet = new LinkedHashSet<>(List.of(vertxProperties.getBlackIP())); + createHttpServer(createServerAR ->{ + if (createServerAR.succeeded()) { + log.info("GatewayConfig 启动服务成功"); + // 添加路由 + Route addAppConfigRoute = httpRouter.post("/vertx/app/config"); + addAppConfigRoute.handler(BodyHandler.create(false)) + .handler(ctx ->{ + String s = ctx.body().asString(); + log.info(s); + JSONObject jsonre = new JSONObject(); + jsonre.put("code", 200); + jsonre.put("msg", "success"); + ctx.response() + .putHeader("Content-Type", "application/json") + .end(jsonre.toJSONString()); + }); + + } else { + log.error("GatewayConfig 启动服务失败", createServerAR.cause()); + stop(); + } + }); + } else { + log.error("GatewayConfig 启动服务失败:", ar.cause()); + stop(); + } + }); + } + + + public void stop() { + log.info("close GatewayConfigVerticle !"); + vertx.close(); + } + + /** + * 创建http服务器 + * + * @param createHttp + */ + public void createHttpServer(Handler> createHttp) { + // 所有ip都能访问 + HttpServerOptions httpServerOptions = new HttpServerOptions().setHost("0.0.0.0"); + if (vertxProperties.isSSL()) { + httpServerOptions.setSsl(true) + .setKeyStoreOptions(new JksOptions().setPassword("changeit").setPath("keystore.jks")); + } + httpRouter = Router.router(vertx); + httpRouter.route().handler(this::filterBlackIP); + httpRouter.route().handler(CorsHandler.create().addRelativeOrigin(".*")); + // 创建http服务器 + vertx.createHttpServer(httpServerOptions) + .requestHandler(httpRouter) + .listen(vertxProperties.getConfigPort(), res -> { + if (res.succeeded()) { + log.info("GatewayConfigServer Running on port {} by HTTP", vertxProperties.getConfigPort()); + createHttp.handle(Future.succeededFuture()); + } else { + log.error("create HTTP Server (GatewayConfigServer) failed : GatewayConfigServer Running on port {} by HTTP", vertxProperties.getConfigPort()); + createHttp.handle(Future.failedFuture(res.cause())); + } + }); + } + + /** + * 过滤黑名单 + * + * @param rct + */ + public void filterBlackIP(RoutingContext rct) { + + String host = rct.request().remoteAddress().host(); + if (blackIpSet.contains(host)) { + HttpServerResponse response = rct.response(); + response.putHeader(HttpHeaders.CONTENT_TYPE, "text/html"); + response.setStatusCode(GatewayError.FORBIDDEN.getCode()); + response.setStatusMessage(GatewayError.FORBIDDEN.getReasonPhrase()); + response.end("" + + "

you can't access this service

" + + ""); + } else { + rct.next(); + } + } +} \ No newline at end of file diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/GatewayRPCVerticle.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/GatewayRPCVerticle.java new file mode 100644 index 0000000..b3d6c9e --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/GatewayRPCVerticle.java @@ -0,0 +1,79 @@ +package com.sf.vertx.init; + +import io.vertx.config.ConfigRetriever; +import io.vertx.config.ConfigRetrieverOptions; +import io.vertx.config.ConfigStoreOptions; +import io.vertx.core.AbstractVerticle; +import io.vertx.core.AsyncResult; +import io.vertx.core.Future; +import io.vertx.core.Handler; +import io.vertx.core.http.HttpServer; +import io.vertx.core.http.HttpServerResponse; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.handler.BodyHandler; +import io.vertx.ext.web.handler.StaticHandler; +import io.vertx.redis.client.impl.RedisClient; + + +/** + * 网关RPC支持 + */ +public class GatewayRPCVerticle extends AbstractVerticle { + + + + + public void start() { + ConfigStoreOptions store = new ConfigStoreOptions() + .setType("file") + .setFormat("yaml") + .setConfig(new JsonObject() + .put("path", "application.yaml") + ); + + ConfigRetriever retriever = ConfigRetriever.create(vertx, + new ConfigRetrieverOptions().addStore(store)); + // Create redisClient + JsonObject cachedConfig = retriever.getCachedConfig(); + + //RedisOptions config = new RedisOptions() + //.("127.0.0.1"); + + //redisClient = new RedisClient(vertx, config); + } + + private void startWebApp(Handler> next) { + // Create a router object. + Router router = Router.router(vertx); + + // Bind "/" to our hello message. + router.route("/").handler(routingContext -> { + HttpServerResponse response = routingContext.response(); + response + .putHeader("content-type", "text/html") + .end("

Hello from my first Vert.x 3 application

"); + }); + + router.route("/assets/*").handler(StaticHandler.create("assets")); + + /* router.get("/api/whiskies").handler(this::getAll); + router.route("/api/whiskies*").handler(BodyHandler.create()); + router.post("/api/whiskies").handler(this::addOne); + router.get("/api/whiskies/:id").handler(this::getOne); + router.put("/api/whiskies/:id").handler(this::updateOne); + router.delete("/api/whiskies/:id").handler(this::deleteOne);*/ + + + // Create the HTTP server and pass the "accept" method to the request handler. + vertx + .createHttpServer() + .listen( + // Retrieve the port from the configuration, + // default to 8080. + config().getInteger("http.port", 8080), + next::handle + ); + } + +} \ No newline at end of file diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/RedisManager.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/RedisManager.java new file mode 100644 index 0000000..7f054ec --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/RedisManager.java @@ -0,0 +1,62 @@ +package com.sf.vertx.init; + +import cn.hutool.core.util.StrUtil; +import io.vertx.core.AsyncResult; +import io.vertx.core.Future; +import io.vertx.core.Handler; +import io.vertx.core.Vertx; +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; +import io.vertx.redis.client.*; +import io.vertx.redis.client.impl.RedisClient; + +import java.util.Objects; +import java.util.UUID; + +/** + * 功能描述: + * + * @author a_kun + * @date 2024/5/27 16:53 + */ +public class RedisManager { + + private static RedisAPI redisAPI; + private static Redis redis; + + private static RedisProperties redisProperties; + + public static void initRedisConfig(Vertx vertx, JsonObject redisConfig, Handler> handler) { + try { + if (redis == null) { + redisProperties = redisConfig.mapTo(RedisProperties.class); + //解析配置 + RedisOptions options = new RedisOptions() + .setType(redisProperties.getClientType()) + .setPoolName(redisProperties.getPoolName()) + .setMaxPoolSize(redisProperties.getMaxPoolSize()) + .setMaxPoolWaiting(redisProperties.getMaxPoolWaiting()) + .setPoolCleanerInterval(redisProperties.getPoolCleanerInterval()); + // password + if (StrUtil.isNotBlank(redisProperties.getPassword())) { + options.setPassword(redisProperties.getPassword()); + } + // connect address [redis://localhost:6379/0, redis://localhost:6779/1] + for (String url : redisProperties.getUrls()) { + options.addConnectionString(url); + } + // sentinel + if (redisProperties.getClientType().equals(RedisClientType.SENTINEL)) { + options.setRole(redisProperties.getRole()).setMasterName(redisProperties.getMasterName()); + } + //创建redis实例 + redis = Redis.createClient(vertx, options); + redisAPI = RedisAPI.api(redis); + } + handler.handle(Future.succeededFuture()); + } catch (Exception e) { + handler.handle(Future.failedFuture(e)); + } + } + +} diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/RedisProperties.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/RedisProperties.java new file mode 100644 index 0000000..1a03310 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/RedisProperties.java @@ -0,0 +1,26 @@ +package com.sf.vertx.init; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import io.vertx.redis.client.RedisClientType; +import io.vertx.redis.client.RedisRole; +import lombok.Data; + +/** + * Redis配置 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class RedisProperties { + private RedisClientType clientType = RedisClientType.STANDALONE; + private String[] urls; + private String password; + private String poolName = "vertx-redis"; + private int poolCleanerInterval = 30000; + private int maxPoolSize = 8; + private int maxPoolWaiting = 32; + private String masterName = "redis_master"; + private RedisRole role = RedisRole.MASTER; + + private Integer expireTime = 60 * 60; + +} diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/SacVertxConfig.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/SacVertxConfig.java new file mode 100644 index 0000000..ddc5991 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/SacVertxConfig.java @@ -0,0 +1,46 @@ +/* +package com.sf.vertx.init; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import lombok.Data; + +*/ +/*** + * 配置文件 + * @author xy + * + *//* + +//@Component +@Data +public class SacVertxConfig { + @Value("${server.vertx.server.default.port:80}") + private Integer port; + + @Value("${server.vertx.cluster.ip:127.0.0.1}") + private String clusterIp; + + @Value("${server.vertx.cluster.networkPort:5701}") + private Integer networkPort; + + @Value("${server.vertx.cluster.portAutoIncrement:false}") + private boolean portAutoIncrement; + + @Value("${server.vertx.sacResponseHeaderKey:sacErrorCode}") + private String sacResponseHeaderKey; + + @Value("${server.vertx.rpcUri:/rpc}") + private String rpcUri; + + @Value("${server.vertx.isSSL:false}") + private boolean isSSLs; + + @Value("${server.vertx.deploymentMode:1}") + private Integer deploymentMode; + + @Value("${server.vertx.cluster.clusterName:sac}") + private String clusterName; +} +*/ diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/VertxProperties.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/VertxProperties.java new file mode 100644 index 0000000..53d1206 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/VertxProperties.java @@ -0,0 +1,26 @@ +package com.sf.vertx.init; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import io.vertx.core.json.JsonObject; +import lombok.Data; + +import java.util.Map; + + +/** + * Redis配置 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class VertxProperties { + private int deploymentMode = 1; + private boolean isSSL = false; + private int rpcPort = 80; + private String rpcUri; + private String sacResponseHeaderKey; + private String environment; + private int configPort = 5566; + private Map cluster; + private String[] blackIP;// 黑名单 + +} diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/pojo/ClusterEventMsg.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/pojo/ClusterEventMsg.java new file mode 100644 index 0000000..697fc60 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/pojo/ClusterEventMsg.java @@ -0,0 +1,13 @@ +package com.sf.vertx.pojo; + +import java.io.Serializable; + +import lombok.Data; + +@Data +public class ClusterEventMsg implements Serializable{ + private static final long serialVersionUID = -3380175299815557039L; + private int type; // 1: app + private int operation; // 1:新增,修改,3:删除 + private String appCode; +} diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/pojo/SacCurrentLimiting.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/pojo/SacCurrentLimiting.java new file mode 100644 index 0000000..c5c13b8 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/pojo/SacCurrentLimiting.java @@ -0,0 +1,13 @@ +package com.sf.vertx.pojo; + +import com.sf.vertx.api.pojo.Strategy; + +import io.github.resilience4j.ratelimiter.RateLimiterRegistry; +import lombok.Data; + +@Data +public class SacCurrentLimiting { + + private RateLimiterRegistry registry; + private Strategy strategy; +} diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/AesUtils.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/AesUtils.java new file mode 100644 index 0000000..f910e7e --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/AesUtils.java @@ -0,0 +1,84 @@ +package com.sf.vertx.security; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +/** + * 本类采用对称加密 加密算法:{@link #PADDINIG_MODE} + * {@link #RESULT_TYPE} 密文结果:1=base64 2=hex + */ +public class AesUtils { + /** + * 整平方法 + */ + private static final int RESULT_TYPE = 2; + + /** + * 加密方法 + */ + private static final String AES_ALGORITHM = "AES"; + + /** + * 填充方法 + */ + private static final String PADDINIG_MODE = "AES/CBC/PKCS5Padding"; + + /** + * 偏移量 + */ + private static final byte[] IV = "0000000000000000".getBytes(); + + public static String encrypt(String s, String k) throws Exception { + SecretKeySpec key = new SecretKeySpec(StringUtils.getBytes(k), AES_ALGORITHM); + byte[] data = encrypt(StringUtils.getBytes(s), key); + String result; + switch (RESULT_TYPE) { + case 1: + result = Base64Utils.encode(data); + break; + case 2: + result = HexUtils.bytes2Hex(data); + break; + default: + throw new Exception("Unsupport Result Type"); + } + return result; + } + + public static String decrypt(String s, String k) throws Exception { + SecretKeySpec key = new SecretKeySpec(StringUtils.getBytes(k), AES_ALGORITHM); + byte[] data; + switch (RESULT_TYPE) { + case 1: + data = Base64Utils.decode(s); + break; + case 2: + data = HexUtils.hex2Bytes(s); + break; + default: + throw new Exception("Unsupport Result Type"); + } + return StringUtils.bytes2String(decrypt(data, key)); + } + + private static byte[] encrypt(byte[] data, SecretKeySpec keySpec) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + IvParameterSpec ivspec = new IvParameterSpec(IV); + Cipher cipher = Cipher.getInstance(PADDINIG_MODE); + cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivspec); + return cipher.doFinal(data); + } + + private static byte[] decrypt(byte[] data, SecretKeySpec keySpec) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + IvParameterSpec ivspec = new IvParameterSpec(IV); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, keySpec, ivspec); + return cipher.doFinal(data); + } +} diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/Base64Utils.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/Base64Utils.java new file mode 100644 index 0000000..02d002f --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/Base64Utils.java @@ -0,0 +1,31 @@ +package com.sf.vertx.security; + +import java.io.UnsupportedEncodingException; +import java.util.Base64; + +/** + * 将Base64字符串和字节数组互转 + */ +public class Base64Utils { + + /** + * 将字节数组编码为base64字符串 + * + * @param b 字节数组 + * @return String 字符串 + */ + public static String encode(byte[] b) { + return Base64.getEncoder().encodeToString(b); + } + + /** + * 将字符串转base64为字节数组 + * + * @param s 字符串 + * @return byte[] 字节数组 + */ + public static byte[] decode(String s) { + return Base64.getDecoder().decode(s); + } + +} diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/HexUtils.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/HexUtils.java new file mode 100644 index 0000000..c3842c8 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/HexUtils.java @@ -0,0 +1,53 @@ +package com.sf.vertx.security; + +/** + * 将字节数组和HEX字符串进行互转 + */ +public class HexUtils { + private static final char[] HEX = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + /** + * 字节数组转字符串 + * + * @param byteArr 字节数组 + * @return 字符串 + */ + public static String bytes2Hex(byte[] byteArr) { + StringBuilder sb = new StringBuilder(byteArr.length); + for (byte b : byteArr) { + String sTemp = Integer.toHexString(255 & b); + if (sTemp.length() < 2) { + sb.append(0); + } + sb.append(sTemp.toUpperCase()); + } + return sb.toString(); + } + + /** + * HEX字符串转字节数组 + * + * @param str 字符串 + * @return 字节数组 + */ + public static byte[] hex2Bytes(String str) { + if (str == null) { + return null; + } else { + char[] hex = str.toCharArray(); + int length = hex.length / 2; + byte[] raw = new byte[length]; + + for (int i = 0; i < length; ++i) { + int high = Character.digit(hex[i * 2], 16); + int low = Character.digit(hex[i * 2 + 1], 16); + int value = high << 4 | low; + if (value > 127) { + value -= 256; + } + raw[i] = (byte) value; + } + return raw; + } + } +} diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/MainSecurity.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/MainSecurity.java new file mode 100644 index 0000000..3fb9e61 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/MainSecurity.java @@ -0,0 +1,84 @@ +/* +package com.sf.vertx.security; + +import java.util.logging.Logger; + +*/ +/** + * 本项目中真正对外提供服务的工具类 + *//* + +public class MainSecurity { + private static final Logger LOGGER = Logger.getLogger(MainSecurity.class.getName()); + + */ +/** + * 加密,当失败的时候,返回空字符串 + * + * @param content + * @param pubKey + * @return + *//* + + public static String rsaEncrypt(String content, String pubKey) { + try { + return RSAUtil.encrypt(content, pubKey); + } catch (Exception e) { + LOGGER.info("RSA加密失败"); + e.printStackTrace(); + return null; + } + } + + public static String rsaDecrypt(String content, String priKey) { + try { + return RSAUtil.decrypt(content, priKey); + } catch (Exception e) { + LOGGER.info("RSA解密失败"); + e.printStackTrace(); + return null; + } + } + + public static String aesEncrypt(String content, String key) { + try { + return AesUtils.encrypt(content, key); + } catch (Exception e) { + e.printStackTrace(); + LOGGER.info("AES加密失败"); + return null; + } + } + + public static String aesDecrypt(String content, String key) { + try { + return AesUtils.decrypt(content, key); + } catch (Exception e) { + e.printStackTrace(); + LOGGER.info("AES解密失败"); + return null; + } + } + + public static String sign(String content) { + try { + return MessageDigest.md5(content); + } catch (Exception e) { + e.printStackTrace(); + LOGGER.info("MD5加密失败"); + return null; + } + } + + public static void main(String[] args) { +// System.out.println(aesEncrypt("{\n" +// + " \"errorCode\": \"中文\",\n" +// + " \"result\": 0,\n" +// + " \"data\": {\n" +// + " \"username\" : \"测试\"\n" +// + " }\n" +// + "}", "dadddsdfadfadsfa33323223")); +// System.out.println(aesDecrypt("59A69B6BBCF046C3CF9953C5CC078CC638602D454BBCE8CF8F0DA6AF1F3A4707686263C834A612C5C6F22D9F897B13B434A53E32AAD4036E12A5098565AB1AD352B400FC23354ECE977DDC670F793992D7F884264A9689B000E37157B4D41351", "dadddsdfadfadsfa33323223")); + } +} +*/ diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/MessageDigest.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/MessageDigest.java new file mode 100644 index 0000000..e570d2e --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/MessageDigest.java @@ -0,0 +1,47 @@ +package com.sf.vertx.security; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.security.NoSuchAlgorithmException; + +public class MessageDigest { + + public static String md5(String text) throws NoSuchAlgorithmException { + java.security.MessageDigest digest = java.security.MessageDigest.getInstance("md5"); + byte[] buffer = digest.digest(text.getBytes()); + StringBuilder sb = new StringBuilder(); + for (byte b : buffer) { + int a = b & 0xff; + String hex = Integer.toHexString(a); + + if (hex.length() == 1) { + hex = 0 + hex; + } + sb.append(hex); + } + return sb.toString(); + } + + private static String hmacSha1(String data, String key, int type) throws Exception { + //根据给定的字节数组构造一个密钥,第二参数指定一个密钥算法的名称 + SecretKeySpec signinKey = new SecretKeySpec(key.getBytes(), "HmacSHA1"); + //生成一个指定 Mac 算法 的 Mac 对象 + Mac mac = Mac.getInstance("HmacSHA1"); + //用给定密钥初始化 Mac 对象 + mac.init(signinKey); + //完成 Mac 操作 + byte[] rawHmac = mac.doFinal(StringUtils.getBytes(data)); + String result; + switch (type) { + case 1: + result = Base64Utils.encode(rawHmac); + break; + case 2: + result = HexUtils.bytes2Hex(rawHmac); + break; + default: + throw new Exception("Unsupport Type"); + } + return result; + } +} \ No newline at end of file diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/RSA2Utils.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/RSA2Utils.java new file mode 100644 index 0000000..5ab966d --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/RSA2Utils.java @@ -0,0 +1,276 @@ +//package com.sf.vertx.security; +// +// +//import javax.crypto.BadPaddingException; +//import javax.crypto.Cipher; +//import javax.crypto.IllegalBlockSizeException; +//import javax.crypto.NoSuchPaddingException; +//import java.io.ByteArrayOutputStream; +//import java.io.IOException; +//import java.io.UnsupportedEncodingException; +//import java.security.*; +//import java.security.spec.InvalidKeySpecException; +//import java.security.spec.PKCS8EncodedKeySpec; +//import java.security.spec.X509EncodedKeySpec; +//import java.util.List; +// +///** +// * 本类进行非对称加密,不推荐使用非对称加密对长字符串进行加密或者解密,徒增资源消耗,另外由于长度限制,过长的字符串的加密和解密会使用循环,对数据分段加密;本类采用的 +// * 密钥字符串均为Base64加密后的 +// * 另外所有异常都会抛出 +// * 下面将会列举几个可以自定义或者暴露出去的接口和参数 +// * {@link #IS_LONG_TEXT} 是否否对长文本处理 +// * {@link #RESULT_TYPE} 密文结果:1=base64 2=hex +// * {@link #RSA_ALGORITHM} RSA算法 +// * {@link #encrypt(String, String)} 加密方法 +// * {@link #decrypt(String, String)} 解密方法 +// * {@link #getKeyPair} 解密方法 +// */ +//public class RSA2Utils { +// /** +// * 是否对长文本加密;请参照{@link #MAX_DECRYPT_BLOCK}和{@link #MAX_ENCRYPT_BLOCK} +// */ +// private static final boolean IS_LONG_TEXT = true; +// /** +// * 结果类型 +// */ +// private static final int RESULT_TYPE = 2; +// /** +// * RSA 算法 +// */ +// private static final String RSA_ALGORITHM = "RSA"; +// /** +// * 长文本解密块大小 +// */ +// private static final int MAX_DECRYPT_BLOCK = 128; +// /** +// * 长文本加密块大小 +// */ +// private static final int MAX_ENCRYPT_BLOCK = 117; +// /** +// * KyeSize +// */ +// private static final int KEY_SIZE = 2048; +// +// /** +// * 加密 +// * +// * @param content 待加密的字符串 +// * @param pubKey 公钥字符串 +// * @return 加密后的文本 +// * @throws Exception 异常 +// */ +// public static String encrypt(String content, String pubKey) throws Exception { +// byte[] data = StringUtils.getBytes(content); +// PublicKey publicKey = string2PubKey(pubKey); +// byte[] resultArr; +// if (IS_LONG_TEXT) { +// resultArr = encryptLongStr(data, publicKey); +// } else { +// resultArr = encrypt(data, publicKey); +// } +// String result; +// switch (RESULT_TYPE) { +// case 1: +// result = Base64Utils.encode(resultArr); +// break; +// case 2: +// result = HexUtils.bytes2Hex(resultArr); +// break; +// default: +// throw new Exception("Unsupport result type"); +// } +// return result; +// } +// +// /** +// * @param content 密文内容 +// * @param priKey 私钥 +// * @return 解密后的字符串 +// * @throws Exception 异常 +// */ +// public static String decrypt(String content, String priKey) throws Exception { +// byte[] data; +// switch (RESULT_TYPE) { +// case 1: +// data = Base64Utils.decode(content); +// break; +// case 2: +// data = HexUtils.hex2Bytes(content); +// break; +// default: +// throw new Exception("Unsupport result type"); +// } +// PrivateKey privateKey = string2PrivateKey(priKey); +// byte[] result; +// if (IS_LONG_TEXT) { +// result = decryptLongStr(data, privateKey); +// } else { +// result = decrypt(privateKey, data); +// } +// return StringUtils.bytes2String(result); +// } +// +// /** +// * 响应公私钥对 +// * +// * @return 0号 公钥 1号 私钥 +// * @throws NoSuchAlgorithmException 异常 +// */ +// public static List getKeyPair() throws NoSuchAlgorithmException { +// KeyPair keyPairObj = getKeyPairObj(); +// return List.of(Base64Utils.encode(keyPairObj.getPublic().getEncoded()), Base64Utils.encode(keyPairObj.getPrivate().getEncoded())); +// } +// +// /** +// * 将公钥字符串转化为对象 +// * +// * @param s base64字符串 +// * @return 公钥 +// * @throws NoSuchAlgorithmException 异常 +// * @throws UnsupportedEncodingException 异常 +// * @throws InvalidKeySpecException 异常 +// */ +// private static PublicKey string2PubKey(String s) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeySpecException { +// KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM); +// X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64Utils.decode(s)); +// return keyFactory.generatePublic(keySpec); +// } +// +// /** +// * 对段字符串进行加密 +// * +// * @param bytes 字节数组 +// * @param publicKey 公钥 +// * @return 加密后的数组 +// * @throws InvalidKeyException 异常 +// * @throws BadPaddingException 异常 +// * @throws IllegalBlockSizeException 异常 +// * @throws NoSuchPaddingException 异常 +// * @throws NoSuchAlgorithmException 异常 +// */ +// private static byte[] encrypt(byte[] bytes, PublicKey publicKey) throws InvalidKeyException, BadPaddingException, IllegalBlockSizeException, NoSuchPaddingException, NoSuchAlgorithmException { +// Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); +// cipher.init(Cipher.ENCRYPT_MODE, publicKey); +// return cipher.doFinal(bytes); +// } +// +// /** +// * 对长字符串进行加密 +// * +// * @param bytes 字节数组 +// * @param publicKey 公钥 +// * @return 加密后的数组 +// * @throws NoSuchPaddingException 异常 +// * @throws NoSuchAlgorithmException 异常 +// * @throws InvalidKeyException 异常 +// */ +// private static byte[] encryptLongStr(byte[] bytes, PublicKey publicKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException { +// Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); +// cipher.init(Cipher.ENCRYPT_MODE, publicKey); +// int inputLen = bytes.length; +// byte[] encryptedData = new byte[0]; +// try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { +// int offSet = 0; +// byte[] cache; +// int i = 0; +// // 对数据分段加密 +// while (inputLen - offSet > 0) { +// if (inputLen - offSet > MAX_ENCRYPT_BLOCK) { +// cache = cipher.doFinal(bytes, offSet, MAX_ENCRYPT_BLOCK); +// } else { +// cache = cipher.doFinal(bytes, offSet, inputLen - offSet); +// } +// out.write(cache, 0, cache.length); +// i++; +// offSet = i * MAX_ENCRYPT_BLOCK; +// } +// encryptedData = out.toByteArray(); +// } catch (IOException | BadPaddingException | IllegalBlockSizeException e) { +// e.printStackTrace(); +// } +// return encryptedData; +// } +// +// /** +// * 私钥字符串转为私钥对象 +// * +// * @param priStr 私钥字符串 +// * @return 私钥对象 +// * @throws NoSuchAlgorithmException 异常 +// * @throws InvalidKeySpecException 异常 +// */ +// private static PrivateKey string2PrivateKey(String priStr) throws NoSuchAlgorithmException, InvalidKeySpecException { +// PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64Utils.decode(priStr)); +// KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM); +// return keyFactory.generatePrivate(keySpec); +// } +// +// /** +// * 解密 +// * +// * @param privateKey 私钥 +// * @param bytes 字节数组 +// * @return 解密后的字节数组 +// * @throws NoSuchPaddingException 异常 +// * @throws NoSuchAlgorithmException 异常 +// * @throws BadPaddingException 异常 +// * @throws IllegalBlockSizeException 异常 +// * @throws InvalidKeyException 异常 +// */ +// public static byte[] decrypt(PrivateKey privateKey, byte[] bytes) throws NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException, InvalidKeyException { +// Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm()); +// cipher.init(Cipher.DECRYPT_MODE, privateKey); +// return cipher.doFinal(bytes); +// } +// +// /** +// * 解密 +// * +// * @param data 解密前的字节数组 +// * @param privateKey 私钥 +// * @return 解密后的字节数组 +// * @throws InvalidKeyException 异常 +// * @throws NoSuchPaddingException 异常 +// * @throws NoSuchAlgorithmException 异常 +// */ +// public static byte[] decryptLongStr(byte[] data, PrivateKey privateKey) throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException { +// Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm()); +// cipher.init(Cipher.DECRYPT_MODE, privateKey); +// int inputLen = data.length; +// byte[] result = new byte[0]; +// try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { +// int offSet = 0; +// byte[] cache; +// int i = 0; +// // 对数据分段解密 +// while (inputLen - offSet > 0) { +// if (inputLen - offSet > MAX_DECRYPT_BLOCK) { +// cache = cipher.doFinal(data, offSet, MAX_DECRYPT_BLOCK); +// } else { +// cache = cipher.doFinal(data, offSet, inputLen - offSet); +// } +// out.write(cache, 0, cache.length); +// i++; +// offSet = i * MAX_DECRYPT_BLOCK; +// } +// result = out.toByteArray(); +// } catch (BadPaddingException | IllegalBlockSizeException | IOException e) { +// e.printStackTrace(); +// } +// return result; +// } +// +// /** +// * 获得一堆公私钥 +// * +// * @return KeyPair对象 +// * @throws NoSuchAlgorithmException 异常 +// */ +// private static KeyPair getKeyPairObj() throws NoSuchAlgorithmException { +// KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA_ALGORITHM); +// SecureRandom secureRandom = new SecureRandom(StringUtils.getBytes(String.valueOf(System.currentTimeMillis()))); +// keyPairGenerator.initialize(KEY_SIZE, secureRandom); +// return keyPairGenerator.genKeyPair(); +// } +//} diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/RSAUtil.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/RSAUtil.java new file mode 100644 index 0000000..f3a0047 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/RSAUtil.java @@ -0,0 +1,154 @@ +/* +package com.sf.vertx.security; + +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.HashMap; +import java.util.Map; + +import javax.crypto.Cipher; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.lang3.ArrayUtils; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class RSAUtil { + + // RSA最大加密明文大小 + private static final int MAX_ENCRYPT_BLOCK = 117; + + // RSA最大解密密文大小 + private static final int MAX_DECRYPT_BLOCK = 128; + + private RSAUtil() { + } + + */ +/** + * 获取公钥和私钥对,key为公钥,value为私钥 + * + * @return + * @throws NoSuchAlgorithmException + *//* + + public static Map genKeyPair() throws NoSuchAlgorithmException { + KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA"); + keyPairGen.initialize(1024, new SecureRandom()); + KeyPair keyPair = keyPairGen.generateKeyPair(); + RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); + RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); + String publicKeyString = null; + String privateKeyString = null; + + try { + publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()), "UTF-8"); + privateKeyString = new String(Base64.encodeBase64(privateKey.getEncoded()), "UTF-8"); + } catch (UnsupportedEncodingException var7) { + var7.printStackTrace(); + } + + Map keyPairMap = new HashMap<>(); + keyPairMap.put("publicKey", publicKeyString); + keyPairMap.put("privateKey", privateKeyString); + return keyPairMap; + } + + public static String encrypt(String str, String publicKey) throws Exception { + byte[] decoded = Base64.decodeBase64(publicKey); + RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA") + .generatePublic(new X509EncodedKeySpec(decoded)); + Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); + cipher.init(1, pubKey); + // 分段加密 + // URLEncoder编码解决中文乱码问题 + byte[] data = URLEncoder.encode(str, "UTF-8").getBytes("UTF-8"); + // 加密时超过117字节就报错。为此采用分段加密的办法来加密 + byte[] enBytes = null; + for (int i = 0; i < data.length; i += MAX_ENCRYPT_BLOCK) { + // 注意要使用2的倍数,否则会出现加密后的内容再解密时为乱码 + byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(data, i, i + MAX_ENCRYPT_BLOCK)); + enBytes = ArrayUtils.addAll(enBytes, doFinal); + } + return Base64.encodeBase64String(enBytes); + } + + */ +/** + * 公钥分段解密 + * + * @param str + * @param privateKey + * @return + * @throws Exception + *//* + + public static String decrypt(String str, String privateKey) throws Exception { + // 获取公钥 + byte[] decoded = Base64.decodeBase64(privateKey); + RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA") + .generatePrivate(new PKCS8EncodedKeySpec(decoded)); + Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); + cipher.init(2, priKey); + byte[] data = Base64.decodeBase64(str.getBytes("UTF-8")); + + // 返回UTF-8编码的解密信息 + int inputLen = data.length; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int offSet = 0; + byte[] cache; + int i = 0; + // 对数据分段解密 + while (inputLen - offSet > 0) { + if (inputLen - offSet > MAX_DECRYPT_BLOCK) { + cache = cipher.doFinal(data, offSet, MAX_DECRYPT_BLOCK); + } else { + cache = cipher.doFinal(data, offSet, inputLen - offSet); + } + out.write(cache, 0, cache.length); + i++; + offSet = i * 128; + } + byte[] decryptedData = out.toByteArray(); + out.close(); + return URLDecoder.decode(new String(decryptedData, "UTF-8"), "UTF-8"); + } + + public static void main(String[] args) throws Exception { + Map keyMap = genKeyPair(); + String publicKey = keyMap.get("publicKey"); + String privateKey = keyMap.get("privateKey"); + privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIBjjfp2vkdIDzw9vTzei9S1Gg8KLfPWNYfESwgqGvPY/ps3rIfgPH4na3qy957AY/zHExjaPGbbBRBRPV3xg1vrccr0oZLPiC5n7+4Z5TR6wYiPOn3osSjCweUJ7LttOTAr8sqvaHlrDbpZD+ZoOiB0TEWWD9TvI4pK0sVs7JYRAgMBAAECgYAJ08T3c80Y/u2mnGmunfC5U3LnDY4KpN30Uky1d2aYfWawqhRnUp1CwUDvc4EzajHFFJZUP8khjNhwgS1nsk+t9fnz/GJSS2ZYIOn/i1WEKCJvCILuFpx9tqQM1I9EdueeW1VQgm24o6vbBTP6JzoRXN/l/dAGldluY+Y2JclVeQJBAIyZdxIo1G7qs2nbKkynQaPY/ogmn7VLTWwowri1bhWt0zzcDu3/TgLLPldWVwYUuRChyVvX3cuQB0ICwiKTB3kCQQDpxGufjUgalAZwe+RCxIDriKVvYmB/krk/escAR39Ya6Od7nHSbmdBUmc7vjLbVN3BsYWZrlxHnGelEetebqVZAkBJrxvR7of2YRYJwgxXA8jIv64VWHiWoJJAvtPdzWeWAPUVjhZc8FHH8RAI4XzV+QJMDx3h/i2Ew0SqeZuYVwmxAkBh2T3TQyfzOBKZ8sHQ0L/F1ySoQt1xiNDRqWqyyzqaoDOUX8J0+pFt3jgn4a0X8aYA9XWepkUqFGWtyppipJ3BAkEAigeRnLS/J4Z6inWy4XJfFrggYvtQvWQnGfjHNQijm7+c8Nb+gs2EjbkEHg13yFUpBa3sVPN+aOhP2R41t4EywA=="; + publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCAY436dr5HSA88Pb083ovUtRoPCi3z1jWHxEsIKhrz2P6bN6yH4Dx+J2t6sveewGP8xxMY2jxm2wUQUT1d8YNb63HK9KGSz4guZ+/uGeU0esGIjzp96LEowsHlCey7bTkwK/LKr2h5aw26WQ/maDogdExFlg/U7yOKStLFbOyWEQIDAQAB"; + log.info("publicKey = " + publicKey); + log.info("privateKey = " + privateKey); + String originValue = "{\n" + + " \"data\": \"1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说\",\n" + + " \"data1\": \"11是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说\",\n" + + " \"data2\": \"11是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说\"\n" + + "}"; + // 加密前:原数据123456,测试一下 + System.out.println("加密前:" + originValue); + + String encrypt = encrypt(originValue, publicKey); + log.info("加密后:" + encrypt); + encrypt = "HxlTvNpgMBj3tcqcXWQc1SFvn8c4nv0HYBlvuVy07BUa1ynQXhhNoT9o7iX5/edJ9xLGITsWRMUfQvuND9ttsyQS44oRmbkRBAdLZQOxaS4hkMDGleWMZII0VgQbULeOd7SEq5Ba2UU7TDySg0VLRgRRgnxt2qUig9Gx/3quQY19Ts9Wu+NBRi1JvCQD5bKi+9BW+jMKIjZ5hx2MwsVVIQDkoQKLcRTV6lejrXdPP7fFo6rG3/OULM7+U/rw2EoC+F+2NRIoEIQj2jclmM8kqAspNJH2NmP/0lsgjay7/nK2nU1Gz/zeQsSVgJmggTUxTImKk5eoqckuNdPD2mwyRQ=="; + String decrypt = decrypt(encrypt, privateKey); + log.info("解密后:" + decrypt); + } +} + + +*/ diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/StringUtils.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/StringUtils.java new file mode 100644 index 0000000..700c7f4 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/StringUtils.java @@ -0,0 +1,53 @@ +package com.sf.vertx.security; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +public class StringUtils { + /** + * 编码方法 + */ + private static final Charset CHARSET = StandardCharsets.UTF_8; + + /** + * 根据本类的常量对字符串进行获取字节数组的操作 + * + * @param s String:待处理的字符串 + * @return byte[] 形成的字节数组 + */ + public static byte[] getBytes(String s) { + return s.getBytes(CHARSET); + } + + /** + * 将字节数组转为文本 + * + * @param data 字节数组 + * @return 文本 + */ + public static String bytes2String(byte[] data) { + return new String(data, CHARSET); + } + + + /** + * 字符串是不是空的 + * + * @param str 字符串 + * @return 结果 + */ + public static boolean isBlank(String str) { + int strLen; + if (str != null && (strLen = str.length()) != 0) { + for (int i = 0; i < strLen; ++i) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } else { + return true; + } + } + +} diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/service/AppConfigService.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/service/AppConfigService.java new file mode 100644 index 0000000..d110daa --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/service/AppConfigService.java @@ -0,0 +1,13 @@ +package com.sf.vertx.service; + +import com.sf.vertx.api.pojo.AppConfig; +import com.sf.vertx.api.pojo.VertxConfig; + +public interface AppConfigService { + + void saveAppConfig(AppConfig appConfig); + + void deleteAppConfig(AppConfig appConfig); + + void saveVertxConfig(VertxConfig vertxConfig); +} diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/service/impl/AppConfigServiceImpl.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/service/impl/AppConfigServiceImpl.java new file mode 100644 index 0000000..a784756 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/service/impl/AppConfigServiceImpl.java @@ -0,0 +1,82 @@ +/* +package com.sf.vertx.service.impl; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +import com.alibaba.fastjson2.JSONObject; +import com.sf.vertx.api.pojo.AppConfig; +import com.sf.vertx.api.pojo.VertxConfig; +import com.sf.vertx.constans.RedisKeyConfig; +import com.sf.vertx.handle.AppConfigHandler; +import com.sf.vertx.pojo.ClusterEventMsg; +import com.sf.vertx.service.AppConfigService; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Service +public class AppConfigServiceImpl implements AppConfigService { + @Value("${server.vertx.environment}") + private String vertxEnvironment; + + @Autowired + private RedisTemplate redisTemplate; + + */ +/*** + * 新增、修改 + * + * @param appConfig + *//* + + public void saveAppConfig(AppConfig appConfig) { + redisTemplate.opsForZSet().add(RedisKeyConfig.APP_CONFIG_SET_KEY, appConfig.getAppCode(), 0); + String appCodeKey = RedisKeyConfig.APP_CONFIG_PREFIX_KEY + ":" + appConfig.getAppCode(); + redisTemplate.opsForValue().set(appCodeKey, JSONObject.toJSONString(appConfig)); + + // 发布集群消息 + ClusterEventMsg msg = new ClusterEventMsg(); + msg.setType(1); + msg.setOperation(1); + msg.setAppCode(appConfig.getAppCode()); + AppConfigHandler.publishClusterEventMsg(msg); + } + + */ +/*** + * 删除 + * + * @param appConfig + *//* + + public void deleteAppConfig(AppConfig appConfig) { + redisTemplate.opsForZSet().remove(RedisKeyConfig.APP_CONFIG_SET_KEY, appConfig.getAppCode()); + String appCodeKey = RedisKeyConfig.APP_CONFIG_PREFIX_KEY + ":" + appConfig.getAppCode(); + redisTemplate.delete(appCodeKey); + + // 发送集群消息 + ClusterEventMsg msg = new ClusterEventMsg(); + msg.setType(1); + msg.setOperation(3); + msg.setAppCode(appConfig.getAppCode()); + AppConfigHandler.publishClusterEventMsg(msg); + } + + */ +/*** + * 新增、修改 + * + * @param appConfig + *//* + + public void saveVertxConfig(VertxConfig vertxConfig) { + String vertxConfigKey = RedisKeyConfig.VERTX_CONFIG_STRING_KEY; + redisTemplate.opsForValue().set(vertxConfigKey, JSONObject.toJSONString(vertxConfig)); + AppConfigHandler.initVertxConfig(); + } + +} +*/ diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/utils/AppUtils.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/utils/AppUtils.java new file mode 100644 index 0000000..a0c6728 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/utils/AppUtils.java @@ -0,0 +1,269 @@ +/* +package com.sf.vertx.utils; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.NumberUtil; +import com.sf.vertx.api.pojo.*; +import com.sf.vertx.enums.GatewayError; +import com.sf.vertx.enums.GatewayServiceType; +import com.sf.vertx.enums.MatchType; +import com.sf.vertx.enums.RequestMethod; +import com.sf.vertx.handle.AppConfigHandler; +import io.vertx.circuitbreaker.CircuitBreaker; +import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.RoutingContext; +import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +import java.math.BigDecimal; +import java.util.List; + +import static com.sf.vertx.constans.SACConstants.*; + +*/ +/** + * 功能描述: + * + * @author a_kun + * @date 2024/5/24 12:30 + *//* + +@Slf4j +@UtilityClass +public class AppUtils { + + */ +/** + * 将AppConfig设置到上下文中 + * 配合getAppConfigFromRoutingContext(RoutingContext rc)使用 + * @param appConfig + * @param rc + *//* + + public static void setAppConfigToRoutingContext(AppConfig appConfig, RoutingContext rc) { + rc.put(APP_CONFIG,appConfig); + } + + */ +/** + * 从路由上下文获取AppConfig + * @param rc + * @return AppConfig + *//* + + public static AppConfig getAppConfigFromRoutingContext(RoutingContext rc) { + return rc.get(APP_CONFIG); + } + + */ +/** + * 将AppConfig设置到上下文中 + * 配合getApiConfigFromRoutingContext(RoutingContext rc)使用 + * @param apiConfig + * @param rc + *//* + + public static void setApiConfigIntoRoutingContext(ApiConfig apiConfig, RoutingContext rc) { + rc.put(API_CONFIG,apiConfig); + } + + */ +/** + * 从路由上下文获取ApiConfig + * @param rc + * @return ApiConfig + *//* + + public static ApiConfig getApiConfigFromRoutingContext(RoutingContext rc) { + return rc.get(API_CONFIG); + } + + public static boolean isEnableMockApi(ApiConfig apiConfig) { + if (apiConfig != null) { + return apiConfig.getMockDefaultHttpStatus() != null; + } + return false; + } + + */ +/** + * 判断API服务类型是否是SAC规范服务类型 + * + * @param apiServiceType + * @return + *//* + + public static boolean isSACServiceType(String apiServiceType) { + return GatewayServiceType.SAC.getCode().equals(apiServiceType); + } + + public static boolean isEnableDataSecurity(AppConfig appConfig) { + return appConfig != null && appConfig.getDataSecurity() != null; + } + + public static MockResponse mock(RoutingContext rc) { + AppConfig appConfig = getAppConfigFromRoutingContext(rc); + ApiConfig apiConfig = getApiConfigFromRoutingContext(rc); + String apiServiceType = rc.get(API_SERVICE_TYPE); + if (AppUtils.isEnableMockApi(apiConfig)) { + // 如果是sac服务,query和body参数都从body中取 + Integer httpStatus = apiConfig.getMockDefaultHttpStatus(); + String mockResponse = apiConfig.getMockDefaultResponse(); + List mockExpectations = apiConfig.getMockExpectations(); + if (mockExpectations != null && !mockExpectations.isEmpty()) { + String bodyStr = rc.body().asString(); + if (AppUtils.isEnableDataSecurity(appConfig)) { + bodyStr = AppConfigHandler.bodyDecrypt(bodyStr, appConfig.getAppCode()); + } + JsonObject jsonBody = new JsonObject(bodyStr); + for (MockExpectation mockExpectation : mockExpectations) { + // 匹配条件需要全部匹配成功 + boolean matchSuccess = true; + for (MockMatchCondition matchCondition : mockExpectation.getMatchConditions()) { + if (!matchSuccess) { + break; + } + MatchType matchType = MatchType.getByCode(matchCondition.getMatchType()); + if (matchType == null) { + matchSuccess = false; + break; + } + String parameterPosition = matchCondition.getParameterPosition(); + String parameterKey = matchCondition.getParameterKey(); + List parameterValue = matchCondition.getParameterValue(); + String requestParameterValue = getRequestParameterValue(AppUtils.isSACServiceType(apiServiceType), parameterPosition, parameterKey, rc.request(), jsonBody); + if (!MatchType.IS_NULL.equals(matchType) && !MatchType.NOT_NULL.equals(matchType)) { + // 需要值而没有设置值,直接匹配失败 + if (CollectionUtil.isEmpty(parameterValue) || requestParameterValue == null) { + matchSuccess = false; + break; + } + } + + switch (matchType) { + case EQ: + matchSuccess = parameterValue.get(0).equals(requestParameterValue); + break; + case NOT_EQ: + matchSuccess = !parameterValue.get(0).equals(requestParameterValue); + break; + case GT: + if (NumberUtil.isNumber(requestParameterValue) && NumberUtil.isNumber(parameterValue.get(0))) { + matchSuccess = new BigDecimal(requestParameterValue).compareTo(new BigDecimal(parameterValue.get(0))) > 0; + } else { + matchSuccess = requestParameterValue.compareTo(parameterValue.get(0)) > 0; + } + break; + case GE: + if (NumberUtil.isNumber(requestParameterValue) && NumberUtil.isNumber(parameterValue.get(0))) { + matchSuccess = new BigDecimal(requestParameterValue).compareTo(new BigDecimal(parameterValue.get(0))) >= 0; + } else { + matchSuccess = requestParameterValue.compareTo(parameterValue.get(0)) >= 0; + } + break; + case LT: + if (NumberUtil.isNumber(requestParameterValue) && NumberUtil.isNumber(parameterValue.get(0))) { + matchSuccess = new BigDecimal(requestParameterValue).compareTo(new BigDecimal(parameterValue.get(0))) < 0; + } else { + matchSuccess = requestParameterValue.compareTo(parameterValue.get(0)) < 0; + } + break; + case LE: + if (NumberUtil.isNumber(requestParameterValue) && NumberUtil.isNumber(parameterValue.get(0))) { + matchSuccess = new BigDecimal(requestParameterValue).compareTo(new BigDecimal(parameterValue.get(0))) <= 0; + } else { + matchSuccess = requestParameterValue.compareTo(parameterValue.get(0)) <= 0; + } + break; + case IN: + matchSuccess = parameterValue.contains(requestParameterValue); + break; + case NOT_IN: + matchSuccess = !parameterValue.contains(requestParameterValue); + break; + case IS_NULL: + matchSuccess = requestParameterValue == null; + break; + case NOT_NULL: + matchSuccess = requestParameterValue != null; + break; + default: + break; + } + + } + if (matchSuccess) { + httpStatus = mockExpectation.getHttpStatus(); + mockResponse = mockExpectation.getMockResponse(); + break; + } + } + } + return new MockResponse(httpStatus, mockResponse); + } + return null; + } + + private static String getRequestParameterValue(Boolean isServiceTypeSac, String parameterPosition, String parameterKey, HttpServerRequest request, JsonObject jsonBody) { + switch (parameterPosition) { + case "query": + if (isServiceTypeSac) { + return jsonBody.getString(parameterKey); + } + return request.getParam(parameterKey); + case "header": + return request.getHeader(parameterKey); + case "body": + return jsonBody.getString(parameterKey); + default: + break; + } + return null; + } + + public static String getResponseMsgByGatewayError(GatewayError gatewayError) { + if (gatewayError != null) { + return gatewayError.getReasonPhrase(); + } + return getResponseMsgByGatewayError(GatewayError.DEFAULT_SERVICE_ERROR); + } + + public static JsonObject getResponseJsonByGatewayError(GatewayError gatewayError) { + if (gatewayError != null) { + JsonObject jsonObject = new JsonObject(); + jsonObject.put("code",gatewayError.getCode()); + jsonObject.put("msg",gatewayError.getReasonPhrase()); + return jsonObject; + } + return getResponseJsonByGatewayError(GatewayError.DEFAULT_SERVICE_ERROR); + } + + public static boolean isAnalysisBody(AppConfig appConfig, ApiConfig apiConfig,String apiServiceType) { + // 不是Mock(是mock模式的话,存在body就解析。) + if (AppUtils.isEnableMockApi(apiConfig)) { + return true; + } + // SAC模式,实际API请求方式GET,DELETE,HEAD请求需要解析body,可能需要二次处理。(SAC模式请求方式参数统一使用body传递) + // SAC模式,如果是POST或者PUT,并且没有设置数据加密则跳过 + if (AppUtils.isSACServiceType(apiServiceType)) { + RequestMethod requestMethod = RequestMethod.getByCode(apiConfig.getMethod()); + if (RequestMethod.GET.equals(requestMethod) + || RequestMethod.DELETE.equals(requestMethod) + || RequestMethod.HEAD.equals(requestMethod)) { + return true; + } + String keyCircuitBreaker = appConfig.getAppCode() + ":" + apiConfig.getApiCode() + ":" + "CIRCUIT_BREAKER"; + CircuitBreaker circuitBreaker = AppConfigHandler.getApiCodeCircuitBreaker(keyCircuitBreaker); + boolean isDataSecurity = AppUtils.isEnableDataSecurity(appConfig); + // 文件上传不走加解密 + return (isDataSecurity || circuitBreaker != null); + }else { + // 未启用Mock,OPEN模式都不会解析body + return false; + } + + } +} +*/ diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/utils/Filter.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/utils/Filter.java new file mode 100644 index 0000000..638c1da --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/utils/Filter.java @@ -0,0 +1,91 @@ +package com.sf.vertx.utils; + +import java.util.concurrent.atomic.AtomicBoolean; + +import io.vertx.core.Handler; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.streams.ReadStream; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class Filter implements ReadStream { + + private final AtomicBoolean paused = new AtomicBoolean(); + private ReadStream stream; + private Buffer expected = Buffer.buffer(); + private Handler dataHandler; + private Handler exceptionHandler; + private Handler endHandler; + + /*** + * + * @param s + * @param encryption true: 请求参数解密, false: 返回数据 加密 + * @return + */ + public ReadStream init(String uri, ReadStream s, Boolean encryption) { + stream = s; + stream.handler(buff -> { + if (dataHandler != null) { + byte[] bytes = new byte[buff.length()]; + for (int i = 0; i < bytes.length; i++) { + // bytes[i] = (byte) (('a' - 'A') + buff.getByte(i)); + bytes[i] = (byte) (buff.getByte(i)); + } + + String res = new String(bytes); + log.info("request uri:{}, return data:{}", uri, res); + expected.appendBytes(bytes); + dataHandler.handle(Buffer.buffer(bytes)); + } + }); + stream.exceptionHandler(err -> { + if (exceptionHandler != null) { + exceptionHandler.handle(err); + } + }); + stream.endHandler(v -> { + if (endHandler != null) { + endHandler.handle(v); + } + }); + return this; + } + + @Override + public ReadStream pause() { + paused.set(true); + stream.pause(); + return this; + } + + @Override + public ReadStream resume() { + stream.resume(); + return this; + } + + @Override + public ReadStream fetch(long amount) { + stream.fetch(amount); + return this; + } + + @Override + public ReadStream exceptionHandler(Handler handler) { + exceptionHandler = handler; + return this; + } + + @Override + public ReadStream handler(Handler handler) { + dataHandler = handler; + return this; + } + + @Override + public ReadStream endHandler(Handler handler) { + endHandler = handler; + return this; + } +} \ No newline at end of file diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/utils/ProxyTool.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/utils/ProxyTool.java new file mode 100644 index 0000000..770511b --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/utils/ProxyTool.java @@ -0,0 +1,82 @@ +/* +package com.sf.vertx.utils; + +import java.util.List; +import java.util.regex.Pattern; + +import com.sf.vertx.api.pojo.Node; +import com.sf.vertx.api.pojo.RouteContent; +import com.sf.vertx.arithmetic.roundRobin.SacLoadBalancing; +import com.sf.vertx.arithmetic.roundRobin.WeightedRoundRobin; +import com.sf.vertx.handle.AppConfigHandler; + +import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.net.SocketAddress; +import io.vertx.ext.web.handler.HttpException; +import lombok.extern.slf4j.Slf4j; + +*/ +/*** + * 反向代理工具类 + * + * @author xy + * + *//* + +@Slf4j +public class ProxyTool { + + public static SocketAddress resolveOriginAddress(HttpServerRequest request) { + String appCode = request.getHeader(AppConfigHandler.getAppCodeHeaderKey()); + String apiCode = request.getHeader(AppConfigHandler.getApiCodeHeaderKey()); + log.info("uri:{}, header appCode:{},apiCode:{}", request.uri(), appCode, apiCode); + // 判断 "routeType": "WEIGHT_ROUTE", // 路由类型 WEIGHT_ROUTE ,HEADER_ROUTE + String key = appCode + ":" + apiCode; + Integer routerType = AppConfigHandler.routerType(key); + SocketAddress socketAddress = null; + switch (routerType) { + case 1: + SacLoadBalancing sacLoadBalancing = AppConfigHandler.getLoadBalancing(key); + Node node = sacLoadBalancing.selectNode(); + socketAddress = SocketAddress.inetSocketAddress(node.getPort(), node.getIp()); + log.info("sacLoadBalancing address:{},port:{}", socketAddress.host(), socketAddress.port()); + return socketAddress; + case 2: + List routeContentList = AppConfigHandler.routerHeaderConentList(key); + if(routeContentList != null && routeContentList.size() > 0) { + for (RouteContent routeContent : routeContentList) { + String headerValue = request.getHeader(routeContent.getHeaderKey()); + List headerValues = routeContent.getHeaderValues(); + // String matchType = routeContent.getMatchType(); + if(headerValues.contains(headerValue)) { + socketAddress = SocketAddress.inetSocketAddress(routeContent.getServerAddress().getPort(), routeContent.getServerAddress().getHost()); + log.info("sacLoadBalancing address:{},port:{}", socketAddress.host(), socketAddress.port()); + return socketAddress; + } + } + } + break; + } + + // 抛出异常,无法找到负载均衡node节点 + throw new HttpException(10021); + } + + public static boolean regexMatch(String pattern, String target) { + return Pattern.matches(pattern, target); + } + + public static SacLoadBalancing roundRobin(List nodeList) { + WeightedRoundRobin weightedRoundRobin = new WeightedRoundRobin(); + for (Node node : nodeList) { + int weight = node.getWeight() != null ? node.getWeight() : 1; + node.setWeight(weight); + node.setCurrentWeight(weight); + node.setEffectiveWeight(weight); + weightedRoundRobin.totalWeight += node.getEffectiveWeight(); + } + weightedRoundRobin.init(nodeList); + return weightedRoundRobin; + } +} +*/ diff --git a/zt-vertx/zt-vertx-service/src/main/resources/META-INF/MANIFEST.MF b/zt-vertx/zt-vertx-service/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 0000000..1beae65 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1,2 @@ +Automatic-Module-Name: io.vertx.core + diff --git a/zt-vertx/zt-vertx-service/src/main/resources/META-INF/native-image/io.vertx/vertx-core/reflect-config.json b/zt-vertx/zt-vertx-service/src/main/resources/META-INF/native-image/io.vertx/vertx-core/reflect-config.json new file mode 100644 index 0000000..a489713 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/resources/META-INF/native-image/io.vertx/vertx-core/reflect-config.json @@ -0,0 +1,38 @@ +[ + { + "name": "java.lang.Thread$Builder", + "condition": { + "typeReachable": "io.vertx.core.impl.VertxImpl" + }, + "methods": [ + { + "name": "factory", + "parameterTypes": [] + } + ] + }, + { + "name": "java.lang.Thread$Builder$OfVirtual", + "condition": { + "typeReachable": "io.vertx.core.impl.VertxImpl" + }, + "methods": [ + { + "name": "name", + "parameterTypes": ["java.lang.String", "long"] + } + ] + }, + { + "name": "java.lang.Thread", + "condition": { + "typeReachable": "io.vertx.core.impl.VertxImpl" + }, + "methods": [ + { + "name": "ofVirtual", + "parameterTypes": [] + } + ] + } +] diff --git a/zt-vertx/zt-vertx-service/src/main/resources/META-INF/services/io.vertx.core.spi.launcher.CommandFactory b/zt-vertx/zt-vertx-service/src/main/resources/META-INF/services/io.vertx.core.spi.launcher.CommandFactory new file mode 100644 index 0000000..d6e4603 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/resources/META-INF/services/io.vertx.core.spi.launcher.CommandFactory @@ -0,0 +1,18 @@ +# Copyright (c) 2011-2017 Contributors to the Eclipse Foundation +# +# This program and the accompanying materials are made available under the +# terms of the Eclipse Public License 2.0 which is available at +# http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +# which is available at https://www.apache.org/licenses/LICENSE-2.0. +# +# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + +# Core commands +io.vertx.core.impl.launcher.commands.RunCommandFactory +io.vertx.core.impl.launcher.commands.VersionCommandFactory +io.vertx.core.impl.launcher.commands.BareCommandFactory + +# Background application control +io.vertx.core.impl.launcher.commands.ListCommandFactory +io.vertx.core.impl.launcher.commands.StartCommandFactory +io.vertx.core.impl.launcher.commands.StopCommandFactory diff --git a/zt-vertx/zt-vertx-service/src/main/resources/META-INF/vertx/vertx-version.txt b/zt-vertx/zt-vertx-service/src/main/resources/META-INF/vertx/vertx-version.txt new file mode 100644 index 0000000..f2ab45c --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/resources/META-INF/vertx/vertx-version.txt @@ -0,0 +1 @@ +${project.version} \ No newline at end of file diff --git a/zt-vertx/zt-vertx-service/src/main/resources/application.yaml b/zt-vertx/zt-vertx-service/src/main/resources/application.yaml new file mode 100644 index 0000000..34332be --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/resources/application.yaml @@ -0,0 +1,39 @@ +# 开发环境配置 + +vertx: + deploymentMode: 1 # 1:单机 2:集群 + isSSL: false # vertx https或者http启动, ssl 证书有过期时间 + rpcPort: 80 # RPC调用使用的端口 + rpcUri: /rpc.sac # RPC调用使用的url + sacResponseHeaderKey: sacErrorCode + environment: dev + configPort: 5566 # 注册配置的端口 + blackIP: + - 192.168.1.20 + - 192.168.1.21 + cluster: + ip: 127.0.0.1 + clusterName: sac-dev + networkPort: 5701 + portAutoIncrement: false + +# 日志配置 +logging: + level: + com.sf: info + io.vertx: debug + org.springframework: warn + + +# redis 配置 +redis: + clientType: STANDALONE + poolName: vertx-redis + maxPoolSize: 8 + maxPoolWaiting: 32 + poolCleanerInterval: 30000 + urls: + - redis://192.168.1.23:22002/1 + # 密码 + password: + diff --git a/zt-vertx/zt-vertx-service/src/main/resources/cacerts.jks b/zt-vertx/zt-vertx-service/src/main/resources/cacerts.jks new file mode 100644 index 0000000000000000000000000000000000000000..bd8a4036506883f92784bf36313b3fdfe731fcfc GIT binary patch literal 1286 zcmV+h1^N0gf&~Hs0Ru3C1gr)LDuzgg_YDCD0ic2eodkjenJ|I`l`w(?kp>AWhDe6@ z4FLxRpn?Q~FoFbr0s#Opf&_O42`Yw2hW8Bt2LUi<1_>&LNQU+thDZTr0|Wso1Q31C{mUoj(c&I6Hy3(lsMmmk1OUkh-jH|}-h8@WX7uoWk3U>_;xu=LMxhm2X?(E(sgvz^doV2LkGOKUqa%7iKA*Q=j0#8cSp(}; zZ~5Nn0jUB%^XUV#83usgWq7tlW+9&4{{TtbMu!3k%v&NJ6!% zE6@0ZsOelS`G1IpJaMgFpgE_Ubi3od!6#=91V1uZW-MUuQO9@H^|wN050meA>cu-; z{w-jt;gkJ*BVfVkGcpHyaywm?tTb9AbT;#4 zL2~r-SwgxX2VWjEox|>h`--{OQ6ibf_Y^m2*HQ}*wT6x~+~5`3*_!#_U_oUU-OiVP ztD4G2kuaF5TExWH5Am+B4Iid&X2ex@fi6!VE7S;8A-C=F!s~z#D!MI@y*hYa!|SKc zLeqB+i5{-}F9^mQdVi2@abHHEg7+NzA1yZ}!OCO2fxfnFbruR6Yq1)ijZM!j1gX^I zcd=k&m3ZpSo&10nn1-Ilj@3I-!nLE4o{#sKBc}V;W!}0pWC>wshtMeMP;x!2(pdp4{GbsL<3OpGD$E6YrKjrjhw>I}(E;=@FoJv}`R#8T{oMEyad?VDZQ=eLZ zY|zGaa~;p+ahsju!Hy-#$}9XCV`)3J<+6!lU0dmdt6JY3UznRLs8ZGV-OqlW(ArvO z&G0-poMC|6=*=5`t-q&K{&dN91`T#_9ARN%ybk{4Nq^7MSLZ(mRo%zTO?=nC61s>W zqHx3|wSS!zNC9O71OfpC00bZ+sT{7Q_l06OE|7UT wteKOuVqbu;Fu}{D)G5Aw(4gvxv5WbO5I{*Lx literal 0 HcmV?d00001 diff --git a/zt-vertx/zt-vertx-service/src/main/resources/cluster.xml b/zt-vertx/zt-vertx-service/src/main/resources/cluster.xml new file mode 100644 index 0000000..e521a5a --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/resources/cluster.xml @@ -0,0 +1,30 @@ + + + + + + + 5701 + + + + + 127.0.0.1 + + 127.0.0.1 + + + + + + + + + + http://127.0.0.1:8080/mancenter + + + diff --git a/zt-vertx/zt-vertx-service/src/main/resources/i18n/messages.properties b/zt-vertx/zt-vertx-service/src/main/resources/i18n/messages.properties new file mode 100644 index 0000000..93de005 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/resources/i18n/messages.properties @@ -0,0 +1,38 @@ +#错误消息 +not.null=* 必须填写 +user.jcaptcha.error=验证码错误 +user.jcaptcha.expire=验证码已失效 +user.not.exists=用户不存在/密码错误 +user.password.not.match=用户不存在/密码错误 +user.password.retry.limit.count=密码输入错误{0}次 +user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟 +user.password.delete=对不起,您的账号已被删除 +user.blocked=用户已封禁,请联系管理员 +role.blocked=角色已封禁,请联系管理员 +login.blocked=很遗憾,访问IP已被列入系统黑名单 +user.logout.success=退出成功 + +length.not.valid=长度必须在{min}到{max}个字符之间 + +user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头 +user.password.not.valid=* 5-50个字符 + +user.email.not.valid=邮箱格式错误 +user.mobile.phone.number.not.valid=手机号格式错误 +user.login.success=登录成功 +user.register.success=注册成功 +user.notfound=请重新登录 +user.forcelogout=管理员强制退出,请重新登录 +user.unknown.error=未知错误,请重新登录 + +##文件上传消息 +upload.exceed.maxSize=上传的文件大小超出限制的文件大小!
允许的文件最大大小是:{0}MB! +upload.filename.exceed.length=上传的文件名最长{0}个字符 + +##权限 +no.permission=您没有数据的权限,请联系管理员添加权限 [{0}] +no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}] +no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}] +no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}] +no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}] +no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}] diff --git a/zt-vertx/zt-vertx-service/src/main/resources/keystore.jks b/zt-vertx/zt-vertx-service/src/main/resources/keystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..4da3ff65712fb6f1d30cfb0ca00f788a3e11530f GIT binary patch literal 2756 zcma)8XE+;-7EU5DB349EBWA6VsL|Sr*s8VnEVat_8MReo6tz;ciq>kWTD41!AnKCR znr&jXRvSC0)$+OH>G#}U_x?EVdCvQu_xyaHgT_MfARrAI3-zI8kWAE1+-CyP0gJFu zdoUJieMXz3v5>a^k|32}ETsI5EJLc2i1IX~e4OV2U7+7%K=UfQHf1|NA0{0Rlkt(K5Is>H|G!z(7eb zD{pY7gCgZH?6%gkuTd8&TEJNF$A}LGF%vX8l(isT*n@)~L`wFvgY3w+86S~cj9L$kRoVtlJ@o$)CgFCPG zNBf=g*<31A@iCUd`KzeO2RcA~tvxO&X}1JGziLU+-;vKEb9$<$CLQILVGVFO=^Q?h z2;PJ@j7*Kt{vXVASoK#qmsYx-!~^C{ zylF;_=xK~aKSQgOarpw$pM{~~f|L$jyX4A*KH@;x{<%iAa&Qx*9^NzKb*@4Dbe24N zNzAwrROzVXSJyp3@s*%(4VtXS@AryFf-?g7E?82K!L5uE8T{@U{(EdhdKJ&&;}08^ zWu&!;u8q6lc4-XP%A2Df__x;i6jNN-JDHl)tZKjIex5?wJX-^^JC?HPuD&$8{>Kef zPb(VcmB)m*!!IVub+RXD_bA3sKC(6f+t3C5xxv_ojk@!3fR{_-R;eRkF^iS)A4uC( zEzfwjMQLE)qUl3aKDH#jRkH}sU+^Vlctj3&9x_zypC!CIA;m}Ob<_Q|_sWXyZC_DK zHz8yLO}R40YpLFIp%l}gnKx9r6DHg?`92Xmt?uk#>`Ngmm03!MusJa&aU0MdJq6ey z@=`PNcbim;6N40a(+zzP2sQPb{$oZz{N@}F(>Dhf3BPatU#Kje54ERIuIZn|2`qY% z-B(0QW%Oa6GY@LxRteRZSQcr$!M(OTB?YgY^Ksi|$mfu#6`8n4O{k`5rjRVQV=Z2~73K79w%6Z1p>jUjCrIWCuNVS)*5~ZVZqFMkcO6E?J@ih_Uu3z{M{M)b zp`UpVCQ}|q3xx|`HC^8&m75e+bmk0&Sy$P{hJwSpZNQP6WeO*xp@A1f3r$b2AA$;Q zs@bd7lwM`fQ;8D~-J=STw9(zy0wTDJ7!c>ZVp`xp7wf6E^*M1!<&hI)Yd`u6Gm#K-2XF}ZvvAD@l-?-JYbR`fcF*~%x3{PXkfSqSqR zUxq(%iI~S;Mt^l0^%5Z$UGliaAYriTx2KaHdg<|(I67tJi2R{cspwbwPqPSBV&3eN zlQV&C^`tIw{3jNOb+^GMHW+(K=d0D|&p%J~<6g7dbYKp6^Tclx?ePvvzcf4#_v$1G z?WY-EJu>io;{N%aC`%&<{_dJadgGFgd6@k7i{aaBPP+*Yx7T6-Ly~eAwiV>-wY ztlmwQHAaj5j!OoF?0J4JKoGzK5D2*QFO&wj0{j5pXBdPQ{Ckl@76FD^c;EIAki9Ic zpr9lxk4B>}E1byMl6DpMlh*QAihdK zF>>txiEIfw|SHC z?h1%WmACTinIh-|(`r7&u^yOU!d_6xvO&ys+=P1Om~~=GoXROxj$}LMSMtV-we$ru z&8*NEyi&z#-gNhYTYF9X{`U{w4wpcA*}jgOtwchsL*>c|EaB-*x>U@7WP2XG?l?{F zPWqj&7a1Fa=~QQg$srl{GgGloDW)_b1Sx-4&&o9yW>_`2d7q{;~Td=o`W&r#| zZ|zNrKA+jOME$;j=kaR8Vi%8Th2z@I0C{+12fa)1c-K(K>9u)TeUUVsOhI`aR*hdN zJdxbESIe=qHBqbgtl{51nSo{ZP?2SYKhQlchn_>t&*c}m`eM@bmHWFW58nrRbaA5Q zU3y0w{$x&pmdB5y@`J;C7J!|@2dQdK#xqA1b1rdE9h|zapsJ6kr=e>dW4f;Mi=ojU zUZdf4PT(6}ZiYw}3G|)J%#U;FdHVJA&dD``9R1oj%bgxr1T|jTS9EWv`D&-yNaCoI zznDhyRLQhLZ@A0NdD37?sV{8x(OQkD+4K$A=4LXxJJ+8w?nQGtRbC2Ul!|l5R7;={ zW|4JnqVqVCnRr=gdM!JUZ&6G!qez{zQ_1Dl{hV}DWQRJnfvLUm6}&&di?E#OX6l#t zlCS6UQQJ$t)@Jc{5||-9+2py!fM+ZmCkYO8W&$cX3|GFFXyPdP?Ijw9wJU2)c{I_^O2|dq@TkW0S zFvU@7H^sc2IwB6;AZ!sydGNlBDEw+(-EEAKjQwKS@Vkmm`W;{K`=bHQa`h$+BbGCU zSvPg7ebh2R++;zLj&5vmD%6PxFmoHT^HvM|I9%Lq>&g@ep9)3?=_w`li<=)SlR?YRH`+^&~UVjXy_%AKF_NFcAI=0QWV&1qJ8?AefkOIxW zUPZ`pG)BEhOS<`%_{qkD{rfO-St%=lWviSi)zi5?G}&k?yJ8D2GUy#HO`=v545tmZ zK>~C_gvfyHEHpc~N4nEk;ScVNuHr%(0gLM)*8KJ=`NlCT%_LvpnyX#m&#GM6hb4JL z>kI=(K@=Ud30fA-^!u{|0U-deKnH(?^!V|#Y>~n1p6Pp;vvM})wUf)?0!8jwtkjne e^}vW;hq$Lp&DPspz1=`$H@p%wXzKLO2>J_E^6%*Y literal 0 HcmV?d00001 diff --git a/zt-vertx/zt-vertx-service/src/main/resources/log4j2.xml b/zt-vertx/zt-vertx-service/src/main/resources/log4j2.xml new file mode 100644 index 0000000..f475925 --- /dev/null +++ b/zt-vertx/zt-vertx-service/src/main/resources/log4j2.xml @@ -0,0 +1,123 @@ + + + + + ./vertx-logs + + + + + + + + + + + + + + + + + + %d %p %C{} [%t] %m%n + + + + + + + + + + + + + + + + + + + + + + %d %p %C{} [%t] %m%n + + + + + + + + + + + + + + + + + + + + + + %d %p %C{} [%t] %m%n + + + + + + + + + + + + + + + + + + %d %p %C{} [%t] %m%n + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/zt-vertx/zt-vertx-service/src/main/resources/server.cer b/zt-vertx/zt-vertx-service/src/main/resources/server.cer new file mode 100644 index 0000000000000000000000000000000000000000..b9b8a4636cea7a4eac4f78b100215f29af2de045 GIT binary patch literal 896 zcmXqLVy-c0VoF)S%*4pV#K~~&eO6{w%bG9)UN%mxHjlRNyo`+8tPBP@h5`nBY|No7 zY{KlJdD(gS<#{j>4h#`?3=u925pE0-X1IufoH(zMiGiu1v4N$bfq_93m}_BZ2<6h* zHBF34$iczL%D~*j$j@NV#K^_e#K_2SAZ?mg>eEbnk(i|BXD)@6!rl%iT7xbBg}kia zv@iZr%d6!LlijyV-rU8k)akYMQ~Jtcr}~D47n?b`?$?Nk{?dJ!FVHPFP5N3ndv$(6 zRhFEGj^T{G9gS*pX86xxV4k;ca?WZFPM=%e|KrT}`ngPwzjkN;EsleBNy49&%kTQd zy+DkaLE@Pw>(T4K=RL8J=Q!~?C34O2N~ZS3HPa0|IVOEbOOcKWI31E?c8rCcQgg&Uu~xR!`4JWF7iam?3H# zeD^`jAz`MEE1y1@d%5FGQ|4zu3#s1OZiip=$D|%^WnyMzU|g(dAa5WGOd+y-EMhDo zVT}2HZ88dCE3Vo`JwJ1=(r29^CvqSH(-bfe85#1E;lrKtOKyMw}o+rL@&rF8X{ zmNw`8+aS7d#utm9xhp6A-Ku|8Dk*fbxJFfP>dkA3ce?67?^?1gR4y=z@xmAV3yv=? zZ)o&f-Cprjc-QsN50Nune<~Hfb&jz4IoE8-^#8Bk|5$r)>xnn%9g%ZpuCu?Eb|fP^ z^-{~Vm`?|z?g>3-niyVVvrSZ0rexozZ?;`VN_)50@=RboTz7AkMDmLdVr7i!%=3=D t=dLS$7Guy8wy1Y<$-*X!MaMrrn