SAC规范RPC调用支持

This commit is contained in:
akun 2024-05-22 18:09:35 +08:00
parent 4b4d4cd2b7
commit a7d7679047
2 changed files with 87 additions and 105 deletions

View File

@ -20,6 +20,7 @@ import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RequestBody; import io.vertx.ext.web.RequestBody;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.HttpException; import io.vertx.ext.web.handler.HttpException;
import io.vertx.httpproxy.*;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
@ -50,10 +51,6 @@ import io.vertx.core.spi.cluster.ClusterManager;
import io.vertx.ext.web.Route; import io.vertx.ext.web.Route;
import io.vertx.ext.web.Router; import io.vertx.ext.web.Router;
import io.vertx.ext.web.client.WebClient; import io.vertx.ext.web.client.WebClient;
import io.vertx.httpproxy.HttpProxy;
import io.vertx.httpproxy.ProxyContext;
import io.vertx.httpproxy.ProxyInterceptor;
import io.vertx.httpproxy.ProxyResponse;
import io.vertx.spi.cluster.hazelcast.HazelcastClusterManager; import io.vertx.spi.cluster.hazelcast.HazelcastClusterManager;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -185,23 +182,28 @@ public class AppConfigHandler {
return true; return true;
} }
ApiConfig apicodeConfig = AppConfigHandler.getApicodeConfig(apiCodeCacheKey); ApiConfig apicodeConfig = AppConfigHandler.getApicodeConfig(apiCodeCacheKey);
// GET,DELETE,HEAD请求不解析body
RequestMethod requestMethod = RequestMethod.getByCode(apicodeConfig.getMethod());
if (requestMethod == null || RequestMethod.GET.equals(requestMethod)
|| RequestMethod.DELETE.equals(requestMethod)
|| RequestMethod.HEAD.equals(requestMethod)){
return false;
}
// 模糊匹配不解析body // 模糊匹配不解析body
String uri = apicodeConfig.getUri(); String uri = apicodeConfig.getUri();
if (StringUtils.equals(uri, "*")) { if (StringUtils.equals(uri, "*")) {
return false; return false;
} }
// SAC请求方式GET,DELETE,HEAD请求需要解析body,SAC请求方式参数统一使用body传递可能需要二次处理
if (isServiceTypeSac(apiCodeCacheKey)){
RequestMethod requestMethod = RequestMethod.getByCode(apicodeConfig.getMethod());
if (RequestMethod.GET.equals(requestMethod)
|| RequestMethod.DELETE.equals(requestMethod)
|| RequestMethod.HEAD.equals(requestMethod)){
return true;
}
}
if (StringUtils.startsWith(contentType, "multipart")){
return false;
}
String keyCircuitBreaker = apiCodeCacheKey + ":" + "CIRCUIT_BREAKER"; String keyCircuitBreaker = apiCodeCacheKey + ":" + "CIRCUIT_BREAKER";
CircuitBreaker circuitBreaker = AppConfigHandler.getApiCodeCircuitBreaker(keyCircuitBreaker); CircuitBreaker circuitBreaker = AppConfigHandler.getApiCodeCircuitBreaker(keyCircuitBreaker);
boolean isDataSecurity = AppConfigHandler.isDataSecurity(appCode); boolean isDataSecurity = AppConfigHandler.isDataSecurity(appCode);
// 文件上传不走加解密 // 文件上传不走加解密
return (isDataSecurity || circuitBreaker != null) && StringUtils.startsWith(contentType, "multipart") == false; return (isDataSecurity || circuitBreaker != null);
} }
/*** /***
@ -257,13 +259,17 @@ public class AppConfigHandler {
public static MockResponse mock(String key, RoutingContext rc) { public static MockResponse mock(String key, RoutingContext rc) {
if (APICODE_CONFIG_MAP.get(key) != null && APICODE_CONFIG_MAP.get(key).getMockDefaultHttpStatus() != null){ if (APICODE_CONFIG_MAP.get(key) != null && APICODE_CONFIG_MAP.get(key).getMockDefaultHttpStatus() != null){
String appCode = rc.request().headers().get(AppConfigHandler.getAppCodeHeaderKey());
// 如果是sac服务query和body参数都从body中取 // 如果是sac服务query和body参数都从body中取
Integer httpStatus = APICODE_CONFIG_MAP.get(key).getMockDefaultHttpStatus(); Integer httpStatus = APICODE_CONFIG_MAP.get(key).getMockDefaultHttpStatus();
String mockResponse = APICODE_CONFIG_MAP.get(key).getMockDefaultResponse(); String mockResponse = APICODE_CONFIG_MAP.get(key).getMockDefaultResponse();
List<MockExpectation> mockExpectations = APICODE_CONFIG_MAP.get(key).getMockExpectations(); List<MockExpectation> mockExpectations = APICODE_CONFIG_MAP.get(key).getMockExpectations();
if (mockExpectations != null && !mockExpectations.isEmpty()){ if (mockExpectations != null && !mockExpectations.isEmpty()){
RequestBody body = rc.body(); String bodyStr = rc.body().asString();
JsonObject jsonBody = body.asJsonObject(); if (AppConfigHandler.isDataSecurity(appCode)) {
bodyStr = AppConfigHandler.bodyDecrypt(bodyStr, appCode);
}
JsonObject jsonBody = new JsonObject(bodyStr);
for (MockExpectation mockExpectation : mockExpectations) { for (MockExpectation mockExpectation : mockExpectations) {
// 匹配条件需要全部匹配成功 // 匹配条件需要全部匹配成功
boolean matchSuccess = true; boolean matchSuccess = true;

View File

@ -17,12 +17,12 @@ import java.util.ListIterator;
import java.util.Map; import java.util.Map;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import cn.hutool.core.util.StrUtil;
import com.sf.vertx.enums.RequestMethod;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import com.sf.vertx.api.pojo.DataSecurity;
import com.sf.vertx.constans.SacErrorCode; import com.sf.vertx.constans.SacErrorCode;
import com.sf.vertx.handle.AppConfigHandler; import com.sf.vertx.handle.AppConfigHandler;
import com.sf.vertx.security.MainSecurity;
import com.sf.vertx.utils.ProxyTool; import com.sf.vertx.utils.ProxyTool;
import io.vertx.circuitbreaker.CircuitBreaker; import io.vertx.circuitbreaker.CircuitBreaker;
@ -57,6 +57,8 @@ import io.vertx.httpproxy.cache.CacheOptions;
import io.vertx.httpproxy.spi.cache.Cache; import io.vertx.httpproxy.spi.cache.Cache;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import static org.apache.commons.lang3.StringUtils.EMPTY;
@Slf4j @Slf4j
public class ReverseProxy implements HttpProxy { public class ReverseProxy implements HttpProxy {
@ -242,16 +244,15 @@ public class ReverseProxy implements HttpProxy {
} }
} }
private Future<ProxyResponse> breakerAndSecurity(CircuitBreaker circuitBreaker, ProxyRequest proxyRequest) { private Future<ProxyResponse> breakerCall(CircuitBreaker circuitBreaker
, ProxyRequest proxyRequest
, HttpRequest<Buffer> requestBuffer
, String body
) {
String appCode = proxyRequest.headers().get(AppConfigHandler.getAppCodeHeaderKey()); String appCode = proxyRequest.headers().get(AppConfigHandler.getAppCodeHeaderKey());
String apiCode = proxyRequest.headers().get(AppConfigHandler.getApiCodeHeaderKey());
String key = appCode + ":" + apiCode;
return Future.future(p -> { return Future.future(p -> {
circuitBreaker.executeWithFallback(promise -> { circuitBreaker.executeWithFallback(promise -> {
HttpRequest<Buffer> requestBuffer = methodGetRequestBuffer(proxyRequest); requestBuffer.sendJson(body, h -> {
requestBuffer.timeout(AppConfigHandler.getApicodeConfigTimeOut(key));
requestBuffer.putHeaders(proxyRequest.headers())
.sendJson(AppConfigHandler.bodyDecrypt(ctx.getBodyAsString(), appCode), h -> {
if (h.succeeded()) { if (h.succeeded()) {
log.info("==========uri:{},response http code:{}", proxyRequest.getURI(), log.info("==========uri:{},response http code:{}", proxyRequest.getURI(),
h.result().statusCode()); h.result().statusCode());
@ -267,12 +268,19 @@ public class ReverseProxy implements HttpProxy {
proxyRequest.release(); proxyRequest.release();
JsonObject responseData = h.result().bodyAsJsonObject(); JsonObject responseData = h.result().bodyAsJsonObject();
log.info("responseData:{}", responseData); log.info("responseData:{}", responseData);
Buffer buffer;
// 加密 // 加密
String dataStr = AppConfigHandler.bodyEncrypt(responseData.toString(), appCode); if (AppConfigHandler.isDataSecurity(appCode)) {
log.info("aesEncrypt dataStr:{}", dataStr); String dataStr = AppConfigHandler.bodyEncrypt(responseData.toString(), appCode);
Buffer buffer = Buffer.buffer(dataStr); log.info("encrypt dataStr:{}", dataStr);
ProxyResponse proxyResponse = proxyRequest.response().setStatusCode(200) buffer = Buffer.buffer(dataStr);
.putHeader("content-type", "application/json").setBody(Body.body(buffer)); }else {
buffer = responseData.toBuffer();
}
ProxyResponse proxyResponse = proxyRequest.response()
.setStatusCode(200)
.putHeader("content-type", "application/json")
.setBody(Body.body(buffer));
p.complete(proxyResponse); p.complete(proxyResponse);
} else { } else {
// end(proxyRequest, 502); // end(proxyRequest, 502);
@ -303,66 +311,11 @@ public class ReverseProxy implements HttpProxy {
}); });
} }
private Future<ProxyResponse> breaker(CircuitBreaker circuitBreaker, ProxyRequest proxyRequest) {
String appCode = proxyRequest.headers().get(AppConfigHandler.getAppCodeHeaderKey());
String apiCode = proxyRequest.headers().get(AppConfigHandler.getApiCodeHeaderKey());
String key = appCode + ":" + apiCode;
return Future.future(p -> {
circuitBreaker.executeWithFallback(promise -> {
HttpRequest<Buffer> requestBuffer = methodGetRequestBuffer(proxyRequest);
requestBuffer.timeout(AppConfigHandler.getApicodeConfigTimeOut(key));
requestBuffer.putHeaders(proxyRequest.headers()).sendJson(ctx.getBodyAsString(), h -> {
if (h.succeeded()) {
log.info("==========uri:{},response http code:{}", proxyRequest.getURI(),
h.result().statusCode());
if (h.result().statusCode() == 200) {
// promise.complete();
promise.complete("1");
} else {
promise.fail("2");
}
// 释放资源
proxyRequest.release();
JsonObject responseData = h.result().bodyAsJsonObject();
log.info("responseData:{}", responseData);
ProxyResponse proxyResponse = proxyRequest.response().setStatusCode(200)
.putHeader("content-type", "application/json")
.setBody(Body.body(responseData.toBuffer()));
p.complete(proxyResponse);
} else {
promise.fail("3");
}
});
}, v -> {
// 需要传递当前状态half-open , close, 还是统计失败次数
log.info(circuitBreaker.name() + " executed when the circuit is opened:{}", v.getMessage());
if (v instanceof HalfOpenCircuitException) {
log.info(circuitBreaker.name() + " half open circuit");
return v.getMessage();
} else if (v instanceof OpenCircuitException) {
log.info(circuitBreaker.name() + " open circuit");
} else if (v instanceof NoStackTraceThrowable) {
log.info(circuitBreaker.name() + " close circuit");
return v.getMessage();
}
return "3";
}, ar -> {
log.info(circuitBreaker.name() + " interface failed result.{} ", ar);
if (StringUtils.equals(ar.result(), "3")) { // 全开,熔断
end(proxyRequest, 10016);
}
});
});
}
private Future<ProxyResponse> security(CircuitBreaker circuitBreaker, ProxyRequest proxyRequest) { private Future<ProxyResponse> normalCall(ProxyRequest proxyRequest, HttpRequest<Buffer> requestBuffer, String body) {
String appCode = proxyRequest.headers().get(AppConfigHandler.getAppCodeHeaderKey()); String appCode = proxyRequest.headers().get(AppConfigHandler.getAppCodeHeaderKey());
String apiCode = proxyRequest.headers().get(AppConfigHandler.getApiCodeHeaderKey());
String key = appCode + ":" + apiCode;
return Future.future(p -> { return Future.future(p -> {
HttpRequest<Buffer> requestBuffer = methodGetRequestBuffer(proxyRequest); requestBuffer.sendJson(body,
requestBuffer.timeout(AppConfigHandler.getApicodeConfigTimeOut(key));
requestBuffer.putHeaders(proxyRequest.headers()).sendJson(AppConfigHandler.bodyDecrypt(ctx.getBodyAsString(), appCode),
h -> { h -> {
if (h.succeeded()) { if (h.succeeded()) {
log.info("==========uri:{},response http code:{}", proxyRequest.getURI(), log.info("==========uri:{},response http code:{}", proxyRequest.getURI(),
@ -372,12 +325,15 @@ public class ReverseProxy implements HttpProxy {
proxyRequest.release(); proxyRequest.release();
JsonObject responseData = h.result().bodyAsJsonObject(); JsonObject responseData = h.result().bodyAsJsonObject();
log.info("responseData:{}", responseData); log.info("responseData:{}", responseData);
Buffer bodyBuffer = responseData.toBuffer();
// 加密 // 加密
String dataStr = AppConfigHandler.bodyEncrypt(responseData.toString(), appCode); if (AppConfigHandler.isDataSecurity(appCode)) {
log.info("encrypt dataStr:{}", dataStr); String dataStr = AppConfigHandler.bodyEncrypt(responseData.toString(), appCode);
Buffer buffer = Buffer.buffer(dataStr); log.info("encrypt dataStr:{}", dataStr);
bodyBuffer = Buffer.buffer(dataStr);
}
ProxyResponse proxyResponse = proxyRequest.response().setStatusCode(200) ProxyResponse proxyResponse = proxyRequest.response().setStatusCode(200)
.putHeader("content-type", "application/json").setBody(Body.body(buffer)); .putHeader("content-type", "application/json").setBody(Body.body(bodyBuffer));
p.complete(proxyResponse); p.complete(proxyResponse);
} }
} else { } else {
@ -388,26 +344,48 @@ public class ReverseProxy implements HttpProxy {
}); });
} }
private HttpRequest<Buffer> methodGetRequestBuffer(ProxyRequest proxyRequest) {
private HttpRequest<Buffer> buildCallRequestBuffer(ProxyRequest proxyRequest,String body) {
String appCode = proxyRequest.headers().get(AppConfigHandler.getAppCodeHeaderKey());
String apiCode = proxyRequest.headers().get(AppConfigHandler.getApiCodeHeaderKey());
String key = appCode + ":" + apiCode;
SocketAddress socketAddress = ProxyTool.resolveOriginAddress(proxyRequest.proxiedRequest()); SocketAddress socketAddress = ProxyTool.resolveOriginAddress(proxyRequest.proxiedRequest());
HttpRequest<Buffer> requestBuffer = null; HttpRequest<Buffer> requestBuffer;
String requestURI = proxyRequest.getURI();
switch (proxyRequest.getMethod().name()) { switch (proxyRequest.getMethod().name()) {
case "PUT": case "PUT":
requestBuffer = mainWebClient.put(socketAddress.port(), socketAddress.host(), proxyRequest.getURI()); requestBuffer = mainWebClient.put(socketAddress.port(), socketAddress.host(), requestURI);
break; break;
case "DELETE": case "DELETE":
requestBuffer = mainWebClient.delete(socketAddress.port(), socketAddress.host(), proxyRequest.getURI()); requestBuffer = mainWebClient.delete(socketAddress.port(), socketAddress.host(), requestURI);
break; break;
case "HEAD": case "HEAD":
requestBuffer = mainWebClient.head(socketAddress.port(), socketAddress.host(), proxyRequest.getURI()); requestBuffer = mainWebClient.head(socketAddress.port(), socketAddress.host(), requestURI);
break; break;
case "GET": case "GET":
requestBuffer = mainWebClient.get(socketAddress.port(), socketAddress.host(), proxyRequest.getURI()); requestBuffer = mainWebClient.get(socketAddress.port(), socketAddress.host(), requestURI);
break; break;
default: default:
requestBuffer = mainWebClient.post(socketAddress.port(), socketAddress.host(), proxyRequest.getURI()); requestBuffer = mainWebClient.post(socketAddress.port(), socketAddress.host(), requestURI);
break; break;
} }
requestBuffer.timeout(AppConfigHandler.getApicodeConfigTimeOut(key));
requestBuffer.putHeaders(proxyRequest.headers());
// 处理sac请求方式
RequestMethod requestMethod = RequestMethod.getByCode(proxyRequest.getMethod().name());
if (RequestMethod.GET.equals(requestMethod)
|| RequestMethod.DELETE.equals(requestMethod)
|| RequestMethod.HEAD.equals(requestMethod)){
JsonObject jsonBody = new JsonObject(body);
Map<String, String> queryParams = new HashMap<>();
for (Map.Entry<String, Object> entry : jsonBody) {
String val = entry.getValue() != null ? entry.getValue().toString() : EMPTY;
queryParams.put(entry.getKey(),val);
requestURI = StrUtil.replace(requestURI,"{" + entry.getKey() +"}",val);
}
requestBuffer.uri(requestURI);
requestBuffer.queryParams().addAll(queryParams);
}
return requestBuffer; return requestBuffer;
} }
@ -421,16 +399,13 @@ public class ReverseProxy implements HttpProxy {
String keyCircuitBreaker = appCode + ":" + apiCode + ":" + "CIRCUIT_BREAKER"; String keyCircuitBreaker = appCode + ":" + apiCode + ":" + "CIRCUIT_BREAKER";
CircuitBreaker circuitBreaker = AppConfigHandler.getApiCodeCircuitBreaker(keyCircuitBreaker); CircuitBreaker circuitBreaker = AppConfigHandler.getApiCodeCircuitBreaker(keyCircuitBreaker);
if (AppConfigHandler.isAnalysisBody(appCode, apiCode, contentType)) { if (AppConfigHandler.isAnalysisBody(appCode, apiCode, contentType)) {
String body = AppConfigHandler.isDataSecurity(appCode) ? AppConfigHandler.bodyDecrypt(ctx.body().asString(), appCode):ctx.body().asString();
HttpRequest<Buffer> requestBuffer = buildCallRequestBuffer(proxyRequest,body);
try { try {
if (AppConfigHandler.isDataSecurity(appCode) && circuitBreaker != null) { if (circuitBreaker != null) {
return breakerAndSecurity(circuitBreaker, proxyRequest); return breakerCall(circuitBreaker, proxyRequest,requestBuffer,body);
} else if (AppConfigHandler.isDataSecurity(appCode)) {
return security(circuitBreaker, proxyRequest);
} else if (circuitBreaker != null) {
return breaker(circuitBreaker, proxyRequest);
} else { } else {
log.info("not match any condition.appCode:{},apiCode:{}", appCode, apiCode); return normalCall(proxyRequest,requestBuffer,body);
throw new HttpException(10013);
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@ -443,7 +418,7 @@ public class ReverseProxy implements HttpProxy {
} else { } else {
Future<HttpClientRequest> f = resolveOrigin(proxyRequest.proxiedRequest()); Future<HttpClientRequest> f = resolveOrigin(proxyRequest.proxiedRequest());
f.onFailure(err -> { f.onFailure(err -> {
log.info("error:{}", err); log.info("error:", err);
// Should this be done here ? I don't think so // Should this be done here ? I don't think so
HttpServerRequest proxiedRequest = proxyRequest.proxiedRequest(); HttpServerRequest proxiedRequest = proxyRequest.proxiedRequest();
proxiedRequest.resume(); proxiedRequest.resume();
@ -493,4 +468,5 @@ public class ReverseProxy implements HttpProxy {
} }
} }