• 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();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    Serializer 接口

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

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

    实现

    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);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    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);
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    参数调优

    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)
    
    
    • 1
    • 2
    • 3
    • 4

    服务器端

    new ServerBootstrap().option() // 是给 ServerSocketChannel 配置参数
    
    new ServerBootstrap().childOption() // 给 SocketChannel 配置参数
    
    
    • 1
    • 2
    • 3
    • 4

    SO_BACKLOG

    属于 ServerSocketChannal 参数

    三次握手

    半连接队列:还未完成三次握手的连接信息
    全连接队列:已经完成三次握手的连接信息

    client server syns queue(半连接队列) accept queue(全连接队列) bind() listen() connect() 1. SYN SYN_SEND put SYN_RCVD 2. SYN + ACK ESTABLISHED 3. ACK put ESTABLISHED accept() client server syns 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, 值) 来设置大小

    Bootstrap bootstrap = new Bootstrap();
    //配置参数, 队列大小为2(一般设置1024,大一点)
    bootstrap.option(ChannelOption.SO_BACKLOG, 2) 
    
    • 1
    • 2
    • 3

    断点的意义:使连接放入队列里面,但没有从队列中取出
    在这里插入图片描述
    如果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) 
    
    • 1
    • 2
    • 3

    SO_SNDBUF & SO_RCVBUF

    这两个参数决定了滑动窗口的上限:

    • SO_SNDBUF 属于 SocketChannal 参数
    • SO_RCVBUF 既可用于 SocketChannal 参数,也可以用于 ServerSocketChannal 参数(建议设置到 ServerSocketChannal 上)

    目前不需要去调整这两个参数,操作系统会自动对这两个参数进行调整

    可以在netty代码中去调整

    ALLOCATOR

    ByteBuf的分配器:

    • 属于 SocketChannal 参数
    • 用来分配 ByteBuf, ctx.alloc()
    
    
    
    • 1
    • 2

    RCVBUF_ALLOCATOR

    • 属于 SocketChannal 参数
    • 控制 netty 接收缓冲区大小
    • 负责入站数据的分配,决定入站缓冲区的大小(并可动态调整),统一采用 direct (直接内存
      ),具体池化还是非池化由 allocator 决定
  • 相关阅读:
    化工&python | PID控制器优化算法
    C语言中的文件操作
    探索随机森林: 机器学习中的集成学习神器
    【计算机毕业设计】27.仓库管理系统源码
    【C++从入门到踹门】第十五篇:set 和 map
    IBM:《2024年消费者调研:无处不在的人工智能彻底变革零售业》
    用增之Google
    scartch考核标准
    Antd表格性能优化
    Spring之IoC
  • 原文地址:https://blog.csdn.net/yyuggjggg/article/details/126614881