feat: 升级1.0.7,重构执行器逻辑,增加方法缓存,提高并发性能

This commit is contained in:
wangyu 2023-01-31 19:04:51 +08:00
parent bf756ee743
commit 6aaa9b8cbe
8 changed files with 172 additions and 52 deletions

View File

@ -13,7 +13,7 @@
<groupId>group.flyfish</groupId> <groupId>group.flyfish</groupId>
<artifactId>rest-proxy</artifactId> <artifactId>rest-proxy</artifactId>
<version>1.0.6</version> <version>1.0.7</version>
<properties> <properties>
<maven.compiler.source>8</maven.compiler.source> <maven.compiler.source>8</maven.compiler.source>
@ -22,7 +22,7 @@
<java.version>1.8</java.version> <java.version>1.8</java.version>
<commons-collection.version>4.4</commons-collection.version> <commons-collection.version>4.4</commons-collection.version>
<commons.lang.version>2.6</commons.lang.version> <commons.lang.version>2.6</commons.lang.version>
<sdk.version>1.0.6</sdk.version> <sdk.version>1.0.7</sdk.version>
</properties> </properties>
<packaging>pom</packaging> <packaging>pom</packaging>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>group.flyfish</groupId> <groupId>group.flyfish</groupId>
<artifactId>rest-proxy</artifactId> <artifactId>rest-proxy</artifactId>
<version>1.0.6</version> <version>1.0.7</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>group.flyfish</groupId> <groupId>group.flyfish</groupId>
<artifactId>rest-proxy</artifactId> <artifactId>rest-proxy</artifactId>
<version>1.0.6</version> <version>1.0.7</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -2,7 +2,6 @@ package group.flyfish.rest.registry.proxy;
import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JavaType;
import group.flyfish.rest.annotation.AutoMapping; import group.flyfish.rest.annotation.AutoMapping;
import group.flyfish.rest.annotation.RestApi;
import group.flyfish.rest.annotation.RestService; import group.flyfish.rest.annotation.RestService;
import group.flyfish.rest.configuration.RestClientProperties; import group.flyfish.rest.configuration.RestClientProperties;
import group.flyfish.rest.configuration.configure.PropertiesConfigurable; import group.flyfish.rest.configuration.configure.PropertiesConfigurable;
@ -11,12 +10,14 @@ import group.flyfish.rest.core.client.RestClient;
import group.flyfish.rest.core.client.RestClientBuilder; import group.flyfish.rest.core.client.RestClientBuilder;
import group.flyfish.rest.mapping.RestResultMapping; import group.flyfish.rest.mapping.RestResultMapping;
import group.flyfish.rest.registry.RestApiRegistry; import group.flyfish.rest.registry.RestApiRegistry;
import group.flyfish.rest.registry.proxy.entity.RestMethod;
import group.flyfish.rest.registry.proxy.support.ArgumentResolveContext; import group.flyfish.rest.registry.proxy.support.ArgumentResolveContext;
import group.flyfish.rest.registry.proxy.support.RestArgumentResolverComposite; import group.flyfish.rest.registry.proxy.support.RestArgumentResolverComposite;
import group.flyfish.rest.registry.proxy.support.UrlCompiler; import group.flyfish.rest.registry.proxy.support.UrlCompiler;
import group.flyfish.rest.registry.wrapper.DefaultRestResultMapping; import group.flyfish.rest.registry.wrapper.DefaultRestResultMapping;
import group.flyfish.rest.utils.DataUtils; import group.flyfish.rest.utils.DataUtils;
import group.flyfish.rest.utils.JacksonUtil; import group.flyfish.rest.utils.JacksonUtil;
import lombok.Getter;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.config.RequestConfig; import org.apache.http.client.config.RequestConfig;
@ -25,14 +26,13 @@ import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import java.beans.Transient;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Map; import java.util.Map;
import java.util.stream.Stream; import java.util.concurrent.ConcurrentHashMap;
/** /**
* Rest代理执行器 * Rest代理执行器
@ -42,13 +42,17 @@ import java.util.stream.Stream;
@Slf4j @Slf4j
public class RestProxyInvoker implements InvocationHandler, PropertiesConfigurable { public class RestProxyInvoker implements InvocationHandler, PropertiesConfigurable {
// 方法缓存
private final Map<Integer, RestMethod> methods = new ConcurrentHashMap<>();
// 要代理的目标类 // 要代理的目标类
private final Class<?> targetType; private final Class<?> targetType;
// 服务映射 // 服务映射
private final RestService restService; private final RestService restService;
// 配置属性 // 配置属性
@Getter
private RestClientProperties properties; private RestClientProperties properties;
// 初始的基本路径 // 初始的基本路径
@Getter
private String baseUrl; private String baseUrl;
// 超时时间 // 超时时间
private RequestConfig config; private RequestConfig config;
@ -59,6 +63,7 @@ public class RestProxyInvoker implements InvocationHandler, PropertiesConfigurab
// 鉴权提供者 // 鉴权提供者
private RestAuthProvider authProvider; private RestAuthProvider authProvider;
/** /**
* 构造器 * 构造器
* *
@ -103,34 +108,27 @@ public class RestProxyInvoker implements InvocationHandler, PropertiesConfigurab
* 执行rest请求的地方这里很简单易懂 * 执行rest请求的地方这里很简单易懂
* *
* @param proxy 代理对象 * @param proxy 代理对象
* @param method 代理方法 * @param target 代理方法
* @param args 参数 * @param args 参数
* @return 结果 * @return 结果
* @throws Throwable 可能抛出的异常 * @throws Throwable 可能抛出的异常
*/ */
@Override @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { public Object invoke(Object proxy, Method target, Object[] args) throws Throwable {
// 处理基本方法被代理的情况 // 解析方法做基本验证
if (AopUtils.isEqualsMethod(method)) { RestMethod method = methods.computeIfAbsent(target.hashCode(), k -> RestMethod.resolve(target, this));
Object obj = args[0]; if (method.isInvalid()) {
// The target does not implement the equals(Object) method itself.
return null != obj && ClassUtils.isAssignable(targetType, obj.getClass());
} else if (AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return -1;
}
// 无视proxy因为啥也没
RestApi restApi = AnnotationUtils.findAnnotation(method, RestApi.class);
if (null == restApi) {
throw new IllegalAccessException("【Rest调用】未声明rest配置的方法被调用请检查代码"); throw new IllegalAccessException("【Rest调用】未声明rest配置的方法被调用请检查代码");
} }
RestArgumentResolverComposite composite = registry.getComposite(); RestArgumentResolverComposite composite = registry.getComposite();
// 第一步就解析参数 // 第一步就解析参数
ArgumentResolveContext context = composite.resolve(restApi, method, args); ArgumentResolveContext context = composite.resolve(method, args);
// 构造和调用这里的restClient不保存状态 // 构造和调用这里的restClient不保存状态
RestClientBuilder builder = RestClient.create().url(determineUrl(restApi, context)).method(restApi.method()).config(config); RestClientBuilder builder = RestClient.create().url(resolveUrl(method.getUrl(), context))
.method(method.getMethod())
.config(config);
// 需要带cookie的带上 // 需要带cookie的带上
if (restApi.credentials()) { if (method.isCredentials()) {
builder.withCredential(); builder.withCredential();
} }
// 判断情况赋值参数 // 判断情况赋值参数
@ -162,7 +160,7 @@ public class RestProxyInvoker implements InvocationHandler, PropertiesConfigurab
// 设置客户端 // 设置客户端
client.setClient(registry.getProvider()); client.setClient(registry.getProvider());
// 是否对结果进行映射 // 是否对结果进行映射
boolean map = null != mapping && null == AnnotationUtils.findAnnotation(method, Transient.class); boolean map = null != mapping && method.isBare();
// 执行请求 // 执行请求
Object result = execute(client, method, map); Object result = execute(client, method, map);
// 结果映射 // 结果映射
@ -177,7 +175,7 @@ public class RestProxyInvoker implements InvocationHandler, PropertiesConfigurab
* @param map 是否映射结果 * @param map 是否映射结果
* @return 结果 * @return 结果
*/ */
private Object execute(RestClient client, Method method, boolean map) throws IOException { private Object execute(RestClient client, RestMethod method, boolean map) throws IOException {
// 构建带泛型的返回值自动判断是否是简单类型 // 构建带泛型的返回值自动判断是否是简单类型
JavaType constructed = JacksonUtil.getMapper().constructType(method.getGenericReturnType()); JavaType constructed = JacksonUtil.getMapper().constructType(method.getGenericReturnType());
// 特殊处理映射 // 特殊处理映射
@ -236,21 +234,7 @@ public class RestProxyInvoker implements InvocationHandler, PropertiesConfigurab
* *
* @return 结果 * @return 结果
*/ */
private String determineUrl(RestApi restApi, ArgumentResolveContext context) { private String resolveUrl(String url, ArgumentResolveContext context) {
String url;
// 解析url以支持PathVariable
if (DataUtils.isNotBlank(restApi.url())) {
url = restApi.url();
} else {
// 构建基础url优先级从小到大依次找同时尝试取字典值
url = Stream.of(restApi.baseUrl(), this.baseUrl)
.filter(DataUtils::isNotBlank)
.findFirst()
// 判定和赋值
.map(found -> found.startsWith("#") ? properties.getDictUrl(found.substring(1)) : found)
.map(base -> base + restApi.uri())
.orElseThrow(() -> new IllegalArgumentException("【Rest调用】未指定url或baseurl无法调用远端服务器"));
}
// 尝试解析路径参数 // 尝试解析路径参数
return context.hasPathParams() ? UrlCompiler.compile(url, context.getPathParams()) : url; return context.hasPathParams() ? UrlCompiler.compile(url, context.getPathParams()) : url;
} }
@ -268,4 +252,25 @@ public class RestProxyInvoker implements InvocationHandler, PropertiesConfigurab
} }
return properties.getAuthProvider(); return properties.getAuthProvider();
} }
/**
* 如果调用到了Object类的方法则绕行
*
* @param method 方法
* @param args 参数
* @return 结果
*/
@Deprecated
private Object passObjectMethod(Method method, Object[] args) {
// 处理基本方法被代理的情况
if (AopUtils.isEqualsMethod(method)) {
Object obj = args[0];
// The target does not implement the equals(Object) method itself.
return null != obj && ClassUtils.isAssignable(targetType, obj.getClass());
} else if (AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return -1;
}
return null;
}
} }

View File

@ -0,0 +1,117 @@
package group.flyfish.rest.registry.proxy.entity;
import group.flyfish.rest.annotation.RestApi;
import group.flyfish.rest.enums.HttpMethod;
import group.flyfish.rest.registry.proxy.RestProxyInvoker;
import group.flyfish.rest.utils.DataUtils;
import lombok.Getter;
import org.springframework.core.annotation.AnnotationUtils;
import java.beans.Transient;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.util.stream.Stream;
/**
* 表示单个请求的最小单元
*
* @author wangyu
*/
public class RestMethod {
// 方法参数缓存避免clone
@Getter
private Parameter[] parameters;
// 解析@Transient注解的结果
@Getter
private boolean bare;
// 解析后的路径
@Getter
private String url;
// http方法
@Getter
private HttpMethod method;
// 是否携带cookie
@Getter
private boolean credentials;
// 多个参数时使用合并的body
@Getter
private boolean mergeBody;
// 带泛型的返回类型
@Getter
private Type genericReturnType;
// 不带泛型的返回类型
@Getter
private Class<?> returnType;
// 是否不可用状态
@Getter
private boolean invalid;
private RestMethod(Method method, RestProxyInvoker invoker) {
// 执行初始化
init(method, invoker);
}
/**
* 解析代理方法
*
* @param method 方法
* @return 结果
*/
public static RestMethod resolve(Method method, RestProxyInvoker invoker) {
return new RestMethod(method, invoker);
}
/**
* 初始化方法
*/
private void init(Method method, RestProxyInvoker invoker) {
RestApi restApi = AnnotationUtils.findAnnotation(method, RestApi.class);
// 无视proxy因为啥也没
if (null == restApi) {
this.invalid = true;
return;
}
this.url = determineUrl(restApi, invoker);
this.method = restApi.method();
this.credentials = restApi.credentials();
this.mergeBody = restApi.mergedBody();
this.parameters = method.getParameters();
this.bare = null != AnnotationUtils.findAnnotation(method, Transient.class);
this.genericReturnType = method.getGenericReturnType();
this.returnType = method.getReturnType();
}
/**
* 决定基本url优先级 方法注解url > 方法注解baseUrl + uri > 全局配置 + uri
*
* @return 结果
*/
private String determineUrl(RestApi restApi, RestProxyInvoker invoker) {
String url;
// 解析url以支持PathVariable
if (DataUtils.isNotBlank(restApi.url())) {
return restApi.url();
} else {
// 构建基础url优先级从小到大依次找同时尝试取字典值
return Stream.of(restApi.baseUrl(), invoker.getBaseUrl())
.filter(DataUtils::isNotBlank)
.findFirst()
// 判定和赋值
.map(found -> found.startsWith("#") ?
invoker.getProperties().getDictUrl(found.substring(1)) : found)
.map(base -> base + restApi.uri())
.orElseThrow(() -> new IllegalArgumentException("【Rest调用】未指定url或baseurl无法调用远端服务器"));
}
}
}

View File

@ -1,7 +1,7 @@
package group.flyfish.rest.registry.proxy.support; package group.flyfish.rest.registry.proxy.support;
import group.flyfish.rest.annotation.RestApi;
import group.flyfish.rest.entity.Multipart; import group.flyfish.rest.entity.Multipart;
import group.flyfish.rest.registry.proxy.entity.RestMethod;
import group.flyfish.rest.utils.DataUtils; import group.flyfish.rest.utils.DataUtils;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
@ -18,8 +18,8 @@ import java.util.Map;
@Builder @Builder
public class ArgumentResolveContext { public class ArgumentResolveContext {
// // 析的方法
private RestApi annotation; private RestMethod method;
// 参数 // 参数
private Map<String, Object> param; private Map<String, Object> param;

View File

@ -1,8 +1,7 @@
package group.flyfish.rest.registry.proxy.support; package group.flyfish.rest.registry.proxy.support;
import group.flyfish.rest.annotation.RestApi; import group.flyfish.rest.registry.proxy.entity.RestMethod;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter; import java.lang.reflect.Parameter;
import java.util.List; import java.util.List;
import java.util.stream.IntStream; import java.util.stream.IntStream;
@ -24,14 +23,13 @@ public class RestArgumentResolverComposite {
/** /**
* 执行解析 * 执行解析
* *
* @param annotation 注解
* @param method 方法 * @param method 方法
* @param args 参数 * @param args 参数
* @return 结果 * @return 结果
*/ */
public ArgumentResolveContext resolve(RestApi annotation, Method method, Object[] args) { public ArgumentResolveContext resolve(RestMethod method, Object[] args) {
// 上下文 // 上下文
ArgumentResolveContext context = ArgumentResolveContext.builder().annotation(annotation).build(); ArgumentResolveContext context = ArgumentResolveContext.builder().method(method).build();
// 参数元 // 参数元
Parameter[] parameters = method.getParameters(); Parameter[] parameters = method.getParameters();
// 循环处理 // 循环处理

View File

@ -53,7 +53,7 @@ public class RestParamArgumentResolver implements RestArgumentResolver {
.filter(DataUtils::isNotBlank) .filter(DataUtils::isNotBlank)
.orElse(parameter.getName()); .orElse(parameter.getName());
// 启用合并请求体合并入 // 启用合并请求体合并入
if (context.getAnnotation().mergedBody()) { if (context.getMethod().isMergeBody()) {
context.setBody(name, value); context.setBody(name, value);
} else { } else {
context.setParam(name, value); context.setParam(name, value);