背景
会社は一律にduboを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はdangdubboxであり、dangdubは実際には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.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.0.1
2.0.2は2.6.7 // 2.6.3以降では、プロトコルの変更によりバージョンの値は2.0.2となります。
2.8.xはダブボックス // 実際は2.5.xもベースにしているので、実質的にはまだ下位バージョンです
2.8.4以下のプロジェクト
2.8.6以下のプロジェクト
解決方法
2.8.xのアイテムはすべて2.6.7にアップデートするだけで、他のすべての2.5.x、2.6.xは互換性があります。