一.Netty序列化介绍

  序列化管理操作:Java原生实现(性能比较差)、JSON(Restful)、MessagePack、Marshalling、AVRO、....

  netty本身直接支持有原生的 Java序列化操作,直接配置已有的程序类即可

  MessagePack:类似于JSON,但是要比JSON传输的更加小巧同时速度也快。它定义一个自己的压缩算法,例如:boolean只有true和false,但是有了MP就可以通过0和1描述了;

  Marshalling:使用JBoss实现的序列化处理操作,是基于传统的Java序列化形式的一种升级。

  JSON:是一种标准做法,但是JSON需要清楚的问题是传输体积要大,但是传输的信息明确,本次使用FastJSON操作。

 

二.实例

1.公共netty类

Member.java

package com.bijian.vo;

import java.io.Serializable;
public class Member implements Serializable {
    private String mid ;
    private String name ;
    private Integer age ;
    private Double salary ;

    public Member() {}
    public Member(String mid,String name,Integer age,Double salary) {
        this.mid = mid ;
        this.name = name ;
        this.age = age ;
        this.salary = salary ;
    }

    public String getMid() {
        return mid;
    }

    public void setMid(String mid) {
        this.mid = mid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Double getSalary() {
        return salary;
    }

    public void setSalary(Double salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Member{" +
                "mid='" + mid + '\'' +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }
}

EchoServerHandler.java

package com.bijian.http.server.server.handler;

import com.bijian.vo.Member;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;

/**
 * 处理Echo的操作方式,其中ChannelInboundHandlerAdapter是针对于数据输入的处理
 * Netty是基于NIO的一种开发框架的封装,这里面和AIO是没有任何关系的。
 */
public class EchoServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        try {
            System.out.println(msg.getClass() + " **************");
            Member member = (Member) msg ;
            System.err.println("{服务器}" + member);
            member.setName("【ECHO】" + member.getName());
            ctx.writeAndFlush(member); // 回应的输出操作
        } finally {
            ReferenceCountUtil.release(msg) ; // 释放缓存
        }
    }

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

EchoClientHandler.java

package com.bijian.http.server.client.handler;

import com.bijian.vo.Member;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;

/**
 * 需要进行数据的读取操作,服务器端处理完成的数据信息会进行读取
 */
public class EchoClientHandler extends ChannelInboundHandlerAdapter {
    private static final int REPEAT = 500;// 消息重复发送次数

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Member member = new Member();   // 现在直接进行对象的发送
        member.setMid("xiaoli");
        member.setName("小李老师");
        member.setAge(16);
        member.setSalary(1.1);
        ctx.writeAndFlush(member) ; // 直接进行对象实例发送
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 只要服务器端发送完成信息之后,都会执行此方法进行内容的输出操作
        try {
            Member member = (Member) msg ;
            System.out.println(member); // 输出服务器端的响应内容
        } finally {
            ReferenceCountUtil.release(msg); // 释放缓存
        }
    }

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

EchoServerMain.java

package com.bijian.http.server.server.main;

import com.bijian.http.server.server.EchoServer;

public class EchoServerMain {
    public static void main(String[] args) throws Exception {
        new EchoServer().run();
    }
}

EchoClientMain.java

package com.bijian.http.server.client.main;

import com.bijian.http.server.client.EchoClient;

public class EchoClientMain {
    public static void main(String[] args) throws Exception {
        new EchoClient().run();
    }
}

2.使用Java原生的序列化程序管理实现对象网络传输

项目结构如下:

.net 序列化 性能 netty序列化协议_.net

EchoServer.java

package com.bijian.http.server.server;

import com.bijian.http.server.server.handler.EchoServerHandler;
import com.bijian.info.HostInfo;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;

/**
 * 实现了基础的线程池与网络连接的配置项
 */
public class EchoServer {
    public void run() throws Exception {    // 进行服务器端的启动处理
        // 线程池是提升服务器性能的重要技术手段,利用定长的线程池可以保证核心线程的有效数量
        // 在Netty之中线程池的实现分为两类:主线程池(接收客户端连接)、工作线程池(处理客户端连接)
        EventLoopGroup bossGroup = new NioEventLoopGroup(10); // 创建接收线程池
        EventLoopGroup workerGroup = new NioEventLoopGroup(20); // 创建工作线程池
        System.out.println("服务器启动成功,监听端口为:" + HostInfo.PORT);
        try {
            // 创建一个服务器端的程序类进行NIO启动,同时可以设置Channel
            ServerBootstrap serverBootstrap = new ServerBootstrap();   // 服务器端
            // 设置要使用的线程池以及当前的Channel类型
            serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class);
            // 接收到信息之后需要进行处理,于是定义子处理器
            serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel socketChannel) throws Exception {
                    socketChannel.pipeline().addLast(new ObjectDecoder(ClassResolvers.cacheDisabled((this.getClass().getClassLoader())))) ;
                    socketChannel.pipeline().addLast(new ObjectEncoder()) ;
                    socketChannel.pipeline().addLast(new EchoServerHandler()); // 追加了处理器
                }
            });
            // 可以直接利用常亮进行TCP协议的相关配置
            serverBootstrap.option(ChannelOption.SO_BACKLOG, 128);
            serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
            // ChannelFuture描述的时异步回调的处理操作
            ChannelFuture future = serverBootstrap.bind(HostInfo.PORT).sync();
            future.channel().closeFuture().sync();// 等待Socket被关闭
        } finally {
            workerGroup.shutdownGracefully() ;
            bossGroup.shutdownGracefully() ;
        }
    }
}

EchoClient.java

package com.bijian.http.server.client;

import com.bijian.http.server.client.handler.EchoClientHandler;
import com.bijian.info.HostInfo;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;

public class EchoClient {
    public void run() throws Exception {
        // 1、如果现在客户端不同,那么也可以不使用多线程模式来处理;
        // 在Netty中考虑到代码的统一性,也允许你在客户端设置线程池
        EventLoopGroup group = new NioEventLoopGroup(); // 创建一个线程池
        try {
            Bootstrap client = new Bootstrap(); // 创建客户端处理程序
            client.group(group).channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true) // 允许接收大块的返回数据
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new ObjectDecoder(ClassResolvers.cacheDisabled((this.getClass().getClassLoader())))) ;
                            socketChannel.pipeline().addLast(new ObjectEncoder()) ;
                            socketChannel.pipeline().addLast(new EchoClientHandler()); // 追加了处理器
                        }
                    });
            ChannelFuture channelFuture = client.connect(HostInfo.HOST_NAME, HostInfo.PORT).sync();
            channelFuture.channel().closeFuture().sync(); // 关闭连接
        } finally {
            group.shutdownGracefully();
        }
    }
}

  先启动EchoServerMain.java,再启动EchoClientMain.java,运行效果如下所示:

.net 序列化 性能 netty序列化协议_java_02

.net 序列化 性能 netty序列化协议_.net 序列化 性能_03

 

3.MessagePack实现传输序列化

项目结构如下所示:

.net 序列化 性能 netty序列化协议_.net_04

pom.xml中引入msgpack

<dependency>
       <groupId>org.msgpack</groupId>
       <artifactId>msgpack</artifactId>
</dependency>

Member.java增加@Message注解

package com.bijian.vo;

import org.msgpack.annotation.Message;

import java.io.Serializable;
@Message
public class Member implements Serializable {
    private String mid ;
    private String name ;
    private Integer age ;
    private Double salary ;

    public Member() {}
    public Member(String mid,String name,Integer age,Double salary) {
        this.mid = mid ;
        this.name = name ;
        this.age = age ;
        this.salary = salary ;
    }

    public String getMid() {
        return mid;
    }

    public void setMid(String mid) {
        this.mid = mid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Double getSalary() {
        return salary;
    }

    public void setSalary(Double salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Member{" +
                "mid='" + mid + '\'' +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }
}

测试类MessagePackDemoA.java

package com.bijian.msgpack;

import com.bijian.vo.Member;
import org.msgpack.MessagePack;
import org.msgpack.template.Templates;

import java.util.ArrayList;
import java.util.List;

public class MessagePackDemoA {
    public static void main(String[] args) throws Exception {
        List<Member> allMembers = new ArrayList<Member>() ;
        for(int x = 0 ; x < 10 ; x ++) {
            Member member = new Member() ;
            member.setMid("MLDN - " + x);
            member.setName("Hello - " + x);
            member.setAge(10);
            member.setSalary(1.1);
            allMembers.add(member) ;
        }
        MessagePack msgPack = new MessagePack() ;
        byte data [] = msgPack.write(allMembers) ;
        System.out.println(data.length);
        {   // 将数据解析回来
            List<Member> all = msgPack.read(data, Templates.tList(msgPack.lookup(Member.class))) ;
            System.out.println(all);
        }
    }
}

  运行测试类结果如下:

.net 序列化 性能 netty序列化协议_线程池_05

MessagePackDecoder.java

package com.bijian.netty.serious;

import com.bijian.vo.Member;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import org.msgpack.MessagePack;

import java.util.List;

public class MessagePackDecoder extends MessageToMessageDecoder<ByteBuf> {
    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf msg, List<Object> list) throws Exception {
        int len = msg.readableBytes(); // 获取读取的数据长度
        byte[] data = new byte[len]; // 准备读取数据的空间
        msg.getBytes(msg.readerIndex(), data, 0, len); // 读取数据
        MessagePack msgPack = new MessagePack() ;
        System.out.println(msgPack.read(data));
        // list.add(msgPack.read(data)) ;
        list.add(msgPack.read(data,msgPack.lookup(Member.class))) ;
    }
}

MessagePackEncoder.java

package com.bijian.netty.serious;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import org.msgpack.MessagePack;

public class MessagePackEncoder extends MessageToByteEncoder<Object> {
    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, Object msg, ByteBuf byteBuf) throws Exception {
        MessagePack msgPack = new MessagePack() ;
        byte [] raw = msgPack.write(msg) ; // 进行对象的编码操作
        byteBuf.writeBytes(raw) ;
    }
}

EchoServerHandler.java

package com.bijian.netty.server.handler

import com.bijian.vo.Member;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;

/**
 * 处理Echo的操作方式,其中ChannelInboundHandlerAdapter是针对于数据输入的处理
 * Netty是基于NIO的一种开发框架的封装,这里面和AIO是没有任何关系的。
 */
public class EchoServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        try {
            System.out.println(msg.getClass() + " **************");
            Member member = (Member) msg ;
            System.err.println("{服务器}" + member);
            member.setName("【ECHO】" + member.getName());
            ctx.writeAndFlush(member); // 回应的输出操作
        } finally {
            ReferenceCountUtil.release(msg) ; // 释放缓存
        }
    }

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

EchoServer.java

package com.bijian.http.server.server;

import com.bijian.http.server.server.handler.EchoServerHandler;
import com.bijian.info.HostInfo;
import com.bijian.netty.serious.MessagePackDecoder;
import com.bijian.netty.serious.MessagePackEncoder;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;

/**
 * 实现了基础的线程池与网络连接的配置项
 */
public class EchoServer {
    public void run() throws Exception {    // 进行服务器端的启动处理
        // 线程池是提升服务器性能的重要技术手段,利用定长的线程池可以保证核心线程的有效数量
        // 在Netty之中线程池的实现分为两类:主线程池(接收客户端连接)、工作线程池(处理客户端连接)
        EventLoopGroup bossGroup = new NioEventLoopGroup(10); // 创建接收线程池
        EventLoopGroup workerGroup = new NioEventLoopGroup(20); // 创建工作线程池
        System.out.println("服务器启动成功,监听端口为:" + HostInfo.PORT);
        try {
            // 创建一个服务器端的程序类进行NIO启动,同时可以设置Channel
            ServerBootstrap serverBootstrap = new ServerBootstrap();   // 服务器端
            // 设置要使用的线程池以及当前的Channel类型
            serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class);
            // 接收到信息之后需要进行处理,于是定义子处理器
            serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel socketChannel) throws Exception {
                    socketChannel.pipeline().addLast(new LengthFieldBasedFrameDecoder(65536,0,4,0,4)) ;
                    socketChannel.pipeline().addLast(new MessagePackDecoder()) ;
                    socketChannel.pipeline().addLast(new LengthFieldPrepender(4)) ; // 与属性个数保持一致
                    socketChannel.pipeline().addLast(new MessagePackEncoder()) ;
                    socketChannel.pipeline().addLast(new EchoServerHandler()); // 追加了处理器
                }
            });
            // 可以直接利用常亮进行TCP协议的相关配置
            serverBootstrap.option(ChannelOption.SO_BACKLOG, 128);
            serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
            // ChannelFuture描述的时异步回调的处理操作
            ChannelFuture future = serverBootstrap.bind(HostInfo.PORT).sync();
            future.channel().closeFuture().sync();// 等待Socket被关闭
        } finally {
            workerGroup.shutdownGracefully() ;
            bossGroup.shutdownGracefully() ;
        }
    }
}

EchoClient.java

package com.bijian.http.server.client;

import com.bijian.http.server.client.handler.EchoClientHandler;
import com.bijian.info.HostInfo;
import com.bijian.netty.serious.MessagePackDecoder;
import com.bijian.netty.serious.MessagePackEncoder;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;

public class EchoClient {
    public void run() throws Exception {
        // 1、如果现在客户端不同,那么也可以不使用多线程模式来处理;
        // 在Netty中考虑到代码的统一性,也允许你在客户端设置线程池
        EventLoopGroup group = new NioEventLoopGroup(); // 创建一个线程池
        try {
            Bootstrap client = new Bootstrap(); // 创建客户端处理程序
            client.group(group).channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true) // 允许接收大块的返回数据
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new LengthFieldBasedFrameDecoder(65536,0,4,0,4)) ;
                            socketChannel.pipeline().addLast(new MessagePackDecoder()) ;
                            socketChannel.pipeline().addLast(new LengthFieldPrepender(4)) ; // 与属性个数保持一致
                            socketChannel.pipeline().addLast(new MessagePackEncoder()) ;
                            socketChannel.pipeline().addLast(new EchoClientHandler()); // 追加了处理器
                        }
                    });
            ChannelFuture channelFuture = client.connect(HostInfo.HOST_NAME, HostInfo.PORT).sync();
            channelFuture.channel().closeFuture().sync(); // 关闭连接
        } finally {
            group.shutdownGracefully();
        }
    }
}

  MessagePack实现传输序列化与使用Java原生的序列化程序管理实现对象网络传输项目的变化如下:

.net 序列化 性能 netty序列化协议_java_06

  先启动EchoServerMain.java,再启动EchoClientMain.java,运行效果如下所示:

.net 序列化 性能 netty序列化协议_线程池_07

.net 序列化 性能 netty序列化协议_线程池_08

4.Marshalling序列化实例

项目结构如下所示:

.net 序列化 性能 netty序列化协议_java_09

Member.java中的@Message注解去掉

MarshallingCodeFactory.java

package com.bijian.netty.serious;

import io.netty.handler.codec.marshalling.*;
import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.Marshalling;
import org.jboss.marshalling.MarshallingConfiguration;

public class MarshallingCodeFactory {
    public static MarshallingDecoder buildMarshallingDecoder() {
        MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial") ;  // 获取原始的JDK序列化
        MarshallingConfiguration configuration = new MarshallingConfiguration() ;
        UnmarshallerProvider provider = new DefaultUnmarshallerProvider(marshallerFactory,configuration) ;
        int maxSize = 1024 << 2 ; // 设置单个对象的最大长度
        MarshallingDecoder decoder = new MarshallingDecoder(provider,maxSize) ;
        return decoder ;
    }
    public static MarshallingEncoder buildMarshallingEncoder() {
        MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial") ;  // 获取原始的JDK序列化
        MarshallingConfiguration configuration = new MarshallingConfiguration() ;
        MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory,configuration) ;
        MarshallingEncoder encoder = new MarshallingEncoder(provider) ;
        return encoder ;
    }
}

EchoServer.java

package com.bijian.http.server.server;

import com.bijian.http.server.server.handler.EchoServerHandler;
import com.bijian.info.HostInfo;
import com.bijian.netty.serious.MarshallingCodeFactory;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;

/**
 * 实现了基础的线程池与网络连接的配置项
 */
public class EchoServer {
    public void run() throws Exception {    // 进行服务器端的启动处理
        // 线程池是提升服务器性能的重要技术手段,利用定长的线程池可以保证核心线程的有效数量
        // 在Netty之中线程池的实现分为两类:主线程池(接收客户端连接)、工作线程池(处理客户端连接)
        EventLoopGroup bossGroup = new NioEventLoopGroup(10); // 创建接收线程池
        EventLoopGroup workerGroup = new NioEventLoopGroup(20); // 创建工作线程池
        System.out.println("服务器启动成功,监听端口为:" + HostInfo.PORT);
        try {
            // 创建一个服务器端的程序类进行NIO启动,同时可以设置Channel
            ServerBootstrap serverBootstrap = new ServerBootstrap();   // 服务器端
            // 设置要使用的线程池以及当前的Channel类型
            serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class);
            // 接收到信息之后需要进行处理,于是定义子处理器
            serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel socketChannel) throws Exception {
                    socketChannel.pipeline().addLast(MarshallingCodeFactory.buildMarshallingEncoder()) ;
                    socketChannel.pipeline().addLast(MarshallingCodeFactory.buildMarshallingDecoder()) ;
                    socketChannel.pipeline().addLast(new EchoServerHandler()); // 追加了处理器
                }
            });
            // 可以直接利用常亮进行TCP协议的相关配置
            serverBootstrap.option(ChannelOption.SO_BACKLOG, 128);
            serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
            // ChannelFuture描述的时异步回调的处理操作
            ChannelFuture future = serverBootstrap.bind(HostInfo.PORT).sync();
            future.channel().closeFuture().sync();// 等待Socket被关闭
        } finally {
            workerGroup.shutdownGracefully() ;
            bossGroup.shutdownGracefully() ;
        }
    }
}

EchoClient.java

package com.bijian.http.server.client;

import com.bijian.http.server.client.handler.EchoClientHandler;
import com.bijian.info.HostInfo;
import com.bijian.netty.serious.MarshallingCodeFactory;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;

public class EchoClient {
    public void run() throws Exception {
        // 1、如果现在客户端不同,那么也可以不使用多线程模式来处理;
        // 在Netty中考虑到代码的统一性,也允许你在客户端设置线程池
        EventLoopGroup group = new NioEventLoopGroup(); // 创建一个线程池
        try {
            Bootstrap client = new Bootstrap(); // 创建客户端处理程序
            client.group(group).channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true) // 允许接收大块的返回数据
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(MarshallingCodeFactory.buildMarshallingEncoder()) ;
                            socketChannel.pipeline().addLast(MarshallingCodeFactory.buildMarshallingDecoder()) ;
                            socketChannel.pipeline().addLast(new EchoClientHandler()); // 追加了处理器
                        }
                    });
            ChannelFuture channelFuture = client.connect(HostInfo.HOST_NAME, HostInfo.PORT).sync();
            channelFuture.channel().closeFuture().sync(); // 关闭连接
        } finally {
            group.shutdownGracefully();
        }
    }
}

  Marshalling序列化与MessagePack实现传输序列化的变化如下:

.net 序列化 性能 netty序列化协议_.net 序列化 性能_10

  echo-netty下的pom.xml

.net 序列化 性能 netty序列化协议_线程池_11

  echo-netty下的pom.xml完整内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>echo</artifactId>
        <groupId>cn.mldn</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>echo-netty</artifactId>

    <packaging>jar</packaging>
    <dependencies>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.mldn</groupId>
            <artifactId>echo-util</artifactId>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.marshalling</groupId>
            <artifactId>jboss-marshalling</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jboss.marshalling</groupId>
            <artifactId>jboss-marshalling-serial</artifactId>
        </dependency>
    </dependencies>
</project>

  工程下的pom.xml

 

.net 序列化 性能 netty序列化协议_线程池_12

  pom.xml完整内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.mldn</groupId>
    <artifactId>echo</artifactId>
    <version>1.0</version>
    <modules>
        <module>echo-base</module>
        <module>echo-util</module>
        <module>echo-netty</module>
    </modules>
    <name>netty</name>
    <!-- FIXME change it to the project's website -->
    <url>http://www.example.com</url>
    <packaging>pom</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <jdk.version>1.8</jdk.version>
        <maven-compiler-plugin.version>3.7.0</maven-compiler-plugin.version>
        <junit.version>4.12</junit.version>
        <netty.version>4.1.31.Final</netty.version>
        <echo.version>1.0</echo.version>
        <msgpack.version>0.6.12</msgpack.version>
        <jboss-marshalling.version>2.0.6.Final</jboss-marshalling.version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>cn.mldn</groupId>
                <artifactId>echo-util</artifactId>
                <version>${echo.version}</version>
            </dependency>
            <dependency>
                <groupId>io.netty</groupId>
                <artifactId>netty-all</artifactId>
                <version>${netty.version}</version>
            </dependency>
            <dependency>
                <groupId>org.jboss.marshalling</groupId>
                <artifactId>jboss-marshalling</artifactId>
                <version>${jboss-marshalling.version}</version>
            </dependency>
            <dependency>
                <groupId>org.jboss.marshalling</groupId>
                <artifactId>jboss-marshalling-serial</artifactId>
                <version>${jboss-marshalling.version}</version>
            </dependency>

            <dependency>
                <groupId>org.msgpack</groupId>
                <artifactId>msgpack</artifactId>
                <version>${msgpack.version}</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven-compiler-plugin.version}</version>
                <configuration>
                    <source>${jdk.version}</source><!-- 源代码使用的开发版本 -->
                    <target>${jdk.version}</target><!-- 需要生成的目标class文件的编译版本 -->
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

  先启动EchoServerMain.java,再启动EchoClientMain.java,运行效果如下所示:

.net 序列化 性能 netty序列化协议_java_13

.net 序列化 性能 netty序列化协议_java_14

5.JSON序列化实例

项目结构如下:

.net 序列化 性能 netty序列化协议_java_15

JSONDecoder.java

package com.bijian.http.server.serious;

import com.bijian.vo.Member;
import com.alibaba.fastjson.JSON;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;

import java.util.List;

public class JSONDecoder extends MessageToMessageDecoder<ByteBuf> {

    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf msg, List<Object> list) throws Exception {
        int len = msg.readableBytes(); // 可以用的数据长度
        byte data[] = new byte[len];
        msg.getBytes(msg.readerIndex(), data, 0, len);
        list.add(JSON.parseObject(new String(data)).toJavaObject(Member.class)) ;
    }
}

JSONEncoder.java

package com.bijian.http.server.serious;

import com.alibaba.fastjson.JSONObject;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;

public class JSONEncoder extends MessageToByteEncoder<Object> {
    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, Object msg, ByteBuf out) throws Exception {
        byte data [] = JSONObject.toJSONString(msg).getBytes() ;
        out.writeBytes(data) ;
    }
}

EchoServer.java

package com.bijian.http.server.server;

import com.bijian.http.server.server.handler.EchoServerHandler;
import com.bijian.info.HostInfo;
import com.bijian.http.server.serious.JSONDecoder;
import com.bijian.http.server.serious.JSONEncoder;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;

/**
 * 实现了基础的线程池与网络连接的配置项
 */
public class EchoServer {
    public void run() throws Exception {    // 进行服务器端的启动处理
        // 线程池是提升服务器性能的重要技术手段,利用定长的线程池可以保证核心线程的有效数量
        // 在Netty之中线程池的实现分为两类:主线程池(接收客户端连接)、工作线程池(处理客户端连接)
        EventLoopGroup bossGroup = new NioEventLoopGroup(10); // 创建接收线程池
        EventLoopGroup workerGroup = new NioEventLoopGroup(20); // 创建工作线程池
        System.out.println("服务器启动成功,监听端口为:" + HostInfo.PORT);
        try {
            // 创建一个服务器端的程序类进行NIO启动,同时可以设置Channel
            ServerBootstrap serverBootstrap = new ServerBootstrap();   // 服务器端
            // 设置要使用的线程池以及当前的Channel类型
            serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class);
            // 接收到信息之后需要进行处理,于是定义子处理器
            serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel socketChannel) throws Exception {
                    socketChannel.pipeline().addLast(new LengthFieldBasedFrameDecoder(65536,0,4,0,4)) ;
                    socketChannel.pipeline().addLast(new JSONDecoder()) ;
                    socketChannel.pipeline().addLast(new LengthFieldPrepender(4)) ;
                    socketChannel.pipeline().addLast(new JSONEncoder()) ;
                    socketChannel.pipeline().addLast(new EchoServerHandler()); // 追加了处理器
                }
            });
            // 可以直接利用常亮进行TCP协议的相关配置
            serverBootstrap.option(ChannelOption.SO_BACKLOG, 128);
            serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
            // ChannelFuture描述的时异步回调的处理操作
            ChannelFuture future = serverBootstrap.bind(HostInfo.PORT).sync();
            future.channel().closeFuture().sync();// 等待Socket被关闭
        } finally {
            workerGroup.shutdownGracefully() ;
            bossGroup.shutdownGracefully() ;
        }
    }
}

EchoClient.java

package com.bijian.http.server.client;

import com.bijian.http.server.client.handler.EchoClientHandler;
import com.bijian.info.HostInfo;
import com.bijian.http.server.serious.JSONDecoder;
import com.bijian.http.server.serious.JSONEncoder;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;

public class EchoClient {
    public void run() throws Exception {
        // 1、如果现在客户端不同,那么也可以不使用多线程模式来处理;
        // 在Netty中考虑到代码的统一性,也允许你在客户端设置线程池
        EventLoopGroup group = new NioEventLoopGroup(); // 创建一个线程池
        try {
            Bootstrap client = new Bootstrap(); // 创建客户端处理程序
            client.group(group).channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true) // 允许接收大块的返回数据
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new LengthFieldBasedFrameDecoder(65536, 0, 4, 0, 4));
                            socketChannel.pipeline().addLast(new JSONDecoder());
                            socketChannel.pipeline().addLast(new LengthFieldPrepender(4));
                            socketChannel.pipeline().addLast(new JSONEncoder());
                            socketChannel.pipeline().addLast(new EchoClientHandler()); // 追加了处理器
                        }
                    });
            ChannelFuture channelFuture = client.connect(HostInfo.HOST_NAME, HostInfo.PORT).sync();
            channelFuture.channel().closeFuture().sync(); // 关闭连接
        } finally {
            group.shutdownGracefully();
        }
    }
}

  echo-netty下的pom.xml

.net 序列化 性能 netty序列化协议_.net_16

  工程下pom.xml内容修改如下:

.net 序列化 性能 netty序列化协议_网络通信_17

  先启动EchoServerMain.java,再启动EchoClientMain.java,效果如下:

.net 序列化 性能 netty序列化协议_线程池_18

.net 序列化 性能 netty序列化协议_线程池_19