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>
<artifactId>rest-proxy</artifactId>
<version>1.0.6</version>
<version>1.0.7</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
@ -22,7 +22,7 @@
<java.version>1.8</java.version>
<commons-collection.version>4.4</commons-collection.version>
<commons.lang.version>2.6</commons.lang.version>
<sdk.version>1.0.6</sdk.version>
<sdk.version>1.0.7</sdk.version>
</properties>
<packaging>pom</packaging>

View File

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

View File

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

View File

@ -2,7 +2,6 @@ package group.flyfish.rest.registry.proxy;
import com.fasterxml.jackson.databind.JavaType;
import group.flyfish.rest.annotation.AutoMapping;
import group.flyfish.rest.annotation.RestApi;
import group.flyfish.rest.annotation.RestService;
import group.flyfish.rest.configuration.RestClientProperties;
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.mapping.RestResultMapping;
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.RestArgumentResolverComposite;
import group.flyfish.rest.registry.proxy.support.UrlCompiler;
import group.flyfish.rest.registry.wrapper.DefaultRestResultMapping;
import group.flyfish.rest.utils.DataUtils;
import group.flyfish.rest.utils.JacksonUtil;
import lombok.Getter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
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.ClassUtils;
import java.beans.Transient;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.stream.Stream;
import java.util.concurrent.ConcurrentHashMap;
/**
* Rest代理执行器
@ -42,13 +42,17 @@ import java.util.stream.Stream;
@Slf4j
public class RestProxyInvoker implements InvocationHandler, PropertiesConfigurable {
// 方法缓存
private final Map<Integer, RestMethod> methods = new ConcurrentHashMap<>();
// 要代理的目标类
private final Class<?> targetType;
// 服务映射
private final RestService restService;
// 配置属性
@Getter
private RestClientProperties properties;
// 初始的基本路径
@Getter
private String baseUrl;
// 超时时间
private RequestConfig config;
@ -59,6 +63,7 @@ public class RestProxyInvoker implements InvocationHandler, PropertiesConfigurab
// 鉴权提供者
private RestAuthProvider authProvider;
/**
* 构造器
*
@ -103,34 +108,27 @@ public class RestProxyInvoker implements InvocationHandler, PropertiesConfigurab
* 执行rest请求的地方这里很简单易懂
*
* @param proxy 代理对象
* @param method 代理方法
* @param target 代理方法
* @param args 参数
* @return 结果
* @throws Throwable 可能抛出的异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 处理基本方法被代理的情况
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;
}
// 无视proxy因为啥也没
RestApi restApi = AnnotationUtils.findAnnotation(method, RestApi.class);
if (null == restApi) {
public Object invoke(Object proxy, Method target, Object[] args) throws Throwable {
// 解析方法做基本验证
RestMethod method = methods.computeIfAbsent(target.hashCode(), k -> RestMethod.resolve(target, this));
if (method.isInvalid()) {
throw new IllegalAccessException("【Rest调用】未声明rest配置的方法被调用请检查代码");
}
RestArgumentResolverComposite composite = registry.getComposite();
// 第一步就解析参数
ArgumentResolveContext context = composite.resolve(restApi, method, args);
ArgumentResolveContext context = composite.resolve(method, args);
// 构造和调用这里的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的带上
if (restApi.credentials()) {
if (method.isCredentials()) {
builder.withCredential();
}
// 判断情况赋值参数
@ -162,7 +160,7 @@ public class RestProxyInvoker implements InvocationHandler, PropertiesConfigurab
// 设置客户端
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);
// 结果映射
@ -177,7 +175,7 @@ public class RestProxyInvoker implements InvocationHandler, PropertiesConfigurab
* @param map 是否映射结果
* @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());
// 特殊处理映射
@ -236,21 +234,7 @@ public class RestProxyInvoker implements InvocationHandler, PropertiesConfigurab
*
* @return 结果
*/
private String determineUrl(RestApi restApi, 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无法调用远端服务器"));
}
private String resolveUrl(String url, ArgumentResolveContext context) {
// 尝试解析路径参数
return context.hasPathParams() ? UrlCompiler.compile(url, context.getPathParams()) : url;
}
@ -268,4 +252,25 @@ public class RestProxyInvoker implements InvocationHandler, PropertiesConfigurab
}
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;
import group.flyfish.rest.annotation.RestApi;
import group.flyfish.rest.entity.Multipart;
import group.flyfish.rest.registry.proxy.entity.RestMethod;
import group.flyfish.rest.utils.DataUtils;
import lombok.Builder;
import lombok.Data;
@ -18,8 +18,8 @@ import java.util.Map;
@Builder
public class ArgumentResolveContext {
//
private RestApi annotation;
// 析的方法
private RestMethod method;
// 参数
private Map<String, Object> param;

View File

@ -1,8 +1,7 @@
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.util.List;
import java.util.stream.IntStream;
@ -24,14 +23,13 @@ public class RestArgumentResolverComposite {
/**
* 执行解析
*
* @param annotation 注解
* @param method 方法
* @param args 参数
* @param method 方法
* @param args 参数
* @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();
// 循环处理

View File

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