最近在学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();
}
}
}
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源码来看,我会在另章中记录自己的学习经验,写得不好,还希望大佬们能指教指教。