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