当前位置: 首页 > article >正文

17.springcloud_openfeign之扩展组件一

文章目录

  • 一、前言
  • 二、默认约定配置
    • FeignAutoConfiguration
      • CachingCapability
      • FeignCachingInvocationHandlerFactory
      • FeignJacksonConfiguration
      • 熔断器配置
      • FeignCircuitBreakerTargeter
      • FeignCircuitBreaker.Builder
    • FeignClientsConfiguration
      • CircuitBreakerFactory
  • 总结

一、前言

前面介绍了springcloud_openfeign可以从父子容器中获取对应的组件从而构建Feign.Builder对象, 现在的企业级开发中, 一般我们都使用的springboot, 而springboot是约定大于配置的, 那么它提供的一套默认配置是什么呢, 本节我们就来认识一下。

约定大于配置(Convention Over Configuration)Spring Boot 的核心设计理念之一,它的主要目的是减少开发者的配置负担,让开发者专注于业务逻辑的实现,而非繁杂的配置。

基本含义

约定大于配置(Convention Over Configuration)指的是:

  • 框架提供一套默认约定(默认规则或行为)。
  • 如果开发者遵循这些约定,就不需要进行额外的配置。
  • 如果开发者需要定制化,可以显式覆盖默认配置。

简单理解:如果你按照框架的约定方式开发,框架会自动提供合理的默认行为,避免你写大量的配置文件。

二、默认约定配置

spring-cloud-openfeign-core模块中, 添加了org.springframework.boot.autoconfigure.AutoConfiguration.imports) 配置文件

org.springframework.cloud.openfeign.hateoas.FeignHalAutoConfiguration
org.springframework.cloud.openfeign.FeignAutoConfiguration
org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration
org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration
org.springframework.cloud.openfeign.loadbalancer.FeignLoadBalancerAutoConfiguration

FeignAutoConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({ FeignClientProperties.class, FeignHttpClientProperties.class,
		FeignEncoderProperties.class })
public class FeignAutoConfiguration {
    
    /**
	 * springcloud的NamedContextFactory中给子容器添加实例用的
	 */
    @Autowired(required = false)
	private List<FeignClientSpecification> configurations = new ArrayList<>();
    
    /**
	 * 实例化feign子容器对象
	 */
	@Bean
	public FeignClientFactory feignContext() {
		FeignClientFactory context = new FeignClientFactory();
		// 设置子容器实例对象
		context.setConfigurations(this.configurations);
		return context;
	}
}

关于FeignClientFactory, 在父子容器的时候有介绍过, 它就是Feign接口父子容器的封装对象, 这里将解析出来的FeignClientSpecification添加到容器对象中去, 用于给子容器添加一些组件对象。

CachingCapability

/**
 * 对InvocationHandlerFactory缓存增强
 */
@Bean
@ConditionalOnProperty(value = "spring.cloud.openfeign.cache.enabled", matchIfMissing = true)
@ConditionalOnBean(CacheInterceptor.class)
public Capability cachingCapability(CacheInterceptor cacheInterceptor) {
    return new CachingCapability(cacheInterceptor);
}

默认添加了一个对请求的增强CachingCapability, 这里注意@ConditionalOnProperty的定义, 它默认是开启的(matchIfMissing = true), 但是它需要一个CacheInterceptor对象, 这个默认是没有提供的,需要去注册一个, 否则启动会报错; 也可以使用spring.cloud.openfeign.cache.enabled=false来关闭这个bean。下面认识一下这个对象

public class CachingCapability implements Capability {

	private final CacheInterceptor cacheInterceptor;

	public CachingCapability(CacheInterceptor cacheInterceptor) {
		this.cacheInterceptor = cacheInterceptor;
	}

	@Override
	public InvocationHandlerFactory enrich(InvocationHandlerFactory invocationHandlerFactory) {
        // 对InvocationHandlerFactory对增强
		return new FeignCachingInvocationHandlerFactory(invocationHandlerFactory, cacheInterceptor);
	}
}

CachingCapability实际就是对InvocationHandlerFactory对象做了增强, 使用FeignCachingInvocationHandlerFactory对象包装原始的InvocationHandlerFactorycacheInterceptor

FeignCachingInvocationHandlerFactory

public class FeignCachingInvocationHandlerFactory implements InvocationHandlerFactory {

    /**
	 * 代理的InvocationHandlerFactory对象
	 */
	private final InvocationHandlerFactory delegateFactory;

    /**
	 * 缓存拦截器
	 */
	private final CacheInterceptor cacheInterceptor;


	@Override
	public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
        // 这里用feign的InvocationHandlerFactory对象构建InvocationHandler
		final InvocationHandler delegateHandler = delegateFactory.create(target, dispatch);
		return (proxy, method, argsNullable) -> {
			Object[] args = Optional.ofNullable(argsNullable).orElseGet(() -> new Object[0]);
			return cacheInterceptor.invoke(new MethodInvocation() {
				// ... 省略部分方法
				@Override
				public Object proceed() throws Throwable {
					return delegateHandler.invoke(proxy, method, args);
				}
			});
		};
	}
}

FeignCachingInvocationHandlerFactory的实际作用就是创建了一个匿名的java.lang.reflect.InvocationHandler, 在实际调用的时候使用CacheInterceptor去执行请求, 所以这个核心就是把CacheInterceptorInvocationHandler对应起来, 然后在CacheInterceptor内部做一些处理后再执行真正的代理工作

CacheInterceptor对象是org.springframework.cache.interceptor包下的一个对象, 它可以对指定参数的结果做缓存, 多次调用的时候不需要重复做真实调用, 直接从缓存中获取结果即可

举个例子

// 服务端接口
@PostMapping("/configDemo/getPerson2")
public Person getPerson2(@RequestBody Person person) {
    System.out.println("uncleqiao 收到body:" + person);
    person.setName("小杜同学");
    person.setAge(20);
    return person;
}

/**
 * 定义一个客户端缓存拦截器
 */
@Configuration
public class MyCacheInterceptorConfig {

    @Bean
    @ConditionalOnMissingBean(CacheOperationSource.class)
    public CacheOperationSource cacheOperationSource() {
        // 对缓存相关的注解生效
        return new AnnotationCacheOperationSource(false);
    }

    @Bean
    public org.springframework.cache.interceptor.CacheInterceptor myCacheInterceptor(CacheOperationSource cacheOperationSource) {
        CacheInterceptor cacheInterceptor = new CacheInterceptor();
        cacheInterceptor.setCacheOperationSource(cacheOperationSource);
        return cacheInterceptor;
    }
}

// feign接口
@FeignClient(contextId = "urlDemoRemote", url = "localhost:8080", configuration = {MyContextDecoder.class, MyContextEncode.class}, name = "url-demo", path = "/configDemo")
public interface UrlDemoRemote {
    @PostMapping(value = "/getPerson2", consumes = "application/json")
    @Cacheable(cacheNames = "demoCache", key = "'getPerson2'+#p0.name")
    Person getPerson2(Person person);
}

// 测试类
@Test
void acheInterceptorTest() {
    Person person = urlDemoRemote.getPerson2(new Person("小乔同学", 18, 1, LocalDate.now()));
    System.out.println(person);

    Person person2 = urlDemoRemote.getPerson2(new Person("小乔同学", 18, 1, LocalDate.now()));
    System.out.println(person2);
}

结果

服务端打印一次, 客户端打印两次, 即实际只发送了一次请求

// 服务端打印
Person(name=小杜同学, age=20, gender=1, birthday=2024-12-17)

// 客户端打印
Person(name=小杜同学, age=20, gender=1, birthday=2024-12-17)
Person(name=小杜同学, age=20, gender=1, birthday=2024-12-17)

注意spring的缓存默认是用hashMap存在本地, 可以通过自定义cacheManager和Cache对象的手段实现将缓存数据存到其它地方, 例如redis

FeignJacksonConfiguration

/**
 * jackson配置
 */
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Module.class, Page.class, Sort.class })
@ConditionalOnProperty(value = "spring.cloud.openfeign.autoconfiguration.jackson.enabled", havingValue = "true",
        matchIfMissing = true)
protected static class FeignJacksonConfiguration {

    @Bean
    @ConditionalOnMissingBean(PageJacksonModule.class)
    public PageJacksonModule pageJacksonModule() {
        return new PageJacksonModule();
    }

    @Bean
    @ConditionalOnMissingBean(SortJacksonModule.class)
    public SortJacksonModule sortModule() {
        return new SortJacksonModule();
    }

}

默认也提供了对jackson的支持, 这里仅仅是添加了两个Moudle, PageJacksonModule和SortJacksonModule; 分页和排序

DefaultFeignTargeterConfiguration

@Configuration(proxyBeanMethods = false)
@Conditional(FeignCircuitBreakerDisabledConditions.class)
protected static class DefaultFeignTargeterConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public Targeter feignTargeter() {
        return new DefaultTargeter();
    }
}

class DefaultTargeter implements Targeter {

	@Override
	public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignClientFactory context,
			Target.HardCodedTarget<T> target) {
		return feign.target(target);
	}

}

默认使用的target对象; 作为一个默认实现, DefaultTargeter本身啥也没做

熔断器配置

/**
 * 熔断器配置
 */
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(CircuitBreaker.class)
@ConditionalOnProperty(value = "spring.cloud.openfeign.circuitbreaker.enabled", havingValue = "true")
protected static class CircuitBreakerPresentFeignTargeterConfiguration {

    // 默认配置, 当没有CircuitBreakerFactory时生效
    @Bean
    @ConditionalOnMissingBean(CircuitBreakerFactory.class)
    public Targeter defaultFeignTargeter() {
        return new DefaultTargeter();
    }

    /**
     * 1.需要配置CircuitBreakerFactory的bean
     * 2. spring.cloud.openfeign.circuitbreaker.group.enabled=true 来设置circuitBreakerGroupEnabled为true
     */
    @SuppressWarnings("rawtypes")
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnBean(CircuitBreakerFactory.class)
    public Targeter circuitBreakerFeignTargeter(CircuitBreakerFactory circuitBreakerFactory,
            @Value("${spring.cloud.openfeign.circuitbreaker.group.enabled:false}") boolean circuitBreakerGroupEnabled,
            CircuitBreakerNameResolver circuitBreakerNameResolver) {
        return new FeignCircuitBreakerTargeter(circuitBreakerFactory, circuitBreakerGroupEnabled,
                circuitBreakerNameResolver);
    }

}
  1. 它需要设置spring.cloud.openfeign.circuitbreaker.enabled=true来开启

  2. 当没有配置CircuitBreakerFactory对象时, 默认使用的是DefaultTargeter包装了feign的Target对象

  3. 当配置了CircuitBreakerFactory对象时, 使用FeignCircuitBreakerTargeter包装feign的Target对象

FeignCircuitBreakerTargeter

class FeignCircuitBreakerTargeter implements Targeter {

    /**
     * 创建CircuitBreaker的工厂
     */
	private final CircuitBreakerFactory circuitBreakerFactory;

	private final boolean circuitBreakerGroupEnabled;

    /**
     * CircuitBreaker名称解析器
     */
	private final CircuitBreakerNameResolver circuitBreakerNameResolver;
    
}

它有三个属性

  1. circuitBreakerFactory: 用来创建CircuitBreaker对象的工厂
  2. circuitBreakerGroupEnabled:
  3. circuitBreakerNameResolver: CircuitBreaker名称的解析器

并且实现了Targeter接口的target方法

@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignClientFactory context,
        Target.HardCodedTarget<T> target) {
    // 这里如果Feign.Builder不是FeignCircuitBreaker.Builder类型, 直接就执行并返回了
    if (!(feign instanceof FeignCircuitBreaker.Builder builder)) {
        return feign.target(target);
    }
    // 客户端名称,同时它也是容器的名称; 默认是contextId, 否则取name
    String name = !StringUtils.hasText(factory.getContextId()) ? factory.getName() : factory.getContextId();
    // @FeignClient注解的fallback属性对象
    Class<?> fallback = factory.getFallback();
    // 如果配置了
    if (fallback != void.class) {
        return targetWithFallback(name, context, target, builder, fallback);
    }
    // @FeignClient注解的fallbackFactory属性对象
    Class<?> fallbackFactory = factory.getFallbackFactory();
    // 如果配置了
    if (fallbackFactory != void.class) {
        return targetWithFallbackFactory(name, context, target, builder, fallbackFactory);
    }
    // 到这里就是没有熔断对象
    // 1.填充Feign.Builder的属性  2.构建代理对象
    return builder(name, builder).target(target);
}

下面看一下targetWithFallbacktargetWithFallbackFactory方法

targetWithFallback

// 
private <T> T targetWithFallback(String feignClientName, FeignClientFactory context,
			Target.HardCodedTarget<T> target, FeignCircuitBreaker.Builder builder, Class<?> fallback) {
    // 从父子容器中获取fallback对象, 它必须是feign接口的实现类
    T fallbackInstance = getFromContext("fallback", feignClientName, context, fallback, target.type());
    return builder(feignClientName, builder).target(target, fallbackInstance);
}

// 从容器中获取fallbackFactoryClass对象
private <T> T targetWithFallbackFactory(String feignClientName, FeignClientFactory context,
			Target.HardCodedTarget<T> target, FeignCircuitBreaker.Builder builder, Class<?> fallbackFactoryClass) {
    // 从父子容器中获取FallbackFactory对象, 它必须是FallbackFactory接口的实现类
    FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>) getFromContext("fallbackFactory",
            feignClientName, context, fallbackFactoryClass, FallbackFactory.class);
    return builder(feignClientName, builder).target(target, fallbackFactory);
}

// 从容器feignClientName中获取指定类型beanType的实例, 并且它必须是targetType的子类
private <T> T getFromContext(String fallbackMechanism, String feignClientName, FeignClientFactory context, Class<?> beanType, Class<T> targetType) {
		// 父子容器中获取beanType类型的对象
		Object fallbackInstance = context.getInstance(feignClientName, beanType);
		// 对象还必须存在
		if (fallbackInstance == null) {
			throw new ...
		}

		// 该对象是工厂
		if (fallbackInstance instanceof FactoryBean<?> factoryBean) {
			try {
				fallbackInstance = factoryBean.getObject();
			}
			catch (Exception e) {
				...
            }
		}
		else {
			// fallback不是接口类型, 直接抛异常
			if (!targetType.isAssignableFrom(beanType)) {
				throw new ...
			}
		}
		return (T) fallbackInstance;
	}

方法小结

  1. 这里要求我们传入的Feign.Builder对象必须是FeignCircuitBreaker.Builder对象, 才会做熔断的相关动作, 否则直接就给调用了
  2. 客户端的名称(容器名称)取值顺序为 contextId>name
  3. 如果配置了@FeignClient注解的fallback属性对象, 那么从父子容器中获取该对象, 该对象必须是feign目标接口的子类
  4. 如果配置了@FeignClient注解的fallbackFactory属性对象, 那么从父子容器中获取该对象, 该对象必须是FallbackFactory的子类
  5. 填充FeignCircuitBreaker.Builder对象的属性
  6. 创建接口的实例对象

上面是先处理FeignCircuitBreaker.Builder, 再构建feign接口实例对象, 那么我们先来认识一下FeignCircuitBreaker.Builder

FeignCircuitBreaker.Builder

public final class FeignCircuitBreaker {
	public static final class Builder extends Feign.Builder {

		private CircuitBreakerFactory circuitBreakerFactory;

		private String feignClientName;

		private boolean circuitBreakerGroupEnabled;

		private CircuitBreakerNameResolver circuitBreakerNameResolver;
		// 省略部分setter方法
		// 如果fallback存在, 默认构建成FallbackFactory.Default对象
		public <T> T target(Target<T> target, T fallback) {
			return build(fallback != null ? new FallbackFactory.Default<>(fallback) : null).newInstance(target);
		}

		public <T> T target(Target<T> target, FallbackFactory<? extends T> fallbackFactory) {
			return build(fallbackFactory).newInstance(target);
		}

		@Override
		public <T> T target(Target<T> target) {
			return build(null).newInstance(target);
		}

		public Feign build(final FallbackFactory<?> nullableFallbackFactory) {
            // 设置InvocationHandlerFactory对象
			super.invocationHandlerFactory((target, dispatch) -> new FeignCircuitBreakerInvocationHandler(
					circuitBreakerFactory, feignClientName, target, dispatch, nullableFallbackFactory,
					circuitBreakerGroupEnabled, circuitBreakerNameResolver));
			return super.build();
		}
	}
}

它实际上也是Feign.Builder的子类, 覆盖了target方法

  1. @FeignClient的fallback属性默认也会包装成FallbackFactory对象,这里是FallbackFactory.Default, 而@FeignClient的fallbackFactory需要自己实现创建熔断对象
  2. 覆盖的target方法使用熔断对象(FallbackFactory)创建了一个匿名InvocationHandlerFactory对象设置到了Feign.Builder

通过前面的学习, 我们现在是知道InvocationHandlerFactory是用来创建jdk动态代理的方法代理句柄的, 它具备实际调用的回调能力, 我们了解一下这个匿名的InvocationHandlerFactory对象

匿名InvocationHandlerFactory

先回顾一下InvocationHandlerFactory对象

public interface InvocationHandlerFactory {

  InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch);
  // 省略其它内容
}

它仅提供了抽象方法create, 用来创建jdk的方法调用句柄InvocationHandler, 并接受Target和方法句柄对象。那么我们就可以使用匿名类型的lambda表达式表示这个接口的实现

(target, dispatch) -> new FeignCircuitBreakerInvocationHandler(
					circuitBreakerFactory, feignClientName, target, dispatch, nullableFallbackFactory,
					circuitBreakerGroupEnabled, circuitBreakerNameResolver))

所以当调用InvocationHandlerFactory.create方法的时候, 其实就是调用的new FeignCircuitBreakerInvocationHandler动作

class FeignCircuitBreakerInvocationHandler implements InvocationHandler {
	@Override
	public Object invoke(final Object proxy, final Method method, final Object[] args) {
		// early exit if the invoked method is from java.lang.Object
		// code is the same as ReflectiveFeign.FeignInvocationHandler
		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();
		}

		// 获取熔断名称, 默认是[feignClientName-methodName]
		String circuitName = circuitBreakerNameResolver.resolveCircuitBreakerName(feignClientName, target, method);
		// 创建熔断对象
		CircuitBreaker circuitBreaker = circuitBreakerGroupEnabled ? factory.create(circuitName, feignClientName)
				: factory.create(circuitName);
		// 熔断对象执行方法
		Supplier<Object> supplier = asSupplier(method, args);
		// 回调方法不为空
		if (this.nullableFallbackFactory != null) {
			Function<Throwable, Object> fallbackFunction = throwable -> {
				// 回调对象
				Object fallback = this.nullableFallbackFactory.create(throwable);
				try {
					// 调用回调方法
					return this.fallbackMethodMap.get(method).invoke(fallback, args);
				}
				catch (Exception exception) {
					unwrapAndRethrow(exception);
				}
				return null;
			};
			// 熔断器执行
			return circuitBreaker.run(supplier, fallbackFunction);
		}
		// 熔断器执行, 没有回调
		return circuitBreaker.run(supplier);
	}
}

方法小结

  1. 提供了对equals, hashCode, toString的支持
  2. 使用CircuitBreakerFactory创建熔断器对象CircuitBreaker
  3. 封装目标执行方法为Supplier<Object>
  4. 如果有熔断回调对象nullableFallbackFactory, 则使用circuitBreaker.run(supplier, fallbackFunction);执行, 支持对熔断的回到
  5. 如果没有熔断回调对象, 熔断器直接执行目标方法circuitBreaker.run(supplier)

这里看一下对目标方法的封装

private Supplier<Object> asSupplier(final Method method, final Object[] args) {
    // 获取当前线程的请求属性
    final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
    final Thread caller = Thread.currentThread();
    return () -> {
        // 是否是异步调用
        boolean isAsync = caller != Thread.currentThread();
        try {
            // 设置请求属性
            if (isAsync) {
                RequestContextHolder.setRequestAttributes(requestAttributes);
            }
            // 调用方法句柄
            return dispatch.get(method).invoke(args);
        }
        catch (RuntimeException throwable) {
            throw throwable;
        }
        catch (Throwable throwable) {
            throw new RuntimeException(throwable);
        }
        finally {
            // 清除请求属性
            if (isAsync) {
                RequestContextHolder.resetRequestAttributes();
            }
        }
    };
}

这个方法比较简单, 异步的时候设置当前上下文请求属性到异步现成中, 然后执行目标方法, 这里判断异步动作是比较巧妙的。

因为返回的Supplier对象是个可执行请求的方法, 它可能同步执行也可能异步执行, 如果是同步执行, 那么不管后面执行动作是在哪里发生, 那么当前线程和执行时候的线程会是同一个线程, 如果放在异步中执行, 那么当前线程和这段lambda表达的Supplier将会在不同的线程中, caller != Thread.currentThread()就会成立。

FeignClientsConfiguration

上面介绍到FeignCircuitBreakerTargeter#target方法如果Feign.Builder不是FeignCircuitBreaker.Builder类型时, 它不会走构建熔断回调部分, 那么FeignCircuitBreaker.Builder对象怎么来的呢?

上面说到, 想要开启熔断回调的第一步就是配置spring.cloud.openfeign.circuitbreaker.enabled=true, 并且前面介绍父子容器的时候知道FeignClientFactory对象在创建的时候会将FeignClientsConfiguration作为defaultConfigType注入到每个子容器中, 而FeignClientsConfiguration中有个bean定义如下

@Configuration(proxyBeanMethods = false)
public class FeignClientsConfiguration {
    
    // .... 省略其它配置
    
	/**
	 * 这个条件FeignCircuitBreakerDisabledConditions默认为true
	 * spring.cloud.openfeign.circuitbreaker.enabled为false或者不配置时, 这里生效, 为true时不生效
	 */
	@Configuration(proxyBeanMethods = false)
	@Conditional(FeignCircuitBreakerDisabledConditions.class)
	protected static class DefaultFeignBuilderConfiguration {

		@Bean
		@Scope("prototype")
		@ConditionalOnMissingBean
		public Feign.Builder feignBuilder(Retryer retryer) {
			return Feign.builder().retryer(retryer);
		}
	}

    // spring.cloud.openfeign.circuitbreaker.enabled为true时, 这里生效
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(CircuitBreaker.class)
	@ConditionalOnProperty("spring.cloud.openfeign.circuitbreaker.enabled")
	protected static class CircuitBreakerPresentFeignBuilderConfiguration {

        // 1. 没有配置Feign.Builder对象 并且 2.没有配置CircuitBreakerFactory对象
		@Bean
		@Scope("prototype")
		@ConditionalOnMissingBean({ Feign.Builder.class, CircuitBreakerFactory.class })
		public Feign.Builder defaultFeignBuilder(Retryer retryer) {
			return Feign.builder().retryer(retryer);
		}

        // 配置了CircuitBreakerFactory对象
		@Bean
		@Scope("prototype")
		@ConditionalOnMissingBean
		@ConditionalOnBean(CircuitBreakerFactory.class)
		public Feign.Builder circuitBreakerFeignBuilder() {
			return FeignCircuitBreaker.builder();
		}

	}
}

这里先看这个条件@Conditional(FeignCircuitBreakerDisabledConditions.class)

/**
 * 当满足以下任一条件时, 该condition 生效
 */
class FeignCircuitBreakerDisabledConditions extends AnyNestedCondition {

	FeignCircuitBreakerDisabledConditions() {
        // 应用在@Configuration注解中
		super(ConfigurationPhase.PARSE_CONFIGURATION);
	}

	/**
	 * 条件1: 断路器CircuitBreaker不存在
	 * 默认存在, 为false
	 */
	@ConditionalOnMissingClass("org.springframework.cloud.client.circuitbreaker.CircuitBreaker")
	static class CircuitBreakerClassMissing {

	}

	/**
	 * 条件2: 断路器CircuitBreaker显示指定未不开启
	 * 没有配置的话, 默认为true
	 */
	@ConditionalOnProperty(value = "spring.cloud.openfeign.circuitbreaker.enabled", havingValue = "false",
			matchIfMissing = true)
	static class CircuitBreakerDisabled {

	}
}

当满足如下一个条件时, FeignCircuitBreakerDisabledConditions为true

  1. 当不存在org.springframework.cloud.client.circuitbreaker.CircuitBreaker.class, 为true; 默认是存在的, 所以默认值是false
  2. spring.cloud.openfeign.circuitbreaker.enabled配置为true, 或者没有配置是为true; 默认是没有配置的, 所以是true

我们平时用的多个@Condition标注在@Bean的方法或者@Configuration的类上, 都属于并且条件, 也就是A成立并且B成立; 这里的AnyNestedCondition就是A成立或者B成立。

所以@Conditional(FeignCircuitBreakerDisabledConditions.class)默认是true, 当@ConditionalOnMissingClass("org.springframework.cloud.client.circuitbreaker.CircuitBreaker")为false, 并且@ConditionalOnProperty(value = "spring.cloud.openfeign.circuitbreaker.enabled", havingValue = "false", matchIfMissing = true)为false时, @Conditional(FeignCircuitBreakerDisabledConditions.class)才为false, 也就是存在一个CircuitBreaker对象, 并且设置spring.cloud.openfeign.circuitbreaker.enabled=false@Conditional(FeignCircuitBreakerDisabledConditions.class)条件会不生效

所以如下配置默认是生效的, 默认使用的是Feign.Builder对象

/**
 * 这个条件FeignCircuitBreakerDisabledConditions默认为true
 */
@Configuration(proxyBeanMethods = false)
@Conditional(FeignCircuitBreakerDisabledConditions.class)
protected static class DefaultFeignBuilderConfiguration {

    @Bean
    @Scope("prototype")
    @ConditionalOnMissingBean
    public Feign.Builder feignBuilder(Retryer retryer) {
        return Feign.builder().retryer(retryer);
    }
}

当配置spring.cloud.openfeign.circuitbreaker.enabled为true时, DefaultFeignBuilderConfiguration不会被装配, 而CircuitBreakerPresentFeignBuilderConfiguration会被装配。

当配置spring.cloud.openfeign.circuitbreaker.enabled为true, 并且配置了CircuitBreakerFactory的bean对象, 那么将会使用FeignCircuitBreaker.Builder对象; 那么CircuitBreakerFactory是什么呢?? 下面介绍

CircuitBreakerFactory

public abstract class CircuitBreakerFactory<CONF, CONFB extends ConfigBuilder<CONF>>
		extends AbstractCircuitBreakerFactory<CONF, CONFB> {

	public abstract CircuitBreaker create(String id);

	public CircuitBreaker create(String id, String groupName) {
		return create(id);
	}
}

它提供了创建CircuitBreaker的方法;

如果我们配置了CircuitBreakerFactory的bean, 并且配置spring.cloud.openfeign.circuitbreaker.enabled为true, 那么是不是就和CircuitBreakerPresentFeignTargeterConfiguration#circuitBreakerFeignTargeter呼应上了

@SuppressWarnings("rawtypes")
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(CircuitBreakerFactory.class)
public Targeter circuitBreakerFeignTargeter(CircuitBreakerFactory circuitBreakerFactory,
        @Value("${spring.cloud.openfeign.circuitbreaker.group.enabled:false}") boolean circuitBreakerGroupEnabled,
        CircuitBreakerNameResolver circuitBreakerNameResolver) {
    return new FeignCircuitBreakerTargeter(circuitBreakerFactory, circuitBreakerGroupEnabled,
            circuitBreakerNameResolver);
}

并且FeignCircuitBreakerInvocationHandler#invoke中的创建CircuitBreaker的部分是不是也就有值了

class FeignCircuitBreakerInvocationHandler implements InvocationHandler {
    
    /**
	 * 创建熔断的工厂
	 */
	private final CircuitBreakerFactory factory;
    
	public Object invoke(final Object proxy, final Method method, final Object[] args) {
        // 创建熔断对象
		CircuitBreaker circuitBreaker = circuitBreakerGroupEnabled ? factory.create(circuitName, feignClientName)
        : factory.create(circuitName);
    }
}

最后我们再看一下CircuitBreaker对象

public interface CircuitBreaker {

	default <T> T run(Supplier<T> toRun) {
		return run(toRun, throwable -> {
			throw new NoFallbackAvailableException("No fallback available.", throwable);
		});
	}
	// p1: 目标执行方法  p2:异常回调
	<T> T run(Supplier<T> toRun, Function<Throwable, T> fallback);
}

它提供一个默认方法和一个抽象方法, 如果我们要实现它的话, 就需要实现<T> T run(Supplier<T> toRun, Function<Throwable, T> fallback);方法。

例如

/**
 * 自定义创建CircuitBreaker的工厂
 */
@Configuration
public class MyCircuitBreakerFactory extends CircuitBreakerFactory {

    @Override
    public CircuitBreaker create(String id) {
        System.out.println("子容器id==" + id);
        return new MyCircuitBreaker();
    }

    @Override
    protected ConfigBuilder configBuilder(String id) {
        return null;
    }

    @Override
    public void configureDefault(Function defaultConfiguration) {

    }
}

/**
 * 自定义熔断器
 */
public class MyCircuitBreaker implements CircuitBreaker {

	private AtomicLong failureCount = new AtomicLong();

	@Override
	public <T> T run(Supplier<T> toRun, Function<Throwable, T> fallback) {
		try {
            // 调用目标方法
			return toRun.get();
		}
		catch (Throwable throwable) {
            // 记录失败次数
			failureCount.incrementAndGet();
            // 熔断回调
			return fallback.apply(throwable);
		}
	}
}

总结

  1. springcloud_openfeign通过自动装配引入了FeignAutoConfiguration配置类
  2. 注入了对父子容器支持的FeignClientFactory对象
  3. 注入CachingCapability对InvocationHandlerFactory的增强, 可以对返回值做缓存处理; 可以通过spring.cloud.openfeign.cache.enabled=false关闭该配置
  4. 默认注入了jackson的两个module, PageJacksonModule和SortJacksonModule; 可以通过spring.cloud.openfeign.autoconfiguration.jackson.enabled=false关闭
  5. 熔断器配置, 满足一下两个配置
  • 配置spring.cloud.openfeign.circuitbreaker.enabled=true
  • 自定义CircuitBreakerFactory的bean
  1. 熔断回调对象fallback和fallbackFactory; 它们是从父子容器中获取的
  • fallback配置的对象必须是feign接口的子类
  • fallbackFactory配置的对象必须是FallbackFactory接口的实现类, 并且这个factory创建的对象必须是feign接口的子类

http://www.kler.cn/a/448815.html

相关文章:

  • UE5仿漫威争锋灵蝶冲刺技能
  • 大数据、人工智能、云计算、物联网、区块链序言【大数据导论】
  • Datawhale AI冬令营——Chat-悟空设计
  • 【C#】Ctrl+ 组合键的使用
  • [JavaScript] 我该怎么去写一个canvas游戏
  • FPGA:FPGA器件选型
  • 生产制造管理系统:现代制造业的智能引擎
  • 什么是漏电?如何预防电气设备漏电引起的火灾?
  • Vivado 编译(单核性能对比+高性能迷你主机+Ubuntu20.04/22.04安装与区别+20.04使用远程命令)
  • 【echarts】创建带有标记线和点击事件的折线图
  • 如何使用 Python 执行 SQL 查询?
  • 基于Linux编写C语言基础命令
  • Django 应用安装脚本 – 如何将应用添加到 INSTALLED_APPS 设置中 原创
  • 【Python】pandas库---数据分析
  • 【RAG实战】Prompting vs. RAG vs. Finetuning: 如何选择LLM应用选择最佳方案
  • 开源呼叫中心系统,柔性动态自适应IVR详解
  • DA-CLIP:Controlling Vision-Language Models for Universal Image Restoration
  • Centos7 部署ZLMediakit
  • 基于Java在线电影院购票选座系统的设计与实现(Springboot框架) 参考文献
  • C语言 单向链表反转问题
  • Screen(一)_简介与安装
  • 达梦官方工具 SQLark数据迁移(oracle->达梦数据库)
  • PHP MySQL 插入多条数据
  • electron-vite【实战】登录/注册页
  • 实践:从一次故障聊聊前端 UI 自动化测试
  • ROS2 python编写 intel realsense D405相机节点通过launch.py启动多个相机并发送图像话题,基于pyrealsense2库