netty系列之:netty中的核心解码器json
简介
程序和程序之间的数据传输方式有很多,可以通过二进制协议来传输,比较流行的像是thrift协议或者google的protobuf。这些二进制协议可以实现数据的有效传输,并且通过二进制的形式可以节省数据的体积,在某些速度和效率优先的情况下是非常有效的。并且如果不同的编程语言之间的相互调用,也可以通过这种二进制的协议来实现。
虽然二进制更加快速和有效,但是对于程序员来说不是很友好,因为一个人很难直接读取二进制文件,虽然也存在一些一些文本的数据传输方式,比如XML,但是XML的繁琐的标签导致了XML在使用中有诸多的不便。于是一种通用的文本文件传输格式json诞生了。
能读到这篇文章的朋友肯定对json不陌生了,当然还有一些更加简洁的文件格式,比如YAML,感兴趣的朋友可以更深入的了解一下。
这里我们想要讲的是netty对json的解码。
java中对json的支持
在java中我们json的使用通常是将一个对象转换成为json进行数据传输,或者将接收到json进行解析,将其转换成为对象。
可惜的是在JDK中并没有提供给一个好用的JSON工具,所以我们一般需要借助第三方的JSON包来实现Object和JSON之间的转换工作。
通常使用的有google的GSON,阿里的FastJSON和jackson等。
这里我们使用google的GSON来进行介绍。
这里我们主要讲解的是java中对象和json的互相转换,所以GSON中其他更加强大的功能这里就不介绍了。
首先我们创建一个JAVA对象,我们定义一个Student类,如下所示:
static class Student {
String name;
String phone;
Integer age;
public Student(String name, String phone, Integer age) {
this.name = name;
this.phone = phone;
this.age = age;
}
}
这个类中,我们为Student定义了几个不同的属性和一个构造函数。接下来我们看下如何使用GSON来对这个对象进行JSON的转换:
Student obj = new Student("tina","188888888",18);
Gson gson = new Gson();
String json = gson.toJson(obj);
System.out.println(json);
Student obj2 = gson.fromJson(json, Student.class);
System.out.println(obj2);
GSON使用起来非常简单,我们构建好Gson对象之后,直接调用它的toJson方法即可将对象转换成为json字符串。
然后调用json的fromJson方法就可以将json字符串转换成为对象。
上面的代码输出如下:
{"name":"tina","phone":"188888888","age":18}
com.flydean.JsonTest$Student@4534b60d
netty对json的解码
netty为json提供了一个解码器叫做JsonObjectDecoder,先来看下JsonObjectDecoder的定义:
public class JsonObjectDecoder extends ByteToMessageDecoder
和前面讲解的base64,byte数组不同的是,JsonObjectDecoder继承的是ByteToMessageDecoder而不是MessageToMessageDecoder。
这说明JsonObjectDecoder是直接从ByteBuf转换成为Json Object对象。
我们知道JDK中并没有JSON这个对象,所有的对象都是从第三方包中引入的,netty并没有引入新的对象,所以netty中从Json中解析出来的对象还是一个ByteBuf对象,在这个ByteBuf中包含了一个Json对象。
JsonObjectDecoder的解析逻辑是怎么样的呢?
首先来看下JsonObjectDecoder中定义的4个state:
private static final int ST_CORRUPTED = -1;
private static final int ST_INIT = 0;
private static final int ST_DECODING_NORMAL = 1;
private static final int ST_DECODING_ARRAY_STREAM = 2;
ST_INIT表示的是decode的初始状态,ST_CORRUPTED表示的是decode中出现的异常状态。
ST_DECODING_NORMAL代表的是一个普通的json,如下所示:
{
"source": "web",
"type": "product_info",
"time": 1641967014440,
"data": {
"id": 30000084318055,
"staging": false
},
"dataId": "123456"
}
ST_DECODING_ARRAY_STREAM代表的是一个数组,对于数组来说,数组也是一个对象,所以数组也可以用json表示,下面就是一个常见的json数组:
[ "Google", "Runoob", "Taobao" ]
JsonObjectDecoder的解码逻辑比较简单,它主要是读取ByteBuf中的数据,通过判断读取的数据和json中特有的大括号,中括号,逗号等分隔符来分割和解析json对象。
要注意的是,JsonObjectDecoder要解码的ByteBuf中的消息应该是UTF-8编码格式的,为什么需要UTF-8格式呢?
这是因为json中那些特有的分隔符,即使在UTF-8中也是用一个byte来存储的,这样我们在读取数据的过程中,可以通过读取的byte值和json的分隔符进行比较,从而来确定json中不同对象的界限。
如果换成其他的编码方式,json中的分隔符可能会用多个byte来表示,这样对我们的解析就提高了难度,因为我们需要知道什么时候是分隔符的开始,什么时候是分隔符的结束。
它的核心解码逻辑如下,首先从ByteBuf中读取一个byte:
byte c = in.getByte(idx);
然后通过调用decodeByte(c, in, idx); 来判断当前的位置是开括号,还是闭括号,是在一个对象的字符串中,还是一个新的对象字符串。
首先需要对当前的state做一个判断,state判断调用的是initDecoding方法:
private void initDecoding(byte openingBrace) {
openBraces = 1;
if (openingBrace == '[' && streamArrayElements) {
state = ST_DECODING_ARRAY_STREAM;
} else {
state = ST_DECODING_NORMAL;
}
}
接着就是对当前的state和自定义的4个状态进行比较,如果是普通的json对象,并且对象已经是闭括号状态,说明该对象已经读取完成,可以将其进行转换并输出了:
if (state == ST_DECODING_NORMAL) {
decodeByte(c, in, idx);
if (openBraces == 0) {
ByteBuf json = extractObject(ctx, in, in.readerIndex(), idx + 1 - in.readerIndex());
if (json != null) {
out.add(json);
}
...
如果state表示目前是一个数组对象,数组对象中可能包含多个对象,这些对象是通过逗号来区分的。逗号之间还可能会有空格,所以需要对这些数据进行特殊判断和处理,如下所示:
else if (state == ST_DECODING_ARRAY_STREAM) {
decodeByte(c, in, idx);
if (!insideString && (openBraces == 1 && c == ',' || openBraces == 0 && c == ']')) {
for (int i = in.readerIndex(); Character.isWhitespace(in.getByte(i)); i++) {
in.skipBytes(1);
}
int idxNoSpaces = idx - 1;
while (idxNoSpaces >= in.readerIndex() && Character.isWhitespace(in.getByte(idxNoSpaces))) {
idxNoSpaces--;
}
ByteBuf json = extractObject(ctx, in, in.readerIndex(), idxNoSpaces + 1 - in.readerIndex());
if (json != null) {
out.add(json);
}
....
最后将解析出来的json对象放入byteBuf的out list中,整个解析到此结束。
总结
以上就是netty中json核心解码器JsonObjectDecoder的使用,它的本质是通过判断json对象中的分割符来分割多个json字符串,然后将分割后的json字符串存入ByteBuf中输出。
看到这里,大家可能会疑惑了,decoder不是和encoder一起出现的吗?为什么netty中只有JsonObjectDecoder,而没有JsonObjectEncoder呢?
事实上,这里的Json对象就是一个包含Json字符的字符串,这个字符串被写入到ByteBuf中,所以这里并不需要特殊的encoder。
本文已收录于 http://www.flydean.com/14-3-netty-codec-json/
最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!
netty系列之:netty中的核心解码器json的更多相关文章
- 【读后感】Netty 系列之 Netty 高性能之道 - 相比 Mina 怎样 ?
[读后感]Netty 系列之 Netty 高性能之道 - 相比 Mina 怎样 ? 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商 ...
- Netty 系列之 Netty 高性能之道 高性能的三个主题 Netty使得开发者能够轻松地接受大量打开的套接字 Java 序列化
Netty系列之Netty高性能之道 https://www.infoq.cn/article/netty-high-performance 李林锋 2014 年 5 月 29 日 话题:性能调优语言 ...
- netty系列之:netty中的核心编码器base64
目录 简介 netty codec的实现逻辑 netty中Base64的实现 netty中的base64编码和解码器 Base64Encoder Base64Decoder 总结 简介 我们知道数据在 ...
- netty系列之:netty中的核心编码器bytes数组
目录 简介 byte是什么 netty中的byte数组的工具类 netty中byte的编码器 总结 简介 我们知道netty中数据传输的核心是ByteBuf,ByteBuf提供了多种数据读写的方法,包 ...
- netty系列之:netty实现http2中的流控制
目录 简介 http2中的流控制 netty对http2流控制的封装 Http2FlowController Http2LocalFlowController Http2RemoteFlowContr ...
- netty系列之:netty中各不同种类的channel详解
目录 简介 ServerChannel和它的类型 Epoll和Kqueue AbstractServerChannel ServerSocketChannel ServerDomainSocketCh ...
- netty系列之:netty架构概述
目录 简介 netty架构图 丰富的Buffer数据机构 零拷贝 统一的API 事件驱动 其他优秀的特性 总结 简介 Netty为什么这么优秀,它在JDK本身的NIO基础上又做了什么改进呢?它的架构和 ...
- Netty系列之Netty可靠性分析
作者 李林锋 发布于 2014年6月19日 | 29 讨论 分享到:微博微信FacebookTwitter有道云笔记邮件分享 稍后阅读 我的阅读清单 1. 背景 1.1. 宕机的代价 1.1. ...
- 【转】Netty系列之Netty编解码框架分析
http://www.infoq.com/cn/articles/netty-codec-framework-analyse/ 1. 背景 1.1. 编解码技术 通常我们也习惯将编码(Encode)称 ...
随机推荐
- vue的seo问题?
seo关系到网站排名, vue搭建spa做前后端分离不好做seo, 可通过其他方法解决: SSR服务端渲染: 将同一个组件渲染为服务器端的 HTML 字符串.利于seo且更快. vue-meta-in ...
- maven在idea中的一点使用技巧
maven在idea中的一点使用技巧 idea已经支持将参数的意思也展示出来,确实很方便. -U是强制拉取,因为如果拉取某个jar包,失败了,那么在一段时间内,idea不会重试,除非指定-U. -X呢 ...
- 在JAVA中如何跳出当前的多重嵌套循环?
在Java中,要想跳出多重循环,可以在外面的循环语句前定义一个标号,然后在里层循环体的代码中使用带有标号的break语句,即可跳出外层循环.
- 事务的 ACID 是指什么?
原子性(Atomic):事务中各项操作,要么全做要么全不做,任何一项操作 的失败都会导致整个事务的失败: 一致性(Consistent):事务结束后系统状态是一致的: 隔离性(Isolated):并发 ...
- Netty学习摘记 —— 心跳机制 / 基于分隔符和长度的协议
本文参考 本篇文章是对<Netty In Action>一书第十一章"预置的ChannelHandler和编解码器"的学习摘记,主要内容为通过 SSL/TLS 保护 N ...
- Azure DevOps 中 Dapr项目自动部署流程实践
注:本文中主要讨论 .NET6.0项目在 k8s 中运行的 Dapr 的持续集成流程, 但实际上不是Dapr的项目部署到K8s也是相同流程,只是k8s的yaml配置文件有所不同 流程选择 基于 Dap ...
- mysql实现in子句的limit查询 (转)
在supesite里面执行一个SQL语句: select * from supe_spaceitems where catid=98 and itemid not in(select itemid f ...
- JavaScript & 6小时了解ES6基本语法
一步一步似爪牙. 前言 学习es6之前我们可能并不知道es6相比es5差距在哪, 但是这并不妨碍我们站在巨人的肩膀上; 程序员就是要乐于尝鲜; 学习es6最终目的是结合es5 一起进行工程项目开发, ...
- ES6-11学习笔记--异步迭代
ES9提供异步迭代: for await of Symbol.asyncIterator function getPromise(time) { return new Promise((resol ...
- pip freeze > requirements.txt` 命令输出文件中出现文件路径而非版本号
pip freeze > requirements.txt 命令输出文件中出现文件路径而非版本号 解决办法: pip list --format=freeze > requirements ...