feign源码解析--fegin的执行过程

一执行逻辑

通过上篇文章分析我们知道,fegin客户端在初始化的时候,会通过jdk动态代理未为每个feignClient生成一个代理类,jdk动态代理主要是通过InvocationHandler接口的实现代理的增强.我们来看看生成代理时InvocationHandler接口的实现是如何创建的

// 通过InvocationHandlerFactory创建InvocationHandlerFactory
InvocationHandler handler = factory.create(target, methodToHandler);
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
        new Class<?>[] {target.type()}, handler);

通过InvocationHandlerFactory 我们可以发现创建的是ReflectiveFeign.FeignInvocationHandler,通过ReflectiveFeign.FeignInvocationHandler来实现动态代理的底层逻辑

public interface InvocationHandlerFactory {

  InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch);

  /**
   * Like {@link InvocationHandler#invoke(Object, java.lang.reflect.Method, Object[])}, except for a
   * single method.
   */
  interface MethodHandler {

    Object invoke(Object[] argv) throws Throwable;
  }

  static final class Default implements InvocationHandlerFactory {

    @Override
    public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
      return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
    }
  }
}

我们可以来看看ReflectiveFeign.FeignInvocationHandler底层是如何处理的,主要是InvocationHandler接口的invoke()的实现
我们可以看到这里主要是对equals和hashCode等的处理,最终会如何处理呢,通过断点我们发现最终会调用SynchronousMethodHandler来实现逻辑的增强处理

@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      if ("equals".equals(method.getName())) {
        try {
          Object otherHandler =
              args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
          return equals(otherHandler);
        } catch (IllegalArgumentException e) {
          return false;
        }
      } else if ("hashCode".equals(method.getName())) {
        return hashCode();
      } else if ("toString".equals(method.getName())) {
        return toString();
      }

      return dispatch.get(method).invoke(args);
    }

那我们看看SynchronousMethodHandler做了那些处理

@Override
  public Object invoke(Object[] argv) throws Throwable {
  	// 根据方法参数转换成一个标准的RequestTemplate 
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    // 将参数转换成一个相应选项
    Options options = findOptions(argv);
    // 获得fegin的重试相关数据
    Retryer retryer = this.retryer.clone();
    while (true) {
      try {
      	// 相关增强实现
        return executeAndDecode(template, options);
      } catch (RetryableException e) {
        try {
         // 发生错误是实现增强增强
          retryer.continueOrPropagate(e);
        } catch (RetryableException th) {
          Throwable cause = th.getCause();
          if (propagationPolicy == UNWRAP && cause != null) {
            throw cause;
          } else {
            throw th;
          }
        }
        if (logLevel != Logger.Level.NONE) {
          logger.logRetry(metadata.configKey(), logLevel);
        }
        continue;
      }
    }
  }

executeAndDecode方法是如何出现处理的呢

  • fegin拦截器相关执行
  • 调用RetryableFeignBlockingLoadBalancerClient处理请求
  • 异步解析和响应解析
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
	// 调用fegin所有的拦截器,并执行里面的相关逻辑 将template转换为feign内部的request
    Request request = targetRequest(template);

	// 打印日志
    if (logLevel != Logger.Level.NONE) {
      logger.logRequest(metadata.configKey(), logLevel, request);
    }

    Response response;
    long start = System.nanoTime();
    try {
    	// 调用RetryableFeignBlockingLoadBalancerClient进行处理
      response = client.execute(request, options);
      // ensure the request is set. TODO: remove in Feign 12
      response = response.toBuilder()
          .request(request)
          .requestTemplate(template)
          .build();
    } catch (IOException e) {
      if (logLevel != Logger.Level.NONE) {
        logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
      }
      throw errorExecuting(request, e);
    }
    // 请求处理时间
    long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);


    if (decoder != null)
    	// 解析请求结果
      return decoder.decode(response, metadata.returnType());

	// 异步结果处理
    CompletableFuture<Object> resultFuture = new CompletableFuture<>();
    asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,
        metadata.returnType(),
        elapsedTime);

    try {
      if (!resultFuture.isDone())
        throw new IllegalStateException("Response handling not done");

      return resultFuture.join();
    } catch (CompletionException e) {
      Throwable cause = e.getCause();
      if (cause != null)
        throw cause;
      throw e;
    }
  }
Request targetRequest(RequestTemplate template) {
	// 遍历所有的拦截器,执行拦截中响应的业务逻辑
    for (RequestInterceptor interceptor : requestInterceptors) {
      interceptor.apply(template);
    }
    return target.apply(template);
  }

在RetryableFeignBlockingLoadBalancerClient中的excute主要做了那些处理呢

  • 更具现有的负载均衡策略,实现客户端的负载均衡
  • 通过http的相关代理实现最终的请求
@Override
	public Response execute(Request request, Request.Options options) throws IOException {
		//  获得URI 
		final URI originalUri = URI.create(request.url());
		// 获取客户端id
		String serviceId = originalUri.getHost();
		Assert.state(serviceId != null, "Request URI does not contain a valid hostname: " + originalUri);
		final LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryFactory.createRetryPolicy(serviceId,
				loadBalancerClient);
		// 构建重试retryTemplate 
		RetryTemplate retryTemplate = buildRetryTemplate(serviceId, request, retryPolicy);
		return retryTemplate.execute(context -> {
			Request feignRequest = null;
			ServiceInstance retrievedServiceInstance = null;
			Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator
					.getSupportedLifecycleProcessors(
							loadBalancerClientFactory.getInstances(serviceId, LoadBalancerLifecycle.class),
							RetryableRequestContext.class, ResponseData.class, ServiceInstance.class);
			String hint = getHint(serviceId);
			DefaultRequest<RetryableRequestContext> lbRequest = new DefaultRequest<>(
					new RetryableRequestContext(null, buildRequestData(request), hint));
			// On retries the policy will choose the server and set it in the context
			// and extract the server and update the request being made
			if (context instanceof LoadBalancedRetryContext) {
				LoadBalancedRetryContext lbContext = (LoadBalancedRetryContext) context;
				ServiceInstance serviceInstance = lbContext.getServiceInstance();
				if (serviceInstance == null) {
					if (LOG.isDebugEnabled()) {
						LOG.debug("Service instance retrieved from LoadBalancedRetryContext: was null. "
								+ "Reattempting service instance selection");
					}
					ServiceInstance previousServiceInstance = lbContext.getPreviousServiceInstance();
					lbRequest.getContext().setPreviousServiceInstance(previousServiceInstance);
					supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStart(lbRequest));
					// 根据当前请求的服务id实现客户端的负载均衡
					retrievedServiceInstance = loadBalancerClient.choose(serviceId, lbRequest);
					if (LOG.isDebugEnabled()) {
						LOG.debug(String.format("Selected service instance: %s", retrievedServiceInstance));
					}
					lbContext.setServiceInstance(retrievedServiceInstance);
				}

				if (retrievedServiceInstance == null) {
					if (LOG.isWarnEnabled()) {
						LOG.warn("Service instance was not resolved, executing the original request");
					}
					org.springframework.cloud.client.loadbalancer.Response<ServiceInstance> lbResponse = new DefaultResponse(
							retrievedServiceInstance);
					supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
							.onComplete(new CompletionContext<ResponseData, ServiceInstance, RetryableRequestContext>(
									CompletionContext.Status.DISCARD, lbRequest, lbResponse)));
					feignRequest = request;
				}
				else {
					if (LOG.isDebugEnabled()) {
						LOG.debug(String.format("Using service instance from LoadBalancedRetryContext: %s",
								retrievedServiceInstance));
					}
					String reconstructedUrl = loadBalancerClient.reconstructURI(retrievedServiceInstance, originalUri)
							.toString();
					feignRequest = buildRequest(request, reconstructedUrl);
				}
			}
			org.springframework.cloud.client.loadbalancer.Response<ServiceInstance> lbResponse = new DefaultResponse(
					retrievedServiceInstance);
			// 通过http的相关代理实现最终的请求
			Response response = LoadBalancerUtils.executeWithLoadBalancerLifecycleProcessing(delegate, options,
					feignRequest, lbRequest, lbResponse, supportedLifecycleProcessors,
					retrievedServiceInstance != null);
			int responseStatus = response.status();
			if (retryPolicy != null && retryPolicy.retryableStatusCode(responseStatus)) {
				if (LOG.isDebugEnabled()) {
					LOG.debug(String.format("Retrying on status code: %d", responseStatus));
				}
				response.close();
				throw new RetryableStatusCodeException(serviceId, responseStatus, response, URI.create(request.url()));
			}
			return response;
		}, new LoadBalancedRecoveryCallback<Response, Response>() {
			@Override
			protected Response createResponse(Response response, URI uri) {
				return response;
			}
		});
	}
Vue3 + TypeScript (简称V3TS) 是结合了 Vue.js 的下一代版本和 TypeScript 这种强类型的 JavaScript 变体来构建应用的技术组合。手撕甘特图通常是一个时间线视图,用于展示项目的进度、任务分配或计划安排。在 V3TS 中制作甘特图,你可以遵循以下步骤: 1. **环境配置**:首先,确保已安装 Vue CLI v3,并设置支持 TypeScript。可以使用命令 `vue create` 创建一个新的 Vue3项目并选择 TypeScript 作为配置。 2. **引入库**:由于甘特图需要数据驱动,你可能需要引入第三方库如 `antv-g6` 或者 `vis` 来处理图形绘制。通过npm或yarn添加依赖。 3. **声明组件**:创建 TypeScript 类型化的组件,例如 `TaskItem.vue`,其中包含任务对象的数据类型 (`Task`) 和方法 (`generateGanttGraph()`). ```typescript import { Component, Prop } from 'vue'; interface Task { id: number; name: string; start: Date; end: Date; } @Component({ // ... }) export default class TaskItem extends Vue { @Prop() task: Task; generateGanttGraph(): void { // 使用甘特图库生成图表逻辑 } } ``` 4. **数据绑定**:在组件内部或父组件中,管理任务列表 (`Task[]`) 并将其绑定到视图上,以便更新显示。 5. **渲染图表**:在组件的模板部分,利用甘特图库提供的 API 渲染任务节点,传递任务数据。 6. **事件监听**:为了动态调整和交互,可以添加事件监听器,比如拖拽、点击事件等。 7. **样式定制**:用 CSS 样式美化甘特图,使其符合项目的视觉风格。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值