This is the sixth day of my participation in the August Text Challenge.More challenges in August

preface

Welcome to our GitHub repository Star: github.com/bin39232820… The best time to plant a tree was ten years ago, followed by now. I know many people don’t play QQ anymore, but for a moment of nostalgia, welcome to join the six-vein Shenjian Java rookie learning group, group chat number: 549684836 encourage everyone to write blog on the road of technology

omg

In the previous section, we looked at Eureka. Let’s review it. First, it is a CS architecture, divided into client and server.

Clients are also divided into producers and consumers, that is, service providers and service consumers. The specific roles of clients are as follows

  • Register the current service with the server when the client starts
  • And to maintain a heartbeat with the server, using background threads
  • Pull each node set of the server and periodically update the service information to the local, because the client also caches the service node information
  • When a service is down, listen for shutdown and report its down status to the server

The service side

  • After startup, get service registration information from other nodes.

  • In the process of operation, evICT tasks are regularly run to eliminate the services that are not renewed on time (including abnormal stop and network failure services).

  • During operation, received register, Renew, cancel requests will be synchronized to other registry nodes. Distributed Data Synchronization (AP)

  • During operation, the self-protection mechanism. , etc.

  • Eureka of SpringCloud principle

What is Feign

Feign is a declarative, templated HTTP Client (used only in the Application Client). Declarative invocation means that a remote method is invoked as if it were a local method, without being aware of manipulating the remote HTTP request. The declarative invocation of Spring Cloud makes it possible to request a remote service using HTTP just like invoking a local method, without the developer being aware that it is a remote method, much less an HTTP request. Feign’s Application makes Spring Cloud microservice invocation like Dubbo, where the Application Client calls the Application Service directly through the interface method, rather than having to parse the returned data through the normal RestTemplate construct request. It solves the problem of letting developers call remote interfaces as if they were calling local methods, without having to focus on the details of interaction with the remote, and without having to focus on distributed environment development.

Feign is a declarative Web service client. It makes writing Web service clients much easier. To use Feign, create an interface and annotate it. It has pluggable annotation support, including Feign annotations and JAX-RS annotations. Feign also supports pluggable encoders and decoders. Spring Cloud adds support for Spring MVC annotations, and support the use of HttpMessageConvertersSpring Web in the use of comments by default. When using Feign, Spring Cloud integrates Ribbon and Eureka to provide a load-balanced HTTP client.

Application deployment structure when developed with Feign

How is Feign designed?

The native Feign

We use A lot of SpringCloud buckets, but really? It only encapsulates the native Fegin, so if you want to get to the bottom of it, knowing more about the native Fegin will help us understand The Spring Cloud Feign

Introduction to Feign usage

Basic usage

The basic use is shown below, an adaptation of the Canonical Retrofit sample.

Interface GitHub {// RequestLine annotations declare request methods and request addresses, which can allow for a query parameter @Requestline ("GET /repos/{owner}/{repo}/ polymorphism "). List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo); } static class Contributor { String login; int contributions; } public static void main(String... args) { GitHub github = Feign.builder() .decoder(new GsonDecoder()) .target(GitHub.class, "https://api.github.com"); // Fetch and print a list of the contributors to this library. List<Contributor> contributors = github.contributors("OpenFeign", "feign"); for (Contributor contributor : contributors) { System.out.println(contributor.login + " (" + contributor.contributions + ")"); }}Copy the code

The custom

Feign has many customizable aspects. As a simple example, you can use feign.Builder () to build an API with your own components. As follows:

interface Bank { @RequestLine("POST /account/{id}") Account getAccountInfo(@Param("id") String id); }... / / AccountDecoder a Decoder () is its own Bank Bank = Feign. Builder (). The Decoder (new AccountDecoder ()). The target (Bank) class, https://api.examplebank.com);Copy the code

Feign dynamic proxy

The default implementation of Feign is ReflectiveFeign, built through Feign.Builder. Before we look at the code, let’s look at the Target object.

Public interface Target<T> {Class<T> type(); // Proxy object name, default url, load balancing use String name(); // Request url address, eg: https://api/v2 String url(); }Copy the code

Where target. type is used to generate the proxy object, and the URL is the address from which the Client object sends the request.

Public Feign build () {/ / client has three implementation JdkHttp/ApacheHttp/okHttp, The default is the realization of the JDK SynchronousMethodHandler Factory synchronousMethodHandlerFactory = new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger, logLevel, decode404, closeAfterDecode, propagationPolicy); ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, errorDecoder, synchronousMethodHandlerFactory); return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder); }Copy the code

Summary: Introduce some main parameters:

  • Client this have nothing to say, there are three kinds of implementation JdkHttp/ApacheHttp/okHttp
  • RequestInterceptor RequestInterceptor
  • The Contract REST annotation parser, which defaults to contract.default (), is a native annotation that supports Feign.
  • The InvocationHandlerFactory generates the JDK dynamic proxy, and the actual execution is delegated to the MethodHandler.

Generating proxy objects

Public <T> T newInstance(Target<T> Target) {// 1. Contract parses methods and annotations on the target.type interface class into MethodMetadata, / / translates into the internal MethodHandler handling 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))); 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; }Copy the code

Summary: newInstance generates a dynamic proxy for the JDK, and as you can see from factory.create(target, methodToHandler) InvocationHandler actually delegates to methodToHandler. MethodToHandler. Default is SynchronousMethodHandler Factory Factory class created.

MethodHandler Method executor

ParseHandlersByName. Apply to generate the actuator MethodHandler of each method, one of the most important step is through analytical MethodMetadata Contract.

public Map<String, Contract parses methods and annotations in the interface class MethodMetadata List<MethodMetadata> Metadata = contract.parseAndValidatateMetadata(key.type()); Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>(); for (MethodMetadata md : Metadata) {/ / 2. BuildTemplate actually Method Method parameters into the Request BuildTemplateByResolvingArgs buildTemplate; if (! Md.formparams ().isEmpty() &&md.template ().bodyTemplate() == null) {// 2.1 form buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder); } else if (md.bodyIndex() ! = null) {/ / @ 2.2 Body annotation buildTemplate = new BuildEncodedTemplateFromArgs (md, encoder, queryMapEncoder); } else {/ / 2.3 the rest buildTemplate = new BuildTemplateByResolvingArgs (md, queryMapEncoder); } // 3. Wrap metadata and buildTemplate as MethodHandler result.put(md.configkey (), factory.create(key, md, buildTemplate,) options, decoder, errorDecoder)); } return result; }Copy the code

Conclusion: This method consists of the following steps:

Contracts unify methods to parse MethodMetadata(*) so that they can be adapted to various REST declarative specifications by implementing different contracts. BuildTemplate actually converts the parameters of the Method Method to Request. Wrap metadata and buildTemplate as MethodHandler.

This creates a target. type proxy, which can send Http requests as if accessing normal methods, just like the RPC Stub model. After understanding proxy, its execution process is actually a model.

Feign calls the procedure

FeignInvocationHandler#invoke

private final Map<Method, MethodHandler> dispatch; public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { ... // Each Method Method corresponds to a MethodHandler return dispatch.get(Method).invoke(args); }Copy the code

Summary: As with the previous conclusion, the actual execution logic is actually delegated to the MethodHandler.

SynchronousMethodHandler#invoke

// Initiate an HTTP request, Public Object invoke(Object[] argv) throws Throwable {// template Constructs the argv parameter as a Request RequestTemplate  template = buildTemplateFromArgs.create(argv); Options options = findOptions(argv); Retryer retryer = this.retryer.clone(); Execute (request, options) while (true) {try {return executeAndDecode(template, options); // Call Client.execute (request, options) while (true) {try {return executeAndDecode(template, options); } the catch (RetryableException e) {try {/ / retry mechanism retryer continueOrPropagate (e); } catch (RetryableException th) { ... } continue; }}}Copy the code

Summary: Invoke primarily performs a retry mechanism for request failures, delegating execution to the executeAndDecode method.

// Generate Request; The second is HTTP request; ExecuteAndDecode (RequestTemplate Template, Options Options) throws Throwable {// 1. Request = targetRequest(template) Request = targetRequest(template); // 2. HTTP request Response Response = client.execute(request, options); Response.class == metadata.returnType()) {byte[] bodyData = Util.toByteArray(response.body().asInputStream()); return response.toBuilder().body(bodyData).build(); }... } Request targetRequest(RequestTemplate template) { requestInterceptors) { interceptor.apply(template); } // Generate Request return target.apply(template);Copy the code

This is the call process of native Feign. Generally speaking, it is divided into two parts: one is the encapsulation of the client and the other is the encapsulation of the calling method

Spring Cloud Feign principle analysis

What about the native Feign we looked at earlier? Spring Cloud is based on SpringBoot, and SpringBoot is based on Spring, so Spring is a glue framework that encapsulates the components, So, that makes it a lot easier

SpringCloud uses Feign in SpringCloud. Feign uses Feign in SpringCloud. Feign uses Feign in SpringCloud

The working principle of

Let’s think about the normal process when we use Feign

  • Added Spring Cloud OpenFeign dependencies
  • The @enableFeigncleints annotation has been added to the SpringBoot boot class
  • Define the interface DemoService according to Feign’s rules and add the @FeignClient annotation
  • Where you need to use the Feign interface DemoService, inject directly with @Autowire
  • Use the interface to complete the call to the server

Then we will analyze based on these steps, this article will not say very in-depth look at each line of source code

  • When the SpringBoot application is started, the processing logic for @EnableFeignClient triggers the program to scan all classes annotated by @FeignClient in the classPath. Resolve these classes as BeanDefinitions and register them in the Spring container
  • When XiaoLiuLiuService is injected into some Feign Bean, Spring will try to find XiaoLiuLiuService’s implementation class from the Sping container
  • Since we have never written the implementation class of XiaoLiuLiuService, the implementation class of XiaoLiuLiuService obtained in the above steps must be feign framework by extending spring’s Bean processing logic. Create a dynamic interface proxy object for XiaoLiuLiuServiceProxy and register it with the Spring container.
  • Spring finally injects the XiaoLiuLiuServiceProxy instance into the Bean that uses XiaoLiuLiuService.
  • When the business request actually occurs, the call to XiaoLiuLiuService is forwarded to the InvocationHandler implemented by Feign framework. The InvocationHandler is responsible for converting the input parameter in the interface to HTTP form. Sends it to the server, parses the HTTP response, converts the result into a Java object, and returns it.

The native FeIGN will help us generate the proxy object, which is the body of the method we call, and the proxy object has the ability to request HTTP requests. So Spring will try to put this kind of object into the context of Spring. So the next time we call it, this object will of course have HTTP request capability.