-
Notifications
You must be signed in to change notification settings - Fork 643
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
[ISSUE #4701]Fix use tcp protocol client send message, it throw a DecoderException #4702
Conversation
… a DecoderException
Codecov ReportAttention:
Additional details and impacted files@@ Coverage Diff @@
## master #4702 +/- ##
============================================
+ Coverage 17.39% 17.46% +0.07%
- Complexity 1759 1770 +11
============================================
Files 797 797
Lines 29850 29850
Branches 2579 2565 -14
============================================
+ Hits 5192 5213 +21
+ Misses 24177 24156 -21
Partials 481 481 ☔ View full report in Codecov by Sentry. |
May I ask if this bug is caused by this PR #4678? And what is the specific cause of this bug? |
Yes |
And what is the specific reason of this bug? |
When the TCP-sent data packet is not sufficient to construct a Package, it will result in an error. If using a custom approach, you need to handle the issues of packet fragmentation and incomplete packets. The previously merged TCP enhancement pull request did not address this, and there would be no issue if packet fragmentation and incomplete packets did not occur. |
Thanks for your explanation. So your PR essentially moves the logic of 谢谢您的说明。所以您的PR相当于将 |
The functional code of encoding has not changed; only the positions were replaced. The decoding code has been repaired to address the issues of packet fragmentation and incomplete packets. It's not just a simple code move. You can compare it with the previous code. |
So this bug is not actually introduced by PR #4678, but rather a bug that existed before, right? |
if (in == null) { | ||
return; | ||
} | ||
if (in.readableBytes() < CONSTANT_MAGIC_FLAG.length + VERSION.length + 4 + 4) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could these 2 magic numbers be changed to constants to indicate that they are the length of the package length value and the length of the header length value?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could these magic numbers be changed to constants to indicate that they are the length of the package length value and the length of the header length value?
These magic numbers are actually part of the protocol. I recommend keeping them. You can think of them as a special marker for EventMesh's custom protocol, similar to the magic number in Java class files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can think of them as a special marker for EventMesh's custom protocol
Actually, I think CONSTANT_MAGIC_FLAG(= serializeBytes("EventMesh")) is also a special marker for EventMesh's custom protocol.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could these 2 magic numbers be changed to constants to indicate that they are the length of the package length value and the length of the header length value?
Using constants can improve readability and maintainability. But in this case, the numbers are integral to the protocol's structure, not arbitrary choices. It is better to stick with protocol-defined markers, otherwise it could lead to ambiguity or unexpected behavior.
validateFlagAndVersion(flagBytes, versionBytes, ctx); | ||
final int packageLength = in.readInt(); | ||
final int headerLength = in.readInt(); | ||
if (in.readableBytes() < packageLength - 13) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you change 13
to a constant to indicate that it is the length of CONSTANT_MAGIC_FLAG plus the length of VERSION?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you change
13
to a constant to indicate that it is the length of CONSTANT_MAGIC_FLAG plus the length of VERSION?
Of course
Yes, Reverting to the previous version is without issues. |
I'm confused. #4678 did not change the decoding logic, only changed the direct decoding logic to call the decoding logic with an internal class object. Why does #4678 cause this bug? |
Thank you for your explanation. Since the version before PR #4678 not only ensure accuracy, but also make decoding logic clearer and simpler, would it be better to revert to the previous version without adding extra decoding complexity? |
It is also possible to roll back to the previous version. This is based on the current transformation. We can see the opinions of others in the community. The previous implementation was to implement decoding and encoding separately using two decoders and two encoders. Now, decoding and encoding are implemented through Codec, which is essentially no different from the previous implementation. It is just the coding method and implementation. The previous implementation was processed through Netty's own internal generic implementation. The current PR is handled by the developer's customization. |
Compared to the previous version before PR #4678, the logic in this PR still adds some complexity, such as embedding the following decoding logic on the basis of previous code, while the previous version was not affected by these details of EventMesh custom protocol.
I completely agree with what you said: we can see the opinions (modify like this or revert) of others in the community. |
Can we use Netty's built-in LengthFieldBasedFrameDecoder to solve the problem of sticky or half packet? |
@karsonto I has resovled. please review the code |
@@ -161,7 +160,9 @@ private Header parseHeader(ByteBuf in, int headerLength) throws JsonProcessingEx | |||
} | |||
final byte[] headerData = new byte[headerLength]; | |||
in.readBytes(headerData); | |||
LogUtils.debug(log, "Decode headerJson={}", deserializeBytes(headerData)); | |||
if (log.isDebugEnabled()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding these modifications to the log printing, you don't have to address them. PR #4698 is currently handling these issues uniformly.
关于日志打印的这些修改,您可以不用处理,PR #4698正在统一处理这些问题。
public static class Decoder extends LengthFieldBasedFrameDecoder { | ||
|
||
public Decoder() { | ||
super(FRAME_MAX_LENGTH, 13, 4, -9, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My suggestion still pertains to the hardcoding of these numbers: 13, 4, -9.
To enhance code maintainability, I believe either defining two more constants like PACKAGE_LENGTH_FIELD_LENGTH (= 4) and HEADER_LENGTH_FIELD_LENGTH (= 4), and then entirely replacing these numbers with constants or variables, or using comments to explain these numbers would be beneficial. I prefer the first approach.
我的建议依然是关于数字的这种硬编码:13、4、-9。
为了让代码易于维护,我觉得要么再定义两个常量比如PACKAGE_LENGTH_FIELD_LENGTH (= 4)、HEADER_LENGTH_FIELD_LENGTH (= 4),然后完全用常量或变量来代替这些数字,要么用注释来说明这些数字。我倾向于第一种方式。
LGTM |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@@ -171,7 +171,7 @@ private Object parseBody(ByteBuf in, Header header, int bodyLength) throws JsonP | |||
} | |||
final byte[] bodyData = new byte[bodyLength]; | |||
in.readBytes(bodyData); | |||
LogUtils.debug(log, "Decode bodyJson={}", deserializeBytes(bodyData)); | |||
LogUtils.debug(log, "Decode headerJson={}", deserializeBytes(bodyData)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is bodyJson
here instead of headerJson
.
@@ -223,7 +223,9 @@ private static Object deserializeBody(String bodyJsonString, Header header) thro | |||
case REDIRECT_TO_CLIENT: | |||
return JsonUtils.parseObject(bodyJsonString, RedirectInfo.class); | |||
default: | |||
LogUtils.warn(log, "Invalidate TCP command: {}", command); | |||
if (log.isWarnEnabled()) { | |||
log.warn("Invalidate TCP command: {}", command); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same situation as mentioned earlier: #4702 (comment)
和前面同样的情况:#4702 (comment)
public static class Decoder extends LengthFieldBasedFrameDecoder { | ||
|
||
public Decoder() { | ||
super(FRAME_MAX_LENGTH, PREFIX_LENGTH, Integer.BYTES, -9, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe Integer.BYTES
and -9
are still not clear enough for developers. Integer.BYTES
: The lengths of version field, package length field, and header length field are all Integer.BYTES
. -9
: Hard-coded.
If you disagree with replacing them with expressions composed of more descriptive constant or variable names, as mentioned earlier, you can provide detailed comments to explain the usage of Integer.BYTES
and -9
here.
我认为Integer.BYTES
和-9
对开发者来说依旧不够清晰。Integer.BYTES
:Version字段, package length字段, header length字段的长度都是Integer.BYTES
。-9
:硬编码。
如果您不赞同将它们换成有助理解的常量名或变量名组成的表达式,正如我之前所说,您可以用详细的注释来说明这里的Integer.BYTES
和-9
。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mxsm I'm curious to understand the thought processes behind choosing a hard-coded value over a dynamic value. I'm wondering about potential future flexibility. Have we considered scenarios where the header structure might evolve, and how a dynamic approach might handle those cases?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mxsm I'm curious to understand the thought processes behind choosing a hard-coded value over a dynamic value. I'm wondering about potential future flexibility. Have we considered scenarios where the header structure might evolve, and how a dynamic approach might handle those cases?
@harshithasudhakar For a custom protocol, once it is determined, it will not undergo significant changes for a considerable period of time. This is because any changes to the protocol structure may introduce compatibility issues, potentially requiring existing users to perform a comprehensive upgrade to resolve them. In such cases, a major version upgrade, such as upgrading to version 2.x, may be necessary to undertake protocol modifications. Once the protocol is modified, it will be necessary to reprocess the encoding and decoding of the protocol. Therefore, I believe there is limited need for dynamic handling of these issues. Dynamically setting values increases the complexity of the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it, thanks for clarifying!
Fixes #4701
Motivation
Explain the content here.
Explain why you want to make the changes and what problem you're trying to solve.
Modifications
Describe the modifications you've done.
Documentation