SpringCloud学习系列----Feign

    最近在学SpringCloud框架的东西,不仅工作上需要,也感觉自身缺乏很多,提高下充实自己,在工作中要会使用,这是最基本的,知其然也要知其所以然,墙裂推荐博客大佬---方志朋,我可是按照他的博客一步一步学下去的,为什么还要写这个博客呢?第一、本人在最近使用时,我俩版本不匹配(小白用的是Finchely.BUILD-SNAPSHOT版本);第二、有的坑我是踩过,解决的方法是亲测可用,我这边可以作补充;第三、记录下自己的学习经历,回首时,能知道哪里观点是错的,毕竟小白需要成长嘛;第四、希望能为需要学习的同学提供一点点的帮助哈。

    初步使用的时候,发现大佬依赖的Sring-cloud-starter-feign,而在我Maven仓库里,没有这个Jar,于是便开始了出坑之旅,在一篇博客中看到,原来SpringCloud的Finchely.BUILD-SNAPSHOT版本时,依赖的是spring-cloud-starter-netflix-feign这个jar。使用完后,开始想理解原理,看源码,在看到feign和Ribbon+Hystrix时,有点疑问,两者都能实现负载均衡的效果,为什么一个可以基于接口,一个需要实现类,是因为在SpringBoot开启@EnableFeignClient feign服务,并在接口中表明@FeignClient 注解的接口。   

@FeignClient(value = "EUREKACLIENT",fallback = SchedualServiceImplHystrix.class)
public interface SchedualService {

    @RequestMapping(value = "/hello" ,method = RequestMethod.GET)
    public String sayHello(@RequestParam(value = "name")String name);

}

 注意到@RequestMapping 这个注解了嘛? 不是只有Controller层会用的嘛?因此,是不是可以把feign处理的过程像对待解析请求处理url一样,在请求该地址时,被feign客户端拦截了,下面是解析请求feign地址的代码,解析被@FeignClient注解的类

public <T> T newInstance(Target<T> target) {
        Map<String, MethodHandler> nameToHandler = this.targetToHandlersByName.apply(target);
        Map<Method, MethodHandler> methodToHandler = new LinkedHashMap();
        List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList();
        Method[] var5 = target.type().getMethods();
        int var6 = var5.length;

        for(int var7 = 0; var7 < var6; ++var7) {
            Method method = var5[var7];
            if (method.getDeclaringClass() != Object.class) {
                if (Util.isDefault(method)) {
                    DefaultMethodHandler handler = new DefaultMethodHandler(method);
                    defaultMethodHandlers.add(handler);
                    methodToHandler.put(method, handler);
                } else {
                    methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
                }
            }
        }

        InvocationHandler handler = this.factory.create(target, methodToHandler);
        T proxy = Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{target.type()}, handler);
        Iterator var12 = defaultMethodHandlers.iterator();

        while(var12.hasNext()) {
            DefaultMethodHandler defaultMethodHandler = (DefaultMethodHandler)var12.next();
            defaultMethodHandler.bindTo(proxy);
        }

        return proxy;
    }

SynchronousMethodHandler实现了MethodHandler,在运行时,生成ResuestTemplate。

final class SynchronousMethodHandler implements MethodHandler {
public Object invoke(Object[] argv) throws Throwable {
        RequestTemplate template = this.buildTemplateFromArgs.create(argv);
        Retryer retryer = this.retryer.clone();

        while(true) {
            try {
                return this.executeAndDecode(template);
            } catch (RetryableException var5) {
                retryer.continueOrPropagate(var5);
                if (this.logLevel != Level.NONE) {
                    this.logger.logRetry(this.metadata.configKey(), this.logLevel);
                }
            }
        }
    }


}
Object executeAndDecode(RequestTemplate template) throws Throwable {
        Request request = this.targetRequest(template);
        if (this.logLevel != Level.NONE) {
            this.logger.logRequest(this.metadata.configKey(), this.logLevel, request);
        }

        long start = System.nanoTime();

        Response response;
        try {
            response = this.client.execute(request, this.options);
            response.toBuilder().request(request).build();
        } catch (IOException var15) {
            if (this.logLevel != Level.NONE) {
                this.logger.logIOException(this.metadata.configKey(), this.logLevel, var15, this.elapsedTime(start));
            }

            throw FeignException.errorExecuting(request, var15);
        }
        .......
    }
package org.springframework.cloud.openfeign.ribbon;

@Configuration
class DefaultFeignLoadBalancedConfiguration {
    DefaultFeignLoadBalancedConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean
    public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory) {
        return new LoadBalancerFeignClient(new Default((SSLSocketFactory)null, (HostnameVerifier)null), cachingFactory, clientFactory);
    }
}

public interface Client {

public static class Default implements Client {
        private final SSLSocketFactory sslContextFactory;
        private final HostnameVerifier hostnameVerifier;

        public Default(SSLSocketFactory sslContextFactory, HostnameVerifier hostnameVerifier) {
            this.sslContextFactory = sslContextFactory;
            this.hostnameVerifier = hostnameVerifier;
        }

        public Response execute(Request request, Options options) throws IOException {
            HttpURLConnection connection = this.convertAndSend(request, options);
            return this.convertResponse(connection).toBuilder().request(request).build();
        }
}
}
 在不配置FeignClient时,默认生成Client的Default这个类,Default默认采用的是HttpUrlConnection,进行通信,获得相应response。通过默认的连接方式获得的默认的Client,在返回状态为成功时,会把这个默认的Client转化为LoadBalanceFeignClient,他会向spring容器注入的是LoadBalanceFeignClient这个负载均衡feign客户端,我们来看看他的execute方法。
public Response execute(Request request, Options options) throws IOException {
        try {
            URI asUri = URI.create(request.url());
            String clientName = asUri.getHost();
            URI uriWithoutHost = cleanUrl(request.url(), clientName);
            RibbonRequest ribbonRequest = new RibbonRequest(this.delegate, request, uriWithoutHost);
            IClientConfig requestConfig = this.getClientConfig(options, clientName);
            return ((RibbonResponse)this.lbClient(clientName).executeWithLoadBalancer(ribbonRequest, requestConfig)).toResponse();
        } catch (ClientException var8) {
            IOException io = this.findIOException(var8);
            if (io != null) {
                throw io;
            } else {
                throw new RuntimeException(var8);
            }
        }
    }
再来看看exeucteWithLoadBalancer()这个方法做了哪些事情
 public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
        LoadBalancerCommand command = this.buildLoadBalancerCommand(request, requestConfig);

        try {
            return (IResponse)command.submit(new ServerOperation<T>() {
                public Observable<T> call(Server server) {
                    URI finalUri = AbstractLoadBalancerAwareClient.this.reconstructURIWithServer(server, request.getUri());
                    ClientRequest requestForServer = request.replaceUri(finalUri);

                    try {
                        return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
                    } catch (Exception var5) {
                        return Observable.error(var5);
                    }
                }
            }).toBlocking().single();
        } catch (Exception var6) {
            Throwable t = var6.getCause();
            if (t instanceof ClientException) {
                throw (ClientException)t;
            } else {
                throw new ClientException(var6);
            }
        }
    }
通过LoadBalancerCommand类的成员方法submit()实现负载均衡的效果。具体还需要结合ribbon源码来看,我会在另章中记录自己的学习经验,写得不好,还希望大佬们能指教指教。





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值