diff --git a/pom.xml b/pom.xml index 7348362..5f6bd1d 100644 --- a/pom.xml +++ b/pom.xml @@ -172,6 +172,11 @@ sf-apijson ${sf.version} + + com.smarterFramework + sf-vertx-api + ${sf.version} + cn.hutool hutool-core diff --git a/sf-system/src/main/java/com/sf/system/deployment/controller/DeploymentApplyEnvironmentController.java b/sf-system/src/main/java/com/sf/system/deployment/controller/DeploymentApplyEnvironmentController.java index c52a34d..59f41e6 100644 --- a/sf-system/src/main/java/com/sf/system/deployment/controller/DeploymentApplyEnvironmentController.java +++ b/sf-system/src/main/java/com/sf/system/deployment/controller/DeploymentApplyEnvironmentController.java @@ -1,26 +1,28 @@ -package com.sf.deployment.controller; +package com.sf.system.deployment.controller; import java.util.List; + import javax.servlet.http.HttpServletResponse; -import com.sf.system.deployment.domain.DeploymentApplyEnvironment; -import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; + import com.sf.common.annotation.Log; import com.sf.common.core.controller.BaseController; import com.sf.common.core.domain.AjaxResult; -import com.sf.common.enums.BusinessType; -import com.sf.deployment.service.IDeploymentApplyEnvironmentService; -import com.sf.common.utils.poi.ExcelUtil; import com.sf.common.core.page.TableDataInfo; +import com.sf.common.enums.BusinessType; +import com.sf.common.utils.poi.ExcelUtil; +import com.sf.system.deployment.domain.DeploymentApplyEnvironment; +import com.sf.system.deployment.service.IDeploymentApplyEnvironmentService; /** * 环境维护Controller diff --git a/sf-system/src/main/java/com/sf/system/deployment/mapper/DeploymentApplyEnvironmentMapper.java b/sf-system/src/main/java/com/sf/system/deployment/mapper/DeploymentApplyEnvironmentMapper.java index 905b7cc..195e6ca 100644 --- a/sf-system/src/main/java/com/sf/system/deployment/mapper/DeploymentApplyEnvironmentMapper.java +++ b/sf-system/src/main/java/com/sf/system/deployment/mapper/DeploymentApplyEnvironmentMapper.java @@ -1,4 +1,4 @@ -package com.sf.deployment.mapper; +package com.sf.system.deployment.mapper; import com.sf.system.deployment.domain.DeploymentApplyEnvironment; diff --git a/sf-system/src/main/java/com/sf/system/deployment/service/IDeploymentApplyEnvironmentService.java b/sf-system/src/main/java/com/sf/system/deployment/service/IDeploymentApplyEnvironmentService.java index 3f8d855..bac2f75 100644 --- a/sf-system/src/main/java/com/sf/system/deployment/service/IDeploymentApplyEnvironmentService.java +++ b/sf-system/src/main/java/com/sf/system/deployment/service/IDeploymentApplyEnvironmentService.java @@ -1,4 +1,4 @@ -package com.sf.deployment.service; +package com.sf.system.deployment.service; import com.sf.system.deployment.domain.DeploymentApplyEnvironment; diff --git a/sf-system/src/main/java/com/sf/system/deployment/service/impl/DeploymentApplyEnvironmentServiceImpl.java b/sf-system/src/main/java/com/sf/system/deployment/service/impl/DeploymentApplyEnvironmentServiceImpl.java index 47945cc..d988e0b 100644 --- a/sf-system/src/main/java/com/sf/system/deployment/service/impl/DeploymentApplyEnvironmentServiceImpl.java +++ b/sf-system/src/main/java/com/sf/system/deployment/service/impl/DeploymentApplyEnvironmentServiceImpl.java @@ -1,12 +1,14 @@ -package com.sf.deployment.service.impl; +package com.sf.system.deployment.service.impl; import java.util.List; -import com.sf.common.utils.DateUtils; -import com.sf.system.deployment.domain.DeploymentApplyEnvironment; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import com.sf.deployment.mapper.DeploymentApplyEnvironmentMapper; -import com.sf.deployment.service.IDeploymentApplyEnvironmentService; + +import com.sf.common.utils.DateUtils; +import com.sf.system.deployment.domain.DeploymentApplyEnvironment; +import com.sf.system.deployment.mapper.DeploymentApplyEnvironmentMapper; +import com.sf.system.deployment.service.IDeploymentApplyEnvironmentService; /** * 环境维护Service业务层处理 diff --git a/sf-vertx-api/pom.xml b/sf-vertx-api/pom.xml index 4b01f87..1269d58 100644 --- a/sf-vertx-api/pom.xml +++ b/sf-vertx-api/pom.xml @@ -19,21 +19,23 @@ lombok provided + + + com.alibaba.fastjson2 + fastjson2 + org.apache.maven.plugins - maven-war-plugin - 3.1.0 + maven-compiler-plugin - false - ${project.artifactId} + 1.8 + 1.8 - ${project.artifactId} - \ No newline at end of file diff --git a/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/AppConfig.java b/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/AppConfig.java index 70319e6..9ce8261 100644 --- a/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/AppConfig.java +++ b/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/AppConfig.java @@ -8,7 +8,8 @@ import lombok.Data; @Data public class AppConfig implements Serializable { private static final long serialVersionUID = 1518165296680157119L; + private String appCode; private boolean exclusiveService; // 预留字段, 独立端口 - private List serverAddressList; - + private List nodeList; + private List strategyList; } diff --git a/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/DataSecurity.java b/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/DataSecurity.java new file mode 100644 index 0000000..c52af2e --- /dev/null +++ b/sf-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; // 国密加解密 + private String publicKey; // 公钥 + private String privateKey; // 私钥 +} diff --git a/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/GatewayInterface.java b/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/GatewayInterface.java new file mode 100644 index 0000000..6c47fc9 --- /dev/null +++ b/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/GatewayInterface.java @@ -0,0 +1,12 @@ +package com.sf.vertx.api.pojo; + +import java.io.Serializable; + +import lombok.Data; + +@Data +public class GatewayInterface implements Serializable { + private static final long serialVersionUID = -313734935498432381L; + private String uri; + private String method; // 大写 +} diff --git a/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/HttpClientOptionsConfig.java b/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/HttpClientOptionsConfig.java new file mode 100644 index 0000000..20aa4ae --- /dev/null +++ b/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/HttpClientOptionsConfig.java @@ -0,0 +1,14 @@ +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 int maxPoolSize; + private int connectTimeout; + private int http2KeepAliveTimeout; + private int idleTimeout; +} diff --git a/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/Node.java b/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/Node.java new file mode 100644 index 0000000..a06af7e --- /dev/null +++ b/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/Node.java @@ -0,0 +1,110 @@ +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 boolean createHttp; // 协议 + private boolean createHttps; + + 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 boolean isCreateHttp() { + return createHttp; + } + + public void setCreateHttp(boolean createHttp) { + this.createHttp = createHttp; + } + + public boolean isCreateHttps() { + return createHttps; + } + + public void setCreateHttps(boolean createHttps) { + this.createHttps = createHttps; + } + + 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/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/ServerAddress.java b/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/ServerAddress.java index d94bcc2..ce814a2 100644 --- a/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/ServerAddress.java +++ b/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/ServerAddress.java @@ -1,14 +1,14 @@ -package com.sf.vertx.api.pojo; - -import java.io.Serializable; - -import lombok.Data; - -@Data -public class ServerAddress implements Serializable { - private static final long serialVersionUID = 2821255113510132943L; - private boolean createHttp; // 协议 - private boolean createHttps; - private String ip; - private int port; -} +//package com.sf.vertx.api.pojo; +// +//import java.io.Serializable; +// +//import lombok.Data; +// +//@Data +//public class ServerAddress implements Serializable { +// private static final long serialVersionUID = 2821255113510132943L; +// private boolean createHttp; // 协议 +// private boolean createHttps; +// private String ip; +// private int port; +//} diff --git a/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/Strategy.java b/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/Strategy.java index 49f8a1c..3897aa5 100644 --- a/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/Strategy.java +++ b/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/Strategy.java @@ -1,14 +1,21 @@ package com.sf.vertx.api.pojo; import java.io.Serializable; +import java.util.List; + +import lombok.Data; /*** * 策略 * @author xy * */ +@Data public class Strategy implements Serializable { private static final long serialVersionUID = -8831406773224882471L; + private DataSecurity dataSecurity; + private List gatewayInterfaceList; + private HttpClientOptionsConfig httpClientOptionsConfig; // 高并发情况,配置接口连接池 } diff --git a/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/VertxConfig.java b/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/VertxConfig.java new file mode 100644 index 0000000..9a50994 --- /dev/null +++ b/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/VertxConfig.java @@ -0,0 +1,13 @@ +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 VertxOptionsConfig vertxOptionsConfig; + +} diff --git a/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/VertxOptionsConfig.java b/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/VertxOptionsConfig.java new file mode 100644 index 0000000..efe4dc5 --- /dev/null +++ b/sf-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; + 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/sf-vertx-api/src/main/java/com/sf/vertx/arithmetic/roundRobin/SacLoadBalancing.java b/sf-vertx-api/src/main/java/com/sf/vertx/arithmetic/roundRobin/SacLoadBalancing.java new file mode 100644 index 0000000..e8fe95e --- /dev/null +++ b/sf-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/sf-vertx-api/src/main/java/com/sf/vertx/arithmetic/roundRobin/WeightedRoundRobin.java b/sf-vertx-api/src/main/java/com/sf/vertx/arithmetic/roundRobin/WeightedRoundRobin.java new file mode 100644 index 0000000..377a3ac --- /dev/null +++ b/sf-vertx-api/src/main/java/com/sf/vertx/arithmetic/roundRobin/WeightedRoundRobin.java @@ -0,0 +1,109 @@ +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 static List nodes = new ArrayList<>(); + // 权重之和 + public static 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.setCreateHttp(tempNodeOfMaxWeight.isCreateHttp()); + nodeOfMaxWeight.setCreateHttps(tempNodeOfMaxWeight.isCreateHttps()); + 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 <= 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 <= totalWeight; i++) { + Node node = weightedRoundRobin2.selectNode(); + System.out.println(Thread.currentThread().getName() + "==第" + i + "次轮询选中[当前权重最大]的节点:" + node + "\n"); + } + + } +} \ No newline at end of file diff --git a/sf-vertx/pom.xml b/sf-vertx/pom.xml index c545464..4ea5e87 100644 --- a/sf-vertx/pom.xml +++ b/sf-vertx/pom.xml @@ -30,6 +30,10 @@ + + com.smarterFramework + sf-vertx-api + org.springframework.boot spring-boot-starter-web @@ -61,6 +65,22 @@ org.apache.commons commons-pool2 + + + io.vertx + vertx-unit + test + io.vertx @@ -115,24 +135,23 @@ com.fasterxml.jackson.core jackson-databind - + - com.alibaba - fastjson - 2.0.48 + com.alibaba.fastjson2 + fastjson2 - - + + org.slf4j slf4j-api - org.slf4j - slf4j-simple - jar - + org.slf4j + slf4j-simple + jar + ch.qos.logback logback-classic @@ -147,6 +166,11 @@ org.apache.commons commons-lang3 + + io.netty + netty-all + + diff --git a/sf-vertx/src/main/java/com/sf/AdminApplication.java b/sf-vertx/src/main/java/com/sf/AdminApplication.java index d148318..7e91c8e 100644 --- a/sf-vertx/src/main/java/com/sf/AdminApplication.java +++ b/sf-vertx/src/main/java/com/sf/AdminApplication.java @@ -26,6 +26,5 @@ public class AdminApplication { public static void main(String[] args) throws Exception { // System.setProperty("spring.devtools.restart.enabled", "false"); APPLICATION_CONTEXT = SpringApplication.run(AdminApplication.class, args); - log.info("dafafsdfa:{}", "dsafs"); } } diff --git a/sf-vertx/src/main/java/com/sf/vertx/constans/RedisConfig.java b/sf-vertx/src/main/java/com/sf/vertx/constans/RedisConfig.java index 46ea55a..0d731c4 100644 --- a/sf-vertx/src/main/java/com/sf/vertx/constans/RedisConfig.java +++ b/sf-vertx/src/main/java/com/sf/vertx/constans/RedisConfig.java @@ -1,5 +1,24 @@ package com.sf.vertx.constans; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + + +@Component public class RedisConfig { - public static final String VETX_ENVIRONMENT_KEY = "vertx:config:"; + + @Value("${server.vertx.environment}") + private String vertxEnvironment; + + public static final String BASE_REDIS_KEY = "vertx:config:"; + public static String APP_CONFIG_SET_KEY = null; + public static String APP_CONFIG_PREFIX_KEY = null; + public static String VERTX_CONFIG_STRING_KEY = null; + + public void init() { + APP_CONFIG_PREFIX_KEY = BASE_REDIS_KEY + vertxEnvironment; + APP_CONFIG_SET_KEY = APP_CONFIG_PREFIX_KEY+":set"; + VERTX_CONFIG_STRING_KEY = BASE_REDIS_KEY + vertxEnvironment + ":vertx"; + } } + diff --git a/sf-vertx/src/main/java/com/sf/vertx/controller/AppConfigController.java b/sf-vertx/src/main/java/com/sf/vertx/controller/AppConfigController.java new file mode 100644 index 0000000..d9309bd --- /dev/null +++ b/sf-vertx/src/main/java/com/sf/vertx/controller/AppConfigController.java @@ -0,0 +1,40 @@ +package com.sf.vertx.controller; + +import org.springframework.beans.factory.annotation.Autowired; +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.sf.vertx.api.pojo.AppConfig; +import com.sf.vertx.service.AppConfigService; + +import lombok.extern.slf4j.Slf4j; + +/*** + * 测试redis + * + * @author xy + * + */ +@RestController +@RequestMapping("/vertx/test/redis") +@Slf4j +public class AppConfigController { + @Autowired + private AppConfigService appConfigService; + + @PostMapping("/addAppConfig") + public String addAppConfig(@RequestBody AppConfig appConfig) { + appConfigService.addAppConfig(appConfig); + return null; + } + + @PostMapping("/addVertxConfig") + public String addVertxConfig() { + return null; + } + + + +} diff --git a/sf-vertx/src/main/java/com/sf/vertx/controller/TestRedisController.java b/sf-vertx/src/main/java/com/sf/vertx/controller/TestRedisController.java deleted file mode 100644 index 0b948f7..0000000 --- a/sf-vertx/src/main/java/com/sf/vertx/controller/TestRedisController.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.sf.vertx.controller; - -import java.util.Set; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.core.ZSetOperations; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import com.sf.vertx.constans.RedisConfig; - -import lombok.extern.slf4j.Slf4j; - -/*** - * 测试redis - * - * @author xy - * - */ -@RestController -@RequestMapping("/vertx/test/redis") -@Slf4j -public class TestRedisController { - @Value("${server.vertx.environment}") - private String vertxEnvironment; - @Autowired - private RedisTemplate redisTemplate; - - @GetMapping("/test") - public String test() { - redisTemplate.opsForValue().set("a", "1"); - return redisTemplate.opsForValue().get("a"); - } - - public String allConfig() { - String setKey = RedisConfig.VETX_ENVIRONMENT_KEY+vertxEnvironment; - // 获取所有app列表 - Set set = redisTemplate.opsForZSet().range(setKey, 0, -1); - for(String appCode : set) { - String appCodeKey = setKey + ":" + appCode; - String appCodeValue = redisTemplate.opsForValue().get(appCodeKey); - if(StringUtils.isNotBlank(appCodeValue)) { - - } - } - - // 添加zset - return null; - } -} diff --git a/sf-vertx/src/main/java/com/sf/vertx/init/DynamicBuildServer.java b/sf-vertx/src/main/java/com/sf/vertx/init/DynamicBuildServer.java index 7849af9..df8118c 100644 --- a/sf-vertx/src/main/java/com/sf/vertx/init/DynamicBuildServer.java +++ b/sf-vertx/src/main/java/com/sf/vertx/init/DynamicBuildServer.java @@ -1,36 +1,33 @@ package com.sf.vertx.init; -import java.util.concurrent.atomic.AtomicReference; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.core.annotation.Order; -import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; -import com.alibaba.fastjson2.JSONObject; -import com.sf.vertx.security.MainSecurity; +import com.sf.vertx.api.pojo.AppConfig; +import com.sf.vertx.api.pojo.Node; +import com.sf.vertx.api.pojo.VertxConfig; +import com.sf.vertx.arithmetic.roundRobin.SacLoadBalancing; +import com.sf.vertx.arithmetic.roundRobin.WeightedRoundRobin; +import com.sf.vertx.constans.RedisConfig; +import com.sf.vertx.service.AppConfigService; -import io.vertx.core.AsyncResult; import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; -import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClient; -import io.vertx.core.http.HttpClientOptions; import io.vertx.core.http.HttpServer; import io.vertx.core.http.HttpServerRequest; import io.vertx.core.net.SocketAddress; import io.vertx.ext.web.Router; -import io.vertx.ext.web.handler.BodyHandler; import io.vertx.ext.web.proxy.handler.ProxyHandler; -import io.vertx.httpproxy.Body; import io.vertx.httpproxy.HttpProxy; -import io.vertx.httpproxy.ProxyContext; -import io.vertx.httpproxy.ProxyInterceptor; -import io.vertx.httpproxy.ProxyRequest; -import io.vertx.httpproxy.ProxyResponse; import lombok.extern.slf4j.Slf4j; /*** @@ -43,31 +40,91 @@ import lombok.extern.slf4j.Slf4j; @Order(value = 10) @Component public class DynamicBuildServer implements ApplicationRunner { + @Value("${server.vertx.server.default.port}") + private Integer serverDefaultPort; + @Autowired - private RedisTemplate redisTemplate; + private AppConfigService appConfigService; + + @Autowired + private RedisConfig redisConfig; @Override public void run(ApplicationArguments args) throws Exception { - appStartLoadData(); - - // 创建服务 + // 初始化redis key + redisConfig.init(); - // 创建路由 - - // 创建非加密路由 - - // 创建加密路由 - + // 加载vertx、应用配置 + appStartLoadData(); } /*** * 应用启动, 从redis读取配置,初始化vertx服务 */ private void appStartLoadData() { - // 数据配置格式 + // 编解码线程池 + Vertx VERTX = Vertx.vertx(new VertxOptions().setWorkerPoolSize(20)); + // 创建HTTP监听 + HttpServer server = VERTX.createHttpServer(); + Router mainHttpRouter = Router.router(VERTX); + VertxConfig vertxConfig = appConfigService.loadVertxConfig(); + Integer serverPort = (vertxConfig == null || vertxConfig.getPort() == null) ? serverDefaultPort : vertxConfig.getPort(); + server.requestHandler(mainHttpRouter).listen(serverPort, h -> { + if (h.succeeded()) { + log.info("HTTP端口监听成功:{}", serverPort); + } else { + log.error("HTTP端口监听失败:{}", serverPort); + } + }); + + ConcurrentHashMap cacheAppConfig = appConfigService.loadAllConfig(); + for (String appCode : cacheAppConfig.keySet()) { + AppConfig appConfig = cacheAppConfig.get(appCode); + // 负载均衡算法 + // 轮训 + SacLoadBalancing sacLoadBalancing = roundRobin(appConfig.getNodeList()); + + + // 有策略 + if (appConfig.getStrategyList() != null && appConfig.getStrategyList().size() > 0) { + } else { + // 无策略,直接走反向代理 +// HttpClientOptions clientOptions = new HttpClientOptions(); +// clientOptions.setMaxPoolSize(20); // 最大连接池大小 +// clientOptions.setConnectTimeout(5000); // 连接超时 毫秒 +// 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(resolveOriginAddress(sacLoadBalancing, request))); + mainHttpRouter.route().handler(ProxyHandler.create(proxy)); + } + } + } + + public SocketAddress resolveOriginAddress(SacLoadBalancing sacLoadBalancing, HttpServerRequest request) { + // TODO 区分https、http + Node node = sacLoadBalancing.selectNode(); + SocketAddress socketAddress = SocketAddress.inetSocketAddress(node.getPort(), node.getIp()); + log.info("负载均衡跳转地址:{}", socketAddress.host() +";"+socketAddress.port()); + return socketAddress; } - + private SacLoadBalancing roundRobin(List nodeList) { + WeightedRoundRobin weightedRoundRobin = new WeightedRoundRobin(); + int weight = 1; + for(Node node : nodeList) { + node.setWeight(weight); + node.setCurrentWeight(weight); + node.setEffectiveWeight(weight); + weight++; + WeightedRoundRobin.totalWeight += node.getEffectiveWeight(); + } + weightedRoundRobin.init(nodeList); + return weightedRoundRobin; + } } diff --git a/sf-vertx/src/main/java/com/sf/vertx/service/AppConfigService.java b/sf-vertx/src/main/java/com/sf/vertx/service/AppConfigService.java new file mode 100644 index 0000000..8664531 --- /dev/null +++ b/sf-vertx/src/main/java/com/sf/vertx/service/AppConfigService.java @@ -0,0 +1,18 @@ +package com.sf.vertx.service; + +import java.util.concurrent.ConcurrentHashMap; + +import com.sf.vertx.api.pojo.AppConfig; +import com.sf.vertx.api.pojo.VertxConfig; + +public interface AppConfigService { + ConcurrentHashMap loadAllConfig(); + + void addAppConfig(AppConfig appConfig); + + void deleteAppConfig(AppConfig appConfig); + + VertxConfig loadVertxConfig(); + + void addVertxConfig(VertxConfig vertxConfig); +} diff --git a/sf-vertx/src/main/java/com/sf/vertx/service/config/RedisConfigService.java b/sf-vertx/src/main/java/com/sf/vertx/service/config/RedisConfigService.java deleted file mode 100644 index 1d9cfda..0000000 --- a/sf-vertx/src/main/java/com/sf/vertx/service/config/RedisConfigService.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.sf.vertx.service.config; - -public interface RedisConfigService { - -} diff --git a/sf-vertx/src/main/java/com/sf/vertx/service/impl/AppConfigServiceImpl.java b/sf-vertx/src/main/java/com/sf/vertx/service/impl/AppConfigServiceImpl.java new file mode 100644 index 0000000..943ede9 --- /dev/null +++ b/sf-vertx/src/main/java/com/sf/vertx/service/impl/AppConfigServiceImpl.java @@ -0,0 +1,119 @@ +package com.sf.vertx.service.impl; + +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.commons.lang3.StringUtils; +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.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.sf.vertx.api.pojo.AppConfig; +import com.sf.vertx.api.pojo.Node; +import com.sf.vertx.api.pojo.VertxConfig; +import com.sf.vertx.constans.RedisConfig; +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; + + /*** + * 从redis加载数据 + */ + public ConcurrentHashMap loadAllConfig() { + ConcurrentHashMap cacheAppConfig = new ConcurrentHashMap<>(); + // 获取所有app列表 + Set set = redisTemplate.opsForZSet().range(RedisConfig.APP_CONFIG_SET_KEY, 0, -1); + for(String appCode : set) { + String appCodeKey = RedisConfig.APP_CONFIG_PREFIX_KEY + ":" + appCode; + String appCodeValue = redisTemplate.opsForValue().get(appCodeKey); + if(StringUtils.isNotBlank(appCodeValue)) { + AppConfig appConfig = JSON.parseObject(appCodeValue, AppConfig.class); + cacheAppConfig.put(appCode, appConfig); + + //JSONObject jsonObject = JSONObject.parseObject(appCodeValue);//从请求体里获得jsonObject + //String oldGoodsStorageModes = jsonObject.getString("nodeList");//解析成字符串 + //字符串转list + //List oldGoodsStoragemodes = JSON.parseArray(oldGoodsStorageModes,Node.class); + //log.info("oldGoodsStoragemodes:{}", JSON.toJSONString(oldGoodsStoragemodes)); + } + } + log.info("cacheAppConfig:{}", JSON.toJSONString(cacheAppConfig)); + return cacheAppConfig; + } + + public AppConfig getAppConfig(String appCode) { + String appCodeKey = RedisConfig.APP_CONFIG_PREFIX_KEY + ":" + appCode; + String appCodeValue = redisTemplate.opsForValue().get(appCodeKey); + if(StringUtils.isNotBlank(appCodeValue)) { + AppConfig appConfig = JSONObject.parseObject(appCodeValue, AppConfig.class); + return appConfig; + } + return null; + } + + /*** + * 新增、修改 + * @param appConfig + */ + public void addAppConfig(AppConfig appConfig) { + redisTemplate.opsForZSet().add(RedisConfig.APP_CONFIG_SET_KEY, appConfig.getAppCode(), 0); + String appCodeKey = RedisConfig.APP_CONFIG_PREFIX_KEY + ":" + appConfig.getAppCode(); + redisTemplate.opsForValue().set(appCodeKey, JSONObject.toJSONString(appConfig)); + + // 发送redis队列,vertx处理 + //String queue = RedisConfig.VETX_ENVIRONMENT_KEY+vertxEnvironment+":list"; + + } + + /*** + * 删除 + * @param appConfig + */ + public void deleteAppConfig(AppConfig appConfig) { + redisTemplate.opsForZSet().remove(RedisConfig.APP_CONFIG_SET_KEY, appConfig.getAppCode()); + String appCodeKey = RedisConfig.APP_CONFIG_PREFIX_KEY + ":" + appConfig.getAppCode(); + redisTemplate.delete(appCodeKey); + + + // 发送redis队列,vertx处理 + //String queue = RedisConfig.VETX_ENVIRONMENT_KEY+vertxEnvironment+":list"; + } + + + /*** + * 加载vertx配置 + */ + public VertxConfig loadVertxConfig() { + String vertxConfigKey = RedisConfig.VERTX_CONFIG_STRING_KEY; + String vertxConfigValue = redisTemplate.opsForValue().get(vertxConfigKey); + if(StringUtils.isNotBlank(vertxConfigValue)) { + VertxConfig vertxConfig = JSONObject.parseObject(vertxConfigValue, VertxConfig.class); + return vertxConfig; + } + return null; + } + + /*** + * 新增、修改 + * @param appConfig + */ + public void addVertxConfig(VertxConfig vertxConfig) { + String vertxConfigKey = RedisConfig.VERTX_CONFIG_STRING_KEY; + redisTemplate.opsForValue().set(vertxConfigKey, JSONObject.toJSONString(vertxConfig)); + + // 发送redis队列,vertx处理 + //String queue = RedisConfig.VETX_ENVIRONMENT_KEY+vertxEnvironment+":list"; + } +} diff --git a/sf-vertx/src/main/java/com/sf/vertx/strategy/security/AesUtils.java b/sf-vertx/src/main/java/com/sf/vertx/strategy/security/AesUtils.java new file mode 100644 index 0000000..691524b --- /dev/null +++ b/sf-vertx/src/main/java/com/sf/vertx/strategy/security/AesUtils.java @@ -0,0 +1,84 @@ +package com.sf.vertx.strategy.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/sf-vertx/src/main/java/com/sf/vertx/strategy/security/Base64Utils.java b/sf-vertx/src/main/java/com/sf/vertx/strategy/security/Base64Utils.java new file mode 100644 index 0000000..06c9ff1 --- /dev/null +++ b/sf-vertx/src/main/java/com/sf/vertx/strategy/security/Base64Utils.java @@ -0,0 +1,31 @@ +package com.sf.vertx.strategy.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/sf-vertx/src/main/java/com/sf/vertx/strategy/security/HexUtils.java b/sf-vertx/src/main/java/com/sf/vertx/strategy/security/HexUtils.java new file mode 100644 index 0000000..c274b71 --- /dev/null +++ b/sf-vertx/src/main/java/com/sf/vertx/strategy/security/HexUtils.java @@ -0,0 +1,53 @@ +package com.sf.vertx.strategy.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/sf-vertx/src/main/java/com/sf/vertx/strategy/security/MainSecurity.java b/sf-vertx/src/main/java/com/sf/vertx/strategy/security/MainSecurity.java new file mode 100644 index 0000000..a58e1cb --- /dev/null +++ b/sf-vertx/src/main/java/com/sf/vertx/strategy/security/MainSecurity.java @@ -0,0 +1,78 @@ +package com.sf.vertx.strategy.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 RSA2Utils.encrypt(content, pubKey); + } catch (Exception e) { + LOGGER.info("RSA加密失败"); + e.printStackTrace(); + return null; + } + } + + public static String rsaDecrypt(String content, String priKey) { + try { + return RSA2Utils.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\": \"test\",\n" + + " \"result\": 0,\n" + + " \"data\": {\n" + + " \"username\" : \"test\"\n" + + " }\n" + + "}", "dadddsdfadfadsfa33323223")); + System.out.println(aesDecrypt("59A69B6BBCF046C3CF9953C5CC078CC68ABBED261030F3C119F65B9A3EB306423F32560751B22616500070BCE57153BE2B6E47CAC380C7D226862558256325826FECC43E957FCACB74E22BA48125FABC01FC77B127E384F8F62D9881741931A8C1ABF0A391F06606A060499EB53EF217", "dadddsdfadfadsfa33323223")); + } +} diff --git a/sf-vertx/src/main/java/com/sf/vertx/strategy/security/MessageDigest.java b/sf-vertx/src/main/java/com/sf/vertx/strategy/security/MessageDigest.java new file mode 100644 index 0000000..b91b43b --- /dev/null +++ b/sf-vertx/src/main/java/com/sf/vertx/strategy/security/MessageDigest.java @@ -0,0 +1,47 @@ +package com.sf.vertx.strategy.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/sf-vertx/src/main/java/com/sf/vertx/strategy/security/RSA2Utils.java b/sf-vertx/src/main/java/com/sf/vertx/strategy/security/RSA2Utils.java new file mode 100644 index 0000000..70db5b2 --- /dev/null +++ b/sf-vertx/src/main/java/com/sf/vertx/strategy/security/RSA2Utils.java @@ -0,0 +1,282 @@ +package com.sf.vertx.strategy.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.ArrayList; +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(); + List data = new ArrayList<>(); + data.add(Base64Utils.encode(keyPairObj.getPublic().getEncoded())); + data.add(Base64Utils.encode(keyPairObj.getPrivate().getEncoded())); + return data; + // jdk 17 + //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/sf-vertx/src/main/java/com/sf/vertx/strategy/security/StringUtils.java b/sf-vertx/src/main/java/com/sf/vertx/strategy/security/StringUtils.java new file mode 100644 index 0000000..c36668f --- /dev/null +++ b/sf-vertx/src/main/java/com/sf/vertx/strategy/security/StringUtils.java @@ -0,0 +1,53 @@ +package com.sf.vertx.strategy.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/sf-vertx/src/main/resources/application.yml b/sf-vertx/src/main/resources/application.yml index 1c69b92..b03def3 100644 --- a/sf-vertx/src/main/resources/application.yml +++ b/sf-vertx/src/main/resources/application.yml @@ -2,6 +2,9 @@ server: vertx: environment: dev + server: + default: + port: 9099 # 服务器的HTTP端口,默认为8080 port: 5566 servlet: diff --git a/sf-vertx/src/test/java/com/sf/vertx/TestEventBus.java b/sf-vertx/src/test/java/com/sf/vertx/TestEventBus.java new file mode 100644 index 0000000..ef48008 --- /dev/null +++ b/sf-vertx/src/test/java/com/sf/vertx/TestEventBus.java @@ -0,0 +1,146 @@ +package com.sf.vertx; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import io.vertx.core.Vertx; +import io.vertx.core.VertxOptions; +import io.vertx.core.eventbus.EventBus; +import io.vertx.core.eventbus.EventBusOptions; +import io.vertx.core.eventbus.MessageConsumer; +import io.vertx.ext.unit.Async; +import io.vertx.ext.unit.TestContext; +import io.vertx.ext.unit.junit.VertxUnitRunner; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@RunWith(VertxUnitRunner.class) +public class TestEventBus { + private Vertx vertx; + + @Before + public void setUp(TestContext tc) { + // 创建vertx服务 + vertx = Vertx.vertx(new VertxOptions().setWorkerPoolSize(100)); + } + + @After + public void tearDown() { + vertx.close(); + } + + /*** + * 发布订阅 + * + * @param tc + */ + @Test + public void testPublish(TestContext tc) { + Async async = tc.async(); + EventBus eventBus = vertx.eventBus(); + MessageConsumer consumer = eventBus.consumer("synAppCode"); + consumer.handler(message -> { + System.out.println("consumer I have received a message: " + message.body()); + message.reply("how interesting!"); + }); + consumer.completionHandler(res -> { + if (res.succeeded()) { + System.out.println("The handler registration has reached all nodes"); + } else { + System.out.println("Registration failed!"); + } + }); + + MessageConsumer consumer1 = eventBus.consumer("synAppCode"); + consumer1.handler(message -> { + System.out.println("consumer1 I have received a message: " + message.body()); + message.reply("how interesting!"); + }); + eventBus.publish("synAppCode", "hello world"); + // eventBus.publish("synAppCode", "hello world"); + + eventBus.request("synAppCode", "Yay! Someone kicked a ball across a patch of grass", ar -> { + if (ar.succeeded()) { + System.out.println("Received reply: " + ar.result().body()); + } + }); + } + + /*** + * 点点发送 在对应地址上注册过的所有处理器中, 仅一个处理器能够接收到发送的消息。 这是一种点对点消息传递模式。 Vert.x + * 使用不严格的轮询算法来选择绑定的处理器。 + * + * @param tc + */ + @Test + public void testSend(TestContext tc) { + Async async = tc.async(); + EventBus eb = vertx.eventBus(); + MessageConsumer consumer = eb.consumer("synAppCode"); + consumer.handler(message -> { + System.out.println("consumer I have received a message: " + message.body()); + }); + consumer.completionHandler(res -> { + if (res.succeeded()) { + System.out.println("The handler registration has reached all nodes"); + } else { + System.out.println("Registration failed!"); + } + }); + + MessageConsumer consumer1 = eb.consumer("synAppCode"); + consumer1.handler(message -> { + System.out.println("consumer1 I have received a message: " + message.body()); + }); + eb.send("synAppCode", "hello world"); + } + + @Test + public void clusterConsumer(TestContext tc) { + Async async = tc.async(); + VertxOptions options = new VertxOptions() + .setEventBusOptions(new EventBusOptions().setClusterPublicHost("192.168.1.68").setClusterPublicPort(1)); + + Vertx.clusteredVertx(options).onComplete(res -> { + if (res.succeeded()) { + Vertx vertx = res.result(); + EventBus eventBus = vertx.eventBus(); + MessageConsumer consumer = eventBus.consumer("synAppCode"); + consumer.handler(message -> { + System.out.println("consumer I have received a message: " + message.body()); + }); + consumer.completionHandler(resData -> { + if (res.succeeded()) { + System.out.println("The handler registration has reached all nodes"); + } else { + System.out.println("Registration failed!"); + } + }); + eventBus.publish("synAppCode", "hello world"); + System.out.println("We now have a clustered event bus: " + eventBus); + } else { + System.out.println("Failed: " + res.cause()); + } + }); + } + + @Test + public void clusterPublish(TestContext tc) { + Async async = tc.async(); + VertxOptions options = new VertxOptions() + .setEventBusOptions(new EventBusOptions().setClusterPublicHost("192.168.1.68").setClusterPublicPort(2)); + + Vertx.clusteredVertx(options).onComplete(res -> { + if (res.succeeded()) { + Vertx vertx = res.result(); + EventBus eventBus = vertx.eventBus(); + eventBus.publish("synAppCode", "hello world"); + } else { + System.out.println("Failed: " + res.cause()); + } + }); + } + +} diff --git a/sf-vertx/src/test/java/com/sf/vertx/TestJson.java b/sf-vertx/src/test/java/com/sf/vertx/TestJson.java new file mode 100644 index 0000000..9a0cecd --- /dev/null +++ b/sf-vertx/src/test/java/com/sf/vertx/TestJson.java @@ -0,0 +1,35 @@ +package com.sf.vertx; + +import java.util.List; + +import org.junit.Test; + +import com.alibaba.fastjson2.JSON; +import com.sf.vertx.api.pojo.Node; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class TestJson { + + @Test + public void json() { + String oldGoodsStorageModes = " [\n" + + " {\n" + + " \"createHttp\": true,\n" + + " \"createHttps\": false,\n" + + " \"ip\": \"localhost\",\n" + + " \"port\": \"9198\"\n" + + " },\n" + + " {\n" + + " \"createHttp\": true,\n" + + " \"createHttps\": false,\n" + + " \"ip\": \"localhost\",\n" + + " \"port\": \"9199\"\n" + + " }\n" + + " ]";//解析成字符串 + //字符串转list + List oldGoodsStoragemodes = JSON.parseArray(oldGoodsStorageModes,Node.class); + log.info("oldGoodsStoragemodes:{}", JSON.toJSONString(oldGoodsStoragemodes)); + } +} diff --git a/sf-vertx/src/test/java/com/sf/vertx/TestProxy.java b/sf-vertx/src/test/java/com/sf/vertx/TestProxy.java index c1220b1..410c6c8 100644 --- a/sf-vertx/src/test/java/com/sf/vertx/TestProxy.java +++ b/sf-vertx/src/test/java/com/sf/vertx/TestProxy.java @@ -14,6 +14,8 @@ import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; import io.vertx.core.buffer.Buffer; +import io.vertx.core.eventbus.EventBus; +import io.vertx.core.eventbus.MessageConsumer; import io.vertx.core.http.HttpClient; import io.vertx.core.http.HttpClientOptions; import io.vertx.core.http.HttpServer; @@ -39,6 +41,16 @@ public class TestProxy { routeUrilRewrite(); } + public static void eventBus() { + // 创建vertx服务 + Vertx VERTX = Vertx.vertx(new VertxOptions().setWorkerPoolSize(100)); + EventBus eb = VERTX.eventBus(); + MessageConsumer consumer = eb.consumer("news.uk.sport"); + consumer.handler(message -> { + System.out.println("I have received a message: " + message.body()); + }); + } + /*** * 测试url重写, 入口/appCode/xxxx/xxx, 转换为/xxxx/xxx */ @@ -57,6 +69,8 @@ public class TestProxy { } }); + + HttpClientOptions clientOptions = new HttpClientOptions(); clientOptions.setMaxPoolSize(20); // 最大连接池大小 clientOptions.setConnectTimeout(5000); // 连接超时 毫秒 @@ -64,16 +78,16 @@ public class TestProxy { clientOptions.setIdleTimeout(1000); // 连接空闲超时 毫秒 HttpClient proxyClient = VERTX.createHttpClient(clientOptions); HttpProxy proxy = HttpProxy.reverseProxy(proxyClient); - + proxy.originSelector(request -> Future.succeededFuture(resolveOriginAddress(request))); SFProxyHandler sfProxyHandler = SFProxyHandler.create(proxy); WebClient mainWebClient = WebClient.create(VERTX); proxy.addInterceptor(new ProxyInterceptor() { @Override public Future handleProxyRequest(ProxyContext context) { - SFProxyHandlerImpl sfProxyHandlerImpl = (SFProxyHandlerImpl)sfProxyHandler; - //String body = sfProxyHandlerImpl.getCtx().getBodyAsString(); - //log.info("ctx.getBodyAsJson():{}", body); + SFProxyHandlerImpl sfProxyHandlerImpl = (SFProxyHandlerImpl) sfProxyHandler; + // String body = sfProxyHandlerImpl.getCtx().getBodyAsString(); + // log.info("ctx.getBodyAsJson():{}", body); JsonObject bodyJson = sfProxyHandlerImpl.getCtx().getBodyAsJson(); HttpServerRequest HttpServerRequest = context.request().proxiedRequest(); // 解密 @@ -81,7 +95,7 @@ public class TestProxy { bodyJson.put("data", data); // end解密 // uri重写 - //context.request().setURI("/vertx/body"); + // context.request().setURI("/vertx/body"); return Future.future(p -> { mainWebClient.postAbs("http://localhost:9198/vertx/body").sendJson(bodyJson.toString(), h -> { if (h.succeeded()) { @@ -91,8 +105,9 @@ public class TestProxy { JsonObject responseData = h.result().bodyAsJsonObject(); // responseData.toBuffer() // 加密 - String dataStr = MainSecurity.aesEncrypt(responseData.toString(), "dadddsdfadfadsfa33323223"); - log.info("dataStr:{}",dataStr); + String dataStr = MainSecurity.aesEncrypt(responseData.toString(), + "dadddsdfadfadsfa33323223"); + log.info("dataStr:{}", dataStr); Buffer buffer = Buffer.buffer(dataStr); // end 加密 ProxyResponse proxyResponse = proxyRequest.response().setStatusCode(200) @@ -105,7 +120,7 @@ public class TestProxy { }); } }); - //mainHttpRouter.route().handler(sfProxyHandler); + // mainHttpRouter.route().handler(sfProxyHandler); mainHttpRouter.route().handler(BodyHandler.create()).handler(sfProxyHandler); } diff --git a/sf-vertx/src/test/java/com/sf/vertx/TestZookeeper.java b/sf-vertx/src/test/java/com/sf/vertx/TestZookeeper.java new file mode 100644 index 0000000..91231ca --- /dev/null +++ b/sf-vertx/src/test/java/com/sf/vertx/TestZookeeper.java @@ -0,0 +1,40 @@ +//package com.sf.vertx; +// +//import org.junit.Test; +//import org.junit.runner.RunWith; +// +//import io.vertx.core.Vertx; +//import io.vertx.core.VertxOptions; +//import io.vertx.core.json.JsonObject; +//import io.vertx.core.spi.cluster.ClusterManager; +//import io.vertx.ext.unit.Async; +//import io.vertx.ext.unit.TestContext; +//import io.vertx.ext.unit.junit.VertxUnitRunner; +//import io.vertx.spi.cluster.zookeeper.ZookeeperClusterManager; +//import lombok.extern.slf4j.Slf4j; +// +//@Slf4j +//@RunWith(VertxUnitRunner.class) +//public class TestZookeeper { +// +// @Test +// public void clusterZookeepre(TestContext tc) { +// Async async = tc.async(); +// JsonObject zkConfig = new JsonObject(); +// zkConfig.put("zookeeperHosts", "127.0.0.1"); +// zkConfig.put("rootPath", "io.vertx"); +// zkConfig.put("retry", new JsonObject().put("initialSleepTime", 3000).put("maxTimes", 3)); +// +// ClusterManager mgr = new ZookeeperClusterManager(zkConfig); +// VertxOptions options = new VertxOptions().setClusterManager(mgr); +// +// Vertx.clusteredVertx(options, res -> { +// if (res.succeeded()) { +// Vertx vertx = res.result(); +// log.info("success"); +// } else { +// log.info("error:{}",res.cause()); +// } +// }); +// } +//} diff --git a/sf-vertx/src/test/resources/zookeeper.json b/sf-vertx/src/test/resources/zookeeper.json new file mode 100644 index 0000000..e69de29