vertx 网关实现:

1、配置动态生成代理
2、负载均衡算法
轮训
3、加解密
This commit is contained in:
ztzh_xieyun 2024-04-23 09:20:46 +08:00
parent fd552c9732
commit 86240708eb
39 changed files with 1549 additions and 141 deletions

View File

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

View File

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

View File

@ -1,4 +1,4 @@
package com.sf.deployment.mapper;
package com.sf.system.deployment.mapper;
import com.sf.system.deployment.domain.DeploymentApplyEnvironment;

View File

@ -1,4 +1,4 @@
package com.sf.deployment.service;
package com.sf.system.deployment.service;
import com.sf.system.deployment.domain.DeploymentApplyEnvironment;

View File

@ -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业务层处理

View File

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

View File

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

View File

@ -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; // 私钥
}

View File

@ -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; // 大写
}

View File

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

View 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 + "}";
}
}

View File

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

View File

@ -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; // 高并发情况,配置接口连接池
}

View File

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

View File

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

View File

@ -0,0 +1,7 @@
package com.sf.vertx.arithmetic.roundRobin;
import com.sf.vertx.api.pojo.Node;
public interface SacLoadBalancing {
Node selectNode();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 区分httpshttp
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;
}
}

View File

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

View File

@ -1,5 +0,0 @@
package com.sf.vertx.service.config;
public interface RedisConfigService {
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,6 +2,9 @@
server:
vertx:
environment: dev
server:
default:
port: 9099
# 服务器的HTTP端口默认为8080
port: 5566
servlet:

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

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

View File

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

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