¡El principio central de Feign en SpringCloud es simple y fácil de entender!

Fingir principios básicos en SpringCloud

Si no comprende los principios básicos de Feign en Spring Cloud, y realmente no comprende la optimización del rendimiento y la optimización de la configuración de Spring Cloud, es imposible dominar realmente Spring Cloud.

Este capítulo comienza con los componentes importantes de las llamadas remotas de Feign, presenta el proceso de ejecución de las llamadas remotas de Feigh, el proceso de creación de la instancia de proxy JDK local de Feign e interpreta a fondo el conocimiento básico de SpringCloud para todos. Para que la mayoría de ingenieros no solo sepan lo que está pasando, sino que también sepan por qué.


El proceso básico de la llamada remota de Fingir

Fingir llamada remota, el núcleo es a través de una serie de encapsulación y procesamiento, la interfaz API de llamada remota definida en la anotación JAVA se convierte finalmente en un formulario de solicitud HTTP, y luego el resultado de la respuesta de la solicitud HTTP se decodifica en JAVA Bean, y luego Volver a llamador. El proceso básico de la llamada remota de Fingir es aproximadamente como se muestra en la siguiente figura.
Inserte la descripción de la imagen aquí
Como puede ver en la figura anterior, Feign modeló la solicitud mediante el procesamiento de anotaciones. Cuando realmente se llama, los parámetros se pasan y luego se aplican a la solicitud de acuerdo con los parámetros, y luego se convierten en una solicitud de solicitud real. A través del mecanismo de proxy dinámico de Feign y JAVA, los desarrolladores de Java pueden completar llamadas HTTP de servicios remotos sin utilizar marcos HTTP para encapsular los mensajes de solicitud HTTP.

Componentes importantes de las llamadas remotas de Fingir

Cuando se inicia el microservicio, Feign escaneará el paquete y creará una instancia de proxy JDK Proxy local para la interfaz remota de acuerdo con las reglas de anotación para la interfaz anotada con @FeignClient. Luego, inyecte estas instancias de proxy proxy local en el contenedor Spring IOC. Cuando se llama al método de interfaz remota, la instancia del proxy Proxy completará el acceso remoto real y devolverá el resultado.

Para presentar claramente el mecanismo operativo y los principios de Feign en SpringCloud, aquí primero clasificaré varios componentes importantes en Feign.

Instancia de proxy JDK Proxy local para interfaz remota

La instancia de proxy JDK Proxy local de la interfaz remota tiene las siguientes características:

(1) instancia de proxy proxy, que implementa una interfaz de llamada remota anotada con @FeignClient;

(2) instancia de proxy proxy, que puede encapsular solicitudes HTTP y enviar solicitudes HTTP internamente;

(3) Instancia de proxy proxy, que puede procesar la respuesta de una solicitud HTTP remota, completar la decodificación del resultado y luego devolverlo a la persona que llama.

Tomemos una sencilla interfaz de llamada de servicio remoto DemoClient como ejemplo para presentar específicamente el proceso de creación de la instancia local de proxy JDK Proxy de la interfaz remota.

La interfaz DemoClient tiene dos métodos abstractos muy simples para llamadas remotas: uno es el método abstracto hello (), que se utiliza para completar la solicitud HTTP de la URL remota "/ api / demo / hello / v1"; el otro es el eco (...) método abstracto, utilizado para completar la solicitud HTTP de la URL remota "/ api / demo / echo / {word} / v1". Los detalles se muestran en la figura siguiente.

Inserte la descripción de la imagen aquí
El código de la interfaz DemoClient es el siguiente:

package com.crazymaker.springcloud.demo.contract.client;
//…省略import

@FeignClient(
        value = "seckill-provider", path = "/api/demo/",
        fallback = DemoDefaultFallback.class)
public interface DemoClient {
    
    

    /**
     * 测试远程调用
     *
     * @return hello
     */
    @GetMapping("/hello/v1")
    Result<JSONObject> hello();


    /**
     * 非常简单的一个 回显 接口,主要用于远程调用
     *
     * @return echo 回显消息
     */
    @RequestMapping(value = "/echo/{word}/v1", method = RequestMethod.GET)
    Result<JSONObject> echo(
            @PathVariable(value = "word") String word);

}

Tenga en cuenta que en el código anterior, la anotación @FeignClient se agrega a la interfaz DemoClient. En otras palabras, cuando se inicia Feign, creará una instancia de proxy JDK Proxy local para ella y la registrará con el contenedor Spring IOC.

¿Cómo usarlo? Puede encontrar la instancia de proxy del contenedor Spring IOC de acuerdo con el tipo de coincidencia (aquí el tipo es el tipo de interfaz DemoClient) a través de la anotación @Resource y ensamblarlo con las variables de miembro requeridas.

El código utilizado por la instancia de proxy JDK Proxy local de DemoClient es el siguiente:

package com.crazymaker.springcloud.user.info.controller;
//…省略import
@Api(value = "用户信息、基础学习DEMO", tags = {
    
    "用户信息、基础学习DEMO"})
@RestController
@RequestMapping("/api/user")
public class UserController {
    
    

    @Resource
DemoClient demoClient;  //装配 DemoClient 的本地代理实例

    @GetMapping("/say/hello/v1")
    @ApiOperation(value = "测试远程调用速度")
    public Result<JSONObject> hello() {
    
    
        Result<JSONObject> result = demoClient.hello();
        JSONObject data = new JSONObject();
        data.put("others", result);
        return Result.success(data).setMsg("操作成功");
    }
//…
}

El proceso de creación de la instancia de proxy JDK Proxy local de DemoClient es más complicado y se presentará más adelante. Veamos primero otros dos componentes lógicos importantes.


Controlador de invocación InvocationHandler

Como todos saben, el paso principal para generar clases de proxy dinámicas a través de JDK Proxy es personalizar un controlador de invocación. Específicamente, es implementar la interfaz del controlador de invocación InvocationHandler en el paquete java.lang.reflect en el JDK, e implementar la invocación de esta interfaz. (...) Métodos abstractos.

Para crear una clase de implementación de proxy para la interfaz remota de Feign, Feign proporciona su propio controlador de invocación predeterminado llamado clase FeignInvocationHandler, que se encuentra en el paquete jar del núcleo de feign-core. Por supuesto, el procesador de llamadas puede ser reemplazado. Si se usa Feign en combinación con Hystrix, será reemplazado con la clase de procesador de llamadas HystrixInvocationHandler, que está en el paquete jar feign-hystrix.

Inserte la descripción de la imagen aquí

El controlador de llamadas predeterminado FeignInvocationHandler

El controlador de invocación predeterminado FeignInvocationHandler es una clase relativamente simple, con una asignación de despacho de miembros de tipo de mapa muy importante, que contiene la asignación de métodos de interfaz remota a los controladores de métodos MethodHandler.

Tomando la interfaz DemoClient en el ejemplo anterior como ejemplo, el diagrama de estructura de memoria del miembro de despacho de FeignInvocationHandler de la clase de implementación de proxy se muestra en la Figura 3.
Inserte la descripción de la imagen aquí
El controlador de invocación predeterminado FeignInvocationHandle, cuando procesa invocaciones de métodos remotos, encontrará el controlador de método MethodHandler correspondiente en el objeto de asignación de despacho de acuerdo con la instancia del método de reflexión de Java, y luego lo entregará al MethodHandler para completar la solicitud HTTP real y el procesamiento de resultados. La interfaz de invocación remota DemoClient del ejemplo anterior tiene dos métodos de invocación remota, por lo que el miembro de despacho de FeignInvocationHandler, el manejador de invocación de la clase de implementación del proxy, tiene dos pares clave-valor.

El código fuente clave de FeignInvocationHandler es el siguiente:

package feign;
//...省略import

public class ReflectiveFeign extends Feign {
    
    

  //...

  //内部类:默认的Feign调用处理器 FeignInvocationHandler
  static class FeignInvocationHandler implements InvocationHandler {
    
    

    private final Target target;
    //方法实例对象和方法处理器的映射
    private final Map<Method, MethodHandler> dispatch;

    //构造函数    
    FeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) {
    
    
      this.target = checkNotNull(target, "target");
      this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);
    }

    //默认Feign调用的处理
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
      //...
	  //首先,根据方法实例,从方法实例对象和方法处理器的映射中,
	  //取得 方法处理器,然后,调用 方法处理器 的 invoke(...) 方法
         return dispatch.get(method).invoke(args);
    }
    //...
  }
 

El código fuente es muy simple, y el foco está en el método invoke (...). Aunque el código central es solo una línea, su función es complicada:

(1) De acuerdo con la instancia del método de reflexión de Java, busque el manejador del método MethodHandler correspondiente en el objeto de mapeo de despacho;

(2) Invoque el método invoke (...) del procesador del método MethodHandler para completar la solicitud HTTP real y el procesamiento de resultados.

Explicación adicional: el procesador del método MethodHandler no tiene una relación de herencia o implementación con la interfaz del procesador de llamadas InvocationHandler ubicada en el paquete java.lang.reflect en el mecanismo de proxy dinámico JDK. MethodHandler es simplemente Fingir personalizado, una interfaz muy simple.


** MethodHandler MethodHandler **

El manejador de métodos de Feign MethodHandler es una interfaz independiente, definida en la interfaz InvocationHandlerFactory, con un solo método invoke (...). El código fuente es el siguiente:

//定义在InvocationHandlerFactory接口中
public interface InvocationHandlerFactory {
    
    
  //…

 //方法处理器接口,仅仅拥有一个invoke(…)方法
  interface MethodHandler {
    
    
    //完成远程URL请求
    Object invoke(Object[] argv) throws Throwable;
  }
//...
}

El método invoke (...) de MethodHandler es el principal responsable de completar la solicitud de URL remota real y luego devolver el resultado de respuesta de la URL remota decodificada. Feign proporciona la clase de implementación SynchronousMethodHandler predeterminada, que proporciona procesamiento de solicitud de sincronización de URL remota básica. Acerca de la clase SynchronousMethodHandler y su relación con MethodHandler, aproximadamente como se muestra en la Figura 4.

Inserte la descripción de la imagen aquí
Para comprender a fondo el procesador de métodos, lea el código fuente del procesador de métodos SynchronousMethodHandler, que es aproximadamente el siguiente:

package feign;
//…..省略import
final class SynchronousMethodHandler implements MethodHandler {
    
    
  //…
  // 执行Handler 的处理
public Object invoke(Object[] argv) throws Throwable {
    
    
        RequestTemplate requestTemplate = this.buildTemplateFromArgs.create(argv);
        Retryer retryer = this.retryer.clone();

        while(true) {
    
    
            try {
    
    
                return this.executeAndDecode(requestTemplate);
            } catch (RetryableException var5) {
    
    
               //…省略不相干代码
            }
        }
}

  //执行请求,然后解码结果
Object executeAndDecode(RequestTemplate template) throws Throwable {
    
    
        Request request = this.targetRequest(template);
        long start = System.nanoTime();
        Response response;
        try {
    
    
            response = this.client.execute(request, this.options);
            response.toBuilder().request(request).build();
        }
}
}

El método invoke (...) de SynchronousMethodHandler llama a su propio método executeAndDecode (...) para solicitar la ejecución y la decodificación de resultados. Los pasos de trabajo del método:

(1) Primero, solicite una instancia de plantilla a través de RequestTemplate para generar una solicitud de instancia de solicitud de URL remota;

(2) Luego use su propio miembro cliente fingido, ejecute (...) para ejecutar la solicitud y obtener la respuesta;

(3) Decodifica el resultado de la respuesta.


Fingir componente de cliente fingir Cliente

El componente de cliente es un componente muy importante en Feign, responsable de la ejecución de un extremo a otro de las solicitudes de URL. La lógica central: envía la solicitud al servidor y decodifica la respuesta después de recibir la respuesta.

La clase feign.Client es la interfaz de nivel superior que representa al cliente, con un solo método abstracto. El código fuente es el siguiente:

package feign;

/**客户端接口
 * Submits HTTP {@link Request requests}. 
Implementations are expected to be thread-safe.
 */
public interface Client {
    
    
  //提交HTTP请求,并且接收response响应后进行解码
  Response execute(Request request, Options options) throws IOException;

}

Debido a las diferentes clases de implementación de feign.Client, los componentes internos y las tecnologías para completar las solicitudes HTTP son diferentes, por lo que feign.Client tiene múltiples implementaciones diferentes. Aquí están algunos ejemplos:

(1) Clase Client.Default: la clase de implementación del cliente feign.Client predeterminada, utiliza internamente HttpURLConnnection para completar el procesamiento de solicitudes de URL;

(2) Clase ApacheHttpClient: clase de implementación del cliente feign.Client que utiliza internamente componentes de código abierto Apache httpclient para completar el procesamiento de solicitudes de URL;

(3) Clase OkHttpClient: clase de implementación del cliente feign.Client que utiliza internamente componentes de código abierto OkHttp3 para completar el procesamiento de solicitudes de URL.

(4) Clase LoadBalancerFeignClient: la clase de implementación del cliente feign.Client que utiliza internamente la tecnología de equilibrio de carga Ribben para completar el procesamiento de solicitudes de URL.

Además, existen algunas clases de implementación de cliente feign.Client que se utilizan en escenarios especiales, y también puede personalizar su propia clase de implementación feign.Client. La siguiente es una breve introducción a las diversas clases de implementación de clientes comunes anteriores.

Inserte la descripción de la imagen aquí
Uno: Cliente Clase predeterminada:

Como clase de implementación de la interfaz de cliente predeterminada, la clase HttpURLConnnection que viene con el JDK se usa dentro de Client.Default para implementar solicitudes de red URL.

Inserte la descripción de la imagen aquí
En JKD1.8, aunque se usa una tecnología de agrupación de conexiones HTTP muy simple en la parte inferior de HttpURLConnnection, su capacidad de reutilización de conexiones HTTP es en realidad muy débil y, por supuesto, su rendimiento es muy bajo. Por razones específicas, consulte el "Análisis en profundidad de Spring Cloud y conexiones largas" a continuación.


Dos: clase ApacheHttpClient

Dentro de la clase de cliente ApacheHttpClient, el componente de código abierto Apache HttpClient se utiliza para procesar solicitudes de URL.

Desde la perspectiva del desarrollo de código, en comparación con la URLConnection del JDK tradicional, Apache HttpClient ha aumentado la facilidad de uso y la flexibilidad. No solo facilita al cliente el envío de solicitudes Http, sino que también facilita a los desarrolladores probar la interfaz. No solo mejora la eficiencia del desarrollo, sino que también facilita la robustez del código.

Desde el punto de vista del rendimiento, Apache HttpClient tiene una función de grupo de conexiones y tiene excelentes capacidades de reutilización de conexiones HTTP. Con respecto al múltiplo de mejora del rendimiento de Apache HttpClient con grupo de conexiones, consulte la siguiente prueba comparativa para obtener más detalles.

La clase ApacheHttpClient está en el paquete jar especial de feign-httpclient. Si se usa, debe verterse en el paquete jar especial de la versión correspondiente a través de la dependencia de Maven u otros métodos.

Tres: clase OkHttpClient

Dentro de la clase de cliente OkHttpClient, el componente de código abierto OkHttp3 se utiliza para completar el procesamiento de solicitudes de URL. Square desarrolló el componente de código abierto OkHttp3 para reemplazar HttpUrlConnection y Apache HttpClient. Debido a que OkHttp3 es mejor compatible con el protocolo SPDY (SPDY es un protocolo de capa de transporte basado en TCP desarrollado por Google para minimizar el retraso de la red, aumentar la velocidad de la red y optimizar la experiencia de red de los usuarios), a partir de Android 4.4, Google ha comenzado a reemplazar HttpURLConnection clase de solicitud en el código fuente de Android con OkHttp. En otras palabras, para el desarrollo de aplicaciones móviles de Android, el componente OkHttp3 es uno de los componentes básicos de desarrollo.

Cuatro: clase LoadBalancerFeignClient

LoadBalancerFeignClient utiliza internamente la tecnología de equilibrio de carga del cliente Ribben para completar el procesamiento de solicitudes de URL. En principio, el modo de proxy de paquete delegado se utiliza simplemente: después de que el componente de equilibrio de carga de Ribben calcula un servidor servidor adecuado, el cliente de proxy delegado de paquete interno completa la solicitud HTTP al servidor servidor; la instancia de proxy de cliente delegado encapsulado El tipo puede ser Cliente .Cliente predeterminado predeterminado, clase de cliente ApacheHttpClient o clase de cliente de alto rendimiento OkHttpClient, u otros tipos de implementación de cliente feign.Client personalizados.

LoadBalancerFeignClient clase de implementación de cliente de equilibrio de carga, como se muestra en la figura siguiente.

Inserte la descripción de la imagen aquí


Feigh proceso de ejecución de llamadas remotas

Dado que existen varios controladores de llamadas InvokeHandler para la instancia de proxy JDK de la interfaz de llamada remota de Feign, el proceso de ejecución de la llamada remota de Feign es ligeramente diferente, pero los pasos principales del proceso de ejecución de la llamada remota son los mismos. Aquí presentamos principalmente el proceso de ejecución de llamadas remotas relacionado con el procesador de llamadas InvokeHandler de dos tipos de instancias de proxy JDK:

(1) El proceso de ejecución de llamadas remotas relacionado con el controlador de llamadas predeterminado FeignInvocationHandler;

(2) El proceso de ejecución de llamadas remotas relacionado con el procesador de llamadas Hystrix HystrixInvocationHandler.

En el proceso de introducción, el proceso de ejecución de la instancia de proxy dinámico remoto JDK Proxy de DemoClient anterior se toma como ejemplo para demostrar y analizar el proceso de ejecución de la llamada remota Feigh.

** Flujo de ejecución de llamadas remotas relacionado con FeignInvocationHandler **

FeignInvocationHandler es el controlador de invocación predeterminado. Si no se realiza una configuración especial para Feign, Feign utilizará este controlador de invocación. Combinado con el proceso de ejecución de llamadas remotas hello () de la instancia de proxy dinámico remoto JDK Proxy anterior de DemoClient, aquí, una introducción detallada al proceso de ejecución de llamadas remotas relacionado con FeignInvocationHandler se muestra aproximadamente en la siguiente figura.

Inserte la descripción de la imagen aquí
El proceso general de ejecución de llamadas remotas se divide aproximadamente en 4 pasos, de la siguiente manera:

Paso 1: ensamblar la instancia de proxy a través de la instancia del contenedor Spring IOC y luego realizar llamadas remotas.

Como se mencionó anteriormente, cuando se inicia Feign, creará una instancia de proxy JDK Proxy local para todas las interfaces remotas anotadas con @FeignClient (incluida la interfaz DemoClient) y la registrará con el contenedor Spring IOC. Aquí, llamaremos a esta instancia de proxy Proxy DemoClientProxy por el momento. Más adelante, presentaremos el proceso de creación específico de esta instancia de proxy Proxy en detalle.

Luego, en el código de llamada de UserController de esta instancia, use las anotaciones de @Resource para que coincidan de acuerdo con el tipo o nombre (aquí el tipo es el tipo de interfaz DemoClient), busque la instancia de proxy del contenedor Spring IOC y ensamble con el miembro donde se ubica la anotación @Resource Variable, el nombre de la variable miembro en este ejemplo es demoClient.

Cuando necesite realizar una llamada remota a hello (), puede llamar directamente al método hello () de la instancia de proxy dinámico JDK Proxy a través de la variable miembro demoClient.

Paso 2: Ejecute InvokeHandler para llamar al método invoke (...) del procesador

Como se mencionó anteriormente, el proceso de invocación del método real de la instancia de proxy dinámico JDK Proxy se completa específicamente con el procesador de invocación InvokeHandler. Por lo tanto, la instancia de proxy DemoClientProxy aquí llamará al método invoke (...) de la instancia del controlador de llamadas FeignInvocationHandler predeterminada.

A través de la introducción detallada del procesador de invocación FeignInvocationHandler, todo el mundo ya sabe que el procesador de invocación predeterminado FeignInvocationHandle mantiene una instancia de método de invocación remota y una asignación de valor-clave del procesador de métodos. FeignInvocationHandle en su método invoke (...) encontrará el procesador del método MethodHandler correspondiente en el objeto de mapeo de despacho de acuerdo con la instancia del método reflejada por Java, y luego este último completará la solicitud HTTP real y el procesamiento de resultados.

Entonces, en el paso 2, FeignInvocationHandle encontrará el controlador de método MethodHandler correspondiente al método hello () de su asignación de despacho, y luego llamará a su método invoke (...).

Paso 3: Ejecute el método invoke (...) del controlador de métodos MethodHandler

A través de la introducción muy detallada del componente sobre el procesador de métodos MethodHandler, todos saben que el procesador de métodos predeterminado de fingir es SynchronousMethodHandler, y su método invoke (...) es principalmente a través del miembro interno fingir cliente miembro cliente para completar la ejecución remota de la solicitud de URL Obtener remoto resultados.

Hay muchos tipos de clientes fingidos, y los diferentes tipos tienen diferentes formas específicas de completar el procesamiento de solicitudes de URL.

Paso 4: a través del miembro del cliente feign.Client, complete la ejecución remota de la solicitud de URL y obtenga resultados remotos

Si el cliente cliente en la instancia del procesador del método MethodHandler es la clase de implementación feign.Client.Default predeterminada, use la clase HttpURLConnnection que viene con el JDK para completar la ejecución de la solicitud de URL remota y obtener resultados remotos.

Si el cliente cliente en la instancia del procesador del método MethodHandler es una clase de implementación de cliente ApacheHttpClient, use el componente de código abierto Apache httpclient para completar la ejecución de la solicitud URL remota y obtener resultados remotos.

A través de los cuatro pasos anteriores, debería poder comprender claramente el proceso de ejecución de llamadas remotas fingidas y el mecanismo operativo en SpringCloud.

De hecho, para introducir el flujo de llamadas predeterminado de forma concisa, en realidad se omite un paso en el flujo anterior: el tercer paso se puede dividir en dos pequeños pasos. ¿por qué? SynchronousMethodHandler no completa directamente la solicitud de URL remota, pero a través del mecanismo de equilibrio de carga, localiza el servidor de servidor remoto apropiado y luego completa la solicitud de URL remota real. En otras palabras, el miembro cliente de la instancia SynchronousMethodHandler no es en realidad del tipo feign.Client.Default, sino del tipo de equilibrio de carga del cliente LoadBalancerFeignClient. Por lo tanto, el tercer paso anterior, si se subdivide aún más, es aproximadamente el siguiente: (1) Primero, a través de la instancia de cliente interna de SynchronousMethodHandler, que es esencialmente responsable de la instancia de LoadBalancerFeignClient de equilibrio de carga del cliente, primero busque el servidor servidor remoto; (2 ) Luego, la instancia de la clase interna feign.Client.Default envuelta por la instancia LoadBalancerFeignClient solicitará al servidor del lado del servidor que complete el procesamiento de la solicitud de URL.

Finalmente, explique que el proceso de ejecución de llamadas remotas predeterminado relacionado con FeignInvocationHandler no puede cumplir con los requisitos del entorno de producción en términos de mecanismo operativo y rendimiento de llamadas. Las principales razones son las siguientes:

(1) No hay ningún mecanismo de supervisión y recuperación de fusibles durante las llamadas remotas;

(2) Tampoco se utiliza la tecnología de grupo de conexiones HTTP de alto rendimiento.

Transferencia desde: https://www.cnblogs.com/crazymakercircle/p/11965726.html

Supongo que te gusta

Origin blog.csdn.net/qq_36551991/article/details/110572654
Recomendado
Clasificación