手写RPC框架Feign

news/2024/5/6 23:32:07/文章来源:https://blog.csdn.net/h273979586/article/details/126750590

Feign原理实现

  • 手写RPC框架Feign
    • 什么是RPC
    • Feign注入原理
        • EnableFeignClients
    • Feign调用入口
    • 手写Feign

手写RPC框架Feign

阅读本文你可获得:
1、RPC原理
2、feign注入原理
3、如何手写feign框架
4、动态代码设计模式应用场景

什么是RPC

RPC:远程过程调用,也就是我们通常说“HTTP”调用,也有人说dubbo不是基于HTTP也是RPC啊,那怎么理解这件事呢?其实RPC就是指两服务之间调用不再写繁琐代码,有个东西把这件事包装起来,像写普通方法一样调用远程服务。

有人从网络分层(5层)角度出发说:feign不属于RPC,dubbo才是真正的RPC,feign底层是HTTP(应用层)或7层调用,dubbo是4层(传输层)调用,真正PRC是4层而不是7层。也有人说RPC就是HTTP,在此小编认为两者说的不准确,RPC不是区分实现过程(4层、7层)只区分调用手段(像调用普通service一样)。

从宏观角度讲只要是开发人员调用外部服务像调用方法一样的框架都叫RPC。

Feign注入原理

使用feign需要在启动类上加@EnableFeignClients注解并写上包名,我们在类上加@FeignClient注解、方法加@GetMapping等注解并配置相应属性feign架构就起作用,那么从这里思考这几个注解起了什么作用?我们倒着分析。

  1. GetMapping、PostMapping 这几个注解同Controller一样这里不再描述;
  2. FeignClient 是对我们类名、请求地址进行统一配置这里不再描述;
  3. EnableFeignClients 这个注解相当重要且它是核心注解,以上两个注解是否起作用,怎么发起HTTP调用都是该注解在工作;

EnableFeignClients

注解上有一个@Import(FeignClientsRegistrar.class)核心代码在FeignClientsRegistrar类中。
该类实现接口ImportBeanDefinitionRegistrar它是Spring扩展点之一,支持我们写的代码封装成BeanDefinition俗称bean,bean的有功能它都有例如:@Autowired注解该bean、postProcessAfterInitializationbean的后置初始化等功能。

  • registerBeanDefinitions 方法中两个方法:registerDefaultConfiguration注册默认配置和registerFeignClients注册feign的配置,我们注入feign的配置所以核心在registerFeignClients方法中。

在这里插入图片描述
scanner是项目启动扫描所有class文件,并设置过滤条件(类上有FeignClient注解),且通过metadata(启动类上EnableFeignClients注解配置包名)并获得注解所扫描对应包名。

在这里插入图片描述
拿到启动类上所有包类,过滤上一步设置好的条件(带FeignClient注解的)类,找到我们定义FeignClient接口类。

在这里插入图片描述
上图四步feign帮我们做了,其实第三步对于我们手写框架来说可以忽略,重要看第四步。

在这里插入图片描述
第一步:构建FeignClientFactoryBean并通过构造函数设置值(生成代理核心)。
第二步:获得bean别名、并设置bean是否是主要bean。
第三步:向Spring中注册bean。


致此我们写的带FeignClient注解的接口注册到spring中,可以通过@Autowired@Resource等方式注入到代码里开始使用,那么有的小伙伴就会问我的实现还没写呢?调用逻辑呢?

Feign调用入口

Java动态代理分为两种:JDK和CGLIB,它两本质区别是有没有实现接口,JDK必须是实现接口,CGLIB可以不用实现接口。feign用的是JDK动态代理所以我们写代码时FeignClient注解要放在接口,刚才注入bean过程也进行了判断,非接口不让注解会报错(第三张图有分析)。

我们最后一张图有说:构建FeignClientFactoryBean并通过构造函数设置值,这里是产生代理核心。该主要实现了FactoryBean返回值就是我们注册到spring中bean的类型,在该类中可以注入其它bean,感觉兴趣的小伙伴可以从源一直点下去,在newInstance类中会生成代理对象,如下图。

在动态代理类中编写具体实现代码。
在这里插入图片描述

以上核对源码分析完成,我们分析源码主要目的是为实现简单feign做铺垫。


手写Feign

如果我们要写一个简单版本的feign那应该怎么做呢?
1、我们需要把接口类注入到spring中。
2、在动态代理中编写实现方法。

听着很简单实际操作也很简单(>‿<)

  • 定义启动类注解MonkeyEnableHttpClient并配置扫描包同EnableFeignClients注解,我们只需要value一个注解即可。
/*** @Author: LailaiMonkey* @Description:* @Date:Created in 2022/09/15 4:11 下午* @Modified By:*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(MonkeyHttpClientRegister.class)
public @interface MonkeyEnableHttpClient {String[] value();
}
  • 定义接口类注解MonkeyHttpClientFeignClient注解,需要知道bean名称(通过反射自动生成)、该类配置全局url、超时时间等信息。
/*** @Author: LailaiMonkey* @Description:* @Date:Created in 2022/09/15 2:48 下午* @Modified By:*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MonkeyHttpClient {/*** 注册bean名称,默认类名** @return*/String name() default "";/*** 调用url** @return*/String url();/*** 读超时时间** @return*/String readTimeout() default "5000";/*** 连接超时时间** @return*/String connectTimeout() default "5000";}
  • 定义注册类MonkeyHttpClientRegister启动时扫描包并注册到Spring中。
/*** @Author: LailaiMonkey* @Description:* @Date:Created in 2022/09/15 4:04 下午* @Modified By:*/
@Slf4j
public class MonkeyHttpClientRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware, BeanClassLoaderAware, ResourceLoaderAware {private Environment environment;private ClassLoader classLoader;private ResourceLoader resourceLoader;@Overridepublic void setEnvironment(Environment environment) {this.environment = environment;}@Overridepublic void setBeanClassLoader(ClassLoader classLoader) {this.classLoader = classLoader;}@Overridepublic void setResourceLoader(ResourceLoader resourceLoader) {this.resourceLoader = resourceLoader;}@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {ClassPathScanningCandidateComponentProvider scanner = getScanner();scanner.setResourceLoader(this.resourceLoader);AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(MonkeyHttpClient.class);scanner.addIncludeFilter(annotationTypeFilter);Map<String, Object> attributes = metadata.getAnnotationAttributes(MonkeyEnableHttpClient.class.getCanonicalName());if (CollectionUtils.isEmpty(attributes)) {return;}Set<String> basePackages = new HashSet<>();for (String pkg : (String[]) attributes.get("value")) {if (StringUtils.hasText(pkg)) {basePackages.add(pkg);}}for (String basePackage : basePackages) {Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);for (BeanDefinition candidateComponent : candidateComponents) {if (candidateComponent instanceof AnnotatedBeanDefinition) {// verify annotated class is an interfaceAnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();Assert.isTrue(annotationMetadata.isInterface(), "@GZHttpClient can only be specified on an interface");Map<String, Object> feignClientAttributeMap = annotationMetadata.getAnnotationAttributes(MonkeyHttpClient.class.getCanonicalName());if (CollectionUtils.isEmpty(feignClientAttributeMap)) {return;}String className = annotationMetadata.getClassName();Class<?> clazz = null;try {clazz = Class.forName(className);} catch (ClassNotFoundException e) {log.error("httpClient start up fail:", e);}String beanName = className.substring(className.lastIndexOf(".") + 1);String alias = beanName.substring(0, 1).toLowerCase().concat(beanName.substring(1)).concat("HttpClient");String name = String.valueOf(feignClientAttributeMap.get("name"));String url = String.valueOf(feignClientAttributeMap.get("url"));if (!StringUtils.isEmpty(name)) {alias = name;}String readTimeout = String.valueOf(feignClientAttributeMap.get("readTimeout"));String connectTimeout = String.valueOf(feignClientAttributeMap.get("connectTimeout"));BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(MonkeyHttpClientFactoryBean.class);definition.addConstructorArgValue(clazz);definition.addConstructorArgValue(url);definition.addConstructorArgValue(readTimeout);definition.addConstructorArgValue(connectTimeout);definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);AbstractBeanDefinition handleDefinition = definition.getBeanDefinition();handleDefinition.setPrimary(true);// 向Spring的上下文中注册bean组件BeanDefinitionHolder holder = new BeanDefinitionHolder(handleDefinition, className, new String[]{alias});BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);}}}}protected ClassPathScanningCandidateComponentProvider getScanner() {return new ClassPathScanningCandidateComponentProvider(false, this.environment) {@Overrideprotected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {if (beanDefinition.getMetadata().isIndependent()) {if (beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().getInterfaceNames().length == 1&& Annotation.class.getName().equals(beanDefinition.getMetadata().getInterfaceNames()[0])) {try {Class<?> target = ClassUtils.forName(beanDefinition.getMetadata().getClassName(), MonkeyHttpClientRegister.this.classLoader);return !target.isAnnotation();} catch (Exception ex) {this.logger.error("Could not load target class: " + beanDefinition.getMetadata().getClassName(), ex);}}return true;}return false;}};}
}
  • 定义工厂类MonkeyHttpClientFactoryBean生成动态代理。
/*** @Author: LailaiMonkey* @Description:* @Date:Created in 2022/9/15 7:14 下午* @Modified By:*/
public class MonkeyHttpClientFactoryBean implements FactoryBean<Object>, InitializingBean {private Class<?> clazz;private String url;private Integer readTimeout;private Integer connectTimeout;public MonkeyHttpClientFactoryBean(Class<?> clazz, String url, Integer readTimeout, Integer connectTimeout) {this.clazz = clazz;this.url = url;this.readTimeout = readTimeout;this.connectTimeout = connectTimeout;}@Overridepublic void afterPropertiesSet() {if (Objects.isNull(clazz)) {throw new MonkeyHttpException("http start up param error, class is null");}if (StringUtils.isBlank(url)) {throw new MonkeyHttpException("http start up param error, url is null");}if (Objects.isNull(readTimeout)) {throw new MonkeyHttpException("http start up param error, readTimeout is null");}if (Objects.isNull(connectTimeout)) {throw new MonkeyHttpException("http start up param error, connectTimeout is null");}}@Overridepublic Object getObject() {return Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz},new MonkeyHttpClientExecute(url, readTimeout, connectTimeout));}@Overridepublic Class<?> getObjectType() {return clazz;}}
  • 定义代理类MonkeyHttpClientExecute这是最重要一步!!!我们接口实现逻辑全在这里面。
/*** @Author: 侯旭日* @Description:* @Date:Created in 2022/1/2 9:21 下午* @Modified By:*/
@Slf4j
public class GZHttpClientExecute implements InvocationHandler {private static final Map<Method, GZHttpClientParamModel> CACHE_PARAM_MODEL = new HashMap<>();private static final Map<Method, GZHttpClientResultModel> CACHE_RESULT_MODEL = new HashMap<>();private static final Map<Method, RetryTemplate> CACHE_RETRY_MAP = new HashMap<>();private static final Map<Class<? extends Throwable>, Boolean> RETRY_CONFIG_MAP = new HashMap<>();static {RETRY_CONFIG_MAP.put(GZHttpException.class, true);RETRY_CONFIG_MAP.put(SocketTimeoutException.class, true);}private CloseableHttpClient httpClient;private String url;private String appKey;private String appSecret;private Integer readTimeout;private Integer connectTimeout;private Integer retry;private HttpSignature httpSignature;private EtcdPropertiesClient etcdPropertiesClient;public GZHttpClientExecute(CloseableHttpClient httpClient, EtcdPropertiesClient etcdPropertiesClient, HttpSignature httpSignature,String url, String appKey, String appSecret,Integer readTimeout, Integer connectTimeout, Integer retry) {this.httpClient = httpClient;this.etcdPropertiesClient = etcdPropertiesClient;this.httpSignature = httpSignature;this.url = url;this.appKey = appKey;this.appSecret = appSecret;this.readTimeout = readTimeout;this.connectTimeout = connectTimeout;this.retry = retry;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {GZRequestMapping requestMapping = AnnotatedElementUtils.getMergedAnnotation(method, GZRequestMapping.class);String errorInfo = "call " + method.getDeclaringClass().getName() + "#" + method.getName();if (Objects.isNull(requestMapping)) {throw new GZHttpException(errorInfo + " not http method");}// 生成参数Map<String, Object> paramMap = createParam(method, args);// 填充uriString path = fillUri(paramMap, requestMapping.path());// 缓存返回值模型cacheResultMode(method);// 处理ModelAttributehandlerModelAttribute(method, args, requestMapping, paramMap);// retryhandlerRetry(method, getRetry(requestMapping));String strUrl = getUrl(url, path);// 获得form-dataList<NameValuePair> formData = getFormData(method, args, requestMapping);// 不是form-data则获得bodyString body = StringUtils.EMPTY;if (CollectionUtils.isEmpty(formData)) {body = getBody(method, args, requestMapping);// 兼容老逻辑,有body且不是泛型则放uri上if (StringUtils.isNotBlank(body) && Boolean.FALSE.equals(CACHE_PARAM_MODEL.get(method).getGeneric())) {Map<String, Object> bodyMap = JsonUtil.of(body, TreeMap.class);strUrl += "?" + bodyMap.entrySet().stream().map(e -> e.getKey() + "=" + (Objects.nonNull(e.getValue()) ? UrlEncoded.encodeString(e.getValue().toString()) : StringUtils.EMPTY)).collect(Collectors.joining("&"));} else {strUrl += httpSignature.urlSignatureParam(paramMap, appKey, appSecret);}}//        // 非get请求且普通参数需放form-data,get请求参数需放url上
//        if (!RequestMethod.GET.equals(requestMapping.method()) && StringUtils.isBlank(body) && CollectionUtils.isEmpty(formData)) {
//            httpSignature.urlSignatureParam(paramMap, appKey, appSecret);
//            for (Map.Entry<String, Object> entry : paramMap.entrySet()) {
//                formData.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));
//            }
//        } else if (RequestMethod.GET.equals(requestMapping.method())) {
//            // 参数签名
//            strUrl += httpSignature.urlSignatureParam(paramMap, appKey, appSecret);
//        }// 获得监控uriString monitorUri = getMonitorUri(url, requestMapping.path());// 请求requestRequest request = setRequest(requestMapping, formData, strUrl, errorInfo, body, monitorUri);String result = sendHttp(method, request, formData, errorInfo, requestMapping, strUrl, paramMap, body);return deserializationResult(method, result);}/*** 获得from-data** @param method* @param args* @param requestMapping* @return*/private List<NameValuePair> getFormData(Method method, Object[] args, GZRequestMapping requestMapping) {List<NameValuePair> nameValuePairs = new ArrayList<>();if (HttpMediaType.MULTIPART_FORM_DATA_VALUE.equals(requestMapping.mediaType())) {switch (requestMapping.method()) {case POST:case PUT:case DELETE:GZHttpClientParamModel paramModel = CACHE_PARAM_MODEL.get(method);// 有body体if (Objects.nonNull(paramModel.getGenericIndex()) && Boolean.FALSE.equals(paramModel.getGeneric())) {Map<String, Object> bodyMap = JsonUtil.of(JsonUtil.toJson(args[paramModel.getGenericIndex()]), TreeMap.class);Map<String, Object> signatureMap = httpSignature.bodySignatureParam(bodyMap, appKey, appSecret);for (Map.Entry<String, Object> entry : signatureMap.entrySet()) {nameValuePairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));}}break;default:return nameValuePairs;}}return nameValuePairs;}/*** 获得request** @param requestMapping* @param formData* @param strUrl* @param errorInfo* @param body* @return*/private Request setRequest(GZRequestMapping requestMapping, List<NameValuePair> formData, String strUrl, String errorInfo, String body, String monitorUri) {Request request;switch (requestMapping.method()) {case GET:request = Request.get(strUrl);break;case POST:request = Request.post(strUrl);setRequest(request, formData, body);break;case PUT:request = Request.put(strUrl);setRequest(request, formData, body);break;case DELETE:request = Request.delete(strUrl);setRequest(request, formData, body);break;default:throw new GZHttpException(errorInfo + " request method error");}request.responseTimeout(Timeout.ofMilliseconds(getReadTimeout(requestMapping))).connectTimeout(Timeout.ofMilliseconds(getConnectTimeout(requestMapping))).addHeader(HttpConstants.METRIC_HTTP_URL_HEADER, monitorUri);return request;}private Request setRequest(Request request, List<NameValuePair> formData, String body) {if (CollectionUtils.isNotEmpty(formData)) {request = request.bodyForm(formData, StandardCharsets.UTF_8);} else {request = request.bodyString(body, ContentType.APPLICATION_JSON);}return request;}/*** 请求http** @param method* @param request* @param errorInfo* @param requestMapping* @param strUrl* @param paramMap* @param body* @return* @throws Throwable*/private String sendHttp(Method method, Request request, List<NameValuePair> formData, String errorInfo, GZRequestMapping requestMapping,String strUrl, Map<String, Object> paramMap, String body) throws Throwable {String result;if (CACHE_RETRY_MAP.containsKey(method)) {RetryTemplate retryTemplate = CACHE_RETRY_MAP.get(method);result = retryTemplate.execute((RetryCallback<String, Throwable>) context -> {String res;try {res = request.execute(httpClient).handleResponse(new GaiaHttpClientResponseHandler(true));if (StringUtils.isBlank(res)) {throw new GZHttpException(errorInfo + " timeout, return is null");}} catch (Exception e) {log.info("call url error:{}, param:{}, body:{}, retryCount:{}, exception:{}", strUrl, JsonUtil.toJson(paramMap), body, context.getRetryCount() + 1, e);throw e;}return res;});} else {try {result = request.execute(httpClient).handleResponse(new GaiaHttpClientResponseHandler(true));} catch (Exception e) {log.info("call url error:{}, param:{}, body:{}, exception:{}", strUrl, JsonUtil.toJson(paramMap), body, e);throw new GZHttpException(errorInfo + " call url: " + strUrl + " error, reason:" + e.getMessage());}}String strFormData = formData.stream().map(m -> m.getName() + "=" + m.getValue()).collect(Collectors.joining(","));if (requestMapping.showLog()) {log.info("call url:{}, param:{}, form-data:{}, body:{}, result:{}", strUrl, JsonUtil.toJson(paramMap), strFormData, body, result);}return result;}/*** 处理ModelAttribute** @param method* @param args* @param requestMapping* @param paramMap*/private void handlerModelAttribute(Method method, Object[] args, GZRequestMapping requestMapping, Map<String, Object> paramMap) {// get请求处理model参数if (RequestMethod.GET.equals(requestMapping.method())) {GZHttpClientParamModel paramModel = CACHE_PARAM_MODEL.get(method);if (Objects.nonNull(paramModel.getGenericIndex()) && Boolean.FALSE.equals(paramModel.getGeneric())) {Object param = args[paramModel.getGenericIndex()];if (param instanceof Map) {paramMap.putAll((Map) param);} else {paramMap.putAll(JsonUtil.of(JsonUtil.toJson(param), Map.class));}}}}/*** 生成参数*/private Map<String, Object> createParam(Method method, Object[] args) {GZHttpClientParamModel paramModel = cacheParamModel(method, args);Map<String, Object> paramMap = new TreeMap<>();for (int i = 0; i < paramModel.getParamNames().size(); i++) {String paramName = paramModel.getParamNames().get(i);if (Objects.isNull(args[i]) || args[i].getClass().getName().startsWith("java.lang")) {paramMap.put(paramName, args[i]);} else if (args[i] instanceof Object[]) {StringBuilder str = new StringBuilder();for (Object o : (Object[]) args[i]) {str.append(o.toString()).append(",");}str.delete(str.length() - 1, str.length());paramMap.put(paramName, str);} else if (args[i] instanceof Map) {paramMap.putAll((Map) args[i]);}}return paramMap;}/*** 缓存入参模型** @param method* @param args*/private GZHttpClientParamModel cacheParamModel(Method method, Object[] args) {GZHttpClientParamModel paramModel = CACHE_PARAM_MODEL.get(method);if (Objects.nonNull(paramModel)) {return paramModel;}return safeCacheParamModel(method, args);}private synchronized GZHttpClientParamModel safeCacheParamModel(Method method, Object[] args) {GZHttpClientParamModel paramModel = CACHE_PARAM_MODEL.get(method);if (Objects.nonNull(paramModel)) {return paramModel;}Parameter[] parameters = method.getParameters();List<String> paramNames = new ArrayList<>();Integer index = null;for (int i = 0; i < parameters.length; i++) {// 获得别名RequestParam requestParam = AnnotatedElementUtils.getMergedAnnotation(parameters[i], RequestParam.class);String paramName = parameters[i].getName();if (Objects.nonNull(requestParam)) {paramName = requestParam.value();}paramNames.add(paramName);// 不是基础类型或数组则为model(Map等均为model)if (!(Objects.isNull(args[i]) || args[i].getClass().getName().startsWith("java.lang") ||args[i] instanceof Object[])) {index = i;}}boolean isGeneric = false;if (Objects.nonNull(index)) {Class<?> parameterType = method.getParameterTypes()[index];if (parameterType.isAssignableFrom(List.class)) {isGeneric = true;}}paramModel = new GZHttpClientParamModel(paramNames, index, isGeneric);CACHE_PARAM_MODEL.put(method, paramModel);return paramModel;}/*** 缓存返回值模型*/private void cacheResultMode(Method method) {if (CACHE_RESULT_MODEL.containsKey(method)) {return;}safeCacheResultMode(method);}private synchronized void safeCacheResultMode(Method method) {if (CACHE_RESULT_MODEL.containsKey(method)) {return;}Type returnType = method.getGenericReturnType();CACHE_RESULT_MODEL.put(method, new GZHttpClientResultModel(returnType));}/*** 处理重试** @param method* @param retry*/private void handlerRetry(Method method, int retry) {if (retry > 0) {if (CACHE_RETRY_MAP.containsKey(method)) {return;}safeCacheRetry(method, retry);}}private synchronized void safeCacheRetry(Method method, int retry) {SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(retry, RETRY_CONFIG_MAP);RetryTemplate template = new RetryTemplate();template.setRetryPolicy(retryPolicy);CACHE_RETRY_MAP.put(method, template);}/*** 填充uri*/private String fillUri(Map<String, Object> paramMap, String path) {StringBuilder sb = new StringBuilder(path);String[] split = sb.toString().split("/");sb.delete(0, sb.length());for (String str : split) {if (StringUtils.isBlank(str)) {continue;}if (str.startsWith("{") && str.endsWith("}")) {String replace = str.replace("{", "").replace("}", "");if (paramMap.containsKey(replace)) {Object value = paramMap.get(replace);str = Objects.isNull(value) ? StringUtils.EMPTY : value.toString();}}sb.append("/").append(str);}return sb.toString();}/*** 获得监控uri*/private String getMonitorUri(String url, String path) {StringBuilder sb = new StringBuilder(path);String[] split = sb.toString().split("/");sb.delete(0, sb.length());for (String str : split) {if (StringUtils.isBlank(str) || str.startsWith("{") && str.endsWith("}")) {continue;}sb.append("/").append(str);}return url + sb.toString();}/*** 反序列化结果** @param method* @param result* @return*/private Object deserializationResult(Method method, String result) {if (StringUtils.isBlank(result)) {throw new GZHttpException("call " + method.getDeclaringClass().getName() + "#" + method.getName() + " timeout, return is null");}Type returnType = CACHE_RESULT_MODEL.get(method).getReturnType();if (Objects.equals(String.class, returnType)) {return result;}return JsonUtil.of(result, new TypeReference<Object>() {@Overridepublic Type getType() {return returnType;}});}/*** 获得body体** @param method* @param args* @return*/private String getBody(Method method, Object[] args, GZRequestMapping requestMapping) {switch (requestMapping.method()) {case POST:case PUT:case DELETE:GZHttpClientParamModel paramModel = CACHE_PARAM_MODEL.get(method);// 有body体if (Objects.nonNull(paramModel.getGenericIndex())) {if (Boolean.TRUE.equals(paramModel.getGeneric())) {return JsonUtil.toJson(args[paramModel.getGenericIndex()]);} else {Map<String, Object> bodyMap = JsonUtil.of(JsonUtil.toJson(args[paramModel.getGenericIndex()]), TreeMap.class);return JsonUtil.toJson(httpSignature.bodySignatureParam(bodyMap, appKey, appSecret));}}break;default:return StringUtils.EMPTY;}return StringUtils.EMPTY;}/*** 获得请求url** @param url* @param path* @return*/private String getUrl(String url, String path) {if (url.endsWith("/")) {return url + path.substring(1);}return url + path;}/*** 获得读超时时间** @param requestMapping* @return*/private int getReadTimeout(GZRequestMapping requestMapping) {int connReadTimeout = readTimeout;int timeout = etcdPropertiesClient.getTimeout(requestMapping.readTimeout());if (timeout > 0) {connReadTimeout = timeout;}return connReadTimeout;}/*** 获得连接超时时间** @param requestMapping* @return*/private int getConnectTimeout(GZRequestMapping requestMapping) {int connConnectTimeout = connectTimeout;int timeout = etcdPropertiesClient.getTimeout(requestMapping.connectTimeout());if (timeout > 0) {connConnectTimeout = timeout;}return connConnectTimeout;}/*** 获得重试次数** @param requestMapping* @return*/private int getRetry(GZRequestMapping requestMapping) {int retryCnt = retry;if (requestMapping.retry() > 0) {retryCnt = requestMapping.retry();}return retryCnt;}
}

执行类小编写了部分伪代码仅供参考相信大家读到该文章技术水平差不了,因为我们是自研框架所以调用逻辑或参数生成都需要我们自己处理,小编写了部分参数处理逻辑如果你们有更好的想法可以扩展也可评论留下联系方式添加小编微信(LailaiMonkeyJava)

demo以RestTemplate作为http发送工具进行测试,调用get请求获得用户信息。

demo地址

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_9379.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于ssm的校运会管理系统设计与实现-计算机毕业设计源码+LW文档

开发语言&#xff1a;Java 框架&#xff1a;ssm JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;…

CSS基础篇---01选择器、字体与文本样式

CSDN话题挑战赛第2期 参赛话题&#xff1a;学习笔记 文章目录 1.初识CSS CSS引入方式 2.选择器 标签&#xff08;元素&#xff09;选择器 class选择器 id选择器 通配符选择器 案例演示 3.字体样式 字体大小 字体粗细 字体样式 字体类型 字体系列 层叠性 font复…

VM关闭虚拟机之后,连接不上前一天设置的静态ip

错误场景&#xff1a; 问题原因&#xff1a; centos7 默认的网卡 ens33 在虚拟机启动时&#xff0c;没有自动启动。导致设置的静态ip不生效&#xff0c;故连接不上。 解决方案&#xff1a; 查看托管是否启动&#xff0c;倘若网卡没有启动很可能是没有加入托管。 查看托管是否…

【强化学习】《动手学强化学习》动态规划算法

【强化学习】《动手学强化学习》动态规划算法一、基本思想二、悬崖漫步环境三、策略迭代算法3.1 策略评估3.2 策略提升3.3 悬崖漫步环境下的策略迭代四、价值迭代算法一、基本思想 动态规划算法在计算机专业课中是特别重要的思想&#xff0c;将待求问题分解成若干个子问题&…

Springboot 集成kafka

一、创建项目并导入pom依赖 <dependency><groupId>org.springframework.kafka</groupId><artifactId>spring-kafka</artifactId> </dependency> 二、修改application.yml配置 1. producer 生产端的配置 spring:#重要提示:kafka配置,该…

Redis介绍和安装

Redis介绍 Redis是一个开源的、基于Key-Value(键-值&#xff09;存储的NoSQL数据库。Redis因其丰富的数据结构、极快的速度、齐全的功能而为人所知&#xff0c;它是目前内存数据库方面的事实标准&#xff0c;是目前使用广泛的开源缓存中间件。 Redis特点 结构丰富&#xff0…

CS231a课程笔记:Lecture2 Camera Models

关于齐次坐标&#xff1a;(15条消息) 为什么要引入齐次坐标&#xff0c;齐次坐标的意义&#xff08;一&#xff09;_追求卓越583的博客-CSDN博客_齐次坐标的意义(15条消息) 为什么要引入齐次坐标&#xff0c;齐次坐标的意义&#xff08;二&#xff09;_追求卓越583的博客-CSDN博…

DNS 解析流程

一、背景 最近&#xff0c;在S3协议项目中调研通过DNS域名解析处理流量负载均衡问题。原来对dns也有一些粗浅的了解&#xff0c;知道通过DNS可以将域名转换为IP地址&#xff0c;也可以做负载均衡。但是DNS的解析流程以及缓存等机制&#xff0c;只是一知半解。正好&#xff0c;…

windows安装nginx并设置开机自启动

在macOS和linux中使用nginx我早已经轻车熟路。突然切到windows的环境中&#xff0c;我反而不会用了。 之前写了《windows使用nginx探索笔记》内容比较冗长&#xff0c;所以本文尽量精简一下。 环境 操作系统&#xff1a;windows 2008R2 Datacenter 已经安装的软件&#xff1…

C语言中malloc(),free(),calloc(),realloc()

申请内存malloc()在申请内存时不会对内存进行初始化赋值 在申请内存后&#xff0c;没有对内存进行初始化的话&#xff0c;这段内存中就存储着系统随机值。 int n 5; int* p (int*)malloc(n * sizeof(int));malloc(size):size就是你想开辟的内存的字节大小。我们通常想要用这段…

SpringCloud基础6——分布式事务,Seata

用于复习快速回顾。 目录 1.分布式事务问题 1.1.本地事务&#xff0c;ACID原则 1.2.分布式事务 1.3.演示分布式事务问题 2.理论基础 2.1.CAP定理 2.1.1.一致性&#xff0c;数据同步 2.1.2.可用性&#xff0c;节点正常访问 2.1.3.分区容错 2.1.4.矛盾 2.2.BASE理论 …

vulnhub-xxe lab: 1

ifconfig nmap 192.168.61.0/24 找到192.168.61.145 目录扫描&#xff08;御剑&#xff09; 192.168.61.145/xxe 192.168.61.145/admin.php 无法访问&#xff0c;但是robots.txt里面写的应该不会是无效网站&#xff0c;所以可能是被拒绝访问了 抓xxe的包 可以发现是用xml写的…

[ web基础篇 ] Burp Suite 爆破 Basic 认证密码

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…

层次选择器

层次选择器 后代选择器简介后代选择器可以选择作为某元素后代的元素(包括儿子,孙子,重孙子) 两个元素之间的层次间隔可以是无限的示例<!DOCTYPE html> <html lang="en"><head><meta charset="UTF-8"><title>Title</t…

怎么把握住股票每天的最佳交易时机?

每个股民都希望自己能够在每天的股价最高点卖出&#xff0c;然后在最低点再买回来&#xff1b;但是怎么去判断最好的交易时机呢&#xff0c;很多人会想很多方法去识别判断最佳交易点&#xff0c;今天给大家分享一种方法&#xff1b;我一直在思考股票交易的底层逻辑是啥&#xf…

如何在基础镜像中安装指定python版本

背景 由于规范要求要使用指定的镜像版本,但是由于该镜像中的python与我使用的版本有差异,怕引起一些不必要的兼容问题,所以我需要自己按基础镜像基础上安装对应版本的python。 Dockerfile 直接上最终dockerfile,为什么这样写,后面说到。 FROM centos:7 # 指定工作目录 WOR…

【2022中国高校计算机大赛 微信大数据挑战赛】Top 1-6 方案总结

前段时间参加了 2022中国高校计算机大赛 微信大数据挑战赛&#xff0c;比赛链接&#xff1a;https://algo.weixin.qq.com/。 由于时间原因精力有限&#xff0c;我们队伍的方案做的比较简陋&#xff1a; 【初赛&#xff1a;rank-18&#xff0c;复赛&#xff1a;rank-40&#xff…

网课查题接口 搜题公众号对接题库教程 (附赠题库接口)

网课查题接口 搜题公众号对接题库教程 &#xff08;附赠题库接口&#xff09; 本平台优点&#xff1a; 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a; 查…

bm19bm7

为什么不定义如果两点相等呢 等于的话峰值统一取右 以右来比较 波峰就行 不一定是最大的 在这里插入代码片 import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可** * param nums…

微信小程序转为App并上架应用市场

先说说背景吧&#xff0c;笔者开发了一款微信工具类小程序&#xff0c;刚开始&#xff0c;小程序的日访问量和用户数都还可以&#xff0c;但后面慢慢的发现&#xff0c;受限于微信小程序平台规则&#xff0c;很难对用户进行更深入的运营&#xff0c;用户流失问题也将逐渐凸显出…