Dubbo源码探析-服务暴露过程

Dubbo 的服务暴露过程

前面已经看到,dubbo 的组件中,ServiceBean和 ReferenceBean 是 比较特殊的。这两个组件,将完成 dubbo 服务的远程 rpc 过程。 ServiceBean 作为服务端,会在 bean 创建成功后,发起服务暴露流 程。其过程如下:
在实现的 InitializingBean 接口中,spring 调用 afterPropertiesSet 方法,发起服务的暴露动作。

 @Override
public void export() {
      super.export();
      // Publish ServiceBeanExportedEvent
      publishExportEvent();
  }

查询当前dubbo环境中有哪些协议,需要对每一个网络协议进行暴露

 private void doExportUrls() {
        List<URL> registryURLs = loadRegistries(true);
        for (ProtocolConfig protocolConfig : protocols) {
            doExportUrlsFor1Protocol(protocolConfig, registryURLs);
        }
    }

最终执行到此动作

Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

Exporter<?> exporter = protocol.export(wrapperInvoker);
exporters.add(exporter);

先将本地服务 ref 包装成 invoker,然后由 protocol 网络协议将invoker 连通到网络上。其核心,即是一旦有 protocol 网络传输过来的请求,则拉起 invoker 动作,并将动作传递到 ref 服务上进行响应。在这个整个过程中,dubbo 不希望每一个具体的协议 ptotocol 去关心目标服务是谁(耦合性太强),于是中间插入了一个 invoker 概念实体来解耦双方的绑定关系

首先,明确目标,invoker 的本义,是 invoke 方法会转嫁对象到目标 ref 上

proxyFactory.getInvoker 会通过Javaassist生成一个代理类如下:

public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException{ com.enjoy.service.OrderServiceImpl w;

try{ 

	w = ((com.enjoy.service.OrderServiceImpl)$1); 

}catch(Throwable e){ 

	throw new IllegalArgumentException(e);

 } try{ 

if( "getDetail".equals( $2 )  &&  $3.length == 1 ) {

  	return ($w)w.getDetail((java.lang.String)$4[0]); }

 } catch(Throwable e) {    

  throw new java.lang.reflect.InvocationTargetException(e); 

 } 

throw new com.alibaba.dubbo.common.bytecode.NoSuchMethodException("Not found method \""+$2+"\" in class com.enjoy.service.OrderServiceImpl.");
 }

protocol.export(wrapperInvoker); 进行服务暴露

registry://localhost:2181/com.alibaba.dubbo.registry.RegistryService?application=busi-provider&dubbo=2.6.2&export=dubbo://10.60.83.4:20880/com.enjoy.service.OrderService?anyhost=true&application=busi-provider&bind.ip=10.60.83.4&bind.port=20880&dubbo=2.6.2&generic=false&interface=com.enjoy.service.OrderService&methods=getDetail&pid=18191&revision=1.0.0&side=provider&timestamp=1596795662656&version=1.0.0&pid=18191&registry=zookeeper&timestamp=1596795662624

这里会根据SPI机制调用到RegistryProtocol类中的export方法,可以参考SPI 机制原理

public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException { 
		// 发布本地 invoker,暴露本地服务,打开服务器端口
		final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);
		// 根据 url 从 registryFactory 中获取对应的 registry
		final Registry registry = getRegistry(originInvoker); 
		//......(省略部分代码)
		if (register) {
			// 向注册中心注册 providerUrl
			register(registryUrl, registedProviderUrl);
			// 本地注册表设置此 provider 注册完成
			ProviderConsumerRegTable.getProviderWrapper(originInvoker).setReg(true);
		} 
		//......(省略部分代码)
		// 向注册中心订阅提供者 url 节点
		registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
		 //......(省略部分代码)
}

这行代码主要用于暴露本地服务,注册中心的逻辑后面在讲

final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);
private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker) {
	    //获取上面url中的export dubbo:// ...
        String key = getCacheKey(originInvoker);
        ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
        if (exporter == null) {
            synchronized (bounds) {
                exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
                if (exporter == null) {
                    final Invoker<?> invokerDelegete = new InvokerDelegete<T>(originInvoker, getProviderUrl(originInvoker));
                    //这里又是SPI,会调用到DubboProtocol
                    exporter = new ExporterChangeableWrapper<T>((Exporter<T>) protocol.export(invokerDelegete), originInvoker);
                    bounds.put(key, exporter);
                }
            }
        }
        return exporter;
    }
dubbo://10.60.83.4:20880/com.enjoy.service.OrderService?anyhost=true&application=busi-provider&bind.ip=10.60.83.4&bind.port=20880&dubbo=2.6.2&generic=false&interface=com.enjoy.service.OrderService&methods=getDetail&pid=18191&revision=1.0.0&side=provider&timestamp=1596795662656&version=1.0.0

这里会启动netty服务
在这里插入图片描述

最后会走到NettyServer的doOpen方法,启动Netty服务

 protected void doOpen() throws Throwable {
        NettyHelper.setNettyLoggerFactory();
        ExecutorService boss = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerBoss", true));
        ExecutorService worker = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerWorker", true));
        ChannelFactory channelFactory = new NioServerSocketChannelFactory(boss, worker, getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS));
        bootstrap = new ServerBootstrap(channelFactory);

        final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
        channels = nettyHandler.getChannels();
        // https://issues.jboss.org/browse/NETTY-365
        // https://issues.jboss.org/browse/NETTY-379
        // final Timer timer = new HashedWheelTimer(new NamedThreadFactory("NettyIdleTimer", true));
        bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
            @Override
            public ChannelPipeline getPipeline() {
                NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
                ChannelPipeline pipeline = Channels.pipeline();
                /*int idleTimeout = getIdleTimeout();
                if (idleTimeout > 10000) {
                    pipeline.addLast("timer", new IdleStateHandler(timer, idleTimeout / 1000, 0, 0));
                }*/
                pipeline.addLast("decoder", adapter.getDecoder());
                pipeline.addLast("encoder", adapter.getEncoder());
                pipeline.addLast("handler", nettyHandler);
                return pipeline;
            }
        });
        // bind
        channel = bootstrap.bind(getBindAddress());
    }

到此服务已经暴露完成

Dubbo 的服务引入过程

dubbo 服务的引入过程,是在 ReferenceBean 的实例化过程中实现的。当 dubbo 启动过程中,遇到@reference,即会创建一个 ReferenceBean 的实例。
此实例一样实现了 InitializingBean 接口,在其调用的 afterPropertiesSet 方法中,会为服务调用方创建一个远程代理对象

  public synchronized T get() {
        if (destroyed) {
            throw new IllegalStateException("Already destroyed!");
        }
        if (ref == null) {
            init();
        }
        return ref;
    }

protocol 协议制作了一个 invoker 对象,你可以通过invoker 对象,向 protocol 协议发送信息(网络传输)
在这里插入图片描述

urls:
registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=busi-clint&dubbo=2.6.2&pid=29217&refer=application=busi-clint&check=false&dubbo=2.6.2&getDetail.async=true&interface=com.enjoy.service.OrderService&methods=getDetail&pid=29217&register.ip=192.168.8.127&revision=1.0.0&side=consumer&timestamp=1596976709184&version=1.0.0&registry=zookeeper&timestamp=1596976757248

interfaceClass:
interface com.enjoy.service.OrderService

这里也是通过SPI机制执行RegistryProtocol类的refer方法完成服务的注册和订阅(这个后面在说)

 @Override
    @SuppressWarnings("unchecked")
    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
        Registry registry = registryFactory.getRegistry(url);
        if (RegistryService.class.equals(type)) {
            return proxyFactory.getInvoker((T) registry, type, url);
        }

        // group="a,b" or group="*"
        Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
        String group = qs.get(Constants.GROUP_KEY);
        if (group != null && group.length() > 0) {
            if ((Constants.COMMA_SPLIT_PATTERN.split(group)).length > 1
                    || "*".equals(group)) {
                return doRefer(getMergeableCluster(), registry, type, url);
            }
        }
        return doRefer(cluster, registry, type, url);
    }

上面的方法创建了invoker对象,下面开始进行动态代理

public class JavassistProxyFactory extends AbstractProxyFactory {

    @Override
    @SuppressWarnings("unchecked")
    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }

    @Override
    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        // TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
        final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
        return new AbstractProxyInvoker<T>(proxy, type, url) {
            @Override
            protected Object doInvoke(T proxy, String methodName,
                                      Class<?>[] parameterTypes,
                                      Object[] arguments) throws Throwable {
                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
            }
        };
    }

}

最后返回个代理对象
在这里插入图片描述

当我们调用orderService.getDetail(“1”);时会调用到InvokerInvocationHandler

public class InvokerInvocationHandler implements InvocationHandler {

    private final Invoker<?> invoker;

    public InvokerInvocationHandler(Invoker<?> handler) {
        this.invoker = handler;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        Class<?>[] parameterTypes = method.getParameterTypes();
        if (method.getDeclaringClass() == Object.class) {
            return method.invoke(invoker, args);
        }
        if ("toString".equals(methodName) && parameterTypes.length == 0) {
            return invoker.toString();
        }
        if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
            return invoker.hashCode();
        }
        if ("equals".equals(methodName) && parameterTypes.length == 1) {
            return invoker.equals(args[0]);
        }
        return invoker.invoke(new RpcInvocation(method, args)).recreate();
    }

}

调用invoke会将下面的信息发送的服务端,并返回数据

Request [id=5, version=2.0.0, twoway=true, event=false, broken=false, data=RpcInvocation [methodName=getDetail, parameterTypes=[class java.lang.String], arguments=[1], attachments={path=com.enjoy.service.OrderService, interface=com.enjoy.service.OrderService, version=1.0.0}]]


    @Override
    public void send(Object message, boolean sent) throws RemotingException {
        super.send(message, sent);

        boolean success = true;
        int timeout = 0;
        try {
            ChannelFuture future = channel.write(message);
            if (sent) {
                timeout = getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
                success = future.await(timeout);
            }
            Throwable cause = future.getCause();
            if (cause != null) {
                throw cause;
            }
        } catch (Throwable e) {
            throw new RemotingException(this, "Failed to send message " + message + " to " + getRemoteAddress() + ", cause: " + e.getMessage(), e);
        }

        if (!success) {
            throw new RemotingException(this, "Failed to send message " + message + " to " + getRemoteAddress()
                    + "in timeout(" + timeout + "ms) limit");
        }
    }

服务端在NettyHandler类中接收请求

   @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
        NettyChannel channel = NettyChannel.getOrAddChannel(ctx.getChannel(), url, handler);
        try {
            handler.received(channel, e.getMessage());
        } finally {
            NettyChannel.removeChannelIfDisconnected(ctx.getChannel());
        }
    }

最后会执行到DubboProtocol获取到invoker,exporterMap是在服务暴露时初始化的

在这里插入图片描述

Invoker<?> getInvoker(Channel channel, Invocation inv) throws RemotingException {
        boolean isCallBackServiceInvoke = false;
        boolean isStubServiceInvoke = false;
        int port = channel.getLocalAddress().getPort();
        String path = inv.getAttachments().get(Constants.PATH_KEY);
        // if it's callback service on client side
        isStubServiceInvoke = Boolean.TRUE.toString().equals(inv.getAttachments().get(Constants.STUB_EVENT_KEY));
        if (isStubServiceInvoke) {
            port = channel.getRemoteAddress().getPort();
        }
        //callback
        isCallBackServiceInvoke = isClientSide(channel) && !isStubServiceInvoke;
        if (isCallBackServiceInvoke) {
            path = inv.getAttachments().get(Constants.PATH_KEY) + "." + inv.getAttachments().get(Constants.CALLBACK_SERVICE_KEY);
            inv.getAttachments().put(IS_CALLBACK_SERVICE_INVOKE, Boolean.TRUE.toString());
        }
        String serviceKey = serviceKey(port, path, inv.getAttachments().get(Constants.VERSION_KEY), inv.getAttachments().get(Constants.GROUP_KEY));

        DubboExporter<?> exporter = (DubboExporter<?>) exporterMap.get(serviceKey);

        if (exporter == null)
            throw new RemotingException(channel, "Not found exported service: " + serviceKey + " in " + exporterMap.keySet() + ", may be version or group mismatch " + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress() + ", message:" + inv);

        return exporter.getInvoker();
    }

然后通过invoker.invoke(invocation);调用到上面服务暴露时Javaassist生成的代理类,最终调用到具体的业务方法

public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException{ com.enjoy.service.OrderServiceImpl w;

try{ 

	w = ((com.enjoy.service.OrderServiceImpl)$1); 

}catch(Throwable e){ 

	throw new IllegalArgumentException(e);

 } try{ 

if( "getDetail".equals( $2 )  &&  $3.length == 1 ) {

  	return ($w)w.getDetail((java.lang.String)$4[0]); }

 } catch(Throwable e) {    

  throw new java.lang.reflect.InvocationTargetException(e); 

 } 

Dubbo 中的 invoker 概念,作用不仅仅于此,它统一了 dubbo 中各 组件间相互交流的规范,大家统一都用 invoker 进行粘合。我们后续将继续展示 invoker 更高一层的作用

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页