Dubbo/Dubbox的dubbo协议实现(一)-服务端启动

本文深入剖析 Dubbo/Dubbox 中的 dubbo 协议服务端启动流程,从 ServiceConfig 的 doExportUrlsFor1Protocol 方法开始,探讨 proxyFactory.getInvoker 如何生成并调用服务,重点关注 openServer(url) 方法,揭示如何通过 Exchangers.bind 创建并绑定服务器。讲解了 Exchanger、ExchangeHandler 和 NettyServer 在服务暴露中的作用,梳理了服务启动的调用链路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前已经分析的dubbo的服务的发现和注册,这里先看一下dubbo协议是如何实现的,之前已经知道了,调用DubboProtocol类的export来暴露服务的,协议实现比较复杂,这里只关系主体实现即排除一些特性功能的处理代码

本章主要处理服务端对应的暴露流程,继续回到···com.alibaba.dubbo.config.ServiceConfig···的doExportUrlsFor1Protocol方法(487行附近)

Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
Exporter<?> exporter = protocol.export(invoker);
exporters.add(exporter);

proxyFactory 是一个全局变量(自适应扩展点),之前已经反复复习不多说明了
proxyFactory.getInvoker实现如下,该方法内部会调用一个封装类com.alibaba.dubbo.common.bytecode.Wrapper,该类封装了实例的方法调用。proxyFactory.getInvoker 会返回一个

public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        // TODO Wrapper类不能正确处理带$的类名
        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);
            }
        };
    }

这里com.alibaba.dubbo.common.bytecode.Wrapper类不做过多说明,其会生成有一个名为invokeMethod封装方法,invokeMethod 方法根据传入实例和方法名,调用实例对应的方法,invokeMethod方法签名如下:

  /**
     *
     * @param o 接口类实例
     * @param n 方法名
     * @param p 参数类型
     * @param v 参数值
     * @return
     * @throws java.lang.reflect.InvocationTargetException
     */
    public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException

所以到这里可以发现,对于服务的提供者来说通过com.alibaba.dubbo.rpc.Invoker的 protected Object doInvoke(T proxy, String methodName,Class

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        /*url此刻内容如下。
        *dubbo://ip:port/服务接口?anyhost=true&application=dsp-ad&default.timeout=10000&dubbo=2.8.4&generic=false&
        *interface=服务接口&logger=log4j&methods=接口方法名&organization=company&owner=weiythi&pid=5948&revision=1.6.1&serialization=kryo&side=provider&timestamp=1511849953486
        */
        URL url = invoker.getUrl();

        // export service.
        // serviceKey为 服务接口类名:端口号
        String key = serviceKey(url);
        //DubboExporter 实现接口 com.alibaba.dubbo.rpc.Exporter ,会在抽象实现com.alibaba.dubbo.rpc.protocol.AbstractExporter 的基础上维护exporterMap ,key
        DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
        exporterMap.put(key, exporter);
        //省略......export an stub service for dispaching event......//

        //开启服务
        openServer(url);

        // modified by lishen 序列化优化相关,暂不去了解
        optimizeSerialization(url);

        return exporter;
    }

这里先找到最关心的点“openServer(url);”,其是dubbo协议服务暴露的入口,其实现如下:

private void openServer(URL url) {
        // find server.
        // 这里是ip:port
        String key = url.getAddress();
        //client 也可以暴露一个只有server可以调用的服务。
        boolean isServer = url.getParameter(Constants.IS_SERVER_KEY,true);
        if (isServer) {//true
            //按照ip:port为key 找到信息交换层的服务端
            ExchangeServer server = serverMap.get(key);
            if (server == null) {//如果木有创建过server端,则创建并将服务端缓存
                serverMap.put(key, createServer(url));
            } else {
                //server支持reset,配合override功能使用
                server.reset(url); //这个暂时木有看懂,等回过头来看
            }
        }
    }

注意这里serverMap虽说不是一个static域,但基于扩展点机制的实现,貌似都是单例的,所以这里一个服务端进程可以为多个provider提供服务,那么createServer(url)是如何开启服务的呢?
createServer(url)的方法实现如下, 注意该方法会通过url总线的server参数来决定使用dubbo的协议实现,dubbox这里默认nio框架是netty

private ExchangeServer createServer(URL url) {
        //默认开启server关闭时发送readonly事件
        url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString());
        //默认开启heartbeat
        url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
        //获得协议的server实现,默认netty
        String str = url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_SERVER);

        if (str != null && str.length() > 0 && ! ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str))
            throw new RpcException("Unsupported server type: " + str + ", url: " + url);

        url = url.addParameter(Constants.CODEC_KEY, Version.isCompatibleVersion() ? COMPATIBLE_CODEC_NAME : DubboCodec.NAME);
        ExchangeServer server;
        try {
            //注意这里的requestHandler 是一个com.alibaba.dubbo.remoting.exchange.ExchangeHandler 的接口实现。
            server = Exchangers.bind(url, requestHandler);
        } catch (RemotingException e) {
            throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
        }
        str = url.getParameter(Constants.CLIENT_KEY);
        if (str != null && str.length() > 0) {
            Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
            if (!supportedTypes.contains(str)) {
                throw new RpcException("Unsupported client type: " + str);
            }
        }
        return server;
    }

在方法中能注意到一个闪闪亮的语句,server = Exchangers.bind(url, requestHandler); 这里传入了一个参数requestHandler,requestHandler在此类是一个com.alibaba.dubbo.remoting.exchange.ExchangeHandler的接口实现,看了下源码这里又引入了一系列暂时不知道用处的类,所以此处暂时忽略接口实现,既然其是一个handler所以接到consumer的调用之后必然会执行的,先放在一边。我们接数据的时候一起看。
com.alibaba.dubbo.remoting.exchange.Exchangers 类主要有两个功能,即服务器的绑定和客户端的连接功能
Exchangers.bind(url, requestHandler); 内部又涉及一系列的扩展点,不贴代码出来了。简单介绍一下, 这里肯定实现的功能就是服务器的绑定了
1.bind方法会根据URL总线的exchanger参数获得一个com.alibaba.dubbo.remoting.exchange.Exchanger 扩展点实例,本例实例为(com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchanger),嗯 目前已知的扩展点实现也就这一个。
2.Exchanger?在一个框架中我们通常把负责数据交换和网络通信的组件叫做Exchanger ,接下来调用 ExchangeServer bind(URL url, ExchangeHandler handler) 方法,注意这里又是一个自适应扩展点。

接下来进入到Exchanger仅有的一个实现com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchanger

    public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
        return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
    }

可见在该方法中,创建了一个com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeServer 对象,当然根据方法定义该类肯定实现接口com.alibaba.dubbo.remoting.exchange.ExchangeServer 这里先暂时不关心该接口行为,因为创建对象前,先对handler进行了两层封装,然后使用com.alibaba.dubbo.remoting.Transporters的bind方法,进行绑定。
对handler的封装可能会绝对handler调用前或调用后的行为,设置决定了handler的调用方式,那么先去确定com.alibaba.dubbo.remoting.transport.DecodeHandler 的运作情况。
额,点开看了一下貌似也挺复杂,因为这个类一顿继承啊~,找一下自己关心的两个东东,分别是,我家的HeaderExchangeHandler在哪里?有木有能让我接受到消息调用到的方法。
然后发现这两个重点是这样的

public DecodeHandler(ChannelHandler handler) {
        super(handler); //父类构造器
    }

    public void received(Channel channel, Object message) throws RemotingException {
        if (message instanceof Decodeable) {
            decode(message);
        }

        if (message instanceof Request) {
            decode(((Request)message).getData());
        }

        if (message instanceof Response) {
            decode( ((Response)message).getResult());
        }
        //调用received方法,既然我们的类已经被封装了两层,那么这里应该调用的是HeaderExchangeHandler
        handler.received(channel, message);
    }

    private void decode(Object message) { //这个方法没有任何handler相关处理,暂不去关心。
        if (message != null && message instanceof Decodeable) {
            try {
                ((Decodeable)message).decode();
                if (log.isDebugEnabled()) {
                    log.debug(new StringBuilder(32).append("Decode decodeable message ")
                                  .append(message.getClass().getName()).toString());
                }
            } catch (Throwable e) {
                if (log.isWarnEnabled()) {
                    log.warn(
                        new StringBuilder(32)
                            .append("Call Decodeable.decode failed: ")
                            .append(e.getMessage()).toString(),
                        e);
                }
            } // ~ end of catch
        } // ~ end of if
    } // ~ end of method decode

那么再去看看 HeaderExchangeHandler 是个啥
这里写图片描述
还有既然会调用received方法,主要去看看received的实现。点开一看略复杂、、、、好多东东,看到这里还不能理解,所以又去敲Transporters.bind的门,因为已经先行忽略了handler这个,这里只关心服务是怎么被open的
代码里又耍流氓了,又是自适应扩展点,但是我已不怕,打开看配置文件直接锁定实现com.alibaba.dubbo.remoting.transport.netty.NettyTransporter 然后去看bind方法(等等索性直接看了)

/*
 * Copyright 1999-2011 Alibaba Group.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alibaba.dubbo.remoting.transport.netty;

import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.remoting.ChannelHandler;
import com.alibaba.dubbo.remoting.Client;
import com.alibaba.dubbo.remoting.RemotingException;
import com.alibaba.dubbo.remoting.Server;
import com.alibaba.dubbo.remoting.Transporter;

/**
 * @author ding.lid
 */
public class NettyTransporter implements Transporter {

    public static final String NAME = "netty";

    public Server bind(URL url, ChannelHandler listener) throws RemotingException {
        return new NettyServer(url, listener);
    }

    public Client connect(URL url, ChannelHandler listener) throws RemotingException {
        return new NettyClient(url, listener);
    }

}

这个很明显么,直接new了一个com.alibaba.dubbo.remoting.transport.netty.NettyServer 出来,那么问题简单了,直接去看构造函数

public NettyServer(URL url, ChannelHandler handler) throws RemotingException{
        super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
    }

调用了父类的构造器

public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
        // 哭 handler又甩给上层了
        super(url, handler);
        localAddress = getUrl().toInetSocketAddress();
        String host = url.getParameter(Constants.ANYHOST_KEY, false)
                        || NetUtils.isInvalidLocalHost(getUrl().getHost())
                        ? NetUtils.ANYHOST : getUrl().getHost();
        bindAddress = new InetSocketAddress(host, getUrl().getPort());
        this.accepts = url.getParameter(Constants.ACCEPTS_KEY, Constants.DEFAULT_ACCEPTS);
        this.idleTimeout = url.getParameter(Constants.IDLE_TIMEOUT_KEY, Constants.DEFAULT_IDLE_TIMEOUT);
        try {
            //好吧,偷偷的调用了doOpen 害我好找
            doOpen();
            if (logger.isInfoEnabled()) {
                logger.info("Start " + getClass().getSimpleName() + " bind " + getBindAddress() + ", export " + getLocalAddress());
            }
        } catch (Throwable t) {
            throw new RemotingException(url.toInetSocketAddress(), null, "Failed to bind " + getClass().getSimpleName()
                                        + " on " + getLocalAddress() + ", cause: " + t.getMessage(), t);
        }

        executor = (ExecutorService) ExtensionLoader.getExtensionLoader(DataStore.class)
                .getDefaultExtension().get(Constants.EXECUTOR_SERVICE_COMPONENT_KEY, Integer.toString(url.getPort()));
    }

主要关注两个事,就是我们的handler哪去了,后面可能还要用呢,还有服务在哪开的?
首先看那第一行,调用父类的构造器,一直跟着找,发现handler在com.alibaba.dubbo.remoting.transport.AbstractPeer 中维护了,然后AbstractPeer又是一个ChannelHandler ,好吧这个关系很混乱。先记着这个关键点,上一次看见类似风格是在RegistryDictory中
既然调用了doOpen,这个就不用想了,方法的实现就在NettyServer里啊,但是netty之前没玩过,所以只能看个热闹

@Override
    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);
        // 这里的NettyHandler 封装了一个this? 这个this是什么?
        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() {
            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());
    }

因为35太难了,我学不会,而且server开启之后只要能一直提供服务就OK,我只能关注另一个点就是接到消息后怎么处理的。所以这里跟消息有关的就一个nettyHandler ,其初始化是通过这样的语句完成的,这里传入了一个this,我们要知道这个this是什么。

final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);

@Sharable
public class NettyHandler extends SimpleChannelHandler {

    private final Map<String, Channel> channels = new ConcurrentHashMap<String, Channel>(); // <ip:port, channel>

    private final URL url;

    private final ChannelHandler handler;
    //.......//
    public NettyHandler(URL url, ChannelHandler handler){
            if (url == null) {
                throw new IllegalArgumentException("url == null");
            }
            if (handler == null) {
                throw new IllegalArgumentException("handler == null");
            }
            this.url = url;
            this.handler = handler;
        }
}

好吧,很巧妙的方式,因为NettyServer他是一个ChannelHandler啊,是一个ChannelHandler啊…啊。
迷只混乱,只能拿出他家家谱,然后默默流眼泪
这个家谱里貌似没有我们熟悉的朋友。
这里写图片描述
看看他的长辈有没有人认识我们上文忽略的com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeServer,因为是这么个关系new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler)))); 所以去看看他的构造器

public HeaderExchangeServer(Server server) {
        if (server == null) {
            throw new IllegalArgumentException("server == null");
        }
        this.server = server;
        this.heartbeat = server.getUrl().getParameter(Constants.HEARTBEAT_KEY, 0);
        this.heartbeatTimeout = server.getUrl().getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, heartbeat * 3);
        if (heartbeatTimeout < heartbeat * 2) {
            throw new IllegalStateException("heartbeatTimeout < heartbeatInterval * 2");
        }
        startHeatbeatTimer();
    }

参数正好是NettyServer所实现的接口com.alibaba.dubbo.remoting.Server ,到这里我们就返回一个ExchangeServer了,不过好像目前为止并没有有什么实质性的动作,回到createServer

好吧,到这一个服务就创建完了。总结一下开启服务这一部的调用关系(引用大佬的)

openServer netty server打开侦听服务,并缓存服务。
dubbo -> export() -> openServer() -> createServer() -> Exchangers.bind()(HeaderExchanger) -> NettyTransporter -> NettyServer.open()(编码解码采用exchange)
netty ChannelFactory(boss worker) ChannelPipeline(责任链模式) ChannelHandler(处理器) — 反应器模式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值