dubbo transport网络传输层

来源:互联网 发布:js 判断鼠标滑动方向 编辑:程序博客网 时间:2024/04/28 19:45

NettyHandler:

继承netty对象SimpleChannelHandler,重写了channelConnected、channelDisconnected、messageReceived、writeRequested、exceptionCaught方法,当netty的通道发生连接、断开连接、收到消息、写入消息、捕获异常等事件时触发NettyHandler中的对应方法。在这些方法中后续的处理链为NettyServer/NettyClient->MultiMessageHandler->HeartbeatHandler->AllChannelHandler->Decodehandler->HeaderExchangerHandler->DubboProtocol$requestHandler。



    @Override    protected void doOpen() throws Throwable {//NettyClient        NettyHelper.setNettyLoggerFactory();        bootstrap = new ClientBootstrap(channelFactory);        // config        // @see org.jboss.netty.channel.socket.SocketChannelConfig        bootstrap.setOption("keepAlive", true);        bootstrap.setOption("tcpNoDelay", true);        bootstrap.setOption("connectTimeoutMillis", getTimeout());        final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);        bootstrap.setPipelineFactory(new ChannelPipelineFactory() {            public ChannelPipeline getPipeline() {                NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyClient.this);                ChannelPipeline pipeline = Channels.pipeline();                pipeline.addLast("decoder", adapter.getDecoder());                pipeline.addLast("encoder", adapter.getEncoder());                pipeline.addLast("handler", nettyHandler);                return pipeline;            }        });    }

    @Override    protected void doOpen() throws Throwable {//NettyServer        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() {            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());    }
通过上面NettyClient和NettyServer的源代码,可以看到在pipeline中都添加了一个nettyHandler,由于nettyHandler会捕获Netty通道的事件,里面会调用NettyClient/NettyServer的connected、disconnected等方法,先以NettyClient.connected方法分析.





在调用到DubboProtocol的refer->getClients->initClient,在initClient内部会调用Exchanger.connect(url,requesthandler),这个handler内容如下:

    private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {                public Object reply(ExchangeChannel channel, Object message) throws RemotingException {            if (message instanceof Invocation) {//只处理远程调用消息Invocation                Invocation inv = (Invocation) message;                Invoker<?> invoker = getInvoker(channel, inv);                //如果是callback 需要处理高版本调用低版本的问题                if (Boolean.TRUE.toString().equals(inv.getAttachments().get(IS_CALLBACK_SERVICE_INVOKE))){                    String methodsStr = invoker.getUrl().getParameters().get("methods");                    boolean hasMethod = false;                    if (methodsStr == null || methodsStr.indexOf(",") == -1){                        hasMethod = inv.getMethodName().equals(methodsStr);                    } else {                        String[] methods = methodsStr.split(",");                        for (String method : methods){                            if (inv.getMethodName().equals(method)){                                hasMethod = true;                                break;                            }                        }                    }                    if (!hasMethod){                        logger.warn(new IllegalStateException("The methodName "+inv.getMethodName()+" not found in callback service interface ,invoke will be ignored. please update the api interface. url is:" + invoker.getUrl()) +" ,invocation is :"+inv );                        return null;                    }                }                RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());                return invoker.invoke(inv);            }            throw new RemotingException(channel, "Unsupported request: " + message == null ? null : (message.getClass().getName() + ": " + message) + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress());        }        @Override        public void received(Channel channel, Object message) throws RemotingException {            if (message instanceof Invocation) {                reply((ExchangeChannel) channel, message);            } else {                super.received(channel, message);            }        }        @Override        public void connected(Channel channel) throws RemotingException {            invoke(channel, Constants.ON_CONNECT_KEY);        }        @Override        public void disconnected(Channel channel) throws RemotingException {            if(logger.isInfoEnabled()){                logger.info("disconected from "+ channel.getRemoteAddress() + ",url:" + channel.getUrl());            }            invoke(channel, Constants.ON_DISCONNECT_KEY);        }                private void invoke(Channel channel, String methodKey) {            Invocation invocation = createInvocation(channel, channel.getUrl(), methodKey);            if (invocation != null) {                try {                    received(channel, invocation);                } catch (Throwable t) {                    logger.warn("Failed to invoke event method " + invocation.getMethodName() + "(), cause: " + t.getMessage(), t);                }            }        }                private Invocation createInvocation(Channel channel, URL url, String methodKey) {            String method = url.getParameter(methodKey);            if (method == null || method.length() == 0) {                return null;            }            RpcInvocation invocation = new RpcInvocation(method, new Class<?>[0], new Object[0]);            invocation.setAttachment(Constants.PATH_KEY, url.getPath());            invocation.setAttachment(Constants.GROUP_KEY, url.getParameter(Constants.GROUP_KEY));            invocation.setAttachment(Constants.INTERFACE_KEY, url.getParameter(Constants.INTERFACE_KEY));            invocation.setAttachment(Constants.VERSION_KEY, url.getParameter(Constants.VERSION_KEY));            if (url.getParameter(Constants.STUB_EVENT_KEY, false)){                invocation.setAttachment(Constants.STUB_EVENT_KEY, Boolean.TRUE.toString());            }            return invocation;        }    };


接着在HeaderExchanger中对DubboProtocol中的handler进行处理,先用HeaderExchangeHandler进行包装,再用DecodeHandler进行了包装。

public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {        return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))));    }


在调用Transporters.connect时会触发NettyTransporter.connect->NettyClient的构造函数->wrapChannelHandler函数->ChannelHandlers.wrap->ChannelHandlers->wrapInternal

protected ChannelHandler wrapInternal(ChannelHandler handler, URL url) {        return new MultiMessageHandler(new HeartbeatHandler(ExtensionLoader.getExtensionLoader(Dispatcher.class)                                        .getAdaptiveExtension().dispatch(handler, url)));    }

从这里可以看到,又在原来handler的基础上封装了AllChannelHandler->HeratbeatHandler->MutliMessageHandler。

所以整个处理链为NettyServer/NettyClient->MutliMessageHandler->HeartbeatHandler->AllChannelHandler->DecodeHandler->HeaderExchangerHandler->DubboProtocol$requestHandler。


MutliMessageHandler

对于消息的received事件,如果消息是指传输的,解码后会由MultiMessage承载,并遍历其中的消息,交由下一个handler处理。
 @SuppressWarnings("unchecked")@Override    public void received(Channel channel, Object message) throws RemotingException {        if (message instanceof MultiMessage) {            MultiMessage list = (MultiMessage)message;            for(Object obj : list) {                handler.received(channel, obj);            }        } else {            handler.received(channel, message);        }    }


HeartbeatHandler

设置通道的读定时间戳。
(1)对于connected事件,设置通道的读写时间戳为当前时间。
(2)对于disconnected事件,清空读写时间戳。
(3)对于sent事件,设置通道的写时间戳为当前时间。
(4)对于received事件,设置通道的读时间戳为当前时间;若消息是心跳消息请求(mEvent=true and mData=null),如果此心跳事件需要响应(mTwoWay=true),则构建响应对象Response,并发送响应消息不做后续channel处理;若消息是心跳响应(mEvent=true and mData=null),记录日志直接返回,不在做后续的channel处理,若不是心跳消息,则交下一个处理器处理。

Dispatcher创建的Handler处理器

根据URL参数dispatcher选择Dispatcher类,不同的Dispatcher创建不同的Handler,表示通道的事件的处理模式,在这些Handler中构建异步执行任务ChannelEventRunnable,异步执行connected、disconnected、received、caught、sent等操作。目前支持四种Handler处理器:
(1)AllChannelHandler:除发送消息之外,所有的事件都采用线程池处理。
(2)ExecutionChannelHandler:与AllChannelHandler不同之处,若创建的线程池ExecutorService不可用,AllChannelHandler将使用共享线程池,而ExecutionChannelHandler只有报错。
(3)ChannelHandler:所有事件都真接有channelhandler处理,不采用线程池。
(4)MessageOnlyChannelHandler:只有接受消息采用线程池。
(5)ConnectionOrderedChannelHandler:单线程,排队处理。
在上述的Handler的初始化过程中,会根据url的参数threadpool来创建线程份,目前支持的线程池类有三种,默认FixedThreadPool。

FixedThreadPool:此线程池启动时即创建固定大小的线程数,不做任何伸缩。
CachedThreadPool:此线程池可伸缩,线程空闲一分钟后回收,新请求重新创建线程。
LimitedThreadPool:此线程一直增长,直到上限,增长后不收缩。

DecodeHandler

对读取的消息进行解码操作。

HeaderExchangerHandler

处理connected、disconnected、sent、received、caught等事件,并调用DubboProtocol$requestHandler内部响应的事件方法。
(1)connected:更新通道的读取时间戳和写入时间戳(这和HeartbeatHandler有点重复啊?),调用DubboProtoco$requestHandler匿名内部类的connected事件方法。
(2)disconnected:清空读写时间戳(和HeartbeatHandler一样?),调用DubboProtocol$requestHandler的disconnected方法。
(3)sent:更新通道的写入时间戳,回调用DubboProtocol$requestHandler的sent,由于requestHandler没有重写sent方法,即调用其父类ChannelHandlerAdapter 的该方法,这是一个空方法。
    public void sent(Channel channel, Object message) throws RemotingException {//HeaderExchangeHandler        Throwable exception = null;        try {            channel.setAttribute(KEY_WRITE_TIMESTAMP, System.currentTimeMillis());            ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel);            try {                handler.sent(exchangeChannel, message);            } finally {                HeaderExchangeChannel.removeChannelIfDisconnected(channel);            }        } catch (Throwable t) {            exception = t;        }        if (message instanceof Request) {            Request request = (Request) message;            DefaultFuture.sent(channel, request);        }        if (exception != null) {            if (exception instanceof RuntimeException) {                throw (RuntimeException) exception;            } else if (exception instanceof RemotingException) {                throw (RemotingException) exception;            } else {                throw new RemotingException(channel.getLocalAddress(), channel.getRemoteAddress(),                                            exception.getMessage(), exception);            }        }    }

如果消息是Request,即发送请求,则根据请求的唯一标识ID获取DefaultFuture对象,并设置发送时间为当前时间,用于在超时之后分析客户端的耗时、服务端的耗时。
(4)更新通道的读取时间戳。
    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) {                // handle 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);            } else if (message instanceof String) {                if (isClientSide(channel)) {                    Exception e = new Exception("hsf 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);        }    }

如果是Request请求,并且需要对请求做回执时,handleRequest会调用DubboProtocol$requestHandler的.reply方法,在该方法中完成服务端本地方法调用并返回结果,然后handleRequest方法中根据Request对象的ID构建Response对象,将回调用服务的执行结果作为Response对象的结果字段;最后调用channel.send方法向客户端发送响应消息。如果是Request对象并且不需要对请求做回执时,则调用DubboProtocol$requestHandler匿名内部类received方法,如果是Response且不是心跳响应,则根据response中的ID获取DefaultFuture对象,若DefaultFuture对象不存在,则该响应已经超时并且已经被删除了,若还存在,则设置response的值,唤醒线程,DefaultFuture对象的get方法返回调用值。

DubboProtocol$requestHandler

(1)connected:检查URL中是否有onconnect参数,若有则回调用该参数指定的方法。
(2)disconnected:检查URL中是否有ondisconnect参数,若在则回调该参数指定的方法。
(3)received:若消息是Invocation对象则调用reply方法,根据生成的serviceKey在AbstractProtocol.exporterMap中查找DubboExporter对象,然后根据此exporter对象获取服务创建的invoker对象,从而完成服务的本地方法调用;若不是则调用父类的received方法,不进行任何处理。