dubbo源码解析之处理请求(五)

源码分析基于dubbo 2.7.1

NettyServerHandler继承了netty的ChannelDuplexHandler,这里只关注channelRead方法

    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler);
        try {
            handler.received(channel, msg);
        } finally {
            NettyChannel.removeChannelIfDisconnected(ctx.channel());
        }
    }

异步处理

handler就是NettyServer, 前面说过NettyServer是一个装饰类,它会调用到AllChannelHandler.received

public void received(Channel channel, Object message) throws RemotingException {
    ExecutorService cexecutor = getExecutorService();
    try {
        cexecutor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message));
    } catch (Throwable t) {
        ...
    }
}

就是给ExecutorService提交一个任务,异步处理请求。

看看ChannelEventRunnable.run,会根据state进行不同的处理。但实际操作都转发到handler继续处理。

解码

再看看DecodeHandler.received

    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());
        }
        // 调用下一个节点
        handler.received(channel, message);
    }

处理请求

HeaderExchangeHandler

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 request = (Request) message;
            if (request.isEvent()) {
                handlerEvent(channel, request);
            } else {
                if (request.isTwoWay()) {
                    Response response = handleRequest(exchangeChannel, request);
                    channel.send(response);
                } else {
                    handler.received(exchangeChannel, request.getData());
                }
            }
        } else if (message instanceof Response) {
            // 处理响应
            handleResponse(channel, (Response) message);
        } ...
    } finally {
        HeaderExchangeChannel.removeChannelIfDisconnected(channel);
    }
}

twoWay表示需要响应的请求。这里调用handleRequest方法,将返回一个Response,最后通过channel将它发送给客户端。

    void handleRequest(final ExchangeChannel channel, Request req) throws RemotingException {
        Response res = new Response(req.getId(), req.getVersion());
        ...
        Object msg = req.getData();
        try {
            // 处理请求
            CompletableFuture<Object> future = handler.reply(channel, msg);
            if (future.isDone()) {
                res.setStatus(Response.OK);
                res.setResult(future.get());
                channel.send(res);  // 发送结果
                return;
            }
            future.whenComplete((result, t) -> {
                ...
            });
        } catch (Throwable e) {
            ...
        }
    }

处理完成后通过channel.send(res);发送结果给客户端,这个方法会调用NettyChannel

    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) {
            ...
    }

可以看到,也是通过netty发送结果。

业务逻辑处理

handler.reply终于调用到DubboProtocol.requestHandler了

        public CompletableFuture<Object> reply(ExchangeChannel channel, Object message) throws RemotingException {
            Invocation inv = (Invocation) message;
            // 获取Invoker
            Invoker<?> invoker = getInvoker(channel, inv);
            ...
            
            RpcContext rpcContext = RpcContext.getContext();
            rpcContext.setRemoteAddress(channel.getRemoteAddress());
            // 调用Invoker
            Result result = invoker.invoke(inv);

            if (result instanceof AsyncRpcResult) {
                return ((AsyncRpcResult) result).getResultFuture().thenApply(r -> (Object) r);
            } else {
                return CompletableFuture.completedFuture(result);
            }
        }

这里也是通过invoker调用,逐渐调用到我们的业务方法。

先看看getInvoker方法

Invoker<?> getInvoker(Channel channel, Invocation inv) throws RemotingException {
    ...

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

    return exporter.getInvoker();
}

很简单,从exporterMap中获取DubboExporter,再获取对应的Invoker。
这里可以回顾上篇文章说到的DubboProtocol.export方法,它用invoker构建了DubboExporter,缓存到exporterMap中。

invoker的创建

那么invoker在哪里创建的呢?

这时要回到dubbo server启动,那里说到ServiceConfig.doExportUrlsFor1Protocol

if (registryURLs != null && registryURLs.size() > 0) {
    for (URL registryURL : 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);
    }
}

是的,invoker就是在这里创建的。(注意,参数ref就是业务逻辑的实现类)

proxyFactory默认使用的是JavassistProxyFactory。

    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        // 生成动态代理类
        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);
            }
        };
    }

再回顾一下RegistryProtocol.doLocalExport方法,为Invoker添加了包装类,生成了InvokerDelegete。所以invoker装饰层次为ProtocolFilterWrapper&Invoker ---> RegistryProtocol&InvokerDelegete ---> DelegateProviderMetaDataInvoker ---> JavassistProxyFactory&AbstractProxyInvoker。
(ProtocolFilterWrapper下面会说到)

JavassistProxyFactory中创建的invoke,会通过Wrapper调用到实际的业务方法。

Wrapper.getWrapper也是动态生成代理类,JavassistProxyFactory正是通过它调用业务方法。
Wrapper会拼凑代码字符串,再通过javassist生成代理类。
过程比较繁琐,直接看生成的代理类吧。

原接口

public interface HelloService {
    String hello(String user) ;
    String hello2(String user) ;
}

生成的Wrapper,关键的方法在invokeMethod

public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException{ 
    com.dubbo.start.service.HelloService w; 
    try{
        w = ((com.dubbo.start.service.HelloService)$1); 
    } catch(Throwable e){ 
        throw new IllegalArgumentException(e); 
    } 
    try{ 
        if( "hello".equals( $2 )  &&  $3.length == 1 ) {
            return ($w)w.hello((java.lang.String)$4[0]); 
        } 
        if( "hello2".equals( $2 )  &&  $3.length == 1 ) {  
            return ($w)w.hello2((java.lang.String)$4[0]);
        } 
    } catch(Throwable e) {     
        throw new java.lang.reflect.InvocationTargetException(e);  
    }
}

$1, $2, $3在javassist中表示方法参数。
可以看到,通过方法名和参数数调用对应的逻辑方法。
所以dubbo暴露的接口就尽量不要方法重构了

Filter

前面也说过了,DubboProtocol处理前,会经过包装类ProtocolListenerWrapper,ProtocolFilterWrapper。

ProtocolFilterWrapper.export 会调用buildInvokerChain,为每一个filter创建一个invoker,

private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
    Invoker<T> last = invoker;
    List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
    if (filters.size() > 0) {
        for (int i = filters.size() - 1; i >= 0; i--) {
            final Filter filter = filters.get(i);
            final Invoker<T> next = last;
            last = new Invoker<T>() {
                ...
                public Result invoke(Invocation invocation) throws RpcException {
                    return filter.invoke(next, invocation);
                }
            };
        }
    }
    return last;
}

dubbo通过url中的service.filter参数查找filter扩展类,可以dubbo:service标签上添加filter属性,用逗号分隔多个filter。

dubbo默认有如下Filter

  • EchoFilter
  • ClassLoaderFilter
  • GenericFilter
  • ContextFilter
  • TraceFilter
  • TimeoutFilter
  • MonitorFilter
  • ExceptionFilter
  • ValidationFilter
  • TpsLimitFilter

可以通过Filter实现限流:
Dubbo之限流TpsLimitFilter源码分析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,277评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,777评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,946评论 0 245
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,271评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,636评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,767评论 1 221
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,989评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,733评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,457评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,674评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,155评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,518评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,160评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,114评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,898评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,822评论 2 280
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,705评论 2 273

推荐阅读更多精彩内容