Red de conocimiento informático - Material del sitio web - Cómo aprender mejor el código fuente de Dubbo

Cómo aprender mejor el código fuente de Dubbo

1. La arquitectura general de Dubbo

1. La integración de Dubbo y Spring

Dubbo puede ser muy simple de usar. Tanto el proveedor como el consumidor se pueden configurar a través del archivo de configuración de Spring. Una vez completada la configuración, puede exponer y llamar a servicios como si usara

bean beans en

spring, sin ver la existencia de dubbo

api. Esto se debe a que Dubbo utiliza el soporte de configuración personalizada en modo extensible proporcionado por Spring. En el archivo de configuración de Spring, puede configurarlo así.

El archivo spring.handlers en META-INF especifica la clase de análisis xml de dubbo:

En el archivo spring.handlers en META-INF, se especifica la clase de análisis xml de dubbo:DubboNamespaceHandler.

Al igual que la clase anterior, DubboNamespaceHandler se resuelve en ServiceConfig, que a su vez se resuelve en ReferenceConfig, y así sucesivamente.

2. extensión jdk spi

Dado que Dubbo es un marco de código abierto, debe proporcionar una gran cantidad de puntos de extensibilidad. Dubbo logra escalabilidad al extender el mecanismo spi de jdk. Específicamente, en el directorio META-INF, coloque el nombre del archivo de la interfaz con el nombre completo. La clave y el valor en el archivo son pares clave-valor, y el valor es el nombre completo de la clase de implementación específica. valor de la bandera. Dado que dubbo adopta el diseño del bus de URL, muchos parámetros se pasan a través del objeto URL, por lo que en la operación real, el valor específico que se utilizará se puede especificar a través del parámetro

valor en la URL.

La extensión spi de Dubbo se implementa a través de ExtensionLoader. Al observar el código fuente de ExtensionLoader, puede ver que Dubbo ha extendido jdk spi en tres aspectos:

(1) solo jdk spi. obtiene todas las implementaciones a través del nombre de la clase de interfaz, mientras que ExtensionLoader obtiene una implementación a través del nombre de la clase de interfaz y el valor clave

(2) Implementación adaptativa, es decir, genera una clase proxy, de modo que Qué clase llamar; se determina dinámicamente en función de algunos parámetros en la llamada real.

(3) Implementación de empaquetado automático Esta clase de implementación generalmente se activa automáticamente y a menudo se usa para clases de empaquetado, como las dos clases de implementación de Protocolo: ProtocolFilterWrapper y ProtocolListenerWrapper.

3. Diseño del bus de URL

Dubbo adopta el diseño del bus de URL para desacoplar cada capa. Nuestro diseño habitual convertirá los parámetros de interacción entre capas en un modelo, por lo que el costo de comunicación entre capas será relativamente grande y será más problemático expandirlo.

Por lo tanto, Dubbo utiliza la forma de URL para realizar la comunicación entre capas. Por ejemplo, cuando se inicia el registro, la URL del parámetro es:

registry://0.0.0.0:9090?codec=registryamp;transporter=netty

Esto significa que el registro actual es , todos los enlaces son IP, el puerto es 9090, el tipo de analizador es registro y el marco de comunicación de red subyacente es netty.

2. Proceso de inicio de Dubbo

Dubbo se divide en tres partes: registro, proveedor de servicios (proveedor) y consumidor de servicios (consumidor).

1. Inicie el centro de registro

El proceso de inicio del centro de registro depende principalmente de dos clases: RegistrySynchronizer y RegistryReceiver. Los métodos de inicialización de estas dos clases son el inicio.

Método de inicio de RegistrySynchronizer:

(1) Cargue toda la información de configuración en la memoria

(2) Guarde la información del registro actual en la base de datos;

(3) Inicia 5 temporizadores.

Las funciones de los 5 temporizadores son las siguientes:

(1) AutoRedirectTask, temporizador de redirección automática. Se ejecuta una vez por hora de forma predeterminada. Si el registro actual tiene más de 1,2 veces el número promedio de conexiones, las conexiones sobrantes se redirigirán a otros registros para equilibrar el número de conexiones en el grupo de registros.

(2) DirtyCheckTask, temporizador de verificación de datos sucios. Su función es: verificar proveedores de caché, proveedores de bases de datos, consumidores de caché, bases de datos

datos del consumidor, borrar datos sucios de proveedores y consumidores no viables para proveedores de caché o consumidores y cantidad

;

Si la base de datos no existe, regístrese nuevamente.

La base de datos no existe, por favor regístrate y suscríbete nuevamente.

(3) ChangedClearTask, que es la tarea de limpieza programada de la tabla de cambios. Su función es leer la tabla de cambios y borrar los datos caducados.

(4) AlivedCheckTask es una tarea de verificación programada para el estado de supervivencia del registro. Actualiza el campo de vencimiento del registro para determinar el estado de supervivencia del registro. Si hay un nuevo registro, se enviará un mensaje de sincronización para notificar a todos los clientes las direcciones de todos los registros actuales.

(5) ChangedCheckTask. Consulte la tabla de cambios para ver los cambios. Los tipos de verificación incluyen: cambios de cobertura de parámetros, cambios de ruta, cambios de consumidor de servicios, cambios de peso y cambios de equilibrio de carga.

Cómo iniciar RegistryReceiver: Inicie el servicio de registro. De forma predeterminada utiliza el marco netty y se vincula al puerto local 9090. NettyServer completa el proceso final de inicio del servicio. Al recibir mensajes, independientemente del decodificador del protocolo dubbo, el orden en que se llaman las clases es

NettyHandler-"NettyServer-"MultiMessageHandler-"HeartbeatHandler-"AllDispatcher-"

DecodeHandler -" HeaderExchangeHandler-"RegistryReceiver-"RegistryValidator-"RegistryFailover-"RegistryExecutor.

2. Inicio del proveedor

El proceso de inicio del proveedor consiste en exportar el método desde ServiceConfig. Los pasos específicos son:

(1) Se expone la jvm local. y no abre ningún El puerto se llama en forma de injvm. Esta llamada es solo una llamada local y no implica comunicación entre procesos.

(2) Llame a la exportación de RegistryProtocol.

(3) Llame a la exportación de DubboProtocol. El protocolo abre el puerto 20880 de forma predeterminada para proporcionar servicios para recibir llamadas remotas de los consumidores.

(4) Cree una conexión al registro creando un nuevo RemoteRegistry.

(5) Registrar la dirección del servicio en el registro.

(6) Accede al registro y suscríbete al servicio.

3. Proceso de inicio del consumidor

El proceso de inicio del consumidor se lleva a cabo mediante el método get de ReferenceConfig. Los pasos específicos son los siguientes:

(1) Al crear un nuevo RemoteRegistry se establece una conexión con el registro.

(2) Cree un nuevo Directorio de Registro y suscríbase al registro. RegistryDirectory se utiliza para mantener la información de servicio obtenida del registro.

(3) Cree una clase de proxy. Cuando se inicia la llamada remota del consumidor, en realidad se llama a InvokerInvocationHandler.

3. Proceso de llamada real

Cuando el consumidor inicia una llamada, la llamada real realizada a través de la clase es:

1. Consumidor:

InvokerInvocationHandler. p>

InvokerInvocationHandler-"MockClusterInvoker (si Mock está configurado, llame directamente a la clase Mock local)-"FailoverClusterInvoker (equilibrio de carga, mecanismo de tolerancia a fallas, reintente dos veces de forma predeterminada cuando ocurre un error)-"RegistryDirectory $InvokerDelegete- "ConsumerContextFilter-"FutureFilter-gt;DubboInvoker

NettyServer-"MultiMessageHandler-HeartbeatHandler-"Todos los despachadores-"DecodeHandler-" HeaderExchangeHandler-"Manejador de mensajes múltiples DubboProtocol.requestHandler-"Manejador de solicitudes de protocolo Dubo EchoFilter -" Filtro de eco ClassLoaderFilter 1, modo de fábrica

ServiceConfig tiene un campo, el código es el siguiente:

protocolo final estático privado = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension( );

Hay mucho código como este en Dubbo. Este también es un patrón de fábrica, excepto que la clase de implementación se obtiene utilizando el mecanismo jdk

spi. La ventaja de esta implementación es que es altamente extensible; si desea extender la implementación, simplemente agregue un archivo en el classpath sin intrusión de código. Además, al igual que la implementación adaptativa

anterior, puede decidir dinámicamente a qué implementación llamar llamando, pero debido a que esta implementación utiliza un proxy dinámico, la depuración del código será más problemática y deberá analizar la clase de implementación de llamada real.

2. Modo decorador

Dubbo utiliza ampliamente el modo decorador tanto en la fase de inicio como en la de llamada.

Para ser más precisos, hay una mezcla de patrones de decorador y cadena de responsabilidad. Por ejemplo, la función de EchoFilter es determinar si se trata de una solicitud de prueba de eco y, de ser así, devolver el contenido directamente. Esta es una forma de cadena de responsabilidad.

.

ClassLoaderFilter solo agrega la función de cambiar el ClassLoader del hilo actual en la función principal, que es un patrón decorador típico.

3. Modo observador

Cuando se inicia el proveedor de Dubbo, necesita interactuar con el registro, primero registrar su propio servicio y luego suscribirse a su propio servicio. modo observador, abre un oyente.

El registro comprobará periódicamente si el servicio se actualiza cada 5 segundos. Si hay una actualización, se enviará una notificación al proveedor del servicio. Si hay una actualización, se envía un mensaje de notificación al proveedor del servicio. Después de que el proveedor reciba el mensaje de notificación, ejecutará el método de notificación

NotifyListener, que ejecutará el método de escucha.

4. Modo proxy dinámico

Dubbo extiende jdk

La implementación adaptativa ExtensionLoader de la clase spi es una implementación de proxy dinámico típica. Dubbo necesita controlar de manera flexible la implementación de clases, es decir, decidir dinámicamente qué clase de implementación llamar en función de los parámetros durante la fase de llamada. Por lo tanto, primero se utiliza el método de generar una clase proxy. El método de generación de clases de proxy se utiliza para lograr llamadas flexibles. El código que genera la clase proxy es el método

createAdaptiveExtensionClassCode de ExtensionLoader. La lógica principal de la clase proxy es obtener el valor del parámetro especificado en el parámetro URL como clave para obtener la clase de implementación.