MisterChangRay / magic-byte

a java tool for faster convertor byte2object
BSD 3-Clause "New" or "Revised" License
49 stars 20 forks source link

探讨消息注册 #33

Closed FULaBUla closed 11 months ago

FULaBUla commented 11 months ago
  1. 在合并请求 https://github.com/MisterChangRay/magic-byte/pull/32 提交的测试用例可以正常序列化数据,但是反序列化后的对象为 null。
  2. wiki 里 https://github.com/MisterChangRay/magic-byte/wiki/%E6%95%B0%E6%8D%AE%E5%AE%9E%E4%BD%93%E5%AE%9A%E4%B9%89%E7%9A%84%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5#2-%E8%87%AA%E8%A1%8C%E8%A7%A3%E6%9E%90%E6%95%B0%E6%8D%AE%E5%92%8C%E4%BB%A3%E7%A0%81 的测试用例是不是有问题,先 Head pack = MagicByte.pack(deviceUploadMessage, Head.class); ,然后 HeartbeatCmd pack1 = MagicByte.pack(deviceUploadMessage, HeartbeatCmd.class); 用同一个二进制数据从头开始解析,数据结果是不对的,因为需要偏移起始位置,但是又要先获得 Head 的全长再去偏移生成对象,比较繁琐
MisterChangRay commented 11 months ago
  1. 在合并请求 Draft: 添加把头和消息都独立封装的测试用例 #32 提交的测试用例可以正常序列化数据,但是反序列化后的对象为 null。
  2. wiki 里 https://github.com/MisterChangRay/magic-byte/wiki/%E6%95%B0%E6%8D%AE%E5%AE%9E%E4%BD%93%E5%AE%9A%E4%B9%89%E7%9A%84%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5#2-%E8%87%AA%E8%A1%8C%E8%A7%A3%E6%9E%90%E6%95%B0%E6%8D%AE%E5%92%8C%E4%BB%A3%E7%A0%81 的测试用例是不是有问题,先 Head pack = MagicByte.pack(deviceUploadMessage, Head.class); ,然后 HeartbeatCmd pack1 = MagicByte.pack(deviceUploadMessage, HeartbeatCmd.class); 用同一个二进制数据从头开始解析,数据结果是不对的,因为需要偏移起始位置,但是又要先获得 Head 的全长再去偏移生成对象,比较繁琐

不会的,这里第一次只解析了消息头, 而heartbeatMessage里面也会包含消息头的,所以这样解析是可以的

MisterChangRay commented 11 months ago

老哥,新版本已经包含消息注册的功能,参考com.github.misterchangray.core.messager包下代码

MisterChangRay commented 11 months ago
  1. 在合并请求 Draft: 添加把头和消息都独立封装的测试用例 #32 提交的测试用例可以正常序列化数据,但是反序列化后的对象为 null。
  2. wiki 里 https://github.com/MisterChangRay/magic-byte/wiki/%E6%95%B0%E6%8D%AE%E5%AE%9E%E4%BD%93%E5%AE%9A%E4%B9%89%E7%9A%84%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5#2-%E8%87%AA%E8%A1%8C%E8%A7%A3%E6%9E%90%E6%95%B0%E6%8D%AE%E5%92%8C%E4%BB%A3%E7%A0%81 的测试用例是不是有问题,先 Head pack = MagicByte.pack(deviceUploadMessage, Head.class); ,然后 HeartbeatCmd pack1 = MagicByte.pack(deviceUploadMessage, HeartbeatCmd.class); 用同一个二进制数据从头开始解析,数据结果是不对的,因为需要偏移起始位置,但是又要先获得 Head 的全长再去偏移生成对象,比较繁琐

测试用例有点问题,修改一下呢

FULaBUla commented 11 months ago
  1. 在合并请求 Draft: 添加把头和消息都独立封装的测试用例 #32 提交的测试用例可以正常序列化数据,但是反序列化后的对象为 null。

不好意思,不太会用 github,没找到像 gitlab 那种标记未就绪的合并请求的功能。

其实,我是想这么用:

@MagicClass
public class Protocol implements MConverter<MagicMessage> {

    @MagicField(order = 1)
    private Head head;

    @MagicField(order = 3)
    @MagicConverter(converter = Protocol.class)
    private MagicMessage body;
}

这样我就可以统一返回一个 protocol 对象,然后相应的业务直接 getBody 就可以拿走用了,如果是示例代码那种,body 对象里放 Head 就感觉有点儿乱

MisterChangRay commented 11 months ago

可以这么理解,消息头是公用的,也就是所有消息都有消息头。

消息整体 = 消息头+消息体。

你拿一个body,本身就只是消息的一部分,不算完整消息的

MisterChangRay commented 11 months ago

也就是每个消息都是独立体,共同体是消息头。

而你这个思路是,所有消息都是共同体,共享一个消息头。你这个自定义个序列化也可以实现。

MisterChangRay commented 11 months ago
  1. 在合并请求 Draft: 添加把头和消息都独立封装的测试用例 #32 提交的测试用例可以正常序列化数据,但是反序列化后的对象为 null。

不好意思,不太会用 github,没找到像 gitlab 那种标记未就绪的合并请求的功能。

  • 这个测试用例的代码是有问题的,我这个用法,反序列化结果是 null,是不能这么用嘛?
  • 我之前也在测试用例里试过不加 @MagicConvert 注解,结果这种情况序列化结果也不对,二进制结果直接就只有 Protocol 里 Head 的二进制数据,剩下的都没有

其实,我是想这么用:

@MagicClass
public class Protocol implements MConverter<MagicMessage> {

    @MagicField(order = 1)
    private Head head;

    @MagicField(order = 3)
    @MagicConverter(converter = Protocol.class)
    private MagicMessage body;
}

这样我就可以统一返回一个 protocol 对象,然后相应的业务直接 getBody 就可以拿走用了,如果是示例代码那种,body 对象里放 Head 就感觉有点儿乱

你这里的思路,需要重新定义一个接口,然后自定义实现解析。也是可以实现的。

MisterChangRay commented 11 months ago
  1. 在合并请求 Draft: 添加把头和消息都独立封装的测试用例 #32 提交的测试用例可以正常序列化数据,但是反序列化后的对象为 null。

不好意思,不太会用 github,没找到像 gitlab 那种标记未就绪的合并请求的功能。

  • 这个测试用例的代码是有问题的,我这个用法,反序列化结果是 null,是不能这么用嘛?
  • 我之前也在测试用例里试过不加 @MagicConvert 注解,结果这种情况序列化结果也不对,二进制结果直接就只有 Protocol 里 Head 的二进制数据,剩下的都没有

其实,我是想这么用:

@MagicClass
public class Protocol implements MConverter<MagicMessage> {

    @MagicField(order = 1)
    private Head head;

    @MagicField(order = 3)
    @MagicConverter(converter = Protocol.class)
    private MagicMessage body;
}

这样我就可以统一返回一个 protocol 对象,然后相应的业务直接 getBody 就可以拿走用了,如果是示例代码那种,body 对象里放 Head 就感觉有点儿乱

  1. 你测试用例里面是将消息体进行了注册,而不是整个消息,所以反序列化为null
  2. 2 自定义序列化需要自己实现这个属性的序列化和反序列化。
FULaBUla commented 11 months ago
  1. 在合并请求 Draft: 添加把头和消息都独立封装的测试用例 #32 提交的测试用例可以正常序列化数据,但是反序列化后的对象为 null。

不好意思,不太会用 github,没找到像 gitlab 那种标记未就绪的合并请求的功能。

  • 这个测试用例的代码是有问题的,我这个用法,反序列化结果是 null,是不能这么用嘛?
  • 我之前也在测试用例里试过不加 @MagicConvert 注解,结果这种情况序列化结果也不对,二进制结果直接就只有 Protocol 里 Head 的二进制数据,剩下的都没有

其实,我是想这么用:

@MagicClass
public class Protocol implements MConverter<MagicMessage> {

    @MagicField(order = 1)
    private Head head;

    @MagicField(order = 3)
    @MagicConverter(converter = Protocol.class)
    private MagicMessage body;
}

这样我就可以统一返回一个 protocol 对象,然后相应的业务直接 getBody 就可以拿走用了,如果是示例代码那种,body 对象里放 Head 就感觉有点儿乱

你这里的思路,需要重新定义一个接口,然后自定义实现解析。也是可以实现的。

是不是更像是 wiki 数据实体定义的最佳实践的 2: 自行解析数据和代码 这部分;

我刚才思考了一下咱们这个框架的逻辑,其实注册的 registerCMD 的 msgClazz 不一定要有cmdField = true,只要我实现了接口的 cmd 方法就可以知道这个实现类的 cmd 号了。然后就可以在 cache 建立号与类的映射关系,然后 pack 方法在反序列化我上面那个 Protocol 的时候我使用 MagicByte.pack(bytes, Protocol.class); 但是到,body 属性的时候发现他是 MagicMessage 再取 cmd 值找到对应类型做反序列化。

而上述这个在操作的时候有个问题在于,现在通过注册的方式既获取了 cmd 号又知道了 cmd 在二进制数据里的长度与位置,但是这个 cmd 位置信息其实可以通过单独注册位置的方式来进行或者通过注解扫描的形式注册

FULaBUla commented 11 months ago
  1. 在合并请求 Draft: 添加把头和消息都独立封装的测试用例 #32 提交的测试用例可以正常序列化数据,但是反序列化后的对象为 null。

不好意思,不太会用 github,没找到像 gitlab 那种标记未就绪的合并请求的功能。

  • 这个测试用例的代码是有问题的,我这个用法,反序列化结果是 null,是不能这么用嘛?
  • 我之前也在测试用例里试过不加 @MagicConvert 注解,结果这种情况序列化结果也不对,二进制结果直接就只有 Protocol 里 Head 的二进制数据,剩下的都没有

其实,我是想这么用:

@MagicClass
public class Protocol implements MConverter<MagicMessage> {

    @MagicField(order = 1)
    private Head head;

    @MagicField(order = 3)
    @MagicConverter(converter = Protocol.class)
    private MagicMessage body;
}

这样我就可以统一返回一个 protocol 对象,然后相应的业务直接 getBody 就可以拿走用了,如果是示例代码那种,body 对象里放 Head 就感觉有点儿乱

  1. 你测试用例里面是将消息体进行了注册,而不是整个消息,所以反序列化为null
  2. 2 自定义序列化需要自己实现这个属性的序列化和反序列化。

那我如果不写 Convert 为啥序列化成二进制的时候只序列化了 Head 没序列化 body 呢?

MisterChangRay commented 11 months ago
  1. 在合并请求 Draft: 添加把头和消息都独立封装的测试用例 #32 提交的测试用例可以正常序列化数据,但是反序列化后的对象为 null。

不好意思,不太会用 github,没找到像 gitlab 那种标记未就绪的合并请求的功能。

  • 这个测试用例的代码是有问题的,我这个用法,反序列化结果是 null,是不能这么用嘛?
  • 我之前也在测试用例里试过不加 @MagicConvert 注解,结果这种情况序列化结果也不对,二进制结果直接就只有 Protocol 里 Head 的二进制数据,剩下的都没有

其实,我是想这么用:

@MagicClass
public class Protocol implements MConverter<MagicMessage> {

    @MagicField(order = 1)
    private Head head;

    @MagicField(order = 3)
    @MagicConverter(converter = Protocol.class)
    private MagicMessage body;
}

这样我就可以统一返回一个 protocol 对象,然后相应的业务直接 getBody 就可以拿走用了,如果是示例代码那种,body 对象里放 Head 就感觉有点儿乱

  1. 你测试用例里面是将消息体进行了注册,而不是整个消息,所以反序列化为null
  2. 2 自定义序列化需要自己实现这个属性的序列化和反序列化。

那我如果不写 Convert 为啥序列化成二进制的时候只序列化了 Head 没序列化 body 呢?

  1. 在合并请求 Draft: 添加把头和消息都独立封装的测试用例 #32 提交的测试用例可以正常序列化数据,但是反序列化后的对象为 null。

不好意思,不太会用 github,没找到像 gitlab 那种标记未就绪的合并请求的功能。

  • 这个测试用例的代码是有问题的,我这个用法,反序列化结果是 null,是不能这么用嘛?
  • 我之前也在测试用例里试过不加 @MagicConvert 注解,结果这种情况序列化结果也不对,二进制结果直接就只有 Protocol 里 Head 的二进制数据,剩下的都没有

其实,我是想这么用:

@MagicClass
public class Protocol implements MConverter<MagicMessage> {

    @MagicField(order = 1)
    private Head head;

    @MagicField(order = 3)
    @MagicConverter(converter = Protocol.class)
    private MagicMessage body;
}

这样我就可以统一返回一个 protocol 对象,然后相应的业务直接 getBody 就可以拿走用了,如果是示例代码那种,body 对象里放 Head 就感觉有点儿乱

  1. 你测试用例里面是将消息体进行了注册,而不是整个消息,所以反序列化为null
  2. 2 自定义序列化需要自己实现这个属性的序列化和反序列化。

那我如果不写 Convert 为啥序列化成二进制的时候只序列化了 Head 没序列化 body 呢?

因为这个 MagicMessage接口只有定义,没有实际数据结构定义啊

FULaBUla commented 11 months ago

目前版本我要用是不是更像是 wiki 数据实体定义的最佳实践的 2: 自行解析数据和代码 这部分?

我刚才思考了一下咱们这个框架的逻辑,其实注册的 registerCMD 的 msgClazz 不一定要有cmdField = true,只要我实现了接口的 cmd 方法就可以知道这个实现类的 cmd 号了。然后就可以在 cache 建立号与类的映射关系,然后 pack 方法在反序列化我上面那个 Protocol 的时候我使用 MagicByte.pack(bytes, Protocol.class); 但是到,body 属性的时候发现他是 MagicMessage 再取 cmd 值找到对应类型做反序列化。

而上述这个在操作的时候有个问题在于,现在通过注册的方式既获取了 cmd 号又知道了 cmd 在二进制数据里的长度与位置,但是这个 cmd 位置信息其实可以通过单独注册位置的方式来进行或者通过注解扫描的形式注册

MisterChangRay commented 11 months ago

目前版本我要用是不是更像是 wiki 数据实体定义的最佳实践的 2: 自行解析数据和代码 这部分?

我刚才思考了一下咱们这个框架的逻辑,其实注册的 registerCMD 的 msgClazz 不一定要有cmdField = true,只要我实现了接口的 cmd 方法就可以知道这个实现类的 cmd 号了。然后就可以在 cache 建立号与类的映射关系,然后 pack 方法在反序列化我上面那个 Protocol 的时候我使用 MagicByte.pack(bytes, Protocol.class); 但是到,body 属性的时候发现他是 MagicMessage 再取 cmd 值找到对应类型做反序列化。

而上述这个在操作的时候有个问题在于,现在通过注册的方式既获取了 cmd 号又知道了 cmd 在二进制数据里的长度与位置,但是这个 cmd 位置信息其实可以通过单独注册位置的方式来进行或者通过注解扫描的形式注册

  1. 实现只能获取到消息id,对于消息偏移量和位置信息是不能获取到,所以需要cmdField属性进行绑定
  2. 我们基于工具类的思路,一切从简的
FULaBUla commented 11 months ago

目前版本我要用是不是更像是 wiki 数据实体定义的最佳实践的 2: 自行解析数据和代码 这部分? 我刚才思考了一下咱们这个框架的逻辑,其实注册的 registerCMD 的 msgClazz 不一定要有cmdField = true,只要我实现了接口的 cmd 方法就可以知道这个实现类的 cmd 号了。然后就可以在 cache 建立号与类的映射关系,然后 pack 方法在反序列化我上面那个 Protocol 的时候我使用 MagicByte.pack(bytes, Protocol.class); 但是到,body 属性的时候发现他是 MagicMessage 再取 cmd 值找到对应类型做反序列化。 而上述这个在操作的时候有个问题在于,现在通过注册的方式既获取了 cmd 号又知道了 cmd 在二进制数据里的长度与位置,但是这个 cmd 位置信息其实可以通过单独注册位置的方式来进行或者通过注解扫描的形式注册

  1. 实现只能获取到消息id,对于消息偏移量和位置信息是不能获取到,所以需要cmdField属性进行绑定
  2. 我们基于工具类的思路,一切从简的

但是目前看起来并没有从简,接口类也没有任何注释,wiki 示例里也没有对实现 cmd 方法有什么描述,什么时候该用,怎么用;目前来看整体的灵活性也不足,限制颇多,没法注册多个 cmd 的偏移。而且,样例代码里的写法是写死的,也容易让人被误解。而且,实际上测试用例里有的我把重写的 cmd 方法删了也不会有任何问题。但有的就会出现 -1 导致反序列化为 null 的结果。所以我建议配置哪个字段是 cmd 不如直接设置全局的 cmd 的长度和偏移位置,甚至这样还可以设置多种 cmd 的情况。然后 cmd() 方法就是类似策略模式,用于匹配对应的对象,这样也会很简单

MisterChangRay commented 11 months ago
FULaBUla commented 11 months ago
  • 文档这边会继续跟进
  • 多个cmd偏移量注册后pack时如何确定使用哪个偏移量来进行呢?所以暂不计划支持多个cmd偏移位置注册,这种直接手动调用解析比较好。
  • 重不重写cmd方法并不会影响序列化结果,cmd是为了拿到magicMessage后识别消息类型, 不然就只能通过instanceof 来识别
  • 全局配置和现在思路是一样的
  • 多个 cmd 的时候加个 参数指定
  • 如果不重写 cmd 方法,那就会出现图中的问题 image
  • 不不不,“全局配置和现在思路是一样的” 这个是不一样的,如果我有地方单独配置就可以不用注解 cmdField = true,然后就可以对类型为 MagicMessage 的属性做动态序列化,而不用像现在这样,必须需要 cmdField = true,选择对应类做整体的序列化
MisterChangRay commented 11 months ago
  • 文档这边会继续跟进
  • 多个cmd偏移量注册后pack时如何确定使用哪个偏移量来进行呢?所以暂不计划支持多个cmd偏移位置注册,这种直接手动调用解析比较好。
  • 重不重写cmd方法并不会影响序列化结果,cmd是为了拿到magicMessage后识别消息类型, 不然就只能通过instanceof 来识别
  • 全局配置和现在思路是一样的
  • 多个 cmd 的时候加个 参数指定
  • 如果不重写 cmd 方法,那就会出现图中的问题 image
  • 不不不,“全局配置和现在思路是一样的” 这个是不一样的,如果我有地方单独配置就可以不用注解 cmdField = true,然后就可以对类型为 MagicMessage 的属性做动态序列化,而不用像现在这样,必须需要 cmdField = true,选择对应类做整体的序列化
FULaBUla commented 11 months ago
  • 文档这边会继续跟进
  • 多个cmd偏移量注册后pack时如何确定使用哪个偏移量来进行呢?所以暂不计划支持多个cmd偏移位置注册,这种直接手动调用解析比较好。
  • 重不重写cmd方法并不会影响序列化结果,cmd是为了拿到magicMessage后识别消息类型, 不然就只能通过instanceof 来识别
  • 全局配置和现在思路是一样的
  • 多个 cmd 的时候加个 参数指定
  • 如果不重写 cmd 方法,那就会出现图中的问题 image
  • 不不不,“全局配置和现在思路是一样的” 这个是不一样的,如果我有地方单独配置就可以不用注解 cmdField = true,然后就可以对类型为 MagicMessage 的属性做动态序列化,而不用像现在这样,必须需要 cmdField = true,选择对应类做整体的序列化
  • 此时cmd获取为-1是因为没有实现方法, 但是不影响序列化结果. 也就是 s1是正常序列化的
  • 第二个没太明白,但是全局设置长度和偏移,不应该和从数据结构中解析出的位置和偏移一样吗

注册的是 MagicMessage 实现类,这样二进制数据过来的时候依旧可以从数据里拿到对应位置的 cmd 值,然后用 pack(byte[], clazz) 的方法解析的时候判断到属性类型是 MagicMessage 就去注册里找我注册对应 cmd 值的实现类,这样就可以实现动态 body 内容了

MisterChangRay commented 11 months ago
MisterChangRay commented 11 months ago
  • 可以修改为未实现调用报错,由于这个不影响序列化结果,所以不用要求强制实现
  • 标记cmdField的目的就是为了获取消息类型字段的值,如果没有就需要在注册时指定。两者都是一样的啊

标记的目的时为了获取消息类型的偏移量和值

FULaBUla commented 11 months ago
  • 可以修改为未实现调用报错,由于这个不影响序列化结果,所以不用要求强制实现
  • 标记cmdField的目的就是为了获取消息类型字段的值,如果没有就需要在注册时指定。两者都是一样的啊

那目前其实就是这个问题:如果我是这么封装的


@MagicClass
public class Protocol implements MagicMessage {

    @MagicField(order = 1)
    private Head head;

    @MagicField(order = 3)
    private MagicMessage body;
}

而且,注册的时候把 cmdField 检查去了,我可以用 MagicByte.registerCMD(-1, Protocol.class); 注册 Protocol 的因为他里面有 cmdField 字段告诉框架偏移量和长度是啥,然后我再注册 MagicMessage 的若干实现类,同时绑定 cmd 码。由于类型擦除的问题,我实现 body 的序列化(其实也就是再调一下 MagicByte.unpackToByte,如果这里能改一下直接对接口类做这个操作更好),然后反序列化的时候能自动识别 cmd 然后反序列化MagicMessage 对应的实现类并赋值给 body 就可以满足需求了

MisterChangRay commented 11 months ago

现在是把每条消息当作一个独立的整体进行解析,他们有一个共同的模板MagicMessage 你的意思是把Protocol当作一个消息模板,

没有实质性的区别的。

MisterChangRay commented 11 months ago

而且比起将模板交给开发者来定义,不如交给开发者开标准。这样更容易上手。 开发者只需要了解实现接口就可以完整的序列化整个对象

实现一个接口,和使用这个接口当作属性。肯定是前者更简单啊

FULaBUla commented 11 months ago
  • 既然protocol里面有cmdfield定义,那么就能成功注册啊,为什么要去除校验检查呢 你的意思是将消息整合到一条消息中,不同消息id解析出不同的body? 这样也可以实现的,和现在实现的思路效果是一样的。

现在是把每条消息当作一个独立的整体进行解析,他们有一个共同的模板MagicMessage 你的意思是把Protocol当作一个消息模板,

没有实质性的区别的。

MisterChangRay commented 11 months ago

没有的,自能自己实现类似的效果

FULaBUla commented 11 months ago

没有的,自能自己实现类似的效果

那不就是我推上去那个测试例子的效果?

MisterChangRay commented 11 months ago

没有的,自能自己实现类似的效果

那不就是我推上去那个测试例子的效果?

可以的

FULaBUla commented 11 months ago

没有的,自能自己实现类似的效果

那不就是我推上去那个测试例子的效果?

可以的

我那个代码还有优化改造的空间吗?