Dubbo源码学习笔记 之 服务端Server启动&请求Handler

           开始,先放一张Dubbo官网的整体设计图,

 

一、Server 启动

     依靠统一的URL 传递配置数据,扩展动态加载机制, 上下层的代码关联非常少。 Protocol 与Exchange 两层之间,就非常明显。

     以默认的dubbo 协议为例 ( 每个协议,处理方式不一样,例如httpProtocol 启动server就简单些 )。 

     1. Netty4 Server端启动,仅是一句代码: server = Exchangers.bind(url, requestHandler); 

       启动Server ,仅仅调用一个静态方法,传入DubboProtocol 类的实例化的 ExchangeHandlerAdapter 实例requestHandler 。

       requestHandler仅有一个reply(ExchangeChannel channel, Object message)方法,message 是已经被反序列化的Invocation对象。

       requestHandler 执行2个重要逻辑:

     1. 根据channel里面的url数据,获取Invoker (Invoker 为 封装好的FilterChain 过滤链) ,

     2. 执行invoker 调用,获取接口服务处理结果,因为Invoker为FilterChain,会优先执行Fitler 里面的逻辑,最后一个调用才执行 最终的 接口实现逻辑调用。

    2.  Exchanges.bind()

       Exchanges.bind()  只有1行关键代码, getExchanger(url).bind(url, handler);

      getExchanger(url)  会调用: ExtensionLoader.getExtensionLoader(Exchanger.class).getExtension(type) , type就是默认的 header ,最终获取到 HeaderExchanger 扩展( 这里没有经过adaptive扩展)。

  在HeaderExchanger的bind 方法里面,会进行handler的再次包装,nettyserver 监听本机端口。

      将代码展开(方法里面内嵌调用,变成前置变量,方便阅读,理解)后,代码如下:

@Override
    public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
        ChannelHandler h  = new DecodeHandler(new HeaderExchangeHandler(handler)); //1. handler 会再次经过2层包装,增加功能
        Server s = Transporters.bind(url,h); //2. Transports 操作会启动netty 监听端口,配置序列化实现,
        
        // 3.返回 会对 上步netty建立的server ,对此再次进行包装,主要增加 channel 空闲的检测,检测到超过一定时间的空闲的channel,会关闭
        return new HeaderExchangeServer( s);  
    }

 DecodeHandler : 该handler逻辑比较简单,对消息和消息内容进行识别,提取真实请求数据,给后续handler处理。   

 HeaderExchangeHandler : 处理channel状态事件,会对channel 写入读写时间属性,对接收请求进行过滤。 目前只看了server端代码,单根据逻辑,该handler 应该是在netty客户端也会用到,会对response进行处理。

 Transporters.bind() 方法,启动nettyServer端。 后面的逻辑较多,下个章节单独讲。

 HeaderExchangeServer: 会对nettyServer 进行包装, 主要增加2个功能:

    a. 对channel进行 空闲时间检测,超过则关闭连接,节省资源。

    b. 如果server关闭,则发送消息给client端,不再发送请求到该server。

二、 nettyServer 启动 

       Transporters.bind(url,handler) ,因为采用默认缺省配置,最终会到org.apache.dubbo.remoting.transport.netty4.NettyTransporter , 直接 new NettyServer(url, listener) 。

      url 是之前的url,listener 就是上面经过包装的 handler, 跟着代码进入NettyServer 。

    public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
        // you can customize name and type of client thread pool by THREAD_NAME_KEY and THREADPOOL_KEY in CommonConstants.
        // the handler will be warped: MultiMessageHandler->HeartbeatHandler->handler
        super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME))); //super方法必须放在第一行,这里无法拆分临时变量
    }

  1. handler包装  

NettyServer在调用super方法的时候,调用 ChannelHandlers.wrap 方法,对传入的handler 再次进行了包装。代码如下:

protected ChannelHandler wrapInternal(ChannelHandler handler, URL url) {
        return new MultiMessageHandler(new HeartbeatHandler(ExtensionLoader.getExtensionLoader(Dispatcher.class)
                .getAdaptiveExtension().dispatch(handler, url)));  //Dispatch.dispatch()方法实际是对handler的再次装饰,根据配置动态的添加新功能
    }

channelhandler.wrap 里面会调用Dispatcher 的扩展,进行dispatch操作,实际是对handler 的包装动态化。

  根据配置不同,调用Dispatch扩展包装后的handler 是不一样的。 默认采用AllDispatcher扩展,用 AllChannelHandler 包装1次。

  采用默认配置,经过包装之后,传入super方法的handler 包装链如下:

     MultiMessageHandler -> HeartbeatHandler -> AllChannelHandler ->  DecodeHandler ->  HeaderExchangeHandler -> DubboProtocol.requestHandler

    MultiMessageHandler :  检查消息是否为MutiMessage ,如果 是,分开单条调用后续handler

    HeartbeatHandler :1.在每个channel动作,对channel标记时间属性, 2. 检查是否心跳请求,是则直接返回心跳,不继续后续请求。

    AllChannelHandler : 1. 将后续handler 包装成 ChannelEventRunnable,捕获后续执行的异常,记录日志 。 2. 包装的runnable 放到独立线程池运行, 达到全流程异步化效果。

2. codec2扩展注入

    用idea 生成NettyServer 类关系图,如下:  

NettyServer 间接继承AbstractEndPoint , 在AbstractEndPoint构造方法里面,会调用ExtensionLoader,获取 Codec2 扩展。

   Codec2 默认扩展名为dobbo ,获取到的是:DubboCountCodec ,在DoubboCountCodec内部,实例化了 DubboCodec,最终的解编码操作还是DubboCodec。

  DubboCountCodec 与DubboCodec 涉及到 dubbo的通讯协议,这个后续再仔细学习。

 继承的AbstractServer的构造方法里,会调用抽象的doOpen()方法,实际调用NettyServer的doOpen() 打开端口监听。

三、 doOpen 启动NettyServer端口监听

doOpen方法代码较少,主要就是NettyServer  设置&启动。

因为netty的封装,一个简单的nettyserver启动,主要关注: 1. 消息处理 handler ,2. 消息解码&编码

如下:

@Override
    protected void doOpen() throws Throwable {
        bootstrap = new ServerBootstrap();

        bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("NettyServerBoss", true));
        workerGroup = new NioEventLoopGroup(getUrl().getPositiveParameter(IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS),
                new DefaultThreadFactory("NettyServerWorker", true));

        // 1. nettyServerHandler  构造方法需要传入 经过链式包装的handler
        final NettyServerHandler nettyServerHandler = new NettyServerHandler(getUrl(), this);
        channels = nettyServerHandler.getChannels();

        bootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)
                .childOption(ChannelOption.SO_REUSEADDR, Boolean.TRUE)
                .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        // FIXME: should we use getTimeout()?
                        int idleTimeout = UrlUtils.getIdleTimeout(getUrl());

                        // 2. encode & decode 编码 adapter ,需要获取父类注入的 codec2对象,
                        NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
                        ch.pipeline()//.addLast("logging",new LoggingHandler(LogLevel.INFO))//for debug
                                .addLast("decoder", adapter.getDecoder())  //3. 收取消息的解码器
                                .addLast("encoder", adapter.getEncoder()) //4.发送消息的编码器
                                .addLast("server-idle-handler", new IdleStateHandler(0, 0, idleTimeout, MILLISECONDS))
                                .addLast("handler", nettyServerHandler); //5. 收发消息的handler
                    }
                });
        // bind
        ChannelFuture channelFuture = bootstrap.bind(getBindAddress());
        channelFuture.syncUninterruptibly();
        channel = channelFuture.channel();
    }

注释1、5 ,就是 netty 的消息handler 实例化与设置。

注释2、3、4 ,为netty消息 解码、编码器的实现与设置。 在注释2,NettyCodecAdapter 内部,会 借由传入的Codec2 对象实现解编码工作。

1. Channel 转换&处理 

   NettyServerHandler 是netty的消息处理实现,在方法channelRead、write等里面,会从入参ChannelHandlerContext里面获取实际的netty 数据channel,转换成Dubbo定义的NettyChannel类, 执行相应后续业务动作。

相关类图如下:

Channel、ChannelHandler 是dubbo定义的抽象接口。

NettyChannel是一个 包装类,持有一个netty的实际数据channel通道,同时实现了Dubbo的Channel、ChannelHandler 的抽象接口, channel handler里面的 需要实际读取、写入数据时,会由nettychannel里面的 channel 属性实现。

NettyChannel是一个中间类,类似的应该还有MinaChannel,功能主要是封装底层传输实现, 包装成框架的逻辑接口 。 是一个隔离中间层。

在上面注释1.中,nettyserverhandler初始化的时候 new NettyServerHandler(getUrl(), this) ,传入了 nettyserver对象。 因为nettyserver是 继承 AbstractPeer ,算是对channelhandler的再次封装。

所以当netty收到消息时,NettyServerHandler 的channelRead方法会被调用:

  public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler);
        try {
            handler.received(channel, msg); // 1. channel 为NettyChannel,里面有netty的实际数据传输channel ,
        } finally {
            NettyChannel.removeChannelIfDisconnected(ctx.channel());
        }
    }

channelRead方法里面,会从netty的ctx里面获取数据channel,然后封装成功nettychannel, 在调用nettychannel的sent方法时,会将对象写入真实的channel,发送!

方法里面的handler 对象,调用包装链如下:

NettyServer -> MultiMessageHandler -> HeartbeatHandler -> AllChannelHandler ->  DecodeHandler ->  HeaderExchangeHandler -> DubboProtocol.requestHandler

HeaderExchangeHandler  是整个 handler 的最后一个,在其received方法里面,会执行 handleRequest 方法,调用持有的 DubboProtocol.requestHandler的reply方法,执行invoke操作,获取方法结果res,执行channel.send
方法,将结果res写入通道。
这里的res 是调用结果,netty会调用启动时设置的 encode编码器,将其对象序列号 & 编码 发送。
channel 是 在 HeaderExchangeHandler 里面经过 包装过一次的 ExchangeChannel。 channel 链为:ExchangeChannel -> NettyChannel



四、总结

Dubbo Server 启动的过程,就是channel handler 层层包装。 Dubbo的装饰模式、链式调用, 遍布整个过程。 Invoke、Handler、Channel 等等,都是如此。 不记录下来,理解非常困难。

在 handler链的最高层是NettyServerHandler, 负责转换 Netty的具体channel与 Dubbo框架的Channel,应用不同的框架传输,应该都存在这样一个handler。
在handler链的最底部,是HeaderExchangeHandler, 调用Invoke,执行Filter链,获取服务结果,将结果写入channel,发送给客户端。
在handler链的中间,会有一个扩展,AllChannelHandler 位置。 让用户可以定制自己的hannler,执行特定操作。 默认的AllChannelHandler 是将后续执行放入线程池,异步化执行。

server启动时传入的encoder、decoder,会调用codec 对象,动态获取 序列化工程,序列号对象& 编码输出。

经过记录一遍,过程更清晰。 

猜你喜欢

转载自www.cnblogs.com/keep-code/p/11066233.html
今日推荐