Spring Cloud OpenFeign – remote call

1. What is Feign?

Feign makes writing java http clients easier, this is an official explanation, the original translation is: Feign makes writing Java http clients easier, Feign is an http request The lightweight framework for calling can call Http requests in the form of Java interface annotations. Feign can template requests by processing annotations. When actually calling, pass in parameters, apply them to the request according to the parameters, and then convert them into real request.

Feign encapsulates the Http call process, which is more suitable for interface-oriented programming habits.

In the scenario of service invocation, we often invoke services based on the Http protocol, and the remote invocation frameworks we often use may include HttpURLConnection, Apache HttpComponents, OkHttp3, Forest, Netty, etc. These frameworks provide own characteristics. From the perspective of role division, their functions are consistent to provide Http call services.

2. Understanding remote calls

  • Local Procedure Call (LPC for short)
  • Remote Procedure Call (RPC for short)

Feign mainly provides us with remote call services. So what is remote call? Remote call can be understood as a method call between different services, which is essentially a network between two hosts Communication, when it comes to network communication, there will inevitably be serialization, deserialization, codec and other issues that must be considered. Some RPC frameworks that are more popular in the industry, for example: Dubbo provides Interface-based remote method call, through the rpc remote call framework, the client only needs to know the definition of the interface to call the remote service.

And feign is mainly used to simplify the code for us to initiate remote calls, using a remote call to the open API of Github as :

/**
 * GitHub client GitHubFeign, access GitHub development platform API, open platform API address: https://www.apifox.cn/apihub/
 *
 * @author: jacklin
 * @date: 2022/6/30 21:20
 */
@FeignClient(name = "github-client", url = "https://api.github.com")
public interface GitHub Feign {


    /**
     * Find github standard library information
     * <p>
     * https://api.github.com/search/repositories v
     *
     * @author: jacklin
     * @since 2022/6/30 21:27
     **/
    @GetMapping(value = "/search/repositories", produces = MediaType.APPLICATION_JSON_VALUE)
    String searchRepositories(@RequestParam("q") String q);
}
Copy Code

Step 1: Introduce OpenFeign components into the Maven pom file.

Step 2: The client needs to define a GitHubFeign interface, which defines a searchRepositories() method. You can see that @FeignClient annotation, and the service name is specified in the brackets: github-client, indicating that this interface is used to remotely call the GitHub API service, url is used to specify the full path of calling the service. The path prefix of other methods must be consistent with the url address. The complete request path URL address: https://api.github.com/search/repositories

Step 3: You need to add the @EnableFeignClients annotation to the service startup class. When the service starts, Spring scans the interface modified by @FeignClints, based on The dynamic proxy generates local JDK Proxy proxy object instances, and then registers these proxy instances in the Spring IOC container. When the remote interface is called, the Proxy proxy instance completes the real remote access and returns the result.

Step 4: Introduce the GitHubFeign service in the Controller to complete the remote service call:

@RestController
@RequestMapping(value = "/github", produces = MediaType.APPLICATION_JSON_VALUE)
public class GithubController {

    @Resource
    private GitHubFeign gitHubFeign;

    /**
     * Find github standard library information
     *
     * @author: jacklin
     * @since 2022/6/30 21:36
     **/
    @GetMapping(value = "/searchRepositories")
    String searchRepositories(@RequestParam(value = "q") String q) {
        return gitHubFeign. searchRepositories(q);
    }
}
Copy Code

The returned result data is as follows:

It can be seen that feign makes the remote call the same as the local method, which greatly simplifies the way of rpc remote call.

3. The difference between OpenFeign and Feign

It can be considered that OpenFeign is an enhanced version of Feign, the difference is that OpenFeign supports Spring MVC annotations

Feign OpenFeign
Feign Written by Netflix, it is a lightweight RESTful HTTP service client in the SpringCloud component and the first generation load balancing client in SpringCloud. OpenFeign was formerly Neflix Feign, and Spring Cloud extended support for SpringMVC annotations on the basis of Feign, such as @RequestMapping and so on. OpenFeign’s @FeignClient can parse the interface under SpringMVC’s @RequestMapping annotation, and generate an implementation class through a dynamic proxy, implement load balancing and call other services in the implementation class.

Both OpenFeign and Feign have built-in Ribbon load balancing components in the bottom layer. After importing OpenFeign dependencies, there is no need to specifically import Ribbon dependencies , used for client load balancing to call registry services.

4. The core working principle of OpenFeign

1 Trigger the Spring application to scan the @FeignClient modified class in the classpath through @EnableFeignCleints.
2 After parsing to the @FeignClient modified class, the Feign framework finally registers a FeignClientFacotoryBean and enters the by extending the registration logic of Spring Bean Deifinition >Spring container
3 When the Spring container initializes other classes that use the @FeignClient interface, it obtains a proxy object Proxy generated by FeignClientFacotryBean.
4 Based on Java’s native dynamic proxy mechanism, calls to Proxy will be uniformly forwarded to an InvocationHandler defined by the Feign framework, and the Handler Complete subsequent HTTP conversion, sending, receiving and HTTP response work.

5. OpenFeign package scanning principle

To implement remote calls through OpenFeign, it involves a core annotation of OpenFeign: @EnableFeignClient, according to the literal meaning, this annotation is to enable the function of OpenFeign, and will generally be added to our startup class superior.

The general flow of Spring package scanning:

  1. Enable the OpenFeign function @EnableFeignClients, and then import the FeignClientsRegistrar class through the @Import(FeignClientsRegistrar.class) import method to enable the loading of OpenFeign components.
@SpringBootApplication
@EnableFeignClients
public class MambaBlockDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(MambaBlockDemoApplication.class, args);
    }
}
Copy Code
  1. FeignClientsRegistrar is responsible for loading the Feign interface, the source code is as follows:
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
   //Registration configuration
   registerDefaultConfiguration(metadata, registry);
   //Register FeignClient
   registerFeignClients(metadata, registry);
}
Copy Code
  1. The registerFeignClients() method will call the findCandidateComponents() method to find all the classes and interfaces annotated with @FeignClients under the basePackages of the specified path.
LinkedHashSet<BeanDefinition> candidateComponents = Set<BeanDefinition> findCandidateComponents(String basePackage)
Copy Code
  1. Only the interfaces decorated by @FeignClient are kept.
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"));
      //Inject into the Spring container
      registerFeignClient(registry, annotationMetadata, attributes);
   }
}
Copy Code

6. Summary

This article uses OpenFeign, which is used by the GitHub open API for remote calls, as a sample code as an entry to explain. Then in-depth analysis of OpenFeign’s operating mechanism and architecture design by way of illustration + interpretation of source code