vertx 网关实现:
1、配置动态生成代理 2、负载均衡算法 轮训 3、加解密
This commit is contained in:
parent
fd552c9732
commit
86240708eb
5
pom.xml
5
pom.xml
@ -172,6 +172,11 @@
|
||||
<artifactId>sf-apijson</artifactId>
|
||||
<version>${sf.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.smarterFramework</groupId>
|
||||
<artifactId>sf-vertx-api</artifactId>
|
||||
<version>${sf.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-core</artifactId>
|
||||
|
@ -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
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.sf.deployment.mapper;
|
||||
package com.sf.system.deployment.mapper;
|
||||
|
||||
import com.sf.system.deployment.domain.DeploymentApplyEnvironment;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.sf.deployment.service;
|
||||
package com.sf.system.deployment.service;
|
||||
|
||||
import com.sf.system.deployment.domain.DeploymentApplyEnvironment;
|
||||
|
||||
|
@ -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业务层处理
|
||||
|
@ -19,21 +19,23 @@
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- 阿里JSON解析器 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<failOnMissingWebXml>false</failOnMissingWebXml>
|
||||
<warName>${project.artifactId}</warName>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
</build>
|
||||
|
||||
</project>
|
@ -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<ServerAddress> serverAddressList;
|
||||
|
||||
private List<Node> nodeList;
|
||||
private List<Strategy> strategyList;
|
||||
}
|
||||
|
@ -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; // 私钥
|
||||
}
|
@ -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; // 大写
|
||||
}
|
@ -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;
|
||||
}
|
110
sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/Node.java
Normal file
110
sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/Node.java
Normal file
@ -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 <br>
|
||||
* final Integer weight:权重,保存配置的权重 <br>
|
||||
* Integer effectiveWeight:有效权重,轮询的过程权重可能变化 <br>
|
||||
* Integer currentWeight:当前权重,比对该值大小获取节点<br>
|
||||
* 第一次加权轮询时:currentWeight = weight = effectiveWeight <br>
|
||||
* 后面每次加权轮询时:currentWeight 的值都会不断变化,其他权重不变 <br>
|
||||
*/
|
||||
public class Node implements Comparable<Node>, 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 + "}";
|
||||
}
|
||||
}
|
@ -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;
|
||||
//}
|
||||
|
@ -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<GatewayInterface> gatewayInterfaceList;
|
||||
private HttpClientOptionsConfig httpClientOptionsConfig; // 高并发情况,配置接口连接池
|
||||
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.sf.vertx.arithmetic.roundRobin;
|
||||
|
||||
import com.sf.vertx.api.pojo.Node;
|
||||
|
||||
public interface SacLoadBalancing {
|
||||
Node selectNode();
|
||||
}
|
@ -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<Node> 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<Node> 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<Node> 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<Node> 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");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -30,6 +30,10 @@
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.smarterFramework</groupId>
|
||||
<artifactId>sf-vertx-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
@ -61,6 +65,22 @@
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>io.vertx</groupId>
|
||||
<artifactId>vertx-hazelcast</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.vertx</groupId>
|
||||
<artifactId>vertx-zookeeper</artifactId>
|
||||
</dependency>
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>io.vertx</groupId>
|
||||
<artifactId>vertx-unit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- Generators -->
|
||||
<dependency>
|
||||
<groupId>io.vertx</groupId>
|
||||
@ -115,24 +135,23 @@
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
|
||||
<!-- 阿里JSON解析器 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>2.0.48</version>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
@ -147,6 +166,11 @@
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -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<String, String> 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<String> 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;
|
||||
}
|
||||
}
|
@ -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<String, String> 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<String, AppConfig> 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<Node> 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;
|
||||
}
|
||||
}
|
||||
|
@ -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<String, AppConfig> loadAllConfig();
|
||||
|
||||
void addAppConfig(AppConfig appConfig);
|
||||
|
||||
void deleteAppConfig(AppConfig appConfig);
|
||||
|
||||
VertxConfig loadVertxConfig();
|
||||
|
||||
void addVertxConfig(VertxConfig vertxConfig);
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package com.sf.vertx.service.config;
|
||||
|
||||
public interface RedisConfigService {
|
||||
|
||||
}
|
@ -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<String, String> redisTemplate;
|
||||
|
||||
/***
|
||||
* 从redis加载数据
|
||||
*/
|
||||
public ConcurrentHashMap<String, AppConfig> loadAllConfig() {
|
||||
ConcurrentHashMap<String, AppConfig> cacheAppConfig = new ConcurrentHashMap<>();
|
||||
// 获取所有app列表
|
||||
Set<String> 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<Node> 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";
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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"));
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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<String> getKeyPair() throws NoSuchAlgorithmException {
|
||||
KeyPair keyPairObj = getKeyPairObj();
|
||||
List<String> 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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -2,6 +2,9 @@
|
||||
server:
|
||||
vertx:
|
||||
environment: dev
|
||||
server:
|
||||
default:
|
||||
port: 9099
|
||||
# 服务器的HTTP端口,默认为8080
|
||||
port: 5566
|
||||
servlet:
|
||||
|
146
sf-vertx/src/test/java/com/sf/vertx/TestEventBus.java
Normal file
146
sf-vertx/src/test/java/com/sf/vertx/TestEventBus.java
Normal file
@ -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<String> 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<String> 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<String> 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<String> 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<String> 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());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
35
sf-vertx/src/test/java/com/sf/vertx/TestJson.java
Normal file
35
sf-vertx/src/test/java/com/sf/vertx/TestJson.java
Normal file
@ -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<Node> oldGoodsStoragemodes = JSON.parseArray(oldGoodsStorageModes,Node.class);
|
||||
log.info("oldGoodsStoragemodes:{}", JSON.toJSONString(oldGoodsStoragemodes));
|
||||
}
|
||||
}
|
@ -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<String> 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<ProxyResponse> 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);
|
||||
}
|
||||
|
||||
|
40
sf-vertx/src/test/java/com/sf/vertx/TestZookeeper.java
Normal file
40
sf-vertx/src/test/java/com/sf/vertx/TestZookeeper.java
Normal file
@ -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());
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
//}
|
0
sf-vertx/src/test/resources/zookeeper.json
Normal file
0
sf-vertx/src/test/resources/zookeeper.json
Normal file
Loading…
x
Reference in New Issue
Block a user