FAST协议详解2 操作符
一、概述
操作符是FAST进行数据压缩的法宝之一,比如一个递增的数字,如果通过传统方式传输(比如二进制)则每一次都需要传递一个完整的数字,而使用递增操作符后,则不需要再传递这个字段,接收方根据模版里的操作符属性,自动将该字段的值+1即可。
二、操作符类型
看接口文档,存在以下类型的操作符。
操作符 |
说明 |
备注 |
CONSTANT |
常量操作符 |
在模版里就定义好了该值,流中不再传输。 |
COPY |
复制操作符 |
流中若前后两个消息该字段值一致,则不需要进行传输。 |
DEFAULT |
默认操作符 |
消息中的字段有默认值,当字段值与默认值不同时进行传输。 |
DELTA |
差值操作符 |
仅传输前后两个消息中该字段的差值。 |
INCREMENT |
递增操作符 |
如果传输前后两个消息中该字段值是递增的,则不需要进行传输。 |
NONE |
无操作符 |
传输前后两个消息该字段无关系,需要传输。 |
TAIL |
接尾操作符 |
仅传输字符串的尾部差异值。 |
三、操作符与编码
1、CONSTANT常量操作符
使用上述代码输出为:
msg111= -> {123, 1, 2, 3}
outByteStr=11000000,11111011,10000001,10000011,
可以看到,模版中第二个字段的操作符被设置为CONSTANT后,在进行编码前,我们不需要对该字段进行设值,编码后的FAST流中也没有对该字段的值进行压缩,但解码后自动获得了该值:2。如果我们对具备CONSTANT操作符的字段进行赋值,会产生报错。
2、COPY复制操作符
使用上述代码输出为:
msg111= -> {123, 1, 2, 3}
msg111= -> {123, 1, 2, 3}
outByteStr=11100000,11111011,10000001,10000010,10000011,10000000,10000001,10000011,
二进制数据解析如下:
二进制数 |
解码过程 |
解码结果 |
11100000 |
|
PMap |
11111011 |
01111011=123 |
123 |
10000001 |
00000001=1 |
1 |
10000010 |
00000010=2 |
2 |
10000011 |
00000011=3 |
3 |
10000000 |
|
PMap |
10000001 |
00000001=1 |
1 |
10000011 |
00000011=3 |
3 |
可以看到,我们将第二个字段设置为COPY操作符,编码时前后两条数据该字段值都是2,则在FAST流中直接省略了该字段的传输。
问题:如果传输不一样的值会如何
使用上述代码输出为:
msg111= -> {123, 1, 2, 3}
msg111= -> {123, 1, 22, 3}
outByteStr=11100000,11111011,10000001,10000010,10000011,10100000,10000001,10010110,10000011,
二进制数解析如下:
二进制数 |
解码过程 |
解码结果 |
11100000 |
|
PMap |
11111011 |
01111011=123 |
123 |
10000001 |
00000001=1 |
1 |
10000010 |
00000010=2 |
2 |
10000011 |
00000011=3 |
3 |
10100000 |
|
PMap |
10000001 |
00000001=1 |
1 |
10010110 |
00010110=22 |
22 |
10000011 |
00000011=3 |
3 |
可以看到,当前后两条数据传输的值不一样时,FAST流中会传递该字段的最新值。
问题:如果传输不一样的值后,再次传输一样的值,是以第一次传输的数据为准,还是以上一次传输的数据为准。
答:始终是以上一个数据为准。举例来说:
//第一段数据
Message message1 = new Message(template);
message1.setInteger(1, 1);
message1.setInteger(2, 2);
message1.setInteger(3, 3);
//第二段数据
Message message2 = new Message(template);
message2.setInteger(1, 1);
message2.setInteger(2, 22);
message2.setInteger(3, 3);
//第三段数据
Message message3 = new Message(template);
message3.setInteger(1, 1);
message3.setInteger(2, 22);
message3.setInteger(3, 3);
上述代码,第三段数据的第二个字段值与第二段数据一致,则FAST编码时第二个字段不再传输。修改下:
//第三段数据
Message message3 = new Message(template);
message3.setInteger(1, 1);
message3.setInteger(2, 2);
message3.setInteger(3, 3);
上述代码,第三段数据的第二个字段值与第一段数据一致,但与第二段不一致则FAST编码时还是会传输该字段值。
3、DEFAULT默认操作符
使用上述代码输出为:
msg111= -> {123, 1, 2, 3}
outByteStr=11000000,11111011,10000001,10000011,
可以看到,当传输值与DEFAULT值一致时,FAST流中将不传输该字段。我们略做修改如下:
Message message = new Message(template);
message.setInteger(1, 1);
message.setInteger(2, 22);
message.setInteger(3, 3);
则输出变为:
msg111= -> {123, 1, 22, 3}
outByteStr=11100000,11111011,10000001,10010110,10000011,
可以看到,当传输值与DEFAULT值不一致时,FAST流中会传输该字段。
4、DELTA差值操作符
使用上述代码输出为:
msg111= -> {123, 1, 2, 3}
msg111= -> {123, 1, 22, 3}
msg111= -> {123, 1, 222, 3}
outByteStr=11000000,11111011,10000001,10000010,10000011,10000000,10000001,10010100,10000011,10000000,10000001,00000001,11001000,10000011,
二进制数解析如下:
二进制数 |
解码过程 |
解码结果 |
11000000 |
|
PMap |
11111011 |
01111011=123 |
123 |
10000001 |
00000001=1 |
1 |
10000010 |
00000010=2 |
2 |
10000011 |
00000011=3 |
3 |
10000000 |
|
PMap |
10000001 |
00000001=1 |
1 |
10010100 |
00010100=20 |
22 |
10000011 |
00000011=3 |
3 |
10000000 |
|
PMap |
10000001 |
00000001=1 |
1 |
00000001,11001000 |
00000001=1 01001000=72 128+72=200 |
222 |
10000011 |
00000011=3 |
3 |
可以看到,使用DELTA差值操作符后,只需要传输前后两个数据的差值即可。
问题:如果是减小而不是增大会怎样?我们做如下修改:
//第一段数据
Message message1 = new Message(template);
message1.setInteger(1, 1);
message1.setInteger(2, 2);
message1.setInteger(3, 3);
//第一段数据编码
byte[] outByte1 = encoder.encode(message1);
//第二段数据
Message message2 = new Message(template);
message2.setInteger(1, 1);
message2.setInteger(2, 22);
message2.setInteger(3, 3);
//第二段数据编码
byte[] outByte2 = encoder.encode(message2);
//第三段数据
Message message3 = new Message(template);
message3.setInteger(1, 1);
message3.setInteger(2, 2);
message3.setInteger(3, 3);
//第三段数据编码
byte[] outByte3 = encoder.encode(message3);
使用上述代码输出为:
msg111= -> {123, 1, 2, 3}
msg111= -> {123, 1, 22, 3}
msg111= -> {123, 1, 2, 3}
outByteStr=11000000,11111011,10000001,10000010,10000011,10000000,10000001,10010100,10000011,10000000,10000001,11101100,10000011,
二进制数解析如下:
二进制数 |
解码过程 |
解码结果 |
11000000 |
|
PMap |
11111011 |
01111011=123 |
123 |
10000001 |
00000001=1 |
1 |
10000010 |
00000010=2 |
2 |
10000011 |
00000011=3 |
3 |
10000000 |
|
PMap |
10000001 |
00000001=1 |
1 |
10010100 |
00010100=20 |
22 |
10000011 |
00000011=3 |
3 |
10000000 |
|
PMap |
10000001 |
00000001=1 |
1 |
11101100 |
1101100= -20 |
2 |
10000011 |
00000011=3 |
3 |
可见,对于减小的情况,只需要传递负值即可。
问题:浮点数也可以这样减小或增大吗?
使用上述代码输出为:
msg111= -> {123, 1, 2.2, 3}
msg111= -> {123, 1, 22.22, 3}
msg111= -> {123, 1, 11.11, 3}
outByteStr=11000000,11111011,10000001,11111111,10010110,10000011,10000000,10000001,11111111,00010001,10011000,10000011,10000000,10000001,10000000,01110111,10101001,10000011,
二进制数解析如下:
二进制数 |
解码过程 |
解码结果 |
11000000 |
|
PMap |
11111011 |
01111011=123 |
123 |
10000001 |
00000001=1 |
1 |
11111111 |
1111111= -1 |
1位小数点 |
10010110 |
00010110=22 |
2.2 |
10000011 |
00000011=3 |
3 |
10000000 |
|
PMap |
10000001 |
00000001=1 |
1 |
11111111 |
1111111= -1 |
-1-1= -2(这里需要加上前一条消息的-1) =2位小数点 |
00010001,10011000 |
00010001=17 00011000=24 128*17+24=2200 |
2200+22=2222 考虑2位小数点=22.22 |
10000011 |
00000011=3 |
3 |
10000000 |
|
PMap |
10000001 |
00000001=1 |
1 |
10000000 |
0000000=0 |
0-2= -2(这里需要加上前一条消息的-2) =2位小数点 |
01110111,10101001 |
11101110101001=-1111 |
2222-1111=1111 考虑2位小数点=11.11 |
10000011 |
00000011=3 |
3 |
从上述解析中可以看出,在进行浮点数传输时,如果使用了差值操作符,除了数值部分是差值传输,小数位数也是差值传输的,这一点非常容易搞错。
5、INCREMENT递增操作符
上述代码输出如下:
msg111= -> {123, 1, 2, 3}
msg111= -> {123, 1, 3, 3}
msg111= -> {123, 1, 4, 3}
outByteStr=11100000,11111011,10000001,10000010,10000011,10000000,10000001,10000011,10000000,10000001,10000011,
二进制数解析如下:
二进制数 |
解码过程 |
解码结果 |
11100000 |
|
PMap |
11111011 |
01111011=123 |
123 |
10000001 |
00000001=1 |
1 |
10000010 |
00000010=2 |
2 |
10000011 |
00000011=3 |
3 |
10000000 |
|
PMap |
10000001 |
00000001=1 |
1 |
10000011 |
00000011=3 |
3 |
10000000 |
|
PMap |
10000001 |
00000001=1 |
1 |
10000011 |
00000011=3 |
3 |
可以看到,第二个字段在流中只发了一次初始值,第二条第三条由于是递增的数值,则不再发第二个字段。
问题:递减可以吗?
实测:不行
问题:只能递增1吗?
实测:是的
问题:浮点数可以递增吗?
实测:不行
6、NONE无操作符
就是没有操作符,流如实进行数据传输。
7、TAIL接尾操作符
上述代码输出如下:
msg111= -> {123, 1, 1234, 3}
msg111= -> {123, 1, 1235, 3}
msg111= -> {123, 1, 1236, 3}
outByteStr=11100000,11111011,10000001,00110001,00110010,00110011,10110100,10000011,10100000,10000001,10110101,10000011,10100000,10000001,10110110,10000011,
二进制数解析如下:
二进制数 |
解码过程 |
解码结果 |
11100000 |
|
PMap |
11111011 |
01111011=123 |
123 |
10000001 |
00000001=1 |
1 |
00110001,00110010,00110011,10110100 |
00110001=49 > 1 00110010=50 > 2 00110011=51 > 3 00110100=52 > 4 |
1234 |
10000011 |
00000011=3 |
3 |
10100000 |
|
PMap |
10000001 |
00000001=1 |
1 |
10110101 |
00110101=53 > 5 |
1235 |
10000011 |
00000011=3 |
3 |
10100000 |
|
PMap |
10000001 |
00000001=1 |
1 |
10110110 |
00110110=54 > 6 |
1236 |
10000011 |
00000011=3 |
3 |
所谓的接尾,并不是在最后接起来,而是对于同样长度的字符串,只传递其“尾部变化”的部分。比如1234->1235,只传递5即可。
四、回顾
需要注意的点如下:
1、具备CONSTANT常量操作符的字段不能赋值
2、COPY复制操作符,指的是该字段若值不变,则流中不传输。但如果变了则还是传输。
3、DEFAULT默认操作符,会预先指定一个默认值,如果传输的值与默认值一致则流中不传输,如果要传输的值与默认值不一样则需要传输。
4、DELTA差值操作符,当数值增大或减小时,只传输增大或缩小的值,比如上一条数据传递的是10,本条传递的是+2,则实际传输值是12。需要注意对于浮点数DECIMAL,其小数点位数也是差值传递的,比如上一条数据是小数点后3位,本条传递的数据最终结果若是小数点后2位,则小数点位数这里应传递+1,这样上一条的-3(小数点后三位)加上这里传递的+1,最终结果是-2(小数点后2位)。
5、INCREMENT递增操作符,注意是只能更加而不能减少。
6、NONE无操作符,就是所有数据都正常传递,不做压缩
7、TAIL接尾操作符,需要注意的是,前后传递的字符串长度必须是一样的才能进行“接尾”,否则会如实传递全部数据。我感觉应该叫“换尾”操作符比较合适。比如1234->1235,这样值传递5即可。如果传递的是45,则最终解析结果就变成了1245。