vlambda博客
学习文章列表

dubbo-版本升级和版本兼容

背景

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

但是升级的时候,发现问题:2.8.x调用2.6.7报错。所以需要先更新2.8.x为2.6.7。

原因

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

@Overrideprotected 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都是兼容的。