Netty(11)序列化/反序列化、Netty参数

本是古典 何须时尚 2024-04-07 11:13 164阅读 0赞

序列化与反序列化

序列化,反序列化主要用在消息正文的转换上

  • 序列化时,需要将 Java 对象变为要传输的数据(可以是 byte[],或 json 等,最终都需要变成 byte[])
  • 反序列化时,需要将传入的正文数据还原成 Java 对象,便于处理

在之前的自定义协议的时候,使用了序列化与反序列化;但是使用的是jdk自带的序列化和反序列化。如下:

  1. // 反序列化
  2. byte[] body = new byte[bodyLength];
  3. byteByf.readBytes(body);
  4. ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(body));
  5. Message message = (Message) in.readObject();
  6. message.setSequenceId(sequenceId);
  7. // 序列化
  8. ByteArrayOutputStream out = new ByteArrayOutputStream();
  9. new ObjectOutputStream(out).writeObject(message);
  10. byte[] bytes = out.toByteArray();

Serializer 接口

在这个接口中,定义了抽象方法,可以有许多的实现

  1. public interface Serializer {
  2. // 反序列化方法
  3. <T> T deserialize(Class<T> clazz, byte[] bytes);
  4. // 序列化方法
  5. <T> byte[] serialize(T object);
  6. }

实现

java

  1. @Override
  2. public <T> T deserialize(Class<T> clazz, byte[] bytes) {
  3. try {
  4. ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
  5. return (T) ois.readObject();
  6. } catch (IOException | ClassNotFoundException e) {
  7. throw new RuntimeException("反序列化失败", e);
  8. }
  9. }
  10. @Override
  11. public <T> byte[] serialize(T object) {
  12. try {
  13. //拿到字节数据
  14. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  15. //将object对象进行转换
  16. ObjectOutputStream oos = new ObjectOutputStream(bos);
  17. oos.writeObject(object);
  18. return bos.toByteArray();
  19. } catch (IOException e) {
  20. throw new RuntimeException("序列化失败", e);
  21. }
  22. }

json

  1. @Override
  2. public <T> T deserialize(Class<T> clazz, byte[] bytes) {
  3. Gson gson = new GsonBuilder().registerTypeAdapter(Class.class, new Serializer.ClassCodec()).create();
  4. String json = new String(bytes, StandardCharsets.UTF_8);
  5. return gson.fromJson(json, clazz);
  6. }
  7. @Override
  8. public <T> byte[] serialize(T object) {
  9. Gson gson = new GsonBuilder().registerTypeAdapter(Class.class, new Serializer.ClassCodec()).create();
  10. String json = gson.toJson(object);
  11. return json.getBytes(StandardCharsets.UTF_8);
  12. }

参数调优

CONNECT_TIMEOUT_MILLIS

CONNECT_TIMEOUT_MILLIS

  • 属于 SocketChannal 参数
  • 用在客户端建立连接时,如果在指定毫秒内无法连接,会抛出 timeout 异常

SO_TIMEOUT 主要用在阻塞 IO,阻塞 IO 中 accept,read 等都是无限等待的,如果不希望永远阻塞,使用它调整超时时间

  • 这个参数不是用在netty里,也不用在Nio里的,其只是用在最传统的ServerSocket、Socket的阻塞IO编程中

客户端.option()

客户端通过 .option() 方法配置参数 给 SocketChannel 配置参数

  1. Bootstrap bootstrap = new Bootstrap();
  2. //配置参数, 300毫秒
  3. bootstrap .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000)

服务器端

  1. new ServerBootstrap().option() // 是给 ServerSocketChannel 配置参数
  2. 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(全连接队列)

  1. 第一次握手,client 发送 SYN 到 server,状态修改为 SYN_SEND,server 收到,状态改变为 SYN_REVD,并将该请求放入 sync queue 队列
  2. 第二次握手,server 回复 SYN + ACK 给 client,client 收到,状态改变为 ESTABLISHED,并发送 ACK 给 server
  3. 第三次握手,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 启用的情况下,逻辑上没有最大值限制,这个设置便被忽略
  • accept queue - 全连接队列

    • 其大小通过 /proc/sys/net/core/somaxconn 指定,在使用 listen 函数时,内核会根据传入的 backlog 参数与系统参数取二者的较小值

      • backlog 是程序中控制全连接队列的一个方式,在Nio中有一个bind()方法,其可以接收两个参数:bind(8080, backlog)
    • 如果 accpet queue 队列满了,server 将发送一个拒绝连接的错误信息到 client
    • 全连接队列的大小作用决定可以有多少个客户端可以在队列中存放,如果客户端的连接超过队列的大小,就认为服务器处理连接达到上限了,就会报一个拒绝连接的错误

netty 中: 可以通过 option(ChannelOption.SO_BACKLOG, 值) 来设置大小

  1. Bootstrap bootstrap = new Bootstrap();
  2. //配置参数, 队列大小为2(一般设置1024,大一点)
  3. 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代码中去调整

  1. Bootstrap bootstrap = new Bootstrap();
  2. //配置参数
  3. 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 决定

发表评论

表情:
评论列表 (有 0 条评论,164人围观)

还没有评论,来说两句吧...

相关阅读

    相关 序列序列

    序列化: 对象的序列化主要有两种用途:   1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;   2) 在网络上传送对象的字节序列。   

    相关 Netty序列协议Protocol buff

    序列化协议 序列化和反序列化 把对象转换为字节序列的过程称为对象的序列化,把字节序列恢复为对象的过程称为对象的反序列化。用途:文件的copy、网络数据的传输 Pro