【源码分析】Feign

@Service("lecOrderService")
public class LecOrderServiceImpl extends ServiceImpl<LecOrderDao, LecOrderEntity> implements LecOrderService {
    @Autowired //自动注入依赖
    SeckillFeignService seckillFeignService;

上述Service的Bean加载过程中,发现存在了这么个依赖,那不得去处理下,参考单例Bean的加载
他这个优秀,缓存中直接就有了,在启动时通过@EnableFeignClients(basePackages = “com.yfxu.lecture.product.feign”) 将所有带有@FeignClient(“lecture-seckill”)的对应SeckillFeignService已经作为FeignClientFactoryBean进入IOC

populateBean()解析并自动注入依赖-> getBean("com.yfxu.lecture.product.feign.SeckillFeignService")
-> 寻思从FeignClientFactoryBean#getObject()取出依赖的对象

只不过这里有些不一样,来看FeignClientFactoryBean的具体实现

	@Override
	public Object getObject() throws Exception {
		return getTarget(); //拿到上下文 并配置拦截器
	}

最终会执行Feign中的下面方法
在这里插入图片描述在ReflectiveFeign中绑定seckillFeignService的统一InvocationHandler处理的动态代理
在这里插入图片描述触发代理调用

 R r1 = seckillFeignService.getSeckillInfoByActId(relationEntity.getActId());
 //统一的方法拦截器
 static class FeignInvocationHandler implements InvocationHandler {
 ...
 @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      ...
      
	  //触发每个方法的SynchronousMethodHandler拦截器
      return dispatch.get(method).invoke(args); 
    }

feign/SynchronousMethodHandler.java:89

final class SynchronousMethodHandler implements MethodHandler {
	@Override
	public Object invoke(Object[] argv) throws Throwable {
	  RequestTemplate template = buildTemplateFromArgs.create(argv); //根据输入参数为每个请求简历一个请求template
	  Options options = findOptions(argv);
	  Retryer retryer = this.retryer.clone(); //克隆一份重试器
	  while (true) { //失败重试机制,尝试一定次数
	    try {
	      return executeAndDecode(template, options);
	    } catch (RetryableException e) {
	      try {
	        retryer.continueOrPropagate(e);
	      } catch (RetryableException th) {
	      ...
	Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
	    Request request = targetRequest(template); //调用请求拦截器 生成request
	
	    if (logLevel != Logger.Level.NONE) {
	      logger.logRequest(metadata.configKey(), logLevel, request);
	    }
	
	    Response response;
	    long start = System.nanoTime();
	    try {
	      response = client.execute(request, options); //由客户端LoadBalancerFeignClient执行对服务器的请求
	      // ensure the request is set. TODO: remove in Feign 12
	      response = response.toBuilder()
	          .request(request)
	          .requestTemplate(template)
	          .build();
	    } catch (IOException e) {
	    ...
	 Request targetRequest(RequestTemplate template) {
	    for (RequestInterceptor interceptor : requestInterceptors) {
	      interceptor.apply(template);
	    }
	    return target.apply(template);
	  }

Client组件

其中Client组件是一个非常重要的组件,Feign最终发送request请求以及接收response响应,都是由Client组件完成的,其中Client的实现类,只要有Client.Default,该类由HttpURLConnnection实现网络请求,另外还支持HttpClient、Okhttp.

在缺失配置feignClient的情况下,会自动注入new Client.Default(),跟踪Client.Default()源码,它使用的网络请求框架为HttpURLConnection,代码如下:

    @Override
    public Response execute(Request request, Options options) throws IOException {
      HttpURLConnection connection = convertAndSend(request, options);
      return convertResponse(connection, request);
    }

但HttpURLConnection没有连接池,对每个地址会保持一个长连接,所以建议配置HttpClient、Okhttp客户端
在这里插入图片描述
参考link

负载均衡请求

通过上述的FeignRibbonClientAutoConfiguration类配置Client的类型(httpurlconnection,okhttp和httpclient)时候,可知最终向容器注入的是LoadBalancerFeignClient,即负载均衡客户端。现在来看下LoadBalancerFeignClient的代码:

openfeign/ribbon/LoadBalancerFeignClient.java:74

@Override
	public Response execute(Request request, Request.Options options) throws IOException {
		try {
			URI asUri = URI.create(request.url());
			String clientName = asUri.getHost();
			URI uriWithoutHost = cleanUrl(request.url(), clientName);
			FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
					this.delegate, request, uriWithoutHost);

			IClientConfig requestConfig = getClientConfig(options, clientName);
			return lbClient(clientName)
					.executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
		}

1.通过lbClient(clientName)生成FeignLoadBalancer类。主要借助Nacos注册中心的服务列表

在这里插入图片描述

DynamicServerListLoadBalancer:{NFLoadBalancer:name=lecture-seckill,current list of Servers=[192.168.101.48:12000],Load balancer stats=Zone stats: {unknown=[Zone:unknown;	Instance count:1;	Active connections count: 0;	Circuit breaker tripped count: 0;	Active connections per server: 0.0;]
},Server stats: [[Server:192.168.101.48:12000;	Zone:UNKNOWN;	Total Requests:0;	Successive connection failure:0;	Total blackout seconds:0;	Last connection made:Thu Jan 01 08:00:00 CST 1970;	First connection made: Thu Jan 01 08:00:00 CST 1970;	Active Connections:0;	total failure count in last (1000) msecs:0;	average resp time:0.0;	90 percentile resp time:0.0;	95 percentile resp time:0.0;	min resp time:0.0;	max resp time:0.0;	stddev resp time:0.0]
]}ServerList:com.alibaba.cloud.nacos.ribbon.NacosServerList@72d5b022

2.其中有个executeWithLoadBalancer()方法,即通过负载均衡的方式请求。

  public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
        RequestSpecificRetryHandler handler = getRequestSpecificRetryHandler(request, requestConfig);
        LoadBalancerCommand<T> command = LoadBalancerCommand.<T>builder()
                .withLoadBalancerContext(this)
                .withRetryHandler(handler)
                .withLoadBalancerURI(request.getUri())
                .build();

        try {
            return command.submit(
                new ServerOperation<T>() {
                    @Override
                    public Observable<T> call(Server server) {
                        URI finalUri = reconstructURIWithServer(server, request.getUri());
                        S requestForServer = (S) request.replaceUri(finalUri);
                        try {
                            return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
                        } 
                        catch (Exception e) {
                            return Observable.error(e);
                        }
                    }
                })
                .toBlocking()
                .single();
        } catch (Exception e) {

其中服务在submit()方法上,点击submit进入具体的方法,这个方法是LoadBalancerCommand的方法:

Observable<T> o = (this.server == null ? this.selectServer() : Observable.just(this.server)).concatMap(new Func1<Server, Observable<T>>() {
            public Observable<T> call(Server server) {
                context.setServer(server);

上述代码中有个selectServe(),该方法是选择服务的进行负载均衡的方法,代码如下:

private Observable<Server> selectServer() {
        return Observable.create(new OnSubscribe<Server>() {
            public void call(Subscriber<? super Server> next) {
                try {
                    Server server = LoadBalancerCommand.this.loadBalancerContext.getServerFromLoadBalancer(LoadBalancerCommand.this.loadBalancerURI, LoadBalancerCommand.this.loadBalancerKey);
                    next.onNext(server);
                    next.onCompleted();
                } catch (Exception var3) {
                    next.onError(var3);
                }

            }
        });
    }

这篇博文写的也还不错:link

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星空•物语

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值