Netty(11)序列化/反序列化、Netty参数
序列化与反序列化
序列化,反序列化主要用在消息正文的转换上
序列化时,需要将 Java 对象变为要传输的数据(可以是 byte[],或 json 等,最终都需要变成 byte[])
反序列化时,需要将传入的正文数据还原成 Java 对象,便于处理
在之前的自定义协议的时候,使用了序列化与反序列化;但是使用的是jdk自带的序列化和反序列化。如下:
// 反序列化
byte[] body = new byte[bodyLength];
byteByf.readBytes(body);
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(body));
Message message = (Message) in.readObject();
message.setSequenceId(sequenceId);
// 序列化
ByteArrayOutputStream out = new ByteArrayOutputStream();
new ObjectOutputStream(out).writeObject(message);
byte[] bytes = out.toByteArray();
Serializer 接口
在这个接口中,定义了抽象方法,可以有许多的实现
public interface Serializer {
// 反序列化方法
<T> T deserialize(Class<T> clazz, byte[] bytes);
// 序列化方法
<T> byte[] serialize(T object);
}
实现
java
@Override
public <T> T deserialize(Class<T> clazz, byte[] bytes) {
try {
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
return (T) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException("反序列化失败", e);
}
}
@Override
public <T> byte[] serialize(T object) {
try {
//拿到字节数据
ByteArrayOutputStream bos = new ByteArrayOutputStream();
//将object对象进行转换
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(object);
return bos.toByteArray();
} catch (IOException e) {
throw new RuntimeException("序列化失败", e);
}
}
json
@Override
public <T> T deserialize(Class<T> clazz, byte[] bytes) {
Gson gson = new GsonBuilder().registerTypeAdapter(Class.class, new Serializer.ClassCodec()).create();
String json = new String(bytes, StandardCharsets.UTF_8);
return gson.fromJson(json, clazz);
}
@Override
public <T> byte[] serialize(T object) {
Gson gson = new GsonBuilder().registerTypeAdapter(Class.class, new Serializer.ClassCodec()).create();
String json = gson.toJson(object);
return json.getBytes(StandardCharsets.UTF_8);
}
参数调优
CONNECT_TIMEOUT_MILLIS
CONNECT_TIMEOUT_MILLIS
属于 SocketChannal 参数
用在客户端建立连接时,如果在指定毫秒内无法连接,会抛出 timeout 异常
SO_TIMEOUT
主要用在阻塞 IO,阻塞 IO 中 accept,read 等都是无限等待的,如果不希望永远阻塞,使用它调整超时时间
这个参数不是用在netty里,也不用在Nio里的,其只是用在最传统的ServerSocket、Socket的阻塞IO编程中
客户端.option()
客户端通过 .option() 方法配置参数 给 SocketChannel 配置参数
Bootstrap bootstrap = new Bootstrap();
//配置参数, 300毫秒
bootstrap .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000)
服务器端
new ServerBootstrap().option() // 是给 ServerSocketChannel 配置参数
new ServerBootstrap().childOption() // 给 SocketChannel 配置参数
SO_BACKLOG
属于 ServerSocketChannal 参数
三次握手
半连接队列:还未完成三次握手的连接信息
全连接队列:已经完成三次握手的连接信息
clientserversyns queue(半连接队列)accept queue(全连接队列)bind()listen()connect()1. SYNSYN_SENDputSYN_RCVD2. SYN + ACKESTABLISHED3. ACKputESTABLISHEDaccept()clientserversyns queue(半连接队列)accept queue(全连接队列)
- 第一次握手,client 发送 SYN 到 server,状态修改为 SYN_SEND,server 收到,状态改变为 SYN_REVD,并将该请求放入 sync queue 队列
- 第二次握手,server 回复 SYN + ACK 给 client,client 收到,状态改变为 ESTABLISHED,并发送 ACK 给 server
- 第三次握手,server 收到 ACK,状态改变为 ESTABLISHED,将该请求从 sync queue 放入 accept queue
SO_BACKLOG
在 linux 2.2 之前,backlog 大小包括了两个队列的大小,在 2.2 之后,分别用下面两个参数来控制两个队列的大小
sync queue - 半连接队列
- 大小通过 /proc/sys/net/ipv4/tcp_max_syn_backlog 指定,在
syncookies
启用的情况下,逻辑上没有最大值限制,这个设置便被忽略
- 大小通过 /proc/sys/net/ipv4/tcp_max_syn_backlog 指定,在
accept queue - 全连接队列
其大小
通过 /proc/sys/net/core/somaxconn 指定
,在使用 listen 函数时,内核会根据传入的 backlog 参数与系统参数,取二者的较小值
- backlog 是程序中控制全连接队列的一个方式,在Nio中有一个bind()方法,其可以接收两个参数:
bind(8080, backlog)
- backlog 是程序中控制全连接队列的一个方式,在Nio中有一个bind()方法,其可以接收两个参数:
如果 accpet queue 队列满了,server 将发送一个拒绝连接的错误信息到 client
- 全连接队列的大小
作用
:决定可以有多少个客户端可以在队列中存放,如果客户端的连接超过队列的大小,就认为服务器处理连接达到上限了,就会报一个拒绝连接的错误
netty 中: 可以通过 option(ChannelOption.SO_BACKLOG, 值) 来设置大小
Bootstrap bootstrap = new Bootstrap();
//配置参数, 队列大小为2(一般设置1024,大一点)
bootstrap.option(ChannelOption.SO_BACKLOG, 2)
断点的意义:使连接放入队列里面,但没有从队列中取出
如果debug启动后,开启三个客户端,由于断点,连接没有并没有从队列中拿出,超过两个连接了,所以会报错,如下:
backlog是如何调用的
查看这个变量是在何处被引用
第一步:找到这个方法的使用位置,可以查看在何处调用了这个方法
发现有三个地方对这个方法进行了调用
nio中的不需要看,直接看netty里面的
第二步:进入netty调用这个方法的位置
进入方法
第三步:查看config这个参数从何来
第四步:查看其实现,进入Nio的
第五步:查找backlog获取的方法
第六步:进入NetUtil查看其是如何赋值的
ulimit -n
属于操作系统参数
限制一个进程能够同时打开最大的文件描述服务的数量
文件描述服务:简称FD,如普通文件,socket都是使用其来表示的
需要在linux系统里面去进行配置
TCP_NODELAY
属于 SocketChannal 参数
netty对这个参数的默认值是false:默认开启了nagle算法
如果服务器希望消息能够及时发出去,所以应该将这个参数设为true
可以在netty代码中去调整
Bootstrap bootstrap = new Bootstrap();
//配置参数
bootstrap.option(ChannelOption.TCP_NODELAY, true)
SO_SNDBUF & SO_RCVBUF
这两个参数决定了滑动窗口的上限:
- SO_SNDBUF 属于 SocketChannal 参数
- SO_RCVBUF 既可用于 SocketChannal 参数,也可以用于 ServerSocketChannal 参数(建议设置到 ServerSocketChannal 上)
目前不需要去调整这两个参数,操作系统会自动对这两个参数进行调整
可以在netty代码中去调整
ALLOCATOR
ByteBuf的分配器:
- 属于 SocketChannal 参数
- 用来分配 ByteBuf, ctx.alloc()
RCVBUF_ALLOCATOR
- 属于 SocketChannal 参数
控制 netty 接收缓冲区大小
- 负责入站数据的分配,决定入站缓冲区的大小(并可动态调整),统一采用 direct (直接内存
),具体池化还是非池化由 allocator 决定
还没有评论,来说两句吧...