Spring Cloud Fegin 상세 설명 (1)
104221 단어 SpringCloud
FeginClient 주해 @ Target (Element Type. TYPE) 수식 은 FeginClient 주해 의 역할 목표 가 인터페이스 에 있 음 을 나타 낸다.FeginClient 주석 에 대응 하 는 속성:
if (!StringUtils.hasText(this.url)) {
String url;
if (!this.name.startsWith("http")) {
url = "http://" + this.name;
}
else {
url = this.name;
}
url += cleanPath();
return loadBalance(builder, context, new HardCodedTarget<>(this.type,
this.name, url));
}
Spring Cloud Fegin 은 요청 과 응답 을 GZIP 압축 하여 통신 효율 을 향상 시 키 는 것 을 지원 합 니 다.압축 설정:
fegin:
compression:
request:
enabled: true
mime-types: text/xml,application/xml,application/json
min-request-size: 2048
response:
enabled: true
주의해 야 할 것 은 GZIP 압축 을 시작 한 후 Fegin 간 호출 은 바 이 너 리 프로 토 콜 을 통 해 전송 되 기 때문에 반환 값 은 ResponseEntity 로 수정 해 야 정상적으로 표시 할 수 있 습 니 다.
다른 설정 과 기본 기능 은 여기 서 소개 하지 않 겠 습 니 다.
2). Spring Cloud Fegin 작업 원리
우리 의 접점 은 Spring 시작 클래스 입 니 다. 그리고 Feign 을 사용 할 때 시작 클래스 에 @ Enable Feign Client 주 해 를 추가 합 니 다.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
//
String[] basePackages() default {};
//
Class<?>[] defaultConfiguration() default {};
// @FeignClient
Class<?>[] clients() default {};
}
SpringBoot 가 시 작 될 때 applicationContext \ # refresh 방법 에서 invokeBean Factory PostProcessors 방법 을 호출 합 니 다. invokeBean Definition Registry PostProcessors () 는 모든 configClass (설정 클래스) @ Import 가 도입 한 ImportBean Definition Registrar 하위 클래스 를 불 러 오고 registerBean Definitions 방법 을 실행 합 니 다.(구체 적 인 과정 에서 독 자 는 FeignClient Registrar \ # registerBeanDefinitions 중단 점 에서 호출 스 택 을 볼 수 있 습 니 다).
구체 적 으로 FeignClient Registrar 를 보십시오.
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
ResourceLoaderAware, EnvironmentAware {
......
// 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());
// FeignClient
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
FeignClient.class);
// @EnableFeignClients clients ,
final Class<?>[] clients = attrs == null ? null
: (Class<?>[]) attrs.get("clients");
// clients , FeignClient
if (clients == null || clients.length == 0) {
scanner.addIncludeFilter(annotationTypeFilter);
basePackages = getBasePackages(metadata);
}
else {
// clients
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)));
}
//
for (String basePackage : basePackages) {
Set<BeanDefinition> candidateComponents = scanner
.findCandidateComponents(basePackage);
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
//
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);
// FeignClientSpecification
registerClientConfiguration(registry, name,
attributes.get("configuration"));
// FeignClient
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
// FeignClient
private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
// FeignClientFactoryBean bean
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientFactoryBean.class);
validate(attributes);
// FeignClient bean
definition.addPropertyValue("url", getUrl(attributes));
definition.addPropertyValue("path", getPath(attributes));
String name = getName(attributes);
definition.addPropertyValue("name", name);
definition.addPropertyValue("type", className);
definition.addPropertyValue("decode404", attributes.get("decode404"));
definition.addPropertyValue("fallback", attributes.get("fallback"));
definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
// Type
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
//
String alias = name + "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);
}
// FeignClientSpecification FeignClientSpecification name @FeignClient name configuration @FeignClient configuration
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
Object configuration) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientSpecification.class);
builder.addConstructorArgValue(name);
builder.addConstructorArgValue(configuration);
registry.registerBeanDefinition(
name + "." + FeignClientSpecification.class.getSimpleName(),
builder.getBeanDefinition());
}
.....
}
위의 코드 를 통 해 알 수 있 듯 이 Spring Cloud 는 @ EnableFeignClient 에서 지정 한 가방 의 모든 @ FeignClient 에 표 시 된 클래스 에 FeignClient Factory Bean 과 FeignClient Specification (각 @ FeignClient 에 표 시 된 클래스 에 대한 지정 설정) 을 만 들 었 습 니 다.
FeignClient Factory Bean 은 Factory Bean (여기 서 Factory Bean 을 소개 하지 않 음) 입 니 다. Spring 을 주입 할 때 Factory Bean 의 getObject 방법 을 호출 합 니 다. 그러면 관건 은 FeignClient Factory Bean 의 getObject 방법 입 니 다.
@Override
public Object getObject() throws Exception {
FeignContext context = applicationContext.getBean(FeignContext.class);
// Feign.Builder ( , , )
Feign.Builder builder = feign(context);
// url
if (!StringUtils.hasText(this.url)) {
String url;
// url
if (!this.name.startsWith("http")) {
url = "http://" + this.name;
}
else {
url = this.name;
}
url += cleanPath();
// Object
return 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 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 targeter.target(this, builder, context, new HardCodedTarget<>(
this.type, this.name, url));
}
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
HardCodedTarget<T> target) {
// Client ribbon LoadBalancerFeignClient
Client client = getOptional(context, Client.class);
if (client != null) {
builder.client(client);
// Targeter Hystrix HystrixTargeter
Targeter targeter = get(context, Targeter.class);
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?");
}
위 에 설정 을 준비 한 다음 에 Targeter 의 target 방법 을 보고 HystrixTargeter 를 직접 봅 니 다.
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
Target.HardCodedTarget<T> target) {
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
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);
}
return feign.target(target);
}
feign 을 feign. hystrix. Hystrix Feign. Builder 로 바 꾸 고 fallback 또는 fallback Factory 에서 feign. hystrix. Hystrix Feign. Builder 를 호출 하 는 target 방법 을 설정 합 니 다.
public <T> T target(Target<T> target, T fallback) {
return build(fallback != null ? new FallbackFactory.Default<T>(fallback) : null)
.newInstance(target);
}
Feign build(final FallbackFactory<?> nullableFallbackFactory) {
super.invocationHandlerFactory(new InvocationHandlerFactory() {
@Override public InvocationHandler create(Target target,
Map<Method, MethodHandler> dispatch) {
return new HystrixInvocationHandler(target, dispatch, setterFactory, nullableFallbackFactory);
}
});
super.contract(new HystrixDelegatingContract(contract));
return super.build();
}
Hystrix Invocation Handler 와 Hystrix Delegating Contract 를 설정 합 니 다.Feign \ # newInstance 방법 을 호출 합 니 다. 사실 지금 Reflective Feign 입 니 다.
@Override
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);
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);
for(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
JDK 의 동적 대 리 를 사용 하여 프 록 시 클래스 를 생 성 했 습 니 다. 구체 적 인 실행 논 리 는 Invocation Handler 인 터 페 이 스 를 보고 factory. create (target, methodToHandler) 를 보 며 feign. hystrix. Hystrix Feign. Builder 의 target 방법 은 Invocation Handler Factory factory 를 Hystrix Invocation Handler 로 설정 하 였 습 니 다.
new InvocationHandlerFactory() {
@Override public InvocationHandler create(Target target,
Map<Method, MethodHandler> dispatch) {
return new HystrixInvocationHandler(target, dispatch, setterFactory, nullableFallbackFactory);
}
Hystrix Invocation Handler 에 들 어가 invoke 방법 을 보 는 것 이 구체 적 인 방법의 집행 논리 입 니 다.
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args)
throws Throwable {
// 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();
}
HystrixCommand<Object> hystrixCommand = new HystrixCommand<Object>(setterMethodMap.get(method)) {
@Override
protected Object run() throws Exception {
try {
return HystrixInvocationHandler.this.dispatch.get(method).invoke(args);
} catch (Exception e) {
throw e;
} catch (Throwable t) {
throw (Error) t;
}
}
@Override
protected Object getFallback() {
if (fallbackFactory == null) {
return super.getFallback();
}
try {
Object fallback = fallbackFactory.create(getExecutionException());
Object result = fallbackMethodMap.get(method).invoke(fallback, args);
if (isReturnsHystrixCommand(method)) {
return ((HystrixCommand) result).execute();
} else if (isReturnsObservable(method)) {
// Create a cold Observable
return ((Observable) result).toBlocking().first();
} else if (isReturnsSingle(method)) {
// Create a cold Observable as a Single
return ((Single) result).toObservable().toBlocking().first();
} else if (isReturnsCompletable(method)) {
((Completable) result).await();
return null;
} else {
return result;
}
} catch (IllegalAccessException e) {
// shouldn't happen as method is public due to being an interface
throw new AssertionError(e);
} catch (InvocationTargetException e) {
// Exceptions on fallback are tossed by Hystrix
throw new AssertionError(e.getCause());
}
}
};
if (isReturnsHystrixCommand(method)) {
return hystrixCommand;
} else if (isReturnsObservable(method)) {
// Create a cold Observable
return hystrixCommand.toObservable();
} else if (isReturnsSingle(method)) {
// Create a cold Observable as a Single
return hystrixCommand.toObservable().toSingle();
} else if (isReturnsCompletable(method)) {
return hystrixCommand.toObservable().toCompletable();
}
return hystrixCommand.execute();
}
Hystrix Command 를 만들어 Hystrix 특성 을 가지 게 하 는 것 이 주요 논리 임 을 알 수 있다.구체 적 인 업 무 는 HystrixInvocation Handler. this. dispatch. get (method). invoke (args) 에 있 습 니 다.바로 MethodHandler 의 invoke 방법 입 니 다.
@Override
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = buildTemplateFromArgs.create(argv);
Retryer retryer = this.retryer.clone();
while (true) {
try {
return executeAndDecode(template);
} catch (RetryableException e) {
retryer.continueOrPropagate(e);
if (logLevel != Logger.Level.NONE) {
logger.logRetry(metadata.configKey(), logLevel);
}
continue;
}
}
}
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 {
response = client.execute(request, options);
// ensure the request is set. TODO: remove in Feign 10
response.toBuilder().request(request).build();
} catch (IOException e) {
if (logLevel != Logger.Level.NONE) {
logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
}
throw errorExecuting(request, e);
}
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
boolean shouldClose = true;
try {
if (logLevel != Logger.Level.NONE) {
response =
logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime);
// ensure the request is set. TODO: remove in Feign 10
response.toBuilder().request(request).build();
}
if (Response.class == metadata.returnType()) {
if (response.body() == null) {
return response;
}
if (response.body().length() == null ||
response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
shouldClose = false;
return response;
}
// Ensure the response body is disconnected
byte[] bodyData = Util.toByteArray(response.body().asInputStream());
return response.toBuilder().body(bodyData).build();
}
if (response.status() >= 200 && response.status() < 300) {
if (void.class == metadata.returnType()) {
return null;
} else {
return decode(response);
}
} else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
return decode(response);
} else {
throw errorDecoder.decode(metadata.configKey(), response);
}
} catch (IOException e) {
if (logLevel != Logger.Level.NONE) {
logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime);
}
throw errorReading(request, response, e);
} finally {
if (shouldClose) {
ensureClosed(response.body());
}
}
}
위 코드 에서 보 듯 이 RequestTemplate 를 통 해 HTTP 요청 을 하류 서비스 에 보 냅 니 다. 이때 RequestTemplate 는 부하 균형 특성 을 가지 고 있 습 니 다 (위 에서 언급 한 Client 는 LoadBalancer FeignClient 입 니 다).
요약: @ EnableFeignClient 주석 이 지정 한 패 키 지 를 검색 합 니 다. @ FeignClient 에 표 시 된 인터페이스 에 JDK 동적 프 록 시 를 통 해 프 록 시 클래스 를 생 성하 고 호출 할 때 RequestTemplate 로 요청 을 보 냅 니 다.부하 균형 과 서 비 스 를 끊 을 지 설정 할 수 있 습 니 다.다음 절 에 서 는 Feign 과 Ribbon, Hystrix 통합 을 소개 합 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
SpringCloud OAuth2 + JWT 인증 인증(一) 인증 서버Spring Cloud oAuth2(1) 라이센스 서버 구축 및 액세스 Spring Cloud oAuth2(2) 리소스 서버 구축 및 테스트 SpringCloud OAuth2 + JWT 인증 인증(一) 인증 서버 S...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.