Netty学习笔记四-序列化

上一节学习了Netty的TCP拆包粘包问题的解决之道,今天学习Netty的序列化。

什么是序列化

引入百科:序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象

Java的序列化

Java原生API提供了对象的输入输出流ObjectIntputStream和ObjectOutputStream,可直接将Java对象作为可存储的字节数组写入到文件,也可以传输到网络上。
Java序列化的目的主要有两个:
1、网络传输
2、对象持久化
但是基于Java的序列化方式在效率和速度上都有明显缺陷,目前已出现多个序列化框架,我们先通过代码比较下Java序列化的缺点

@Data
public class UserInfo implements Serializable {

  private static final long serialVersionUID = -3498249724990274743L;

  private String userName;

  private int userID;

  public byte[] codeC() {
      ByteBuffer buffer = ByteBuffer.allocate(1024);
      byte[] value = this.userName.getBytes();
      buffer.putInt(value.length);
      buffer.put(value);
      buffer.putInt(this.userID);
      buffer.flip();
      value = null;
      byte[] result = new byte[buffer.remaining()];
      buffer.get(result);
      return result;
  }
}

测试程序

public class TestUserInfo {
    public static void main(String[] args) {
        UserInfo info = new UserInfo();
        info.setUserID(100);
        info.setUserName("wcs");
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            ObjectOutputStream os  = new ObjectOutputStream(bos);
            os.writeObject(info);
            os.flush();
            os.close();
            byte[] b = bos.toByteArray();
            System.out.println("the jdk serializable length is:"+b.length);
            bos.close();
            System.out.println("-----------");
            System.out.println("the byte array Serializable length is:"+info.codeC().length);

        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}
image.png

由此可见采用JDK序列化编码后的二进制数组大小是二进制编码的10倍。
下面再比较下序列化的时间长短,对同一个对象进行100万次编码实验,然后统计耗费的总时间:

public class PerformsTestUserInfo {
    public static void main(String[] args) throws IOException{
        UserInfo info = new UserInfo();
        info.setUserID(100);
        info.setUserName("welcome to netty");
        int loop = 100000;
        ByteArrayOutputStream bos = null;
        ObjectOutputStream os = null;
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < loop; i++) {
            bos = new ByteArrayOutputStream();
            os = new ObjectOutputStream(bos);
            os.writeObject(info);
            os.flush();
            os.close();
            byte[] b = bos.toByteArray();
            bos.close();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("---------jbk serializable cost time"+(endTime - startTime)+" ms");
        System.out.println("-------");
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        startTime = System.currentTimeMillis();
        for (int i = 0; i < loop; i++) {
            byte[] b = info.codeC();
        }
        endTime = System.currentTimeMillis();
        System.out.println("---------the byte array serializable cost time is"+(endTime - startTime)+" ms");
    }
}
image.png

由此可见Java序列化的性能只有二进制编码的6.17%.

Netty Java序列化开发

使用Netty对POJO对象进行序列化开发,POJO对象如下:

@Data
public class SubscribeResp  implements Serializable {
    private static final long serialVersionUID = -4261173283103510587L;

    private int subReqId;

    private int respCode;

    private String desc;
    @Override
    public String toString() {
        return "subReqId="+subReqId+" respCode="+respCode+" desc="+desc;
    }
}

在Netty服务端程序中添加解码器ObjectDecode和编码器ObjectEncode
服务端代码如下

public class SubReqServer {
    public void bind(int port) throws Exception {
        //创建两个线程组 一个用于服务端接收客户端的连接
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        //一个用于网络读写
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 1024)
                .handler(new LoggingHandler(LogLevel.INFO))
                .childHandler(new ChannelInitializer() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        socketChannel.pipeline().addLast(new ObjectDecoder(1024 * 1024,
                            ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())));
                        socketChannel.pipeline().addLast(new ObjectEncoder());
                        socketChannel.pipeline().addLast(new SubReqServerHandler());
                    }
                });
            ChannelFuture future = b.bind(port).sync();
            future.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        int port = 8080;
        if (args != null && args.length > 0) {
            try {
                port = Integer.valueOf(args[0]);
            } catch (NumberFormatException e) {

            }
        }
        try {
            new SubReqServer().bind(port);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public class SubReqServerHandler  extends ChannelHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        SubscribeResp resp = (SubscribeResp) msg;
        if ("wcs".equals(resp.getDesc())) {
            System.out.println("Service accept client subsribe resp:[" + resp.toString()+"]");
            ctx.writeAndFlush(buildResponse(resp.getSubReqId()));
        }
    }

    private SubscribeResp buildResponse(int subReqId) {
        SubscribeResp resp = new SubscribeResp();
        resp.setSubReqId(subReqId);
        resp.setRespCode(0);
        resp.setDesc("receive success");
        return resp;
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

在Netty客户端程序中添加解码器ObjectDecode和编码器ObjectEncoder

public class SubReqClient {
    public void connect(int port,String host) throws Exception {
        //创建读写io线程组
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group).channel(NioSocketChannel.class)
                .option(ChannelOption.TCP_NODELAY,true)
                .handler(new ChannelInitializer() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        socketChannel.pipeline().addLast(new ObjectEncoder());
                        socketChannel.pipeline().addLast(new ObjectDecoder(1024 * 1024,
                            ClassResolvers.cacheDisabled(this.getClass().getClassLoader())));
                        socketChannel.pipeline().addLast(new SubReqClientHandler());

                    }
                });
            ChannelFuture f = b.connect(host,port).sync();
            f.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        int port = 8080;
        if (args != null && args.length >0) {
            try {
                port = Integer.valueOf(args[0]);
            }catch (NumberFormatException e) {

            }
        }
        try {
            new SubReqClient().connect(port,"127.0.0.1");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public class SubReqClientHandler extends ChannelHandlerAdapter {

    public SubReqClientHandler() {

    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        for (int i = 0; i< 10000; i++) {
            ctx.write(subReq(i));
        }
        ctx.flush();
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("Service accept client subsribe resp:[" + msg+"]");
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

    private SubscribeResp subReq(int i) {
        SubscribeResp subscribeResp = new SubscribeResp();
        subscribeResp.setSubReqId(i);
        subscribeResp.setDesc("wcs");
        return subscribeResp;
    }
}

服务端运行结果


image.png

客户端运行结果


image.png

你可能感兴趣的:(Netty学习笔记四-序列化)