"Principio de SpringCloud" Componentes principales de la cinta y principios operativos Análisis del código fuente de Wanzi
Hola a todos, en este artículo continuaré analizando el código fuente del componente de equilibrio de carga Ribbon en Spring Cloud. Originalmente, planeé continuar con el artículo sobre generación dinámica de proxy de OpenFeign y hablar directamente sobre cómo Feign integra Ribbon, pero a la mitad de la escritura del artículo, descubrí que si Ribbon no se explica claramente, será difícil entender algunos de los detalles de Ribbon. , Así que todavía planeo hacerlo solo. Escribe un artículo para analizar el código fuente de Ribbon, de modo que cuando hable sobre la integración de Ribbon con Feign, no entre en detalles. Bien, sin más, vayamos directo al tema.
Esto es algo muy simple. Es una encapsulación de datos de la instancia de servicio, que encapsula la IP y el puerto de la instancia de servicio. Si un servicio tiene muchas máquinas, entonces habrá muchos objetos de servidor.
ServerList es una interfaz y su tipo genérico es Server. Proporciona dos métodos, los cuales se utilizan para obtener una lista de instancias de servicio. De hecho, estos dos métodos se implementan de la misma manera en muchas implementaciones. clases, y no hay diferencia. Esta interfaz es muy importante, porque esta interfaz es la interfaz de origen para que Ribbon obtenga datos de servicio. La lista de servicios para el equilibrio de carga de Ribbon llega a través de esta interfaz. Luego, puede pensar si puede proporcionar datos de servicio a Ribbon simplemente implementando esta interfaz. . De hecho, este es el caso en Spring Cloud, los centros de registro como eureka y nacos implementan esta interfaz y proporcionan los datos de la instancia de servicio del centro de registro a Ribbon para el equilibrio de carga.
También puede saber por el nombre que se utiliza para actualizar los datos del registro de servicios. Tiene la única implementación, que es PollingServerListUpdater. Esta clase tiene un método central, que es start. una mirada a la implementación de start.
De este método, podemos ver que primero, isActive.compareAndSet(false, true) se usa para garantizar que este método solo se llamará una vez, y luego se encapsula un Runnable, y este Runnable hace un Lo principal es llamar al método doUpdate de la acción de actualización entrante y luego lanzar el Runnable al grupo de subprocesos con la función de programación de tiempo. Después del tiempo inicialDelayMs (1 s predeterminado), se llamará una vez y luego se llamará. llamó a cada refrescoIntervalMs (30 segundos predeterminados). El método de ejecución de Runnable es el método doUpdate que llama a updateAction.
Entonces, la función principal de esta clase es llamar a la implementación del método doUpdate de la acción de actualización entrante cada 30 segundos.
IRule es responsable del algoritmo de equilibrio de carga, es decir, la implementación real del equilibrio de carga y la obtención de una instancia de servicio es la implementación de esta interfaz. Por ejemplo, implementar la clase RandomRule consiste en seleccionar aleatoriamente una instancia de servicio entre un montón de instancias de servicio.
Es una interfaz de configuración, con una implementación por defecto de DefaultClientConfigImpl, a través de la cual se pueden obtener algunas configuraciones del Ribbon.
La función de esta interfaz es proporcionar la función de obtener la lista de instancias de servicio y seleccionar la instancia de servicio. Aunque proporciona principalmente la función de obtener servicios para el mundo exterior, cuando se implementa, se utiliza principalmente para coordinar los componentes centrales mencionados anteriormente, de modo que puedan coordinar su trabajo, realizando así la función de obtener instancias de servicio.
Existen varias clases de implementación para la implementación de esta interfaz, pero hablaré de dos de las más importantes.
BaseLoadBalancer
Atributos principales
allServerList: almacena en caché todos los datos de la instancia de servicio
upServerList: almacena en caché los datos de la instancia de servicio utilizables.
regla: componente del algoritmo de equilibrio de carga, el valor predeterminado es RoundRobinRule
método principal
setRule: este método establece el algoritmo de equilibrio de carga y establece el conjunto de objetos ILoadBalancer actual. a IRule, de esto podemos concluir que la lista de instancias de servicio para el equilibrio de carga de IRule se obtiene a través de ILoadBalancer, es decir, IRule e ILoadBalancer se refieren entre sí. setRule(regla) generalmente se llama al construir un objeto.
elegirServidor: Consiste en seleccionar una instancia de servicio y delegarla al método de elección de IRule para realizar la selección de la instancia de servicio.
En términos generales, la clase de implementación BaseLoadBalancer ha implementado las funciones de ILoadBalancer, por lo que esto es básicamente suficiente para su uso.
Después de hablar de la clase de implementación BaseLoadBalancer, hablemos de la clase de implementación DynamicServerListLoadBalancer. DynamicServerListLoadBalancer hereda de BaseLoadBalancer, y DynamicServerListLoadBalancer amplía principalmente las funciones de BaseLoadBalancer.
DynamicServerListLoadBalancer
Variables miembro
serverListImpl: Como se mencionó anteriormente, la lista de servicios se obtiene a través de esta interfaz
filtro: juega un filtrado role, generalmente no me importa
updateAction: es una clase interna anónima que implementa el método doUpdate y llamará al método updateListOfServers
serverListUpdater: como se mencionó anteriormente, el valor predeterminado es el único clase de implementación PollingServerListUpdater, también Es decir, el método doUpdate de la updateAction entrante se llamará cada 30 segundos.
¿No es esto una coincidencia? El método de inicio de serverListUpdater requiere una updateAction. Da la casualidad de que la variable miembro tiene una implementación de clase interna anónima de updateAction, por lo que la implementación de updateAction se pasa al inicio. El método de serverListUpdater es en realidad esta clase interna anónima.
Entonces, ¿dónde se llama al método de inicio de serverListUpdater y se pasa updateAction? Se llama durante la construcción. El enlace de llamada específico es llamar a restOfInit -gt; enableAndInitLearnNewServersFeature(). El código fuente no se publicará aquí.
Por lo tanto, de hecho, una vez completada la construcción de DynamicServerListLoadBalancer, de forma predeterminada se reiniciará cada 30 segundos, se llamará al método doUpdate de la clase interna anónima de updateAction, que llamará a updateListOfServers. Así que echemos un vistazo a lo que hace el método updateListOfServers.
La implementación de este método es muy simple: obtener un lote de datos de instancia de servicio llamando a getUpdatedListOfServers de ServerList, luego filtrarlo y finalmente llamar al método updateAllServerList para ingresar al método updateAllServerList.
De hecho, es muy simple: simplemente llame al método setAlive de cada instancia de servicio, establezca isAliveFlag en verdadero y luego llame a setServersList. La función principal del método setServersList es actualizar la instancia de servicio al caché interno, que es allServerList y upServerList mencionados anteriormente. El código fuente no se publicará aquí.
De hecho, después de analizar el método updateListOfServers y combinarlo con el análisis del código fuente anterior, podemos sacar claramente una conclusión: de forma predeterminada, los datos de la instancia de servicio se obtendrán nuevamente a través de ServerList. componente cada 30 segundos y luego se actualiza a En la caché de BaseLoadBalancer, los datos de la instancia de servicio necesarios para el equilibrio de carga de IRule son esta caché interna.
También se puede ver en el nombre de DynamicServerListLoadBalancer que, en comparación con la clase principal BaseLoadBalancer, proporciona la función de actualizar dinámicamente la lista de instancias de servicios internos.
Para que sea más fácil de recordar para todos, hice un dibujo para describir la relación entre estos componentes y cómo funcionan.
Después de hablar sobre algunos componentes principales y su relación con ILoadBalancer, analicemos cómo se usa ILoadBalancer en la cinta.
ILoadBalancer es un componente que puede obtener datos de la instancia de servicio. Entonces, ¿con qué está relacionada la instancia de servicio? Debe estar relacionada con la solicitud, por lo que existe una clase abstracta en la cinta, AbstractLoadBalancerAwareClient, que es utilizado para ejecutar Según lo solicitado, echemos un vistazo a la estructura de esta clase.
Como se puede ver en lo anterior, es necesario pasar un ILoadBalancer durante la construcción.
Hay un método ejecutarWithLoadBalancer en AbstractLoadBalancerAwareClient, que se utiliza para ejecutar solicitudes entrantes de forma de equilibrio de carga.
Este método crea un LoadBalancerCommand, luego llama al método de envío y pasa una clase interna anónima. Esta línea de código en esta clase interna anónima es muy importante.
Esta línea de código reconstruye el URI en función de un servidor determinado. ¿Qué significa esto? Por ejemplo, en el artículo de OpenFeign, dije que una dirección similar a http:// ServerA se uniría según el nombre del servicio. En ese momento, no había ninguna dirección IP del servidor, solo el nombre del servicio. que la dirección solicitada es http: // ServerA/api/sayHello, entonces una cosa que reconstructURIWithServer hace es reemplazar el nombre del servicio ServerA con la IP y el puerto de la máquina donde se encuentra el servicio real. Supongamos que ServerA está ubicado en una máquina. (El servidor encapsula la IP de una determinada máquina) y el puerto) es 192.168.1.101:8088, luego la dirección reconstruida se convierte en http://192.168.1.101:8088/api/sayHello, para que se pueda enviar una solicitud http a un servidor correspondiente al servicio ServerA.
Luego, según la nueva dirección, se llama al método de ejecución de esta clase para ejecutar la solicitud. El método de ejecución es un método abstracto, lo que significa que se entrega a la subclase para su implementación. Puede enviar http implementando este método Solicitud, implemente la llamada rpc.
Entonces, ¿de dónde lo sacó este servidor? De hecho, puede adivinar que debe obtenerse a través de ILoadBalancer, porque el método de envío es relativamente largo. Aquí publicaré directamente parte del código central del método de envío
Selecciona un servidor a través de selectServer. Seleccionaré Servidor. Sin revisar el código fuente, de hecho, finalmente se llama al método selectServer de ILoadBalancer para obtener un servicio. Luego se llama al método de la clase interna anónima mencionada anteriormente para reconstruir el URI y luego se entrega. Vaya al método de ejecución de la subclase para enviar la pregunta http.
Por lo tanto, a través del método ejecutarWithLoadBalancer de AbstractLoadBalancerAwareClient, podemos saber que la función principal de esta clase abstracta es encontrar un Servidor adecuado a través del algoritmo de equilibrio de carga y luego enviar la ruta de solicitud que pasó en http: // ServerA/api/sayHello se reconstruye para que sea similar a http://192.168.1.101:8088/api/sayHello Luego se llama al método de ejecución implementado por la subclase para enviar una solicitud http.
De hecho, casi he hablado sobre los componentes principales y los principios de ejecución de Ribbon hasta este punto. Permítanme hacer un dibujo para resumir.
Después de hablar sobre algunos de los principales. Componentes y principios de ejecución de Ribbon. Echemos un vistazo a las implementaciones que utilizan estos componentes en el entorno Spring Cloud. Después de todo, existen interfaces de tiempo de escritura y algunas son clases abstractas.
Clase de ensamblaje automático de Ribbon: RibbonAutoConfiguration, saqué el código fuente principal
Hay una anotación @RibbonClients en la clase de configuración RibbonAutoConfiguration A continuación, explicaré la función de esta anotación. .
¿SpringClientFactory se siente muy similar a FeignContext en OpenFeign? De hecho, las dos funciones son las mismas. SpringClientFactory también hereda NamedContextFactory, realiza el aislamiento de la configuración y también pasa el valor predeterminado de cada contenedor en la construcción. Método de clase de configuración RibbonClientConfiguration. En cuanto a qué es el aislamiento de configuración, dije en el artículo de OpenFeign que aquellos que no lo tienen claro pueden responder a feign01 en segundo plano para obtener el enlace del artículo.
Problema de prioridad de configuración
La prioridad más baja es la clase de configuración pasada durante la construcción de FeignContext y SpringClientFactory
En cuanto a cómo proviene la prioridad, es en realidad, al construir AnnotationConfigApplicationContext en el método createContext de NamedContextFactory, se pasa uno por uno de acuerdo con la prioridad configurada.
Los beans predeterminados proporcionados por RibbonClientConfiguration
A continuación, echemos un vistazo a los beans predeterminados proporcionados por RibbonClientConfiguration
Los beans correspondientes a la clase de configuración, aquí ConnectTimeout está configurado y ReadTimeout son ambos 1.
IRule, el valor predeterminado es ZoneAvoidanceRule, esta regla tiene una función de filtrado, filtra los servicios de particiones no disponibles (este filtrado no necesita cuidados), después de que el filtrado sea exitoso, continúe usando el sondeo lineal para filtrar Elija uno de los resultados. En cuanto a propertiesFactory, puede dejarlo como está. Esta es la configuración predeterminada en el archivo de configuración. Generalmente no necesita preocuparse si la ve más tarde.
En cuanto a por qué el contenedor elige NacosServerList en lugar de ConfigurationBasedServerList, se debe principalmente a que la clase de configuración NacosRibbonClientConfiguration se importa a través de @RibbonClients, lo que significa que tiene una prioridad más alta que la clase de configuración RibbonClientConfiguration importada por SpringClientFactory.
ServerListUpdater es el PollingServerListUpdater que analizamos. De forma predeterminada, actualiza el caché del servicio interno de BaseLoadBalancer cada 30 segundos.
Luego, en SpringCloud, el centro de registro se puede agregar a la imagen de arriba.
3. Resumen
Este artículo analiza el código fuente de algunos componentes principales en el componente de equilibrio de carga Ribbon, describe claramente las relaciones entre estos componentes uno por uno y también analiza cómo para obtener una instancia de servicio a través de ILoadBalancer y reconstruir el URI al enviar una solicitud. Espero que este artículo pueda permitirle saber cómo funciona Ribbon.