dubbo-版本升级和版本兼容

1,435 阅读2分钟

背景

公司需要统一升级dubbo为2.6.7。

但是升级的时候,发现问题,2.8.x(低版本)调用2.6.7(高版本)报错:

java.io.IOException: Unknown result flag, expect '0' '1' '2', get 4

所以需要先更新2.8.x为2.6.7。

注:2.8.x是当当dubbox,当当其实也是基于2.5.x,所以本质还是低版本。其实就是低版本要先升级为高版本,否则异常。

原因

从2.6.3开始服务提供者端对响应数据的编码这一块变了,导致服务消费者端在解码响应数据的时候没有办法识别标志字段,然后就报错:

java.io.IOException: Unknown result flag, expect '0' '1' '2', get 4
        at com.alibaba.dubbo.rpc.protocol.dubbo.DecodeableRpcResult.decode(DecodeableRpcResult.java:101)
        at com.alibaba.dubbo.rpc.protocol.dubbo.DecodeableRpcResult.decode(DecodeableRpcResult.java:109)
        at com.alibaba.dubbo.rpc.protocol.dubbo.DubboCodec.decodeBody(DubboCodec.java:90)
        at com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec.decode(ExchangeCodec.java:119)
        at com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec.decode(ExchangeCodec.java:80)
        at com.alibaba.dubbo.rpc.protocol.dubbo.DubboCountCodec.decode(DubboCountCodec.java:46)
        at com.alibaba.dubbo.remoting.transport.netty.NettyCodecAdapter$InternalDecoder.messageReceived(NettyCodecAdapter.java:134)
        at org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:80)
        at org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564)
        at org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:559)
        at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:274)
        at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:261)
        at org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:349)
        at org.jboss.netty.channel.socket.nio.NioWorker.processSelectedKeys(NioWorker.java:280)
        at org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:200)
        at org.jboss.netty.util.ThreadRenamingRunnable.run(ThreadRenamingRunnable.java:108)
        at org.jboss.netty.util.internal.DeadLockProofWorker$1.run(DeadLockProofWorker.java:44)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)

具体原因是,低版本只支持标志0 1 2,如果消费者版本是2.0.10到2.6.2,高版本提供者2.6.3及以上就会兼容处理,即响应数据的标志是0 1 2。但是现在消费者是2.8.x,导致高版本提供者2.6.3及以上不能兼容处理,即响应数据的标志是4,由于2.8.x其实也是基于低版本2.5.x开发的,所以也只支持0 1 2,结果导致报错。

dubbo源码

服务提供端-DubboCodec(2.6.7)

@Override
protected void encodeResponseData(Channel channel, ObjectOutput out, Object data, String version) throws IOException {
    Result result = (Result) data;
    // currently, the version value in Response records the version of Request
    boolean attach = Version.isSupportResponseAttatchment(version); //校验
    Throwable th = result.getException();
    if (th == null) {
        Object ret = result.getValue();
        if (ret == null) {
            out.writeByte(attach ? RESPONSE_NULL_VALUE_WITH_ATTACHMENTS : RESPONSE_NULL_VALUE);
        } else {
            out.writeByte(attach ? RESPONSE_VALUE_WITH_ATTACHMENTS : RESPONSE_VALUE); //attach ? 4:1
            out.writeObject(ret);
        }
    } else {
        out.writeByte(attach ? RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS : RESPONSE_WITH_EXCEPTION);
        out.writeObject(th);
    }

    if (attach) {
        // returns current version of Response to consumer side.
        result.getAttachments().put(Constants.DUBBO_VERSION_KEY, Version.getProtocolVersion());
        out.writeObject(result.getAttachments());
    }
}

public static boolean isSupportResponseAttatchment(String version) {
    if (version == null || version.length() == 0) {
        return false;
    }
    // for previous dubbo version(2.0.10/020010~2.6.2/020602), this version is the jar's version, so they need to be ignore
    int iVersion = getIntVersion(version);
    if (iVersion >= 20010 && iVersion <= 20602) { //兼容处理消费者端低版本2.0.10/020010~2.6.2/020602
        return false;
    }

    return iVersion >= LOWEST_VERSION_FOR_RESPONSE_ATTATCHMENT;
}

服务消费端-DecodeableRpcResult(2.8.6)

public Object decode(Channel channel, InputStream input) throws IOException {
    ObjectInput in = CodecSupport.getSerialization(channel.getUrl(), serializationType)
        .deserialize(channel.getUrl(), input);

    try {
        byte flag = in.readByte();
        switch (flag) {
            case DubboCodec.RESPONSE_NULL_VALUE:
                break;
            case DubboCodec.RESPONSE_VALUE:
                try {
                    Type[] returnType = RpcUtils.getReturnTypes(invocation);
                    setValue(returnType == null || returnType.length == 0 ? in.readObject() :
                                 (returnType.length == 1 ? in.readObject((Class<?>) returnType[0])
                                     : in.readObject((Class<?>) returnType[0], returnType[1])));
                } catch (ClassNotFoundException e) {
                    throw new IOException(StringUtils.toString("Read response data failed.", e));
                }
                break;
            case DubboCodec.RESPONSE_WITH_EXCEPTION:
                try {
                    Object obj = in.readObject();
                    if (obj instanceof Throwable == false)
                        throw new IOException("Response data error, expect Throwable, but get " + obj);
                    setException((Throwable) obj);
                } catch (ClassNotFoundException e) {
                    throw new IOException(StringUtils.toString("Read response data failed.", e));
                }
                break;
            default: //不支持4,所以报错了
                throw new IOException("Unknown result flag, expect '0' '1' '2', get " + flag);
        }
        return this;
    } finally {
        // modified by lishen
        if (in instanceof Cleanable) {
            ((Cleanable) in).cleanup();
        }
    }
}

当前dubbo版本

2.5.8及以下 //版本名字一致

2.0.1是2.5.10 //从2.5.9及以上开始(即2.5.9到2.5.10),版本的值是2.0.1

2.0.2是2.6.7 //从2.6.3及以上开始,由于协议改了,版本的值是2.0.2


2.8.x是当当dubbox //其实也是基于2.5.x,所以本质还是低版本

2.8.4的项目

2.8.6的项目

解决方法

所有2.8.x项目更新为2.6.7即可,其他的2.5.x、2.6.x都是兼容的。