Dubbo Learning Record (17) - Invocación de servicio [3] - Empaquetado de Invoker en el lado del consumidor del servicio

Empaquetado de Invoker en el lado del consumidor del servicio

El Invoker en el lado del consumidor del servicio involucra el proceso de exportación del servicio, y el método ReferenceConfigde#get() genera un Invoker de instancia de proxy para regresar; el propósito de este tiempo es
borrar todo el enlace de empaquetado;

ReferenciaConfigde#get()

La lógica exportada está toda en el método init;

    public synchronized T get() {
    
    
        checkAndUpdateSubConfigs();
        if (ref == null) {
    
    
            // 入口
            init();
        }
        return ref;  // Invoke代理
    }
    //下一层createProxy(map)
    private void init() {
    
    

        Map<String, String> map = new HashMap<String, String>();
		//省略参数获取过程;
        // 得到一个代理对象
        ref = createProxy(map);
    }
	
     private T createProxy(Map<String, String> map) {
    
    
        if (shouldJvmRefer(map)) {
    
    
        //省略部分代码
        } else {
    
    
            // @Reference中指定了url属性
            if (url != null && url.length() > 0) {
    
    
            //省略部分代码
                }
            } else {
    
    
      		 // 加载注册中心地址,省略部分代码
            }

            // 如果只有一个url则直接refer得到一个invoker
            if (urls.size() == 1) {
    
    
                // RegistryProtocol.refer() 或者 DubboProtocol.refer()
                invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));
            } else {
    
    
                // 如果有多个url
                // 1. 根据每个url,refer得到对应的invoker
                // 2. 如果这多个urls中存在注册中心url,则把所有invoker整合为RegistryAwareClusterInvoker,该Invoker在调用时,会查看所有Invoker中是否有默认的,如果有则使用默认的Invoker,如果没有,则使用第一个Invoker
                // 2. 如果这多个urls中不存在注册中心url,则把所有invoker整合为FailoverCluster

                List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
                URL registryURL = null; // 用来记录urls中最后一个注册中心url
                for (URL url : urls) {
    
    
                    invokers.add(REF_PROTOCOL.refer(interfaceClass, url));

                    if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {
    
    
                        registryURL = url; // use last registry url
                    }
                }

                // 如果存在注册中心地址
                if (registryURL != null) {
    
     // registry url is available
                    // use RegistryAwareCluster only when register's CLUSTER is available
                    URL u = registryURL.addParameter(CLUSTER_KEY, RegistryAwareCluster.NAME);
                    // StaticDirectory表示静态服务目录,里面的invokers是不会变的, 生成一个RegistryAwareCluster
                    // The invoker wrap relation would be: RegistryAwareClusterInvoker(StaticDirectory) -> FailoverClusterInvoker(RegistryDirectory, will execute route) -> Invoker
                    invoker = CLUSTER.join(new StaticDirectory(u, invokers));
                } else {
    
     // not a registry url, must be direct invoke.
                    // 如果不存在注册中心地址, 生成一个FailoverClusterInvoker
                    invoker = CLUSTER.join(new StaticDirectory(invokers));
                }
            }
        }
        return (T) PROXY_FACTORY.getProxy(invoker);
    }

  1. Primero, juzgará si solo hay una URL de acuerdo con las URL configuradas
  2. Si solo hay uno, llame directamente a REF_PROTOCOL.refer(interfaceClass, urls.get(0)) para crear un Invoker, llame a la fábrica de proxy para devolver la instancia de proxy, y el retorno es un invocador de instancia de FailoverClusterInvoker;
  3. Si hay varias URL, llame a REF_PROTOCOL.refer(interfaceClass, urls.get(0)) para cada URL a fin de crear una instancia de Invoker,
    luego determine si hay una URL de registro
    3.1, establezca CLUSTER_KEY de la URL en Registryaware y luego llamar a CLUSTER. El método join(new StaticDirectory(u, invokers)), a través del mecanismo SPI, llama para generar un invocador de instancia de RegistryAwareClusterInvoker; 3.2 no
    existe, llama directamente a CLUSTER.join(new StaticDirectory(invokers)), y llama a FailoverClusterInvoker a través del mecanismo SPI para generar un invocador de instancia FailoverClusterInvoker;

Llame a CLUSTER.join(new StaticDirectory(invokers)), debido al mecanismo SPI, se llamará primero a la clase Wrapper;

Cuando hay varias URL, hay un centro de registro, la primera capa: MockClusterWrapper#join

La función de esta clase es el procesamiento de solicitudes simuladas;
llame al método de unión

  • Llame a this.cluster.join(directory) para generar una instancia de Invoker y devolverla como un parámetro de construcción de MockClusterInvoker,
  • Generar un invocador de instancia MockClusterInvoker;
  • Es decir, el tipo Invoker más externo es el tipo MockClusterInvoker
public class MockClusterWrapper implements Cluster {
    
    
    private Cluster cluster;
    @Override
    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
    
    
        return new MockClusterInvoker<T>(directory,
                this.cluster.join(directory));
    }

}

En el caso de varias URL, hay un registro, la segunda capa: RegistryAwareClusterInvoker#join

Llame al método de unión para generar una instancia de RegistryAwareClusterInvoker;

public class RegistryAwareCluster implements Cluster {
    
    
    public final static String NAME = "registryaware";
    @Override
    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
    
    
        return new RegistryAwareClusterInvoker<T>(directory);
    }
}

En el caso de varias URL, hay un centro de registro, la tercera capa: FailoverClusterInvoker#join

No importa si es el primer paso de ReferenceConfigde#get() o el paso 3.1, al final se generará una instancia de Invoker de FailoverClusterInvoker, es decir, el tipo de instancia de Invoker finalmente generado llamando a REF_PROTOCOL.refer(interfaceClass, url) es FailoverClusterInvoker

REF_PROTOCOL.refer(interfaceClass, url)

A través de la tecnología de punto de extensión adaptable SPI, la primera llamada es el método de referencia de la clase RegistryProtocol,
llame al método doRefer para generar una instancia de invocador;

  • Parámetro clúster: representa la instancia tolerante a fallas del clúster, el valor predeterminado es FailoverClusterInvoker;
  • registro : dirección del centro de registro;
  • tipo : tipo de interfaz;
  • url : el parámetro de URL del servicio;
    @Override
    @SuppressWarnings("unchecked")
    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
    
    
        // url由 registry:// 改变为---> zookeeper://
        url = URLBuilder.from(url)
                .setProtocol(url.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY))
                .removeParameter(REGISTRY_KEY)
                .build();
        // 这里的cluster是cluster的Adaptive对象
        return doRefer(cluster, registry, type, url);
    }

private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
    
    
        // RegistryDirectory表示动态服务目录,会和注册中心的数据保持同步
        // type表示一个服务对应一个RegistryDirectory,url表示注册中心地址
        // 在消费端,最核心的就是RegistryDirectory
        RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
        directory.subscribe(subscribeUrl.addParameter(CATEGORY_KEY,
                PROVIDERS_CATEGORY + "," + CONFIGURATORS_CATEGORY + "," + ROUTERS_CATEGORY));
        Invoker invoker = cluster.join(directory);
        ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
        return invoker;
    }

Lo mismo llamará a cluster#join para generar una instancia, y también llamará al método Wrapper#join de la clase contenedora, la clase contenedora es MockClusterWrapper, y creará una instancia MockClusterInvoker; luego llamará al método FailoverCluster#join, creará una instancia FailoverClusterInvoker y pasará en la instancia de RegistryDirectory, RegistryDirectory encapsula internamente un atributo DelegateInvoker interno;

La relación entre la capa de pintura y la capa es la siguiente

inserte la descripción de la imagen aquí

    private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
    
    
        RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);

        directory.subscribe(subscribeUrl.addParameter(CATEGORY_KEY,
                PROVIDERS_CATEGORY + "," + CONFIGURATORS_CATEGORY + "," + ROUTERS_CATEGORY));
        // 利用传进来的cluster,join得到invoker,
        Invoker invoker = cluster.join(directory);
        return invoker;
    }

RegistroDirectorio#subscribe

El papel de suscribirse al directorio de monitoreo;

  • Llame al método registration#subscribe para completar la suscripción;
  • Si se utiliza el registro de Zookeeper, el tipo de registro es ZookeeperRegistry, el método de suscripción no está definido en la clase y el método de suscripción está definido en la clase FallbackRegistry heredada;
    public void subscribe(URL url) {
    
    
        setConsumerUrl(url);
        CONSUMER_CONFIGURATION_LISTENER.addNotifyListener(this); // 监听consumer应用
        serviceConfigurationListener = new ReferenceConfigurationListener(this, url); // 监听所引入的服务的动态配置
        registry.subscribe(url, this);
    }

FailbackRegistry#subscribe

  1. FailbackRegistry#subscribe llama al método doSubscribe, implementado por subclases, patrón de fábrica simple;
  2. El método de notificación se llama en el método ZookeeperRegistry#doSubscribe y el método de notificación se define en la clase principal.
  3. El método de notificación de la clase padre AbstractRegistry se llama en FallbackRegistry#notify;
  4. AbstractRegistry vuelve a llamar al método listener#notify; el tipo de oyente es RegistryDirectory
  5. El método refreshOverrideAndInvoker se llama en el método RegistryDirectory#notify;
  6. RegistryDirectory#refreshOverrideAndInvoker llama al método refreshInvoker

public class FailbackRegistry extends AbstractRegistry {
    
    
	 @Override
    public void subscribe(URL url, NotifyListener listener) {
    
    
        super.subscribe(url, listener);
        try {
    
    
            // Sending a subscription request to the server side
            doSubscribe(url, listener);
        } catch (Exception e) {
    
    
         //省略代码
        }
  	  }
  	  
      protected void notify(URL url, NotifyListener listener, List<URL> urls) {
    
    
        try {
    
    
            doNotify(url, listener, urls);
        } catch (Exception t) {
    
    
        }
    }
    //super#notify调用AbstractRegistry#notify方法;
    protected void doNotify(URL url, NotifyListener listener, List<URL> urls) {
    
    
        super.notify(url, listener, urls);
    }
}
//notify方法在父类中定义了, 父类FailbackRegistry 的notify方法中
public class ZookeeperRegistry extends FailbackRegistry {
    
    
    @Override
    public void doSubscribe(final URL url, final NotifyListener listener) {
    
    
        try {
    
    
            if (ANY_VALUE.equals(url.getServiceInterface())) {
    
    
              //省略部分代码
            } else {
    
     //省略部分代码
	            // 单独订阅某一个服务;
                notify(url, listener, urls);
            }
        } catch (Throwable e) {
    
    
        }
    }
}

public class FailbackRegistry extends AbstractRegistry {
    
    
    protected void notify(URL url, NotifyListener listener, List<URL> urls) {
    
    

        for (Map.Entry<String, List<URL>> entry : result.entrySet()) {
    
    
            listener.notify(categoryList);
        }
    }

}

public class RegistryDirectory<T> extends AbstractDirectory<T> implements NotifyListener {
    
    
   @Override
    public synchronized void notify(List<URL> urls) {
    
    
        Map<String, List<URL>> categoryUrls = urls.stream()
                .filter(Objects::nonNull)
                .filter(this::isValidCategory)
                .filter(this::isNotCompatibleFor26x)
                .collect(Collectors.groupingBy(url -> {
    
    
                    if (UrlUtils.isConfigurator(url)) {
    
    
                        return CONFIGURATORS_CATEGORY;
                    } else if (UrlUtils.isRoute(url)) {
    
    
                        return ROUTERS_CATEGORY;
                    } else if (UrlUtils.isProvider(url)) {
    
    
                        return PROVIDERS_CATEGORY;
                    }
                    return "";
                }));
        // 获取服务提供者URL
        List<URL> providerURLs = categoryUrls.getOrDefault(PROVIDERS_CATEGORY, Collections.emptyList());
        refreshOverrideAndInvoker(providerURLs);
    }

    private void refreshOverrideAndInvoker(List<URL> urls) {
    
    
        refreshInvoker(urls);
    }
 }

RegistryDirectory#refreshInvoker

  1. llamará a Invokers para generar un Invoker para cada URL;
  2. Asigne un valor a la propiedad de invocadores personalizados
public class RegistryDirectory<T> extends AbstractDirectory<T> implements NotifyListener {
    
    
private void refreshInvoker(List<URL> invokerUrls) {
    
    
        if (invokerUrls.size() == 1 && invokerUrls.get(0) != null && EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
    
    
        } else {
    
    
            // 这里会先按Protocol进行过滤,并且调用DubboProtocol.refer方法得到DubboInvoker
            Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);// Translate url list to Invoker map
			//给自定的invokers属性赋值;
            this.invokers = multiGroup ? toMergeInvokerList(newInvokers) : newInvokers;
            this.urlInvokerMap = newUrlInvokerMap;
			//省略部分代码;
        }
    }

}

La cuarta capa: RegistryDirectory$InvokerDelegate

a invocadores,

  1. Se creará una instancia de la clase interna InvokerDelegate;
  2. Al llamar a protocol#refer, se llamará al método DubboProtocol#refer para su procesamiento; debido al mecanismo SPI, se llamará primero a la clase contenedora Wrapper para su procesamiento;
public class RegistryDirectory<T> extends AbstractDirectory<T> implements NotifyListener {
    
    
private Map<String, Invoker<T>> toInvokers(List<URL> urls) {
    
    
        for (URL providerUrl : urls) {
    
    
            if (invoker == null) {
    
     // Not in the cache, refer again
                try {
    
    
                    if (enabled) {
    
    
                        // 调用Protocol的refer方法得到一个Invoker
                        invoker = new InvokerDelegate<>(protocol.refer(serviceType, url), url, providerUrl);   }
                } catch (Throwable t) {
    
    }
            } else {
    
    
            }
        }
        return newUrlInvokerMap;
    }
}

La quinta capa: ListenerInvokerWrapper

protocol.refer(type, url) devuelve un Invoker, como parámetro, crea una instancia de ListenerInvokerWrapper;

public class ProtocolListenerWrapper implements Protocol {
    
    
 @Override
    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
    
    
        if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {
    
    
            return protocol.refer(type, url);
        }
        return new ListenerInvokerWrapper<T>(protocol.refer(type, url),
                Collections.unmodifiableList(
                        ExtensionLoader.getExtensionLoader(InvokerListener.class)
                                .getActivateExtension(url, INVOKER_LISTENER_KEY)));
    }


}

protocol.refer(type, url) La siguiente capa llama al método ProtocolFilterWrapper#refer;

La sexta capa: CallbackRegistrationInvoker

  • Después de llamar a buildInvokerChain, se devuelve una instancia de CallbackRegistrationInvoker, por lo tanto, la quinta capa es CallbackRegistrationInvoker;
  • En el método buildInvokerChain, se genera una cadena de procesamiento de filtros;
  • protocol.refer (tipo, url): devuelve la instancia de Invoker ;
  • REFERENCE_FILTER_KEY: el valor es reference.filter
  • CommonConstants.CONSUMER : 值为 consumidor

Trabajar:

  1. Según la clave y el valor, se obtendrán todos los Filtros con el nombre de grupo consumidor;
  2. Cree una instancia de Invocador correspondiente para cada Filtro;
  3. Finalmente, la instancia de Invoker correspondiente al Filter más externo se usa como parámetro para crear una instancia de CallbackRegistrationInvoker;
public class ProtocolFilterWrapper implements Protocol {
    
    
    @Override
    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
    
    
        if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {
    
    
            return protocol.refer(type, url);
        }
        return buildInvokerChain(protocol.refer(type, url), REFERENCE_FILTER_KEY, CommonConstants.CONSUMER);
    }
    
    private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
    
    
        Invoker<T> last = invoker;
        // 根据url获取filter,根据url中的parameters取key为key的value所对应的filter,但是还会匹配group
        List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
        if (!filters.isEmpty()) {
    
    
            for (int i = filters.size() - 1; i >= 0; i--) {
    
    
                final Filter filter = filters.get(i);
                final Invoker<T> next = last;
                last = new Invoker<T>() {
    
    
                    @Override
                    public Result invoke(Invocation invocation) throws RpcException {
    
    
                        Result asyncResult;
                        try {
    
    
                            // 得到一个异步结果
                            asyncResult = filter.invoke(next, invocation);
                        } catch (Exception e) {
    
    
                        }
                        return asyncResult;
                    }
                };
            }
        }
        return new CallbackRegistrationInvoker<>(last, filters);
    }
}

Orden de llamada del filtro de clientes

módulo dubbo-rpc-api

genericimpl=org.apache.dubbo.rpc.filter.GenericImplFilter  //order+20000
activelimit=org.apache.dubbo.rpc.filter.ActiveLimitFilter  // order无
consumercontext=org.apache.dubbo.rpc.filter.ConsumerContextFilter //order = -10000

módulo dubbo-monitor-api

monitor=org.apache.dubbo.monitor.support.MonitorFilter

módulo dubbo-rpc-dubbo

future=org.apache.dubbo.rpc.protocol.dubbo.filter.FutureFilter

La secuencia de llamadas es:

  1. ConsumerContextFilter#invoke
  2. FutureFilter#invoke
  3. MonitorFilter#invoke

Después de llamar a MonitorFilter#refer, se genera la cadena de filtros Invoker;

En el método ProtocolFilterWrapper#refer, el primer parámetro para llamar a buildInvokerChain es: protocol.refer(type, url), es decir, el siguiente Protocolo llama al método DubboProtocol#refer

AbstractProtocol#referir

Refer no está definido en DubboProtocol, que hereda la clase AbstractProtocol, y el método refer está definido en AbstractProtocol;

Séptima capa: AsyncToSyncInvoker

El rol de esta clase: convertir solicitudes asíncronas en solicitudes síncronas, esta clase Invoker es muy importante

  • El método protocolBindingRefer(type, url) devuelve una instancia de Invoker como parámetro y crea una instancia de AsyncToSyncInvoker;
  • protocolBindingRefer es un método abstracto implementado por subclases, aquí está DubboProtocol
public abstract class AbstractProtocol implements Protocol {
    
    
 @Override
    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
    
    
        // 异步转同步Invoker , type是接口,url是服务地址
        // DubboInvoker是异步的,而AsyncToSyncInvoker会封装为同步的
        return new AsyncToSyncInvoker<>(protocolBindingRefer(type, url));
    }

    protected abstract <T> Invoker<T> protocolBindingRefer(Class<T> type, URL url) throws RpcException;
}

DubboProtocol#protocolBindingRefer

Novena capa: DubboInvoker

En el método protocolBindingRefer, se crea y devuelve una instancia de DubboInvoker;

    @Override
    public <T> Invoker<T> protocolBindingRefer(Class<T> serviceType, URL url) throws RpcException {
    
    
        // 在DubboInvoker发送请求时会轮询clients去发送数据
        DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
        invokers.add(invoker);

        return invoker;
    }

Hasta el momento, la cantidad de capas de empaquetado de Invoker tiene un total de 9 capas; si observa detenidamente, debe agregar el Invoker correspondiente al filtro de filtro:

El gráfico final es el siguiente:
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/yaoyaochengxian/article/details/124540740
Recomendado
Clasificación