JBoss Marshalling 序列化技术 编解码器使用 netty 实例

      • 环境准备
          • maven 依赖
          • 项目结构
      • 编码与介绍
          • 功能介绍 & 实现效果
          • 服务端开发
          • 客户端开发
          • Marshalling 编解码器工厂类 & pojo
      • 粘包拆包思考
      • 遇到的坑
          • Marshalling 的jar 版本小于2
          • pojo类要实现Serializable接口

JBoss Marshalling 是一个 Java 对象序列化包,对 JDK 默认的序列化框架进行了优化,但又保持与 Serializable 接口的兼容,同时增加了一些可调用的参数和附加的属性,这些参数可通过工厂类进行配置。

环境准备

maven 依赖
<dependency>
  <groupId>io.nettygroupId>
  <artifactId>netty-allartifactId>
  <version>4.1.10.Finalversion>
dependency>
<dependency>
  <groupId>org.jboss.marshallinggroupId>
  <artifactId>jboss-marshallingartifactId>
  <version>1.4.11.Finalversion>
dependency>
<dependency>
  <groupId>org.jboss.marshallinggroupId>
  <artifactId>jboss-marshalling-serialartifactId>
  <version>1.4.11.Finalversion>
dependency>
项目结构

JBoss Marshalling 序列化技术 编解码器使用 netty 实例_第1张图片

编码与介绍

功能介绍 & 实现效果
// 1. 客户端发送5个Boy实例对象

五个蓝孩子组队去表白:
[name=同学 1, height=170, faceScore=91, isRich=false]
[name=同学 2, height=180, faceScore=89, isRich=true]
[name=同学 3, height=162, faceScore=93, isRich=false]
[name=同学 4, height=169, faceScore=96, isRich=true]
[name=同学 5, height=172, faceScore=84, isRich=false]

// 2. 服务端收到依次收到每个Boy实例对象

收到表白:[name=同学 1, height=170, faceScore=91, isRich=false]
收到表白:[name=同学 2, height=180, faceScore=89, isRich=true]
收到表白:[name=同学 3, height=162, faceScore=93, isRich=false]
收到表白:[name=同学 4, height=169, faceScore=96, isRich=true]
收到表白:[name=同学 5, height=172, faceScore=84, isRich=false]

// 3. 服务端(女孩)判断此boy有没有高富帅属性,给予回复

收到女生回复 :to 同学 1 : 中意你哦!
收到女生回复 :to 同学 2 : 中意你哦!
收到女生回复 :to 同学 3 : 中意你哦!
收到女生回复 :to 同学 4 : 中意你哦!
收到女生回复 :to 同学 5 : 骚年回家继续努力吧!
服务端开发
// Server 服务端主程序

public class Server
{
    public void bind(int port) throws InterruptedException
    {
        EventLoopGroup boss=new NioEventLoopGroup();
        EventLoopGroup worker=new NioEventLoopGroup();
        try
        {
            ServerBootstrap b=new ServerBootstrap();
            b.group(boss,worker)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1280)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer()
                    {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception
                        {
                            ch.pipeline().addLast(
                                    MarshallingCodeCFactory.buildMarshallingDecoder()
                            );
                            ch.pipeline().addLast(
                                    MarshallingCodeCFactory.buildMarshallingEncoder()
                            );
                            ch.pipeline().addLast(
                                    new ServerHandler()
                            );
                        }
                    });

            ChannelFuture f=b.bind(port).sync();

            f.channel().closeFuture().sync();
        } finally
        {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws InterruptedException
    {
        int port = 8005;
        new Server().bind(port);
    }
}
// ServerHandler 服务端Handler

@ChannelHandler.Sharable
public class ServerHandler extends ChannelInboundHandlerAdapter
{
    @Override
    public void channelRead(ChannelHandlerContext ctx , Object msg) throws Exception
    {
        Boy boy= (Boy) msg;
        System.out.println("收到表白:"+boy);
        ctx.writeAndFlush(reply(boy));
    }

    private static GirlResponse reply(Boy boy){
        GirlResponse gr=new GirlResponse();
        if(boy.getFaceScore()>=90||boy.getHeight()>=180||boy.isRich()){
            gr.setMsg("to "+boy.getName()+" : 中意你哦!");
        }else{
            gr.setMsg("to "+boy.getName()+" : 骚年回家继续努力吧!");
        }
        return gr;
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx , Throwable cause) throws Exception
    {
        cause.printStackTrace();
        ctx.close();
    }
}
客户端开发
// 客户端主程序

public class Client
{
    public void connect(int port, String host) throws InterruptedException
    {
        EventLoopGroup group = new NioEventLoopGroup();
        try
        {
            Bootstrap b=new Bootstrap();
            b.group(group)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer()
                    {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception
                        {
                            ch.pipeline().addLast(
                                    MarshallingCodeCFactory.buildMarshallingDecoder()
                            );
                            ch.pipeline().addLast(
                                    MarshallingCodeCFactory.buildMarshallingEncoder()
                            );
                            ch.pipeline().addLast(new ClientHandler());
                        }
                    });

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

    public static void main(String[] args) throws InterruptedException
    {
        int port = 8005;
        String host="127.0.0.1";
        new Client().connect(port, host);
    }
}
// 客户端Handler

public class ClientHandler extends ChannelInboundHandlerAdapter
{
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception
    {
        System.out.println("五个蓝孩子组队去表白:");
        Random r = new Random();
        for (int i = 1; i <= 5; i++)
        {
            Boy boy = new Boy();
            boy.setName("同学 " + i);
            boy.setFaceScore(r.nextInt(20) + 80);
            boy.setHeight(r.nextInt(25) + 160);
            boy.setRich(i % 2 == 0);
            ctx.write(boy);
            System.out.println(boy);
        }
        ctx.flush();
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx , Object msg) throws Exception
    {
        GirlResponse gr= (GirlResponse) msg;
        System.out.println("收到女生回复 :"+gr.getMsg());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx , Throwable cause) throws Exception
    {
        cause.printStackTrace();
        ctx.close();
    }
}
Marshalling 编解码器工厂类 & pojo
// Marshalling 编解码器工厂

public final class MarshallingCodeCFactory
{

    /**
     * 创建Jboss Marshalling解码器MarshallingDecoder
     *
     * @return MarshallingDecoder
     */
    public static MarshallingDecoder buildMarshallingDecoder()
    {
        // 首先通过Marshalling工具类的精通方法获取Marshalling实例对象 参数serial标识创建的是java序列化工厂对象。
        final MarshallerFactory marshallerFactory = Marshalling
                .getProvidedMarshallerFactory("serial");
        // 创建了MarshallingConfiguration对象,配置了版本号为5
        final MarshallingConfiguration configuration = new MarshallingConfiguration();
        configuration.setVersion(5);
        // 根据marshallerFactory和configuration创建provider
        UnmarshallerProvider provider = new DefaultUnmarshallerProvider(
                marshallerFactory , configuration);
        // 构建Netty的MarshallingDecoder对象,俩个参数分别为provider和单个消息序列化后的最大长度
        return new MarshallingDecoder(provider ,
                1024 * 1024 * 1);
    }

    /**
     * 创建Jboss Marshalling编码器MarshallingEncoder
     *
     * @return MarshallingEncoder
     */
    public static MarshallingEncoder buildMarshallingEncoder()
    {
        final MarshallerFactory marshallerFactory = Marshalling
                .getProvidedMarshallerFactory("serial");
        final MarshallingConfiguration configuration = new MarshallingConfiguration();
        configuration.setVersion(5);
        MarshallerProvider provider = new DefaultMarshallerProvider(
                marshallerFactory , configuration);
        // 构建Netty的MarshallingEncoder对象,MarshallingEncoder用于实现序列化接口的POJO对象序列化为二进制数组
        return new MarshallingEncoder(provider);
    }
}

要进行序列化的类一定要实现 Seriaizable接口,并且添加serialVersionUID

// Boy.java

public class Boy implements Serializable
{

    private static final long serialVersionUID = 3333140133888151024L;
    /**
     * 姓名
     */
    private String name;

    /**
     *  身高
     */
    private int height;

    /**
     *  颜值
     */
    private int faceScore;

    /**
     *  是否富有
     */
    private boolean isRich;

    @Override
    public String toString()
    {
        return "[name="+name+", height="+height+", faceScore="+faceScore+", isRich="+isRich+"]";
    }

// GirlResponse.java

public class GirlResponse implements Serializable
{
    private static final long serialVersionUID = -2619994978640439932L;
    private int code;
    private String msg;

粘包拆包思考

测试了下粘包拆包的场景,发现程序运行依旧正常,说明Marshalling 编解码器本身是支持粘包拆包的处理。

究其原因,来看下源码:

// MarshallingEncoder 部分源码

@Sharable
public class MarshallingEncoder extends MessageToByteEncoder<Object> {

    private static final byte[] LENGTH_PLACEHOLDER = new byte[4];
    。。。。
    。。。。
    。。。。
    @Override
    protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
        Marshaller marshaller = provider.getMarshaller(ctx);
        int lengthPos = out.writerIndex();
        out.writeBytes(LENGTH_PLACEHOLDER);

可以看到MarshallingEncoder在编码时会在报文头部加上4个字节(LENGTH_PLACEHOLDER 用来记录报文长度)。

// MarshallingDecoder 部分源码

public class MarshallingDecoder extends LengthFieldBasedFrameDecoder {
。。。
。。。
。。。
public MarshallingDecoder(UnmarshallerProvider provider, int maxObjectSize) {
    // 默认头部长度为4字节
        super(maxObjectSize, 0, 4, 0, 4);
        this.provider = provider;
    }

从上面源码可以发现MarshallingDecoder 继承自LengthFieldBaseFrameDecoder, 这是netty 提供的一个用于解决粘包拆包问题的解码器,主要原理就是在报文头部记录报文长度。

关于粘包拆包问题可以查看》》》

遇到的坑

Marshalling 的jar 版本小于2
// 在版本大于2的情况下,以下获取到的MarshallerFactory 为null
final MarshallerFactory marshallerFactory = Marshalling
                .getProvidedMarshallerFactory("serial");
pojo类要实现Serializable接口

你可能感兴趣的:(netty)