-
Notifications
You must be signed in to change notification settings - Fork 8.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature: multi-version seata protocol support (#6226)
- Loading branch information
Showing
20 changed files
with
734 additions
and
138 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
155 changes: 155 additions & 0 deletions
155
core/src/main/java/org/apache/seata/core/rpc/netty/CompatibleProtocolDecoder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one or more | ||
* contributor license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright ownership. | ||
* The ASF licenses this file to You under the Apache License, Version 2.0 | ||
* (the "License"); you may not use this file except in compliance with | ||
* the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.apache.seata.core.rpc.netty; | ||
|
||
import com.google.common.collect.ImmutableMap; | ||
import io.netty.buffer.ByteBuf; | ||
import io.netty.channel.ChannelHandlerContext; | ||
import io.netty.handler.codec.LengthFieldBasedFrameDecoder; | ||
import org.apache.seata.core.exception.DecodeException; | ||
import org.apache.seata.core.protocol.ProtocolConstants; | ||
import org.apache.seata.core.rpc.netty.v0.ProtocolDecoderV0; | ||
import org.apache.seata.core.rpc.netty.v1.ProtocolDecoderV1; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import java.util.Map; | ||
|
||
/** | ||
* <pre> | ||
* (> 0.7.0) | ||
* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | ||
* +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ | ||
* | magic |Proto| Full length | Head | Msg |Seria|Compr| RequestId | | ||
* | code |colVer| (head+body) | Length |Type |lizer|ess | | | ||
* +-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+ | ||
* | ||
* (<= 0.7.0) | ||
* 0 1 2 3 4 6 8 10 12 14 | ||
* +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ | ||
* | 0xdada | flag | typecode/ | requestid | | ||
* | | | bodylength| | | ||
* +-----------+-----------+-----------+-----------+-----------+-----------+-----------+ | ||
* | ||
* </pre> | ||
* <p> | ||
* <li>Full Length: include all data </li> | ||
* <li>Head Length: include head data from magic code to head map. </li> | ||
* <li>Body Length: Full Length - Head Length</li> | ||
* </p> | ||
*/ | ||
public class CompatibleProtocolDecoder extends LengthFieldBasedFrameDecoder { | ||
|
||
private static final Logger LOGGER = LoggerFactory.getLogger(CompatibleProtocolDecoder.class); | ||
private static Map<Byte, ProtocolDecoder> protocolDecoderMap; | ||
|
||
public CompatibleProtocolDecoder() { | ||
// default is 8M | ||
this(ProtocolConstants.MAX_FRAME_LENGTH); | ||
} | ||
|
||
public CompatibleProtocolDecoder(int maxFrameLength) { | ||
/* | ||
int maxFrameLength, | ||
int lengthFieldOffset, magic code is 2B, and version is 1B, and then FullLength. so value is 3 | ||
int lengthFieldLength, FullLength is int(4B). so values is 4 | ||
int lengthAdjustment, FullLength include all data and read 7 bytes before, so the left length is (FullLength-7). so values is -7 | ||
int initialBytesToStrip we will check magic code and version self, so do not strip any bytes. so values is 0 | ||
*/ | ||
super(maxFrameLength, 3, 4, -7, 0); | ||
protocolDecoderMap = ImmutableMap.<Byte, ProtocolDecoder>builder() | ||
.put(ProtocolConstants.VERSION_0, new ProtocolDecoderV0()) | ||
.put(ProtocolConstants.VERSION_1, new ProtocolDecoderV1()) | ||
.build(); | ||
} | ||
|
||
@Override | ||
protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { | ||
ByteBuf frame; | ||
Object decoded; | ||
byte version; | ||
try { | ||
if (isV0(in)) { | ||
decoded = in; | ||
version = ProtocolConstants.VERSION_0; | ||
} else { | ||
decoded = super.decode(ctx, in); | ||
version = decideVersion(decoded); | ||
} | ||
|
||
if (decoded instanceof ByteBuf) { | ||
frame = (ByteBuf) decoded; | ||
try { | ||
ProtocolDecoder decoder = protocolDecoderMap.get(version); | ||
if (decoder == null) { | ||
throw new UnsupportedOperationException("Unsupported version: " + version); | ||
} | ||
return decoder.decodeFrame(frame); | ||
} finally { | ||
if (version != ProtocolConstants.VERSION_0) { | ||
frame.release(); | ||
} | ||
} | ||
} | ||
} catch (Exception exx) { | ||
LOGGER.error("Decode frame error, cause: {}", exx.getMessage()); | ||
throw new DecodeException(exx); | ||
} | ||
return decoded; | ||
} | ||
|
||
protected byte decideVersion(Object in) { | ||
if (in instanceof ByteBuf) { | ||
ByteBuf frame = (ByteBuf) in; | ||
frame.markReaderIndex(); | ||
byte b0 = frame.readByte(); | ||
byte b1 = frame.readByte(); | ||
if (ProtocolConstants.MAGIC_CODE_BYTES[0] != b0 | ||
|| ProtocolConstants.MAGIC_CODE_BYTES[1] != b1) { | ||
throw new IllegalArgumentException("Unknown magic code: " + b0 + ", " + b1); | ||
} | ||
|
||
byte version = frame.readByte(); | ||
frame.resetReaderIndex(); | ||
return version; | ||
} | ||
return -1; | ||
} | ||
|
||
|
||
protected boolean isV0(ByteBuf in) { | ||
boolean isV0 = false; | ||
in.markReaderIndex(); | ||
byte b0 = in.readByte(); | ||
byte b1 = in.readByte(); | ||
// v1/v2/v3 : b2 = version | ||
// v0 : 1st byte in FLAG(2byte:0x10/0x20/0x40/0x80) | ||
byte b2 = in.readByte(); | ||
if (ProtocolConstants.MAGIC_CODE_BYTES[0] == b0 | ||
&& ProtocolConstants.MAGIC_CODE_BYTES[1] == b1 | ||
&& 0 == b2) { | ||
isV0 = true; | ||
} | ||
|
||
in.resetReaderIndex(); | ||
return isV0; | ||
} | ||
|
||
protected boolean isV0(byte version) { | ||
return version == ProtocolConstants.VERSION_0; | ||
} | ||
} |
79 changes: 79 additions & 0 deletions
79
core/src/main/java/org/apache/seata/core/rpc/netty/CompatibleProtocolEncoder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one or more | ||
* contributor license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright ownership. | ||
* The ASF licenses this file to You under the Apache License, Version 2.0 | ||
* (the "License"); you may not use this file except in compliance with | ||
* the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.apache.seata.core.rpc.netty; | ||
|
||
import com.google.common.collect.ImmutableMap; | ||
import io.netty.buffer.ByteBuf; | ||
import io.netty.channel.ChannelHandlerContext; | ||
import io.netty.handler.codec.MessageToByteEncoder; | ||
import org.apache.seata.common.util.StringUtils; | ||
import org.apache.seata.core.protocol.ProtocolConstants; | ||
import org.apache.seata.core.protocol.RpcMessage; | ||
import org.apache.seata.core.protocol.Version; | ||
import org.apache.seata.core.rpc.netty.v0.ProtocolEncoderV0; | ||
import org.apache.seata.core.rpc.netty.v1.ProtocolEncoderV1; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import java.util.Map; | ||
|
||
/** | ||
* Compatible Protocol Encoder | ||
* <p> | ||
* <li>Full Length: include all data </li> | ||
* <li>Head Length: include head data from magic code to head map. </li> | ||
* <li>Body Length: Full Length - Head Length</li> | ||
* </p> | ||
*/ | ||
public class CompatibleProtocolEncoder extends MessageToByteEncoder { | ||
|
||
private static final Logger LOGGER = LoggerFactory.getLogger(CompatibleProtocolEncoder.class); | ||
|
||
private static Map<Byte, ProtocolEncoder> protocolEncoderMap; | ||
|
||
public CompatibleProtocolEncoder() { | ||
super(); | ||
protocolEncoderMap = ImmutableMap.<Byte, ProtocolEncoder>builder() | ||
.put(ProtocolConstants.VERSION_0, new ProtocolEncoderV0()) | ||
.put(ProtocolConstants.VERSION_1, new ProtocolEncoderV1()) | ||
.build(); | ||
} | ||
|
||
@Override | ||
public void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) { | ||
try { | ||
if (msg instanceof RpcMessage) { | ||
RpcMessage rpcMessage = (RpcMessage) msg; | ||
String sdkVersion = rpcMessage.getOtherSideVersion(); | ||
if (StringUtils.isBlank(sdkVersion)) { | ||
sdkVersion = Version.getCurrent(); | ||
} | ||
byte protocolVersion = Version.calcProtocolVersion(sdkVersion); | ||
ProtocolEncoder encoder = protocolEncoderMap.get(protocolVersion); | ||
if (encoder == null) { | ||
throw new UnsupportedOperationException("Unsupported protocolVersion: " + protocolVersion); | ||
} | ||
|
||
encoder.encode(rpcMessage, out); | ||
} else { | ||
throw new UnsupportedOperationException("Not support this class:" + msg.getClass()); | ||
} | ||
} catch (Throwable e) { | ||
LOGGER.error("Encode request error!", e); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.