dubbo远程调用源码分析(二):服务端接收请求

dubbo远程调用的源码分析,分成了三篇文章地址分别如下:

dubbo远程调用源码分析(一):客户端发送请求

dubbo远程调用源码分析(二):服务端接收请求

dubbo远程调用源码分析(三):客户端接收反馈后的处理


下面看provider端接收到消息后的处理过程

首先要经过NettyClient的事件处理器,顺序是NettyCodecAdapter.DeCoder和NettyHandler

NettyCodecAdapter的Decoder是在NettyCodecAdapter类中定义的

    private class InternalDecoder extends SimpleChannelUpstreamHandler {


        private com.alibaba.dubbo.remoting.buffer.ChannelBuffer buffer =

               com.alibaba.dubbo.remoting.buffer.ChannelBuffers.EMPTY_BUFFER;


        @Override

        public void messageReceived(ChannelHandlerContext ctx, MessageEvent event) throws Exception{

            Object o =event.getMessage();

            if (!(o instanceof ChannelBuffer)) {

               ctx.sendUpstream(event);

                return;

            }


           ChannelBuffer input = (ChannelBuffer) o;

            int readable = input.readableBytes();

            if(readable <= 0) {

                return;

            }

 

           com.alibaba.dubbo.remoting.buffer.ChannelBuffer message;

            if(buffer.readable()) {

                if(buffer instanceof DynamicChannelBuffer) {

                   buffer.writeBytes(input.toByteBuffer());

                   message = buffer;

                } else{

                   int size = buffer.readableBytes() + input.readableBytes();

                   message =com.alibaba.dubbo.remoting.buffer.ChannelBuffers.dynamicBuffer(

                           size > bufferSize ? size : bufferSize);

                   message.writeBytes(buffer, buffer.readableBytes());

                   message.writeBytes(input.toByteBuffer());

                }

            } else {

                message= com.alibaba.dubbo.remoting.buffer.ChannelBuffers.wrappedBuffer(

                       input.toByteBuffer());

            }


           NettyChannel channel = NettyChannel.getOrAddChannel(ctx.getChannel(),url, handler);

            Object msg;

            int saveReaderIndex;


            try {

                //decode object.

                do {

                   saveReaderIndex = message.readerIndex();

                    try{

                       msg = codec.decode(channel, message);

                    }catch (IOException e) {

                       buffer = com.alibaba.dubbo.remoting.buffer.ChannelBuffers.EMPTY_BUFFER;

                       throw e;

                    }

                    if(msg == Codec2.DecodeResult.NEED_MORE_INPUT) {

                       message.readerIndex(saveReaderIndex);

                       break;

                    }else {

                       if (saveReaderIndex == message.readerIndex()) {

                           buffer = com.alibaba.dubbo.remoting.buffer.ChannelBuffers.EMPTY_BUFFER;

                           throw new IOException("Decode without read data.");

                       }

                       if (msg != null) {

                           Channels.fireMessageReceived(ctx, msg, event.getRemoteAddress());

                       }

                    }

                } while(message.readable());

            } finally {

                if(message.readable()) {

                   message.discardReadBytes();

                    buffer= message;

                } else{

                   buffer = com.alibaba.dubbo.remoting.buffer.ChannelBuffers.EMPTY_BUFFER;

                }

               NettyChannel.removeChannelIfDisconnected(ctx.getChannel());

            }

        }

 

        @Override

        public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {

           ctx.sendUpstream(e);

        }

    }
其中的messageReceived()方法就是接收消息之后执行的方法

首先从event把接收到的消息读到ChannelBuffer中,然后用Codec2.decode方法解码

Codec2接口由DubboCodec类实现,decode()方法具体是在在DubboCodec的父类ExchangeCodec类中:

    public Object decode(Channel channel, ChannelBuffer buffer) throws IOException {

        int readable =buffer.readableBytes();

        byte[] header =new byte[Math.min(readable, HEADER_LENGTH)];

       buffer.readBytes(header);

        return decode(channel, buffer, readable, header);

    }

最后调用的decode方法如下:

    protected Object decode(Channel channel, ChannelBuffer buffer, int readable, byte[] header)throws IOException {

        // check magicnumber.

        if (readable> 0 && header[0] != MAGIC_HIGH

                ||readable > 1 && header[1] != MAGIC_LOW) {

            int length= header.length;

            if(header.length < readable) {

                header= Bytes.copyOf(header, readable);

               buffer.readBytes(header, length, readable - length);

            }

            for (int i= 1; i < header.length - 1; i++) {

                if(header[i] == MAGIC_HIGH && header[i + 1] == MAGIC_LOW) {

                   buffer.readerIndex(buffer.readerIndex() - header.length + i);

                   header = Bytes.copyOf(header, i);

                   break;

                }

            }

            return super.decode(channel, buffer, readable, header);

        }

        // checklength.

        if (readable< HEADER_LENGTH) {

            return DecodeResult.NEED_MORE_INPUT;

        }


        // get datalength.

        int len =Bytes.bytes2int(header, 12);

        checkPayload(channel,len);


        int tt = len +HEADER_LENGTH;

        if (readable< tt) {

            return DecodeResult.NEED_MORE_INPUT;

        }


        // limit inputstream.

       ChannelBufferInputStream is = new ChannelBufferInputStream(buffer, len);


        try {

            return decodeBody(channel, is, header);

        } finally {

            if(is.available() > 0) {

                try {

                    if(logger.isWarnEnabled()) {

                       logger.warn("Skip input stream " + is.available());

                    }

                   StreamUtils.skipUnusedStream(is);

                } catch(IOException e) {

                   logger.warn(e.getMessage(), e);

                }

            }

        }

    }

该方法一开始根据head信息,判断当前接收到的消息是否完整,这个判定是和dubbo的响应机制有关,dubbo读取消息是以事件机制(Event)为基础的,是一种NIO的设计,当事件响应机制得到事件后,信息的传输可能不全,如果不全,则终止该次处理,等消息传输完整后再处理。

如果消息完整,需要解码,调用了decodeBody()方法:

    protected Object decodeBody(Channel channel, InputStream is, byte[] header) throws IOException {

        byte flag =header[2], proto = (byte) (flag & SERIALIZATION_MASK);

        Serialization s= CodecSupport.getSerialization(channel.getUrl(), proto);

        ObjectInput in= s.deserialize(channel.getUrl(), is);

        // get requestid.

        long id =Bytes.bytes2long(header, 4);

        if ((flag &FLAG_REQUEST) == 0) {

            // decode response.

            Response res = new Response(id);

            if ((flag& FLAG_EVENT) != 0) {

               res.setEvent(Response.HEARTBEAT_EVENT);

            }

            // getstatus.

            byte status= header[3];

           res.setStatus(status);

            if (status== Response.OK) {

                try {

                   Object data;

                    if(res.isHeartbeat()) {

                       data = decodeHeartbeatData(channel, in);

                    }else if (res.isEvent()) {

                       data = decodeEventData(channel, in);

                    }else {

                       data = decodeResponseData(channel, in, getRequestData(id));

                    }

                   res.setResult(data);

                } catch(Throwable t) {

                   res.setStatus(Response.CLIENT_ERROR);

                   res.setErrorMessage(StringUtils.toString(t));

                }

            } else {

               res.setErrorMessage(in.readUTF());

            }

            return res;

        } else {

            // decoderequest.

            Request req= new Request(id);

           req.setVersion("2.0.0");

           req.setTwoWay((flag & FLAG_TWOWAY) != 0);

            if ((flag& FLAG_EVENT) != 0) {

               req.setEvent(Request.HEARTBEAT_EVENT);

            }

            try {

                Objectdata;

                if(req.isHeartbeat()) {

                   data = decodeHeartbeatData(channel, in);

                } else if (req.isEvent()) {

                   data = decodeEventData(channel, in);

                } else{

                   data = decodeRequestData(channel, in);

                }

               req.setData(data);

            } catch(Throwable t) {

                // badrequest

               req.setBroken(true);

               req.setData(t);

            }

            return req;

        }

    }

该方法最开始从header里面获取了flag和proto,并且获取序列化方式,下面有个flag的if判断,是因为provider接收消息和consumer接收provider反馈时都用这段代码,当provider接收消息时,代码走的是else部分,也就是新建了一个Request对象,然后把消息解码,放入Request对象。

得到Request对象后,回到InternalDecoder的messageReceived()方法,codec.decode()方法获得的Request对象赋值给msg,如果消息是完整的而且正常解码了,会调用Channels.fireMessageReceived()方法做一些信息设置,该方法代码如下:

    /**

     * Sends a {@code"messageReceived"} event to the

     * {@linkChannelUpstreamHandler} which is placed in the closest upstream

     * from the handlerassociated with the specified

     * {@linkChannelHandlerContext}.

     *

     * @param message        the received message

     * @param remoteAddress  the remote address wherethe received message

     *                       came from

     */

    public static void fireMessageReceived(

           ChannelHandlerContext ctx, Object message, SocketAddress remoteAddress){

       ctx.sendUpstream(new UpstreamMessageEvent(

               ctx.getChannel(), message, remoteAddress));

    }

至此NettyCodecAdapter.DeCoder结束,下面是NettyHandler,触发的是NettyHandler的messageReceived()方法:

    @Override

    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {

        NettyChannel channel = NettyChannel.getOrAddChannel(ctx.getChannel(), url, handler);

        try {

           handler.received(channel, e.getMessage());

        } finally {

           NettyChannel.removeChannelIfDisconnected(ctx.getChannel());

        }

    }

其中调用了handler的received()方法,该方法的实现在DecodeHandler类中:

    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);

    }

这里的message是Request,所以走的是第二个if里的判断,这里面是解码,前面已经解码过了,其实不会再解码了,方法的最后是其他handler.received()方法,这个handler的实现类是HeaderExchangeHandler类,他的received()方法如下:

    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("Dubbo client can not supported stringmessage: " + 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);

        }

    }

该方法中的message是Request类型,并且request.isTwoWay是true,会调用handleReuqest方法得到处理结果Response,然后用Channel.send()方法把处理结果发送给consumer,下面是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("Failto decode request due to: " + msg);

           res.setStatus(Response.BAD_REQUEST);


            return res;

        }

        // find handlerby message class.

        Object msg =req.getData();

        try {

            // handledata.

            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;

    }

方法创建了一个Response对象,首先判断请求是否有效,无效则会直接返回标识错误的Response,否则从Request中得到封装的对象,然后调用handler.reply()方法,得到结果,这个handler是由DubboProtocol类中定义的ExchangeHandler类实现的,ExchangeHandler实际上是一个ExchangeHandlerAdapter,除了reply方法之外还实现了Channel接口的很多方法比如connected,received等,ExchangeHandler类的定义和reply方法如下:

private ExchangeHandler requestHandler = newExchangeHandlerAdapter() {

        public Object reply(ExchangeChannel channel, Object message) throws RemotingException {

            if (message instanceof 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 ,invokewill 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());

        }

    //还有很多别的方法,此处省略

    }

reply方法开始的时候调用getInvoker()方法,得到了Invoker,getInvoker()方法如下:

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

        boolean isCallBackServiceInvoke = false;

        boolean isStubServiceInvoke = false;

        int port =channel.getLocalAddress().getPort();

        String path =inv.getAttachments().get(Constants.PATH_KEY);

        //如果是客户端的回调服务.

       isStubServiceInvoke =Boolean.TRUE.toString().equals(inv.getAttachments().get(Constants.STUB_EVENT_KEY));

        if(isStubServiceInvoke) {

            port =channel.getRemoteAddress().getPort();

        }

        //callback

        isCallBackServiceInvoke= isClientSide(channel) && !isStubServiceInvoke;

        if(isCallBackServiceInvoke) {

            path =inv.getAttachments().get(Constants.PATH_KEY) + "." +inv.getAttachments().get(Constants.CALLBACK_SERVICE_KEY);

           inv.getAttachments().put(IS_CALLBACK_SERVICE_INVOKE,Boolean.TRUE.toString());

        }

        String serviceKey = serviceKey(port, path,inv.getAttachments().get(Constants.VERSION_KEY),inv.getAttachments().get(Constants.GROUP_KEY));

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


        if (exporter ==null)

            throw new RemotingException(channel, "Not found exported service: " +serviceKey + " in " + exporterMap.keySet() + ", may be version or group mismatch " + ", channel: consumer: " +channel.getRemoteAddress() + " --> provider: " +channel.getLocalAddress() + ", message:" + inv);

 
        return exporter.getInvoker();

    }

这个方法用端口号,接口地址,版本,分组号等信息获取了一个serviceKey,根据这个serviceKey从exporterMap中获得了目标Invoker,实际上这个exporterMap是在provider初始化的时候生成的,放在DubboProtocol的父类AbstractProtocol中,每个接口都会生成一个Exporter对象放在exporterMap里,用端口号、接口地址、版本、分组号当做map的key,Exporter对象会包含一个Invoker对象。

获取serviceKey的方法serviceKey()的代码在DubboProtocol的父类AbstractProtocol中:

    protected static String serviceKey(int port, String serviceName, String serviceVersion, StringserviceGroup) {

        return ProtocolUtils.serviceKey(port, serviceName, serviceVersion, serviceGroup);

    }

ProtocolUtils.serviceKey()方法代码:

    public static String serviceKey(int port, String serviceName, String serviceVersion, String serviceGroup) {

        StringBuilderbuf = new StringBuilder();

        if(serviceGroup != null && serviceGroup.length() > 0) {

           buf.append(serviceGroup);

           buf.append("/");

        }

       buf.append(serviceName);

        if(serviceVersion != null && serviceVersion.length() > 0 &&!"0.0.0".equals(serviceVersion)) {

           buf.append(":");

           buf.append(serviceVersion);

        }

       buf.append(":");

       buf.append(port);

       return buf.toString();

    }

其实就是用各种参数组合成的一个字符串

回到ExchangeHandler类的reply()方法,方法最后调用的invoker.invoke()方法,这个Invoker反射实际的接口实现类并处理信息,使用了工厂模式和动态代理的方式,这里的代理是Javassist代理,工厂代码在JavassistProxyFactory类中,如下:

public class JavassistProxyFactory extends AbstractProxyFactory {

 
   @SuppressWarnings("unchecked")

    public <T>T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {

        return (T)Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));

    }

 
    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);

            }

        };

    }


}

处理完信息并得到返回值后,回到HeaderExchangeHandler类的received()方法,上面的代码就是调用handleRequest()方法获得Response的过程,然后就调用channel.send()方法,把返回值发送给consumer,channel的实现类NettyChannel,他的send方法就是上面consumer给provider发信息的那个方法,

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

            throw new RemotingException(this, "Failed to send message " + message + "to " + getRemoteAddress()

                    +"in timeout(" + timeout + "ms) limit");

        }

    }

其中的channel.write就是发送的方法,在调用这个方法的时候会触发下行事件的事件处理器,也和consumer往provider发消息时一样,触发NettyCodecAdapter.encoder和NettyHandler,只不过此时调用的是DubboCodec的encodeResponse()方法(具体实现在父类ExchangeCodec中),而不是之前的encodeRequest()方法,所以encodeResponse()方法如下:

    protected void encodeResponse(Channel channel, ChannelBuffer buffer, Response res) throws IOException {

        int savedWriteIndex = buffer.writerIndex();

        try {

           Serialization serialization = getSerialization(channel);

            // header.

            byte[]header = new byte[HEADER_LENGTH];

            // set magic number.

           Bytes.short2bytes(MAGIC, header);

            // set request and serialization flag.

            header[2] =serialization.getContentTypeId();

            if(res.isHeartbeat()) header[2] |= FLAG_EVENT;

            // set response status.

            byte status= res.getStatus();

            header[3] =status;

            // set request id.

           Bytes.long2bytes(res.getId(), header, 4);

 

           buffer.writerIndex(savedWriteIndex + HEADER_LENGTH);

           ChannelBufferOutputStream bos = new ChannelBufferOutputStream(buffer);

            ObjectOutput out = serialization.serialize(channel.getUrl(), bos);

            // encode response data or error message.

            if (status== Response.OK) {

                if(res.isHeartbeat()) {

                   encodeHeartbeatData(channel, out, res.getResult());

                } else{

                   encodeResponseData(channel, out, res.getResult());

                }

            } else out.writeUTF(res.getErrorMessage());

           out.flushBuffer();

           bos.flush();

            bos.close();

 

            int len =bos.writtenBytes();

           checkPayload(channel, len);

           Bytes.int2bytes(len, header, 12);

            // write

           buffer.writerIndex(savedWriteIndex);

           buffer.writeBytes(header); // write header.

           buffer.writerIndex(savedWriteIndex + HEADER_LENGTH + len);

        } catch(Throwable t) {

            // 将buffer内容清空

           buffer.writerIndex(savedWriteIndex);

            // 发送失败信息给Consumer,否则Consumer只能等超时了

            if(!res.isEvent() && res.getStatus() != Response.BAD_RESPONSE) {

               Response r = new Response(res.getId(), res.getVersion());

               r.setStatus(Response.BAD_RESPONSE);

 

                if (t instanceof ExceedPayloadLimitException) {

                   logger.warn(t.getMessage(), t);

                    try{

                       r.setErrorMessage(t.getMessage());

                       channel.send(r);

                       return;

                    } catch (RemotingException e) {

                       logger.warn("Failed to send bad_response info back: " +t.getMessage() + ", cause: " + e.getMessage(), e);

                    }

                } else{

                    //FIXME 在Codec中打印出错日志?在IoHanndler的caught中统一处理?

                   logger.warn("Fail to encode response: " + res + ", send bad_response info instead, cause: " + t.getMessage(), t);

                    try{

                       r.setErrorMessage("Failed to send response: " + res + ",cause: " + StringUtils.toString(t));

                       channel.send(r);

                       return;

                    }catch (RemotingException e) {

                       logger.warn("Failed to send bad_response info back: " + res +", cause: " + e.getMessage(), e);

                    }

                }

            }

 

            // 重新抛出收到的异常

            if (t instanceof IOException) {

                throw (IOException) t;

            } else if(t instanceof RuntimeException) {

                throw(RuntimeException) t;

            } else if(t instanceof Error) {

                throw(Error) t;

            } else {

                throw new RuntimeException(t.getMessage(), t);

            }

        }

    }

和encodeRequest差不多,拼装消息头,编码,序列化消息体,最后调用encodeResponseData方法,因为这里本身调用的是ExchangeCodec的子类DubboCodec,所以方法中的encodeResponseData方法调用的是DubboCodec重写的方法,代码如下:

    @Override

    protected void encodeResponseData(Channel channel, ObjectOutput out, Object data) throws IOException {

        Result result =(Result) data;

 
        Throwable th =result.getException();

        if (th == null){

            Object ret= result.getValue();

            if (ret ==null) {

               out.writeByte(RESPONSE_NULL_VALUE);

            } else {

               out.writeByte(RESPONSE_VALUE);

               out.writeObject(ret);

            }

        } else {

           out.writeByte(RESPONSE_WITH_EXCEPTION);

           out.writeObject(th);

        }

    }

首先判断结果中否包含异常,没有异常再判断结果中有没有返回值类型,要是没有返回值类型就不写消息体了。

至此provider的处理过程结束

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值