1. What is Feign?

Feign is a declarative pseudo Http client that makes writing Http clients much easier. With Feign, you just create an interface and annotate it. It has pluggable annotations that use Feign annotations and JAX-RS annotations. Feign supports pluggable encoders and decoders. Feign integrates the Ribbon by default and is integrated with Eureka

2. What annotations do Feign have

The name of the explain
@RequestLine Methods define HttpMethod and urItemplate. expressions wrapped in {} in UriTemplate can be used on method parameters
@Param Automatic injection of @param method parameters defines template variables whose values can be resolved using template injection as names
@Headers Define header template variables on a class or method, using @param annotations to provide injection of parameter values. If the annotation is added to the interface class, all requests carry the corresponding Header information. If on a method, it will only be added to the corresponding method request
@QueryMap Method defines a key-value pair or POJO, and the parameter values are converted to a query string on the URL
@HeaderMap Method to define a HeaderMap, with UrlTemplate and HeaderTemplate types, using the @param annotation to provide parameter values
@FeignClient The annotation specifies the name of the microservice to be invoked, encapsulating the process of invoking user-API as the consumer invocation template.
@EnableFeignClients The scan declares that they are pretend client interfaces (@feignClient). Configure the component scan directive for use (@Configuration) class.
@SpringQueryMap Spring MVC is equivalent to OpenFeign’s {@QueryMap}.

@ FeignClient,

The name of the explain
Name, Value, and serviceId Name is the alias of value, and value is also the alias of name. Name specifies the name of FeignClient. If the project uses the Ribbon, the name attribute is used as the name of the microservice for service discovery. ServiceId and value are used to specify the serviceId and are not used anymore.
qualifier This property is used to specify the value of the @Qualifier annotation, which is the Qualifier for the FeignClient and can be referenced using the modified value.
url The URL property is generally used for debuggers and allows us to manually specify the address of the @FeignClient call.
decode404 When an HTTP 404 error occurs, the decoder is called if the field bit is true, otherwise a FeignException is thrown
configuration Feign configuration class, you can customize Feign Encoder, Decoder, LogLevel, Contract.
fallback Define a fault tolerant processing class. When the remote interface fails to be called or times out, the fault tolerant logic of the corresponding interface will be called. The class specified by Fallback must implement the interface tagged with @FeignClient
fallbackFactory Factory class, used to generate examples of fallback classes. With this property we can implement fault-tolerant logic common to each interface, reducing repetitive code.
path The path property defines the unified prefix for the current FeignClient.
primary Whether to mark the pseudo proxy as the primary Bean. Default is true.

@EnableFeignClients

The name of the explain
value Alias for the basePackages property, allowing for a more concise writing style.
basePackage Set up automatic scanning of base package paths with @FeignClient annotations.
basePackageClasses This property is a security alternative to the basePackages property. This property scans all classes decorated by @FeignClient under the package for each class specified; This requires consideration of creating a special tag class or interface in each package that has no purpose other than to be referenced by the attribute.
defaultConfiguration This property is custom configured for all Feign clients using @Configuration.
clients Sets the list of classes decorated by the @FeignClient annotation. If clients is not an empty array, FeignClient is loaded without automatic classpath scanning.

2. Basic usage of feign

Basic examples of official website

interface GitHub {

  @RequestLine("GET /repos/{owner}/{repo}/contributors")

  List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);

  @RequestLine("POST /repos/{owner}/{repo}/issues")

  void createIssue(Issue issue, @Param("owner") String owner, @Param("repo") String repo);



public class MyApp {

  public static void main(String... args) {

    GitHub github = Feign.builder().decoder(new GsonDecoder())

                         .target(GitHub.class, "https://api.github.com");

  }

}



Copy the code

Basic example of OpenFeign

<dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-starter-feign</artifactId>

  </dependency>

Copy the code


@SpringBootApplication

@EnableFeignClients

public class Application {

    public static void main(String[] args) {

        SpringApplication.run(Application.class, args);

    }

}

@FeignClient("stores")

public interface StoreClient {

    @RequestMapping(method = RequestMethod.POST, value = "/stores/{storeId}", consumes = "application/json")

    Store update(@PathVariable("storeId") Long storeId, Store store);

}



Copy the code

The HTTP CLIENT sample

<dependency>

    <groupId>io.github.openfeign</groupId>

    <artifactId>feign-httpclient</artifactId>

</dependency>

Copy the code
# configuration

feign.httpclient.enabled = true

Copy the code

OkHttp sample

<dependency>

    <groupId>io.github.openfeign</groupId>

    <artifactId>feign-okhttp</artifactId>

</dependency>

Copy the code
# configuration

feign.okhttp.enabled = true

Copy the code

Spring Cloud Feign configuration item

The name of the explain
Logger.Level loggerLevel Log Level. Default logger.level.none
Integer connectTimeout Connection timeout java.net.HttpURLConnection#getConnectTimeout (), if use Hystrix, the configuration is invalid connection timeout
Integer readTimeout Read timeout java.net.HttpURLConnection#getReadTimeout (), if use Hystrix, the configuration is invalid
Class retryer Retry interface implementation class, the default implementation remyer.never_retry
Class errorDecoder Error encoding, Default errordecoder.default ()
List<Class>requestInterceptors Request interceptor
Boolean decode404 Whether to enable 404 encoding
Class decoder Decoder, which converts an HTTP response into an object, Spring Cloud Feign uses ResponseEntityDecoder
Class encoder An encoder that converts an object into an HTTP request body. Spring Cloud Feign uses SpringEncoder
Class contract Handles Feign interface annotations, and Spring Cloud Feign uses the Spring MVCContract implementation to handle Spring MVC annotations, which is why we can use Springmvc annotations.

Configuration of the sample

feign: 

client: 

default-config: my-config

config:

my-config:

error-decoder: com.example.feign.MyErrorDecoder

logger-level: full

Copy the code

Configuration item source code parsing

-feignClientFactoryBean #configureFeign protected void configureFeign(FeignContext context, Feign.builder Builder) {// Config file, Begin to feign. Client FeignClientProperties properties = applicationContext. GetBean (FeignClientProperties. Class); if (properties ! = null) {if (properties. IsDefaultToProperties ()) {/ / using Java configuration configureUsingConfiguration config (the context, the builder); configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder); configureUsingProperties(properties.getConfig().get(this.name), builder); } else { configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder); configureUsingProperties(properties.getConfig().get(this.name), builder); configureUsingConfiguration(context, builder); } } else { configureUsingConfiguration(context, builder); // Feign default configuration}}Copy the code

Priority of configuration items

Type 1: The configuration file is not configured

Using Java config configuration, has a lower priority to the attitude of a single configuration 1, FeignClientsConfiguration Spring Cloud Feign global default configuration. 2. @enablefeignclients #defaultConfiguration customizes the global defaultConfiguration. FeignClient# Configuration Indicates the local configuration of a Feign interface.

Feign.client. default-to-properties=true(default true)

Java config and application properties (. Yml) configuration, a low priority to the attitude of a single configuration 1, FeignClientsConfiguration Spring Cloud Feign global default configuration. 2. @enablefeignclients #defaultConfiguration customizes the global defaultConfiguration. FeignClient# Configuration Indicates the local configuration of a Feign interface. Application. Properties (.yml) configure the global default configuration of the file, configure the feign.client. Default-config to specify the default value (defult), how to use, Properties (.yml) configuration file local configuration, specify @feignClient# name local configuration.

Feign.client. default-to-properties=false(default true)

Java config and application.properties(.yml) configuration, with priority from low to high. Configure the attribute feign.client.default-config to specify the default value (defult), how to use it, Properties (.yml) configuration file local configuration, specify @feignClient# name local configuration. 3, FeignClientsConfiguration Spring Cloud Feign global default configuration. 4. @enablefeignclients #defaultConfiguration specifies the global defaultConfiguration. FeignClient# Configuration Indicates the local configuration of a single Feign interface.

Copy the code

3. Feign source code brief analysis

Main process steps:

Register the FeignClient configuration class and FeignClient BeanDefinition

Instantiate FeignContext object FeignContext

Create Feign. Builder object

Generate load balancing proxy classes

Generate the default proxy class

Inject into the Spring container

Feign call flow chart

Register the FeignClient configuration class and FeignClient BeanDefinition

One, from the point of view to start the class began, the @ EnableFeignClients comments: @ Import (FeignClientsRegistrar. Class), we don't look at the above registerBeanDefinitions decomposition of two methods: Default configuration methods: 1) registered registerDefaultConfiguration: the above method to start class for reading the above @ EnableFeignClients feign related configuration class declaration in the annotation, the default name for the default, generally do not need to configure. Use the default FeignAutoConfiguration. There is a important method in the method: register configuration registerClientConfiguration, start the process there are two reading feign configuration class, the first will be packaged into FeignClientSpecification bean configuration class, injected into the container. This object is very important and contains the retry policy, timeout policy, logging and other configurations required by FeignClient. If a service is not set, it reads the default configuration. 2) Scanning FeignClient: RegisterFeignClients method mainly scanning classpath, for all the FeignClient generate corresponding BeanDefinition, here in the second place up with registerClientConfiguration register configuration method, The main thing here is to scan the directory under which each project's configuration class is loaded in the container. SpringCloud FeignClient actually makes use of Spring's proxy factory to generate proxy classes, so here all the FeignClient description BeanDefinition is set to FeignClientFactoryBean type. This class in turn inherits FactoryBean, which is obviously a proxy class. In spring, the FactoryBean is a FactoryBean that is used to create proxy beans, so it follows that feign wraps all feignClient beans as feignclientfactorybeans. This is the end of the scanning method. When does a proxy class trigger generation? When Spring refreshes the container and instantiates our business Service, if FeignClient is registered, Spring will instantiate the FeignClient and determine if it is a proxy bean. If it is a proxy bean, The getObject() method of FeignClientFactoryBean is called to generate the proxy bean.Copy the code

Instantiate FeignContext object FeignContext

Main process steps: A, instantiation FeignContex, instantiation Feign context object FeignContext 2, can see Feign configuration Settings to Feign container, And the elements in the collection Is what we mentioned above two call registerClientConfiguration method added, before and after the echo. However, when we introduced Sleuth, Obtain instances of feignContext is configured in the TraceFeignClientAutoConfiguration sleuthFeignContext: can see above creates a TraceFeignContext instance, Because the object inheritance FeignContext, at the same time, through the processor after FeignContextBeanPostProcessor postProcessAfterInitialization method returns TraceFeignContext, So in step 2 above by type to obtain: the applicationContext. GetBean (FeignContext. Class), eventually get TraceFeignContext.Copy the code

Create Feign. Builder object

Feign.builder Builder = get(context, feign.builder.class

1) use Feign alone, by loading FeignClientsConfiguration configuration to create Feign. Builder 2) introduced the hystrix, without introducing the sleuth, By loading FeignClientsConfiguration configuration to create HystrixFeign. Builder 3) at the same time introducing hystrix and sleuth, Loading TraceFeignClientAutoConfiguration configuration to create HystrixFeign. BuilderCopy the code

2. Set retry policy and log components

1) Feign. Builder specifies retry policy, log level, error code, etc. 2) FeignContext is cached. Each service corresponds to a FeignContext, and the service name is used as the key.Copy the code

3. Load the configuration in the following order: first load the configuration class for each service, then load the configuration class on the startup class annotation, and then load the default configuration class. What’s the good of that?

Spring's method of refreshing the container also caches all beans and does not instantiate them if they have been created. Therefore, each FeignClient configuration class is selected first, and the default configuration class is last.Copy the code

Generate load balancing proxy classes

One important judgment is whether the URL declared by FeignClient is empty to determine the specific proxy class to be generated

1) If the Ribbon proxy is empty, the Ribbon proxy will be loaded by default. @feignClient ("MyFeignClient") 2) If the url is not null, the default proxy class is generated, which is called hard coding. @FeignClient(value = "MyFeignClient",url = "http://localhost:8081")Copy the code

This makes it easy for developers to test, so they don’t have to worry about the registry, they just call HTTP, which is a nice development trick. Here the Client object is retrieved from the FeignContext context and there are three more cases:

1) No integration of ribbon and Sleuth: obtain the Default Client: Default instance. 2) Ribbon integrated, sleuth not integrated: get LoadBalanceFeignClient instance. 3) Integrate ribbon and Sleuth: TraceFeignClient instance will be acquiredCopy the code

Feign initializes the entire process of structuring the dynamic proxy

Create ReflectiveFeign from the inner class ParseHandlersByName. ReflectiveFeign The parse interface generates MethodMetadata 3, and ParseHandlersByName generates the RequestTemplate factory based on MethodMetadata Create SynchronousMethodHandler with ParseHandlersByName ReflectiveFeign create FeignInvocationHandler; Pass in the parameter SynchronousMethodHandler and bind DefaultMethodHandler to ReflectiveFeign to create a Proxy based on FeignInvocationHandlerCopy the code

Interface call process:

FeignInvocationHandler (); FeignInvocationHandler (); FeignInvocationHandler (); 3. Invoke the invoke method of the SynchronousMethodHandler based on the method argument passed in. Create a RequestTemplate from the Factory object requestTemplate.factory. The factory uses an Encode 4, SynchronousMethodHandler to traverse the list of RequestInterceptor properties as needed. 4. The SynchronousMethodHandler calls the apply method on its Target attribute. 5. The SynchronousMethodHandler calls its Client's execute method, passing in a Request object. 6. The Client converts the Request to an HTTP Request. When sent, the HTTP Response is converted to a Response object. 7. SynchronousMethodHandler calls a method called Decoder to decode the Response object and return. 8Copy the code

Generate the default proxy class

Now that you understand the logic of step 4, generating the default proxy class is easy to understand. The only difference is that the client implementation class is loadBalanceClient. Note: Regardless of the proxy class, the request is ultimately initiated by the execute method in feign.default, which uses HttpUrlConnection by Default.

Inject into the Spring container

Conclusion: through the spring refresh () method, trigger FeignClientFactoryBean. GetObject () method to obtain the proxy class, and then complete the injection process of the spring container. This implementation is similar to the Dubbo implementation, interested in their own research oh.

4, summarize

Feign extension point summary

The name of the explain
Contract The purpose of a Contract is to parse the interface methods and generate Rest definitions. Feign uses its own defined annotations by default, and also provides the JAXRSContract Javax.ws. Rs annotation interface implementation. SpringContract is the spring Cloud provides SpringMVC annotation implementation
Client Client.Default sends HTTP requests using the Java API’s HttpClientConnection; ApacheHttpClient uses Apache’s Http client to send requests. OkHttpClient uses the OKHttp client to send requests; RibbonClient Uses the Ribbon for client routing
Decoder Json decoder GsonDecoder, JacksonDecoder; XML decoder JAXBDecoder; Stream StreamDecoder
Encoder Default encoder, can only handle String and byte[]; Json encoder GsonEncoder, JacksonEncoder; XML encoder JAXBEncoder
Target HardCodedTarget Defaults to Target and does nothing. LoadBalancingTarget Uses the Ribbon for client routing
Retryer The Default policy is retrans-y. Default, which contains three parameters: interval, maximum interval, and retry times. The system will sleep the entered interval before the first retry. The sleep time for each retry is 1.5 times that of the previous one
RequestInterceptor Modifying the RequestTemplate, such as adding headers to all requests, can be done with an interceptor before the calling client sends a request
InvocationHandler Injected into Feign.Builder via InvocationHandlerFactory, Feign provides an extension to Hystrix for Hystrix access

advantages

1. Good scalability, can have a lot of custom extension points, such as RxJava, Hystrix, etc. 2. 3. Springclound uses components by default. 4. API is simple to use, simplifying HTTP call APICopy the code

disadvantages

1. Many default implementations of components have some pitfalls to avoidCopy the code

This article is formatted using MDNICE