Feign简单使用

Feign

Feign是Netflix开发的⼀个轻量级RESTful的HTTP服务客户端(⽤它来发起请求,远程调⽤的) ,是以
Java接⼝注解的⽅式调⽤Http请求,⽽不⽤像Java中通过封装HTTP请求报⽂的⽅式直接调⽤, Feign被
⼴泛应⽤在Spring Cloud 的解决⽅案中。
类似于Dubbo,服务消费者拿到服务提供者的接⼝,然后像调⽤本地接⼝⽅法⼀样去调⽤,实际发出的
是远程的请求。
Feign可帮助我们更加便捷,优雅的调⽤HTTP API:不需要我们去拼接url然后呢调⽤
restTemplate的api,在SpringCloud中,使⽤Feign⾮常简单,创建⼀个接⼝(在消费者–服务调
⽤⽅这⼀端),并在接⼝上添加⼀些注解,代码就完成了
SpringCloud对Feign进⾏了增强,使Feign⽀持了SpringMVC注解(OpenFeign)
本质:封装了Http调⽤流程,更符合⾯向接⼝化的编程习惯,类似于Dubbo的服务调⽤

Dubbo的调⽤⽅式其实就是很好的⾯向接⼝编程

依赖

在服务调⽤者⼯程(消费)创建接⼝(添加注解)
(效果) Feign = RestTemplate+Ribbon+Hystrix
服务消费者⼯程(⾃动投递微服务)中引⼊Feign依赖(或者⽗类⼯程)

 <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

开启消费者

@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients // 开启Feign
public class AutodeliverApplication8090 {
    public static void main(String[] args) {
        SpringApplication.run(AutodeliverApplication8090.class,args);
    }


    // 使用RestTemplate模板对象进行远程调用
    @LoadBalanced
    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

此时去掉Hystrix熔断的⽀持注解@EnableCircuitBreaker即可包括引⼊的依赖,因为Feign
会⾃动引⼊

编写客户端
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient(name = "edu-service-resume")
public interface ResumeFeignClient {

    @GetMapping("/resume/openstate/{userId}")
    public Integer findDefaultResumeState(@PathVariable(value ="userId" ) Long userId) ;
}

name:调⽤的服务名称,和服务提供者yml⽂件中spring.application.name保持⼀致

1) @FeignClient注解的name属性⽤于指定要调⽤的服务提供者名称,和服务提供者yml⽂件中
spring.application.name保持⼀致
2)接⼝中的接⼝⽅法,就好⽐是远程服务提供者Controller中的Hander⽅法(只不过如同本地调
⽤了),那么在进⾏参数绑定的时,可以使⽤@PathVariable、 @RequestParam、
@RequestHeader等,这也是OpenFeign对SpringMVC注解的⽀持,但是需要注意value必须设
置,否则会抛出异常

使⽤接⼝中⽅法完成远程调⽤(注⼊接⼝即可,实际注⼊的是接⼝的实现)

在Controller中

   @Autowired
    private ResumeFeignClient resumeFeignClient;

    @GetMapping("/feign/{userId}")
    public Integer findfeign(@PathVariable Long userId) {

        String requestUrl = "http://edu-service-resume/resume/openstate/"+userId;
        System.out.println("获取服务实现 拼接的url : "+requestUrl);
        Integer forObject = resumeFeignClient.findDefaultResumeState(userId);

        return forObject;
    }

Feign 本身已经集成了Ribbon依赖和⾃动配置,因此我们不需要额外引⼊依赖,可以通过 ribbon.xx 来
进 ⾏全局配置,也可以通过服务名.ribbon.xx 来对指定服务进⾏细节配置配置

Feign默认的请求处理超时时⻓1s,有时候我们的业务确实执⾏的需要⼀定时间,那么这个时候,我们
就需要调整请求处理超时时⻓, Feign⾃⼰有超时设置,如果配置Ribbon的超时,则会以Ribbon的为准

  • Ribbon设置
#针对的被调⽤⽅微服务名称,不加就是全局⽣效
xxx-service-resume:
  ribbon:
#请求连接超时时间
	#ConnectTimeout: 2000
	#请求处理超时时间
	#ReadTimeout: 5000
	#对所有操作都进⾏重试
	OkToRetryOnAllOperations: true
	####根据如上配置,当访问到故障请求的时候,它会再尝试访问⼀次当前实例(次数由
	MaxAutoRetries配置),
	####如果不⾏,就换⼀个实例进⾏访问,如果还不⾏,再换⼀次实例访问(更换次数由
	MaxAutoRetriesNextServer配置),
	####如果依然不⾏,返回失败信息。
	MaxAutoRetries: 0 #对当前选中实例重试次数,不包括第⼀次调⽤
	MaxAutoRetriesNextServer: 0 #切换实例的重试次数
	NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule #负载策略调整
Feign对熔断器的⽀持

在Feign客户端⼯程配置⽂件(application.yml)中开启Feign对熔断器的⽀持

# 开启Feign的熔断功能
feign:
  hystrix:
    enabled: true

Feign的超时时⻓设置那其实就上⾯Ribbon的超时时⻓设置
Hystrix超时设置(就按照之前Hystrix设置的⽅式就OK了)
注意:
1)开启Hystrix之后, Feign中的⽅法都会被进⾏⼀个管理了,⼀旦出现问题就进⼊对应的回退逻辑处理
2)针对超时这⼀点,当前有两个超时时间设置(Feign/hystrix),熔断的时候是根据这两个时间的最
⼩值来进⾏的,即处理时⻓超过最短的那个超时时间了就熔断进⼊回退降级逻辑

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
##########################################Hystrix的超时时⻓设置
            timeoutInMilliseconds: 15000
  • ⾃定义FallBack处理类(需要实现FeignClient接⼝)
/**
* 降级回退逻辑需要定义⼀个类,实现FeignClient接⼝,实现接⼝中的⽅法
* *
*/
@Component // 别忘了这个注解,还应该被扫描到
public class ResumeFallback implements ResumeServiceFeignClient {
@Override
public Integer findDefaultResumeState(Long userId) {
return -6;
}
}
  • 3在@FeignClient注解中关联2)中⾃定义的处理类
@FeignClient(value = "edu-service-resume",fallback =
ResumeFallback.class,path = "/resume") // 使⽤fallback的时候,类上的
@RequestMapping的url前缀限定,改成配置在@FeignClient的path属性中
//@RequestMapping("/resume")
public interface ResumeServiceFeignClient {

Feign对请求压缩和响应压缩的⽀持

feign:
	compression:
		request:
			enabled: true # 开启请求压缩
				mime-types: text/html,application/xml,application/json # 设置压缩的数据类型,此处也是默认值
				min-request-size: 2048 # 设置触发压缩的⼤⼩下限,此处也是默认值
	 response:
		enabled: true # 开启响应压缩
Feign的⽇志级别配置

Feign是http请求客户端,类似于咱们的浏览器,它在请求和接收响应的时候,可以打印出⽐较详细的
⼀些⽇志信息(响应头,状态码等等)
如果我们想看到Feign请求时的⽇志,我们可以进⾏配置,默认情况下Feign的⽇志没有开启

    1. 开启Feign⽇志功能及级别
/ Feign的⽇志级别(Feign请求过程信息)
// NONE:默认的,不显示任何⽇志----性能最好
// BASIC:仅记录请求⽅法、 URL、响应状态码以及执⾏时间----⽣产问题追踪
// HEADERS:在BASIC级别的基础上,记录请求和响应的header
// FULL:记录请求和响应的header、 body和元数据----适⽤于开发及测试环境定位问题
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLevel() {
return Logger.Level.FULL;
}
}

    1. 配置log⽇志级别为debug
logging:
   level:
    # Feign⽇志只会对⽇志级别为debug的做出响应
      com.lagou.edu.controller.service.ResumeServiceFeignClient: debug

Feign核⼼源码剖析

添加上@FeignClient,真的没有实现的话,能完成远程请求么?

    1. 调用对象发现是个代理
      在这里插入图片描述
    1. 从@EnableFeignClients 正向切⼊
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
//导入一个注册器
@Import({FeignClientsRegistrar.class})
public @interface EnableFeignClients {
    String[] value() default {};

    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};

    Class<?>[] defaultConfiguration() default {};

    Class<?>[] clients() default {};
}

    1. FeignClientsRegistrar
      在这里插入图片描述
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
		ResourceLoaderAware, EnvironmentAware {

	// patterned after Spring Integration IntegrationComponentScanRegistrar
	// and RibbonClientsConfigurationRegistgrar

	private ResourceLoader resourceLoader;

	private Environment environment;

	public FeignClientsRegistrar() {
	}

	@Override
	public void setResourceLoader(ResourceLoader resourceLoader) {
		this.resourceLoader = resourceLoader;
	}

	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
			//4.Feign的全局默认配置注入
		registerDefaultConfiguration(metadata, registry);
		//5. 把标记了@FeignClient注解的类注入
		registerFeignClients(metadata, registry);
	}
    1. registerDefaultConfiguration
      把@EnableFeignClients 中的defaultConfiguration属性配置到类并注入到容器中
	private void registerDefaultConfiguration(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
		Map<String, Object> defaultAttrs = metadata
				.getAnnotationAttributes(EnableFeignClients.class.getName(), true);

		if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
			String name;
			if (metadata.hasEnclosingClass()) {
				name = "default." + metadata.getEnclosingClassName();
			}
			else {
				name = "default." + metadata.getClassName();
			}
			
			registerClientConfiguration(registry, name,
					defaultAttrs.get("defaultConfiguration"));
		}
	}
  • 5.registerFeignClients
    定义扫描器,主要扫描@FeignClient类,并注入的容器
	public void registerFeignClients(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
		ClassPathScanningCandidateComponentProvider scanner = getScanner();
		scanner.setResourceLoader(this.resourceLoader);

		Set<String> basePackages;

		Map<String, Object> attrs = metadata
				.getAnnotationAttributes(EnableFeignClients.class.getName());
		AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
				FeignClient.class);
		final Class<?>[] clients = attrs == null ? null
				: (Class<?>[]) attrs.get("clients");
		if (clients == null || clients.length == 0) {
			scanner.addIncludeFilter(annotationTypeFilter);
			basePackages = getBasePackages(metadata);
		}
		else {
			final Set<String> clientClasses = new HashSet<>();
			basePackages = new HashSet<>();
			for (Class<?> clazz : clients) {
				basePackages.add(ClassUtils.getPackageName(clazz));
				clientClasses.add(clazz.getCanonicalName());
			}
			AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
				@Override
				protected boolean match(ClassMetadata metadata) {
					String cleaned = metadata.getClassName().replaceAll("\\$", ".");
					return clientClasses.contains(cleaned);
				}
			};
			scanner.addIncludeFilter(
					new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
		}

//扫描包下的@FeignClient注解
		for (String basePackage : basePackages) {
			Set<BeanDefinition> candidateComponents = scanner
					.findCandidateComponents(basePackage);
			for (BeanDefinition candidateComponent : candidateComponents) {
				if (candidateComponent instanceof AnnotatedBeanDefinition) {
					// verify annotated class is an interface
					AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
					AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
					Assert.isTrue(annotationMetadata.isInterface(),
							"@FeignClient can only be specified on an interface");

					Map<String, Object> attributes = annotationMetadata
							.getAnnotationAttributes(
									FeignClient.class.getCanonicalName());

					String name = getClientName(attributes);
					//FeignClient中有一个configuration属性把它取出来
					registerClientConfiguration(registry, name,
							attributes.get("configuration"));
					//6.注册客户端为每个客户端生成一个代理
					registerFeignClient(registry, annotationMetadata, attributes);
				}
			}
		}
	}

注册客户端,给每⼀个客户端⽣成代理对象

    1. registerFeignClient
private void registerFeignClient(BeanDefinitionRegistry registry,
			AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
		String className = annotationMetadata.getClassName();
		//根据FeignClient中的属性配置,封装BeanDefinition对象FeignClientFactoryBean
		BeanDefinitionBuilder definition = BeanDefinitionBuilder
				.genericBeanDefinition(FeignClientFactoryBean.class);
		validate(attributes);
		//获取配置的下面的属性
		definition.addPropertyValue("url", getUrl(attributes));
		definition.addPropertyValue("path", getPath(attributes));
		String name = getName(attributes);
		definition.addPropertyValue("name", name);
		String contextId = getContextId(attributes);
		definition.addPropertyValue("contextId", contextId);
		definition.addPropertyValue("type", className);
		definition.addPropertyValue("decode404", attributes.get("decode404"));
		definition.addPropertyValue("fallback", attributes.get("fallback"));
		definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
		//这个设置注入模式setAutowireMode 根据类型注入AUTOWIRE_BY_TYPE
		definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

		String alias = contextId + "FeignClient";
		AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();

		boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be null

		beanDefinition.setPrimary(primary);

		String qualifier = getQualifier(attributes);
		if (StringUtils.hasText(qualifier)) {
			alias = qualifier;
		}

		BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
				new String[] { alias });
		BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
	}

关注FeignClientFactoryBean这个⼯⼚Bean的getObject⽅法,会返回我们的代理对象

@Override
	public Object getObject() throws Exception {
		return getTarget();
	}
	<T> T getTarget() {
		FeignContext context = applicationContext.getBean(FeignContext.class);
		Feign.Builder builder = feign(context);
		//判断FeignClient中的url是否为空,
		if (!StringUtils.hasText(this.url)) {
		//不为空说明是,请求固定的服务提供者,用于测试
		//判断是否为http开始的
			if (!this.name.startsWith("http")) {
				url = "http://" + this.name;
			}
			else {
			//为空就=服务提供者的name
				url = this.name;
			}
			//生成一个代有负载均衡功能的Feign客户端口
			url += cleanPath();
			//1. 生成有带有负载均衡功能的Feign
			return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type,
					this.name, url));
		}
		if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
			this.url = "http://" + this.url;
		}
		String url = this.url + cleanPath();
		Client client = getOptional(context, Client.class);
		if (client != null) {
			if (client instanceof LoadBalancerFeignClient) {
				// not load balancing because we have a url,
				// but ribbon is on the classpath, so unwrap
				client = ((LoadBalancerFeignClient)client).getDelegate();
			}
			builder.client(client);
		}
		Targeter targeter = get(context, Targeter.class);
		return (T) targeter.target(this, builder, context, new HardCodedTarget<>(
				this.type, this.name, url));
	}

    1. loadBalance

	protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
			HardCodedTarget<T> target) {
			//最终执行请求的Client   2.
		Client client = getOptional(context, Client.class);
		//使用builder构造器,包装client 
		if (client != null) {
			builder.client(client);
			Targeter targeter = get(context, Targeter.class);
			//3. 
			return targeter.target(this, builder, context, target);
		}

		throw new IllegalStateException(
				"No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
	}
    1. getOptional
      使用上下文获取根据contextId获取Client
	protected <T> T getOptional(FeignContext context, Class<T> type) {
		return context.getInstance(this.contextId, type);
	}


    1. org.springframework.cloud.openfeign.HystrixTargeter # target

@Override
	public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
						Target.HardCodedTarget<T> target) {
		if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
		//4. 这个对创建
			return feign.target(target);
		}
		feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
		SetterFactory setterFactory = getOptional(factory.getName(), context,
			SetterFactory.class);
		if (setterFactory != null) {
			builder.setterFactory(setterFactory);
		}
		Class<?> fallback = factory.getFallback();
		if (fallback != void.class) {
			return targetWithFallback(factory.getName(), context, target, builder, fallback);
		}
		Class<?> fallbackFactory = factory.getFallbackFactory();
		if (fallbackFactory != void.class) {
			return targetWithFallbackFactory(factory.getName(), context, target, builder, fallbackFactory);
		}
		//4. 创建
		return feign.target(target);
	}
    1. feign.target -feign.Feign
 public <T> T target(Target<T> target) {
 // 反射创建 5.build
  //newInstance 7.
      return build().newInstance(target);
    }
    1. build()方法
    public Feign build() {
      SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
          new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
              logLevel, decode404, closeAfterDecode, propagationPolicy);
      ParseHandlersByName handlersByName =
          new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
              errorDecoder, synchronousMethodHandlerFactory);
              //6. ReflectiveFeign
      return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
    }
    1. ReflectiveFeign

  ReflectiveFeign(ParseHandlersByName targetToHandlersByName, InvocationHandlerFactory factory,
      QueryMapEncoder queryMapEncoder) {
    this.targetToHandlersByName = targetToHandlersByName;
    this.factory = factory;
    this.queryMapEncoder = queryMapEncoder;
  }
    1. newInstance

  public <T> T newInstance(Target<T> target) {
  //方法对应的增强
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

    for (Method method : target.type().getMethods()) {
      if (method.getDeclaringClass() == Object.class) {
        continue;
      } else 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 = factory.create(target, methodToHandler);
    //最终返回JDK的代理对象
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
        new Class<?>[] {target.type()}, handler);

    for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
      defaultMethodHandler.bindTo(proxy);
    }
    return proxy;
  }

在这里插入图片描述
在这里插入图片描述

⼊增强逻辑的,所以接下来我们要关注增强逻辑部分,FeignInvocationHandler

    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();
      }
		//1. 具体的增加又交了了方法
      return dispatch.get(method).invoke(args);
    }

在这里插入图片描述

1. SynchronousMethodHandler#invoke

在这里插入图片描述


  @Override
  public Object invoke(Object[] argv) throws Throwable {
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    Retryer retryer = this.retryer.clone();
    while (true) {
      try {
      //执行后面的逻辑 2
        return executeAndDecode(template);
      } catch (RetryableException e) {
        try {
    1. executeAndDecode
  Object executeAndDecode(RequestTemplate template) throws Throwable {
  //封装请求
    Request request = targetRequest(template);

    if (logLevel != Logger.Level.NONE) {
      logger.logRequest(metadata.configKey(), logLevel, request);
    }

    Response response;
    long start = System.nanoTime();
    try {
    //真正执行 client 就是LoadBalancerFeignClient
      response = client.execute(request, options);

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

LoadBalancerFeignClient.execute

	@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);
			//构建Rabbion请求
			FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
					this.delegate, request, uriWithoutHost);

			IClientConfig requestConfig = getClientConfig(options, clientName);
			//后结的处理,其中有负载均衡
			return lbClient(clientName).executeWithLoadBalancer(ribbonRequest,
					requestConfig).toResponse();
		}
		catch (ClientException e) {
			IOException io = findIOException(e);
			if (io != null) {
				throw io;
			}
			throw new RuntimeException(e);
		}
	}

在这里插入图片描述

在这里插入图片描述

com.netflix.client.AbstractLoadBalancerAwareClient#executeWithLoadBalancer()


    public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
    //构建负载均衡命令
        LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);

        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) {
            Throwable t = e.getCause();
            if (t instanceof ClientException) {
                throw (ClientException) t;
            } else {
                throw new ClientException(e);
            }
        }
        
    }

进⼊submit⽅法,发现使⽤Ribbon在做负载均衡了

在这里插入图片描述

    public Observable<T> submit(final ServerOperation<T> operation) {
        final ExecutionInfoContext context = new ExecutionInfoContext();
        
        if (listenerInvoker != null) {
            try {
                listenerInvoker.onExecutionStart();
            } catch (AbortExecutionException e) {
                return Observable.error(e);
            }
        }

        final int maxRetrysSame = retryHandler.getMaxRetriesOnSameServer();
        final int maxRetrysNext = retryHandler.getMaxRetriesOnNextServer();

        // Use the load balancer
        //1. selectServer选择服务
        Observable<T> o = 
                (server == null ? selectServer() : Observable.just(server))
selectServer
   private Observable<Server> selectServer() {
        return Observable.create(new OnSubscribe<Server>() {
            @Override
            public void call(Subscriber<? super Server> next) {
                try {
                    Server server = loadBalancerContext.getServerFromLoadBalancer(loadBalancerURI, loadBalancerKey);   
                    next.onNext(server);
                    next.onCompleted();
                } catch (Exception e) {
                    next.onError(e);
                }
            }
        });
    }
getServerFromLoadBalancer
    public Server getServerFromLoadBalancer(@Nullable URI original, @Nullable Object loadBalancerKey) throws ClientException {
        String host = null;
        int port = -1;
        if (original != null) {
            host = original.getHost();
        }
        if (original != null) {
            Pair<String, Integer> schemeAndPort = deriveSchemeAndPortFromPartialUri(original);        
            port = schemeAndPort.second();
        }

        // Various Supported Cases
        // The loadbalancer to use and the instances it has is based on how it was registered
        // In each of these cases, the client might come in using Full Url or Partial URL
        //获取负载均衡Ribbon的逻辑
        ILoadBalancer lb = getLoadBalancer();
();
        if (host == null) {
            // Partial URI or no URI Case
            // well we have to just get the right instances from lb - or we fall back
            if (lb != null){
            //选择Server
                Server svc = lb.chooseServer(loadBalancerKey);


默认区域的负载均衡

回到### LoadBalancerFeignClient.execute中

在这里插入图片描述

AbstractLoadBalancerAwareClient

在这里插入图片描述

org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer#execute

在这里插入图片描述

在这里插入图片描述

最终请求的发起使⽤的是HttpURLConnection

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

在这里插入图片描述

Feign是一个HTTP客户端,它可以将HTTP请求转发到其他微服务。使用Feign可以使得微服务之间的调用更加简单和优雅。下面是使用Feign的详细步骤: 1. 添加依赖 在使用Feign之前,需要先在pom.xml文件中添加Feign的依赖,如下所示: ```xml <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> ``` 2. 创建Feign接口 在使用Feign时,需要先创建一个接口,用于定义需要调用的其他微服务的API。这个接口的方法签名和被调用的微服务的API方法签名必须一致。例如,如果需要调用另一个微服务的getUserInfo方法,那么Feign接口的定义如下所示: ```java @FeignClient(name = "user-service") public interface UserService { @GetMapping("/user/getUserInfo") public UserInfo getUserInfo(@RequestParam("userId") String userId); } ``` 在这个例子中,@FeignClient注解用于指定需要调用的微服务的名称,name属性的值为"user-service",这个值需要和被调用的微服务的spring.application.name属性的值一致。接着,定义了一个getUserInfo方法,这个方法的签名与被调用的微服务的getUserInfo方法的签名一致,使用@GetMapping注解标注请求的路径,这里的路径为"/user/getUserInfo"。最后,定义了一个UserInfo类型的返回值,用于封装被调用的微服务的返回结果。 3. 注入Feign接口 在需要使用Feign调用其他微服务的地方,可以直接注入Feign接口,例如在Service层中注入上面定义的UserService接口: ```java @Service public class UserServiceImpl implements UserService { @Autowired private UserService userService; public UserInfo getUserInfo(String userId) { return userService.getUserInfo(userId); } } ``` 在这个例子中,UserServiceImpl类注入了UserService接口,并且调用了getUserInfo方法。在实际运行时,Feign会根据接口的定义动态生成一个HTTP客户端,并将请求转发到其他微服务。 4. 启用Feign使用Feign时,需要在Spring Boot应用程序的启动类上添加@EnableFeignClients注解,如下所示: ```java @SpringBootApplication @EnableFeignClients public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` 在这个例子中,@EnableFeignClients注解用于启用Feign客户端。在启用Feign之后,Spring Boot会自动扫描所有标注了@FeignClient注解的接口,并为它们动态生成HTTP客户端。 以上就是使用Feign的详细步骤。使用Feign可以使得微服务之间的调用更加简单和优雅,提高代码的复用性和可维护性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值