SpringCloud.Honxton 버전 OpenFeign 원리 하편
77199 단어 openfeignspringcloud
전편에서 Openfeign의 사용과 원리를 소개했으니 이편은springcloud와 통합된 부분을 소개할 것이다.전편의 기초가 있으면 원리를 분석할 때 훨씬 간단해지고 스프링 클라우드의 통합 부분에 더 관심을 기울일 것이다.
사용 의존
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
사용 방법
관례에 따라feign을 어떻게 사용하는지 먼저 소개합니다
@FeignClient("client2") // service-id
public interface HelloOpenfeignSpringcloud {
@RequestMapping(name=" /feign",method = RequestMethod.GET, produces="application/x-www-form-urlencoded")
String feign(@RequestParam("feign") String feign);
@RequestMapping(name="/feignPost",method = RequestMethod.POST, produces="application/x-www-form-urlencoded")
String feignPost(@RequestParam("feign") String feign);
@RequestMapping(name = "/feignJson",method = RequestMethod.POST , produces="application/json")
String feignJson(@RequestBody String feign);
}
그리고
@EnableFeignClients
주석을 붙여서 부팅 클래스에 올려야 합니다.통합된 용법의 장점은 다음과 같은 몇 가지가 있음을 알 수 있다
springcloud와openfeign 통합 원리
기본적인 사용을 알고 나면 스프링클라우드 통합 피그 베일을 벗길 때입니다.
다음은 모든feign의 자동 설정 클래스입니다
org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration, 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
및FeignRibbonClientAutoConfiguration
또는FeignLoadBalancerAutoConfiguration
그러면 저희가 하나씩 분석해 보도록 하겠습니다.
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Feign.class) //
@EnableConfigurationProperties({ FeignClientProperties.class,
FeignHttpClientProperties.class })
@Import(DefaultGzipDecoderConfiguration.class)
public class FeignAutoConfiguration {
@Autowired(required = false) // feignclient
private List<FeignClientSpecification> configurations = new ArrayList<>();
@Bean // feignclient spring , serviceId applicationContext , client
public FeignContext feignContext() {
FeignContext context = new FeignContext();
context.setConfigurations(this.configurations);
return context;
}
// feign hystrix
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
protected static class HystrixFeignTargeterConfiguration {
// Targeter , springcloud , DefaultTargeter , Feign.Builder target ,
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new HystrixTargeter();
}
}
// hystrix
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
protected static class DefaultFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new DefaultTargeter();
}
}
// feign , httpclient bean
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ApacheHttpClient.class) //
@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
@ConditionalOnMissingBean(CloseableHttpClient.class)
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
protected static class HttpClientFeignConfiguration {
private final Timer connectionManagerTimer = new Timer(
"FeignApacheHttpClientConfiguration.connectionManagerTimer", true);
@Autowired(required = false)
private RegistryBuilder registryBuilder;
private CloseableHttpClient httpClient;
@Bean
@ConditionalOnMissingBean(HttpClientConnectionManager.class)
public HttpClientConnectionManager connectionManager(
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
FeignHttpClientProperties httpClientProperties) {
final HttpClientConnectionManager connectionManager = connectionManagerFactory
.newConnectionManager(httpClientProperties.isDisableSslValidation(),
httpClientProperties.getMaxConnections(),
httpClientProperties.getMaxConnectionsPerRoute(),
httpClientProperties.getTimeToLive(),
httpClientProperties.getTimeToLiveUnit(),
this.registryBuilder);
this.connectionManagerTimer.schedule(new TimerTask() {
@Override
public void run() {
connectionManager.closeExpiredConnections();
}
}, 30000, httpClientProperties.getConnectionTimerRepeat());
return connectionManager;
}
@Bean
public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory,
HttpClientConnectionManager httpClientConnectionManager,
FeignHttpClientProperties httpClientProperties) {
RequestConfig defaultRequestConfig = RequestConfig.custom()
.setConnectTimeout(httpClientProperties.getConnectionTimeout())
.setRedirectsEnabled(httpClientProperties.isFollowRedirects())
.build();
this.httpClient = httpClientFactory.createBuilder()
.setConnectionManager(httpClientConnectionManager)
.setDefaultRequestConfig(defaultRequestConfig).build();
return this.httpClient;
}
// client , httpclient , bean
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(HttpClient httpClient) {
return new ApacheHttpClient(httpClient);
}
@PreDestroy
public void destroy() throws Exception {
this.connectionManagerTimer.cancel();
if (this.httpClient != null) {
this.httpClient.close();
}
}
}
...
}
다음은feign의 밑바닥 부하 균형의 실현을 소개한다. 여기는 두 가지가 있는데 하나는 리본의 밑바닥 통합이고 다른 하나는 내가 전에 소개한 스프링이 스스로 개발한loadbalancer이다.
@ConditionalOnClass({ ILoadBalancer.class, Feign.class })
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.ribbon.enabled",
matchIfMissing = true)
@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore(FeignAutoConfiguration.class) //
@EnableConfigurationProperties({ FeignHttpClientProperties.class })
@Import({ HttpClientFeignLoadBalancedConfiguration.class,
OkHttpFeignLoadBalancedConfiguration.class,
DefaultFeignLoadBalancedConfiguration.class }) // client client bean
public class FeignRibbonClientAutoConfiguration {
@Bean
@Primary
@ConditionalOnMissingBean
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
public CachingSpringLoadBalancerFactory cachingLBClientFactory(
SpringClientFactory factory) {
return new CachingSpringLoadBalancerFactory(factory);
}
// Client execute
@Bean
@Primary
@ConditionalOnMissingBean
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
public CachingSpringLoadBalancerFactory retryabeCachingLBClientFactory(
SpringClientFactory factory, LoadBalancedRetryFactory retryFactory) {
return new CachingSpringLoadBalancerFactory(factory, retryFactory);
}
@Bean
@ConditionalOnMissingBean
public Request.Options feignRequestOptions() {
return LoadBalancerFeignClient.DEFAULT_OPTIONS;
}
}
// client , feign DefaultClient
@Configuration(proxyBeanMethods = false)
class DefaultFeignLoadBalancedConfiguration {
@Bean
@ConditionalOnMissingBean
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) {
return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory,
clientFactory);
}
}
또 다른 부하 균형기와feign의 통합을 보도록 하겠습니다.
//
@ConditionalOnClass(Feign.class)
@ConditionalOnBean(BlockingLoadBalancerClient.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
@AutoConfigureAfter(FeignRibbonClientAutoConfiguration.class)
@EnableConfigurationProperties(FeignHttpClientProperties.class)
@Configuration(proxyBeanMethods = false)
@Import({ HttpClientFeignLoadBalancerConfiguration.class,
OkHttpFeignLoadBalancerConfiguration.class,
DefaultFeignLoadBalancerConfiguration.class }) // , ribbon
class FeignLoadBalancerAutoConfiguration {
}
// Client
@Configuration(proxyBeanMethods = false)
class DefaultFeignLoadBalancedConfiguration {
@Bean
@ConditionalOnMissingBean
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) {
return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory,
clientFactory);
}
}
위의 모든 설정 클래스를 보고 다들 어지러우실 거라고 믿습니다. 사실 걱정하지 마세요. 아래에 Feign Client 스캔으로 Feign Client Factory Bean을 만들 때 그 때 위의 설정 bean을 모두 주입해서 사용할 수 있기 때문입니다.
우선
@EnableFeignClients
주석은 스캔을 켜는 기능입니다.@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class) //
public @interface EnableFeignClients {
String[] value() default {};
String[] basePackages() default {};//
...
}
//@FeignClient
class FeignClientsRegistrar
implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
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)));
}
// beandefinition
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);
registerClientConfiguration(registry, name,
attributes.get("configuration"));
//
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
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"));
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 });
// beanfactotry
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
}
이상은 스캔과 관련된 실현 원리에 관한 것입니다. 주석을 달고 있는 모든 인터페이스가 결국
FeignClientFactoryBean
형식으로 bean으로 등록되었습니다. 다음은 간단합니다. FeignClientFactoryBean
getobject 방법만 쳐다보면 마지막 수수께끼를 풀 수 있습니다.class FeignClientFactoryBean
implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
<T> T getTarget() {
FeignContext context = this.applicationContext.getBean(FeignContext.class);
// Builder,
Feign.Builder builder = feign(context);
if (!StringUtils.hasText(this.url)) {
if (!this.name.startsWith("http")) {
this.url = "http://" + this.name;
}
else {
this.url = this.name;
}
this.url += cleanPath();
return (T) loadBalance(builder, context,
new HardCodedTarget<>(this.type, this.name, this.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) {
client = ((LoadBalancerFeignClient) client).getDelegate();
}
builder.client(client);
}
// Targeter , DefaultTargeter
Targeter targeter = get(context, Targeter.class);
return (T) targeter.target(this, builder, context, //
new HardCodedTarget<>(this.type, this.name, url));
}
protected Feign.Builder feign(FeignContext context) {
FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
Logger logger = loggerFactory.create(this.type);
// @formatter:off , bean applicationContext ,
Feign.Builder builder = get(context, Feign.Builder.class)
// required values
.logger(logger)
.encoder(get(context, Encoder.class))
.decoder(get(context, Decoder.class))
.contract(get(context, Contract.class));
// @formatter:on
configureFeign(context, builder);
return builder;
}
}
Feign.Builder.class 이 bean은 어떻게 얻습니까
// bean , FeignClientsConfiguration
public class FeignContext extends NamedContextFactory<FeignClientSpecification> {
public FeignContext() {
super(FeignClientsConfiguration.class, "feign", "feign.client.name");
}
}
@Configuration(proxyBeanMethods = false)
public class FeignClientsConfiguration {
...
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
public Feign.Builder feignBuilder(Retryer retryer) {
return Feign.builder().retryer(retryer);
}
...
}
총결산
마지막으로 요약하자면springcloud는feign을 통합시키기 위해 많은 작업을 했고 부하 균형기와 Hystrix를 결합시키기 위해 많은 확장 실현을 했다.이것 또한 배치류가 급격히 팽창하여 우리로 하여금 눈이 어지럽게 한다. 이 글을 보고 너로 하여금 구름과 안개를 헤치고 푸른 하늘을 보게 할지 모르겠지만, 나도 가능한 한 내가 이해한 것을 말하게 했다. 만약 내가 잘못 말한 것이 있다면 아래에서 평론과 교류를 할 수 있다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
spring boot openfeign 은 이로부터 http Client 와 안녕 히 계 세 요.마이크로 서비스 디자인 에서 서비스 간 의 호출 은 매우 정상 적 인 것 입 니 다.보통 저 희 는 http Client 를 사용 하여 원 격 자원 에 대한 호출 을 실현 합 니 다.이런 방법 은 지식 서비스의 주소...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.