SprigCloud——Feign使用と原理

44764 ワード

一、使う
1、feignを配置する
依存を追加
mavenのpomにfeignを追加します.
<dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
設定の有効化
アプリ起動クラスに@EnbaleFeign Clienntsコメントを追加します.
@EnableEurekaClient
@EnableFeignClients
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
2、feignを新規作成する
@FeignClient(name = "SERVICE-NAME", url = "${***}")
public interface TestFeign {

}
内訳:name:マイクロサービスの名称は、必ずeurekaバックグラウンド構成で一致しています.url:feignのコールアドレスfallbackを手動で指定することができます.タグの許容範囲を間違えて実行するクラスです.
feignでインターフェースを定義する方式は、正常インターフェースとは異なりません.パラメータ名などが一致するように注意してください.例えば:
@RequestMapping(value = "/user/message", method = RequestMethod.POST)
JSONObject sendMessage(@RequestParam("userId") String userId, @RequestParam("content") String content);
3、feignを呼び出してServiceImplにfeignインターフェースを注入し、正常に使えばいいです.
@Autowired
TestFeign testFeign;
二、原理
もしspringcloudの関連部品を使わないなら、サービスを呼び出してhttpを歩かなければならなくて、設定はhead、bodyを要求して、その後要求を開始することができます.応答体を取得すると、解析などの操作が必要で、煩雑です.
なぜfeignの使用はこんなに簡単で軽いですか?
1、どうやって有効にするか
起動配置上で@EnbaleFeign Clientsの注釈があるかどうかを確認し、この注釈があれば、パケットスキャンを開始し、@Feign Clientの注釈インターフェースにスキャンされる.この注釈をスキャンした後、BEAN Definitionを通じてIOC容器に注入し、その後の使用を容易にする.
Feign Cliennts Registarでは、register Feign Cliennts()が登録feignの操作を完了しました.
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    ClassPathScanningCandidateComponentProvider scanner = this.getScanner();
	scanner.setResourceLoader(this.resourceLoader);
	Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
	  ......

	//            
	Iterator var17 = ((Set)basePackages).iterator();

    while(var17.hasNext()) {
        String basePackage = (String)var17.next();
        Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
        Iterator var21 = candidateComponents.iterator();

        while(var21.hasNext()) {
            BeanDefinition candidateComponent = (BeanDefinition)var21.next();
            if (candidateComponent instanceof AnnotatedBeanDefinition) {
                AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition)candidateComponent;
                //  feign      
                AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
                Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());
                String name = this.getClientName(attributes);
                //      
                this.registerClientConfiguration(registry, name, attributes.get("configuration"));
                //  feign   
                this.registerFeignClient(registry, annotationMetadata, attributes);
            }
        }
   	}
}
feignクライアントを登録して、注釈を使用する時に構成されるすべての情報を含みます.
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
   String className = annotationMetadata.getClassName();
   BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
   this.validate(attributes);
   definition.addPropertyValue("url", this.getUrl(attributes));
   definition.addPropertyValue("path", this.getPath(attributes));
   String name = this.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"));
   definition.setAutowireMode(2);
   String alias = name + "FeignClient";
   AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
   boolean primary = ((Boolean)attributes.get("primary")).booleanValue();
   beanDefinition.setPrimary(primary);
   String qualifier = this.getQualifier(attributes);
   if (StringUtils.hasText(qualifier)) {
       alias = qualifier;
   }

   BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[]{alias});
   BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
2、請求の仕方
ReflectveFeign内部では、jdkの動的エージェントをターゲットインターフェースに使用して動的エージェントクラスを生成し、ここではInvocationHandler(jdk動的代理原理)の統一した方法ブロックを生成し、インターフェースの各方法に対してSynchronousMethHandlerブロックを生成し、方法上のメタデータを解析し、http要求テンプレートを生成する.
Synch ronousMethodhandlerクラスでRequest Template開始要求を生成する.
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);
    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);
    }
}
httpリクエストを送信します
@Override
public Response execute(Request request, Options options) throws IOException {
  HttpURLConnection connection = convertAndSend(request, options);
  return convertResponse(connection).toBuilder().request(request).build();
}

HttpURLConnection convertAndSend(Request request, Options options) throws IOException {
  final HttpURLConnection
      connection =
      (HttpURLConnection) new URL(request.url()).openConnection();
  if (connection instanceof HttpsURLConnection) {
    HttpsURLConnection sslCon = (HttpsURLConnection) connection;
    if (sslContextFactory != null) {
      sslCon.setSSLSocketFactory(sslContextFactory);
    }
    if (hostnameVerifier != null) {
      sslCon.setHostnameVerifier(hostnameVerifier);
    }
  }
  connection.setConnectTimeout(options.connectTimeoutMillis());
  connection.setReadTimeout(options.readTimeoutMillis());
  connection.setAllowUserInteraction(false);
  connection.setInstanceFollowRedirects(true);
  connection.setRequestMethod(request.method());

  Collection<String> contentEncodingValues = request.headers().get(CONTENT_ENCODING);
  boolean
      gzipEncodedRequest =
      contentEncodingValues != null && contentEncodingValues.contains(ENCODING_GZIP);
  boolean
      deflateEncodedRequest =
      contentEncodingValues != null && contentEncodingValues.contains(ENCODING_DEFLATE);

  boolean hasAcceptHeader = false;
  Integer contentLength = null;
  for (String field : request.headers().keySet()) {
    if (field.equalsIgnoreCase("Accept")) {
      hasAcceptHeader = true;
    }
    for (String value : request.headers().get(field)) {
      if (field.equals(CONTENT_LENGTH)) {
        if (!gzipEncodedRequest && !deflateEncodedRequest) {
          contentLength = Integer.valueOf(value);
          connection.addRequestProperty(field, value);
        }
      } else {
        connection.addRequestProperty(field, value);
      }
    }
  }
  // Some servers choke on the default accept string.
  if (!hasAcceptHeader) {
    connection.addRequestProperty("Accept", "*/*");
  }

  if (request.body() != null) {
    if (contentLength != null) {
      connection.setFixedLengthStreamingMode(contentLength);
    } else {
      connection.setChunkedStreamingMode(8196);
    }
    connection.setDoOutput(true);
    OutputStream out = connection.getOutputStream();
    if (gzipEncodedRequest) {
      out = new GZIPOutputStream(out);
    } else if (deflateEncodedRequest) {
      out = new DeflaterOutputStream(out);
    }
    try {
      out.write(request.body());
    } finally {
      try {
        out.close();
      } catch (IOException suppressed) { // NOPMD
      }
    }
  }
  return connection;
}
三、結び
1、約束は配置より大きい2、新しい瓶に古い酒を入れる.
原文のリンク:https://blog.csdn.net/u010066934/article/details/80967709