Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: multi-version seata protocol support #6226

Merged
merged 74 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
39d88c1
load SeataSerializer by version
Bughue Dec 25, 2023
f1bacdd
Unwinding RpcMessage and Encoder/Decoder dependencies
Bughue Dec 25, 2023
901b886
license
Bughue Dec 26, 2023
ee3c3aa
license
Bughue Dec 26, 2023
b518004
style
Bughue Dec 26, 2023
883adfb
test ProtocolConstants.VERSION
Bughue Dec 26, 2023
492672c
version serialize
Bughue Dec 27, 2023
94cb0cf
v0
Bughue Dec 28, 2023
6dc20a2
Merge branch '2.x' of https://github.com/seata/seata into dev-mlv-com…
Bughue Dec 29, 2023
a151eba
v0
Bughue Dec 29, 2023
289ed97
Merge branch '2.x' of https://github.com/seata/seata into dev-mlv-rpc…
Bughue Dec 29, 2023
8c197af
v0
Bughue Dec 29, 2023
d8c19a4
v0
Bughue Dec 29, 2023
958ef77
v0
Bughue Dec 29, 2023
2a0d836
v0
Bughue Dec 29, 2023
59e9f8c
v0
Bughue Dec 29, 2023
227464b
v0
Bughue Dec 29, 2023
c27c63b
license
Bughue Dec 29, 2023
0afa845
license
Bughue Dec 29, 2023
4959cbe
license
Bughue Dec 29, 2023
dd7c314
style
Bughue Dec 29, 2023
b7bdb6d
style
Bughue Dec 29, 2023
dfd9bf2
style
Bughue Dec 29, 2023
48a6090
style
Bughue Jan 2, 2024
7d1a6b3
test
Bughue Jan 2, 2024
33e6e4e
test
Bughue Jan 2, 2024
379ba28
test
Bughue Jan 2, 2024
a9b4d5d
test
Bughue Jan 3, 2024
95573d3
fix test
Bughue Jan 4, 2024
181a421
inner class
Bughue Jan 4, 2024
d2804fb
inner class
Bughue Jan 4, 2024
8da65ff
Merge branch '2.x' into dev-mlv-rpc-msg
xingfudeshi Jan 18, 2024
91c171d
style
Bughue Jan 22, 2024
342d306
single pattern
Bughue Jan 22, 2024
439d132
Merge branch '2.x' of https://github.com/seata/seata into dev-mlv-ser…
Bughue Jan 22, 2024
74c48d5
conflit
Bughue Jan 22, 2024
72d47a1
Merge branch '2.x' of https://github.com/seata/seata into dev-mlv-rpc…
Bughue Jan 22, 2024
93c972b
Merge branch '2.x' of https://github.com/seata/seata into dev-mlv-ser…
Bughue Feb 4, 2024
0f8c187
resolve conflict
Bughue Feb 4, 2024
c1c7f3f
Merge branch '2.x' of https://github.com/seata/seata into dev-mlv-rpc…
Bughue Feb 4, 2024
cf4fe41
resolve conflict
Bughue Feb 4, 2024
9607bcd
resolve conflict
Bughue Feb 4, 2024
5b433cd
import
Bughue Feb 7, 2024
704669a
style
Bughue Feb 28, 2024
91418a1
Merge branch '2.x' of https://github.com/seata/seata into dev-mlv-rpc…
Bughue Mar 4, 2024
00a4e8e
style
Bughue Mar 4, 2024
c728968
Merge branch '2.x' of https://github.com/seata/seata into dev-mlv-rpc…
Bughue Mar 4, 2024
f17581c
Merge branch '2.x' of https://github.com/seata/seata into dev-mlv-com…
Bughue Mar 4, 2024
12d99d5
Merge branch 'dev-mlv-rpc-msg' of github.com:Bughue/seata into dev-ml…
Bughue Mar 5, 2024
8943b39
Merge branch 'dev-mlv-serialize-version' of github.com:Bughue/seata i…
Bughue Mar 5, 2024
7396cf9
Merge branch '2.x' of https://github.com/seata/seata into dev-mlv-com…
Bughue Mar 5, 2024
3f7a3c8
fix test
Bughue Mar 5, 2024
2558ba3
Merge branch '2.x' of https://github.com/seata/seata into dev-mlv-com…
Bughue Mar 6, 2024
ef146b7
optimize version
Bughue Mar 7, 2024
df9ea50
optimize version
Bughue Mar 7, 2024
af647e7
optimize version
Bughue Mar 7, 2024
2a92556
Merge branch 'dev-mlv-rpc-msg' of github.com:Bughue/seata into dev-ml…
Bughue Mar 7, 2024
3841379
optimize version
Bughue Mar 8, 2024
d876d10
optimize version
Bughue Mar 8, 2024
3f0abf0
Merge branch '2.x' of https://github.com/seata/seata into dev-mlv-rpc…
Bughue Mar 8, 2024
637e3da
Merge branch 'dev-mlv-rpc-msg' of github.com:Bughue/seata into dev-ml…
Bughue Mar 8, 2024
6f8c019
optimize version
Bughue Mar 8, 2024
c0dfb51
optimize version
Bughue Mar 8, 2024
c2f68aa
Merge branch 'dev-mlv-rpc-msg' of github.com:Bughue/seata into dev-ml…
Bughue Mar 8, 2024
b5bb8be
optimize version
Bughue Mar 8, 2024
2c4543c
Merge branch '2.x' of https://github.com/seata/seata into dev-mlv-com…
Bughue Apr 22, 2024
9d30454
Merge branch '2.x' of https://github.com/seata/seata into dev-mlv-ser…
Bughue Apr 30, 2024
2f5feb0
merge
Bughue Apr 30, 2024
a807053
Merge branch 'dev-mlv-serialize-version' of github.com:Bughue/seata i…
Bughue Apr 30, 2024
0ebf2ce
merge
Bughue Apr 30, 2024
7add8d9
check style
Bughue Apr 30, 2024
aeb24c8
check style
Bughue Apr 30, 2024
9f6d80f
Merge branch '2.x' of https://github.com/seata/seata into dev-mlv-com…
Bughue Jun 24, 2024
237aeb5
style
Bughue Jun 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions core/src/main/java/org/apache/seata/core/protocol/RpcMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public class RpcMessage implements Serializable {
private Map<String, String> headMap = new HashMap<>();
private Object body;

private String otherSideVersion;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

为什么要在rpcmessage中增加版本字段?
Why add a version field to rpcmessage?


/**
* Gets id.
*
Expand Down Expand Up @@ -169,6 +171,14 @@ public void setMessageType(byte messageType) {
this.messageType = messageType;
}

public String getOtherSideVersion() {
return otherSideVersion;
}

public void setOtherSideVersion(String otherSideVersion) {
this.otherSideVersion = otherSideVersion;
}

@Override
public String toString() {
return StringUtils.toString(this);
Expand Down
10 changes: 10 additions & 0 deletions core/src/main/java/org/apache/seata/core/protocol/Version.java
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,16 @@ public static long convertVersionNotThrowException(String version) {
return -1;
}

public static byte calcProtocolVersion(String sdkVersion) throws IncompatibleVersionException {
long version = convertVersion(sdkVersion);
long v0 = convertVersion(VERSION_0_7_1);
if (version <= v0) {
return ProtocolConstants.VERSION_0;
} else {
return ProtocolConstants.VERSION_1;
}
}

private static long calculatePartValue(String partNumeric, int size, int index) {
return Long.parseLong(partNumeric) * Double.valueOf(Math.pow(100, size - index)).longValue();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public Object sendSyncRequest(String resourceId, String clientId, Object msg, bo
if (channel == null) {
throw new RuntimeException("rm client is not connected. dbkey:" + resourceId + ",clientId:" + clientId);
}
RpcMessage rpcMessage = buildRequestMessage(msg, ProtocolConstants.MSGTYPE_RESQUEST_SYNC);
RpcMessage rpcMessage = buildRequestMessage(channel, msg, ProtocolConstants.MSGTYPE_RESQUEST_SYNC);
return super.sendSync(channel, rpcMessage, NettyServerConfig.getRpcRequestTimeout());
}

Expand All @@ -78,7 +78,7 @@ public Object sendSyncRequest(Channel channel, Object msg) throws TimeoutExcepti
if (channel == null) {
throw new RuntimeException("client is not connected");
}
RpcMessage rpcMessage = buildRequestMessage(msg, ProtocolConstants.MSGTYPE_RESQUEST_SYNC);
RpcMessage rpcMessage = buildRequestMessage(channel, msg, ProtocolConstants.MSGTYPE_RESQUEST_SYNC);
return super.sendSync(channel, rpcMessage, NettyServerConfig.getRpcRequestTimeout());
}

Expand All @@ -87,26 +87,42 @@ public void sendAsyncRequest(Channel channel, Object msg) {
if (channel == null) {
throw new RuntimeException("client is not connected");
}
RpcMessage rpcMessage = buildRequestMessage(msg, ProtocolConstants.MSGTYPE_RESQUEST_ONEWAY);
RpcMessage rpcMessage = buildRequestMessage(channel, msg, ProtocolConstants.MSGTYPE_RESQUEST_ONEWAY);
super.sendAsync(channel, rpcMessage);
}

@Override
public void sendAsyncResponse(RpcMessage rpcMessage, Channel channel, Object msg) {
final Channel clientChannel = msg instanceof HeartbeatMessage
? channel
: ChannelManager.getSameClientChannel(channel);

if (clientChannel == null) {
throw new RuntimeException("Not found client channel to response | channel: " + channel);
Channel clientChannel = channel;
if (!(msg instanceof HeartbeatMessage)) {
clientChannel = ChannelManager.getSameClientChannel(channel);
}

RpcMessage rpcMsg = buildResponseMessage(rpcMessage, msg, msg instanceof HeartbeatMessage
if (clientChannel != null) {
RpcMessage rpcMsg = buildResponseMessage(channel, rpcMessage, msg, msg instanceof HeartbeatMessage
? ProtocolConstants.MSGTYPE_HEARTBEAT_RESPONSE
: ProtocolConstants.MSGTYPE_RESPONSE);
super.sendAsync(clientChannel, rpcMsg);
super.sendAsync(clientChannel, rpcMsg);
} else {
throw new RuntimeException("channel is error.");
}
}


private RpcMessage buildResponseMessage(Channel channel, RpcMessage fromRpcMessage, Object msg, byte messageType) {
RpcMessage rpcMessage = super.buildResponseMessage(fromRpcMessage, msg, messageType);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

为什么不能直接通过channel上下文中的版本去创建对应协议版本的子类?
Why can't I create a subclass of the corresponding protocol version directly from the version in the channel context?

RpcContext rpcContext = ChannelManager.getContextFromIdentified(channel);
rpcMessage.setOtherSideVersion(rpcContext.getVersion());
return rpcMessage;
}

protected RpcMessage buildRequestMessage(Channel channel, Object msg, byte messageType) {
RpcMessage rpcMessage = super.buildRequestMessage(msg, messageType);
RpcContext rpcContext = ChannelManager.getContextFromIdentified(channel);
rpcMessage.setOtherSideVersion(rpcContext.getVersion());
return rpcMessage;
}


@Override
public void registerProcessor(int messageType, RemotingProcessor processor, ExecutorService executor) {
Pair<RemotingProcessor, ExecutorService> pair = new Pair<>(processor, executor);
Expand Down
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;
}
}
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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be renamed MultiProtocolEncoder


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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@
import org.apache.seata.common.exception.FrameworkException;
import org.apache.seata.common.thread.NamedThreadFactory;
import org.apache.seata.core.rpc.RemotingBootstrap;
import org.apache.seata.core.rpc.netty.v1.ProtocolV1Decoder;
import org.apache.seata.core.rpc.netty.v1.ProtocolV1Encoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -134,8 +132,8 @@ public void initChannel(SocketChannel ch) {
new IdleStateHandler(nettyClientConfig.getChannelMaxReadIdleSeconds(),
nettyClientConfig.getChannelMaxWriteIdleSeconds(),
nettyClientConfig.getChannelMaxAllIdleSeconds()))
.addLast(new ProtocolV1Decoder())
.addLast(new ProtocolV1Encoder());
.addLast(new CompatibleProtocolDecoder())
.addLast(new CompatibleProtocolEncoder());
if (channelHandlers != null) {
addChannelPipelineLast(ch, channelHandlers);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@
import org.apache.seata.common.thread.NamedThreadFactory;
import org.apache.seata.config.ConfigurationFactory;
import org.apache.seata.core.rpc.RemotingBootstrap;
import org.apache.seata.core.rpc.netty.v1.ProtocolV1Decoder;
import org.apache.seata.core.rpc.netty.v1.ProtocolV1Encoder;
import org.apache.seata.discovery.registry.MultiRegistryFactory;
import org.apache.seata.discovery.registry.RegistryService;
import org.slf4j.Logger;
Expand Down Expand Up @@ -161,8 +159,8 @@ public void start() {
@Override
public void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new IdleStateHandler(nettyServerConfig.getChannelMaxReadIdleSeconds(), 0, 0))
.addLast(new ProtocolV1Decoder())
.addLast(new ProtocolV1Encoder());
.addLast(new CompatibleProtocolDecoder())
.addLast(new CompatibleProtocolEncoder());
if (channelHandlers != null) {
addChannelPipelineLast(ch, channelHandlers);
}
Expand Down
Loading
Loading