dubbo的handler机制

Dubbo的整套handler。。。反正我刚看的时候挺头疼。从Protocol层到Transporter层。纵深3层。从DubboProtocol构建,被逐层传递到NettyServer,然后在逐层返回。整个过程中,还被不断包裹,从同步到异步,不停变换用法和说法。总之,看得挺累,还是坚持逐个分析一遍。下面来说说。

先看一张图,是使用Dubbo Protocol并且使用Netty作为服务器的情况下Handler的整个包装过程。

image

解释下上面这张图

1.在DubboProtocol中构建ExchangeHandler命名requestHandler

2.在Exchange层做两次包装new DecodeHandler(new HeaderExchangeHandler(requestHandler)),具体参考类:HeaderExchanger

​ ① 使用HeaderExchangeHandler做一次包装,HeaderExchangeHandler的作用是实现了Request和Response的概念,当接到received请求后,将请求转为reply。请参考类HeaderExchangeHandler,对这部分不熟悉的可以参考文章dubbo的exchange层

​ ② 使用DecodeHandler做一次包装,DecodeHandler的作用是用来对Request MessageResponse Message做解码操作,解码完成后才能给HeaderExchangeHandler使用。

3.在Exchange层包装后的Handler会被传递到Transporter层(NettyTransporter)并且把类型转换成ChannelHandler,因为ChannelHandler更为抽象。

4.HandlerTransporter层流转,会被传递到NettyServer

5.在NettyServer中被AllChannelHandler包装,其作用是把NettyServer接收到的请求转移给Transporter层的线程池来处理。同步转异步。

6.Handler被再次NettyServerHandler包装,NettyServerHandler的父类是ChannelDuplexHandler。它属于NettyHandler。由Netty来管理和调用其中的回调方法。Netty在接受到channelActivechannelRead等方法后,会把请求转移给DubboHandler,这样每当请求过来,NettyHandler接到请求就立马把数据和相关信息转交给DubboHandler,由DubboHandler来管理了。

上述的整个包装过程基本提现了Dubbo ProtocolHandler的转移过程。从DubboProtocol构建逐层向下传递。当Netty接到TCP请求后,调用过程又逐层向上传递。下面看下几个关键转折点的代码。

1.请参考DubboProtocol类,Dubbo Handler初始化创建的地方。

private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {
    // ----------------------此处省略一堆代码------------------------
};

2.下面看下HeaderExchangeHandlerRequestResponse概念重点提现的地方

public class HeaderExchangeHandler implements ChannelHandlerDelegate {
    // ----------------------此处生路一堆代码------------------------
    @Override // 接受信息
    public void received(Channel channel, Object message) throws RemotingException {
        channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());
        ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel);
        try {
            if (message instanceof Request) {// 看看信息是不是Request
                // handle request.
                Request request = (Request) message;
                if (request.isEvent()) {
                    handlerEvent(channel, request);
                } else {
                    if (request.isTwoWay()) {// twoWay代表这个消息要回复
                        // 服务器端接到请求,调用handleRequest得到Response。
                        // 这边就提现了Request和Response的概念
                        // handleRequest,实际上是去做实际的业务动作了
                        Response response = handleRequest(exchangeChannel, request);
                        channel.send(response);
                    } else {
                        // 不需要返回
                        handler.received(exchangeChannel, request.getData());
                    }
                }
            } else if (message instanceof Response) {// 看看信息是不是Response
                // 如果是Response,那么消息肯定是从Privoder方发来的。
                handleResponse(channel, (Response) message);
            } else if (message instanceof String) {
                if (isClientSide(channel)) {
                    Exception e = new Exception("Dubbo client can not supported string message: " + message + " in channel: " + channel + ", url: " + channel.getUrl());
                    logger.error(e.getMessage(), e);
                } else {
                    String echo = handler.telnet(channel, (String) message);
                    if (echo != null && echo.length() > 0) {
                        channel.send(echo);
                    }
                }
            } else {
                handler.received(exchangeChannel, message);
            }
        } finally {
            HeaderExchangeChannel.removeChannelIfDisconnected(channel);
        }
    }
    
    // 收到Privoder方返回的Response信息,并且做出处理
    static void handleResponse(Channel channel, Response response) throws RemotingException {
        if (response != null && !response.isHeartbeat()) {
            // 调用DefaultFuture.received来通知Response消息到了。
            DefaultFuture.received(channel, response);
        }
    }
  
    // 服务器端接到请求,调用handleRequest得到Response。
    // 这边就提现了Request和Response的概念
    // handleRequest,实际上是去做实际的业务动作了
    Response handleRequest(ExchangeChannel channel, Request req) throws RemotingException {
        Response res = new Response(req.getId(), req.getVersion());
        if (req.isBroken()) {
            Object data = req.getData();

            String msg;
            if (data == null) msg = null;
            else if (data instanceof Throwable) msg = StringUtils.toString((Throwable) data);
            else msg = data.toString();
            res.setErrorMessage("Fail to decode request due to: " + msg);
            res.setStatus(Response.BAD_REQUEST);

            return res;
        }
        // find handler by message class.
        Object msg = req.getData();
        try {
            // handle data.
            // 完成实际的业务动作,也就是调用DubboProtocol.requestHandler.reply类中的实现。
            // 并且返回Response信息
            Object result = handler.reply(channel, msg);
            res.setStatus(Response.OK);
            res.setResult(result);
        } catch (Throwable e) {
            res.setStatus(Response.SERVICE_ERROR);
            res.setErrorMessage(StringUtils.toString(e));
        }
        return res;
    }
    // ----------------------此处生路一堆代码------------------------
}

上面的代码比较多,这边总结下。received方法用于接受信息,这个方法是ProviderConsumer共用的。Provider接收的信息必然是Request,它所处的角色就类似与服务器。Consumer接收的信息必然是Response,它所处的角色就类似于客户端。当然,对于事件Event类信息另说,这边为了思路清晰,不展开细说。

角色Provider接收Request信息。然后就是做业务动作,接着就是判断是否回复Response,要看twoWay这个标识。具体再看下代码中的一些注释。追踪下需要回复Response的情况(因为大部分情况下,我们都是用同步请求,需要回复Response

找到代码Object result = handler.reply(channel, msg);reply动作会调用到DubboProtocol.requestHandler.reply。这个地方如果自己跟代码会比较乱。对Dubbo Handler不太熟悉可以看文章dubbo的handler机制。提醒下,看HeaderExchanger.bind动作的实现,会发现DubboProtocol.requestHandler被包起来了。所以这个地方reply一定是调用DubboProtocol.requestHandler的。看下那块代码。

@Override// 执行invoke并且返回Response信息
public Object reply(ExchangeChannel channel, Object message) throws RemotingException {
    if (message instanceof Invocation) {
        Invocation inv = (Invocation) message;
        Invoker invoker = getInvoker(channel, inv);
                // ----------------------此处生路一堆代码------------------------
        RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());
        // 执行invoke并且返回Response信息
        return invoker.invoke(inv);
    }
}

invoker.invoke(inv);执行的是服务在初始化的时候Protocol.export出来的。实际export出来的Exporter会被过滤器链给包住。这部分知识可以查看文档dubbo的filter

3.看下NettyServerHandler的源码,Netty handler服务与Dubbo handler职责交接的地方。

public class NettyServerHandler extends ChannelDuplexHandler {
        // dubbo的handler
    private final ChannelHandler handler;

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.fireChannelActive();

        NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler);
        try {
            if (channel != null) {
                channels.put(NetUtils.toAddressString((InetSocketAddress) ctx.channel().remoteAddress()), channel);
            }
            // netty的客户端激活消息被传递给dubbo的handler,并转换概念变为connected
            handler.connected(channel);
        } finally {
            NettyChannel.removeChannelIfDisconnected(ctx.channel());
        }
    }


    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler);
        try {
            // netty的数据到达消息被传递给dubbo的handler,并转换概念变为received
            handler.received(channel, msg);
        } finally {
            NettyChannel.removeChannelIfDisconnected(ctx.channel());
        }
    }
}

上面的类,省略打大部分代码。只是以客户端建立连接和TCP数据到达作为案例说下。其他场景也是类似。

总结

DubboHandler纵深3层,包装过程较多,代码追踪比较麻烦。本文用一张示意图说了具体的包装过程。源码追踪过程要关注3个重要点位。

1.DubboProtocol类,Dubbo Handler初始化创建的地方

2.HeaderExchangeHandler类,RequestResponse概念重点提现的地方

3.NettyServerHandler类,Netty Handler服务与Dubbo Handler职责交接的地方

你可能感兴趣的:(dubbo的handler机制)