• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

无信不立

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

【Netty】netty的编解码技术

一、编解码技术

1、java序列化

1、java序列化的实现

java提供了对象输入(ObjectInputStream),输出流(ObjectOutputStream),可以将java对象作为可存储的字节数组写入文件,也可以传输到网络上。

2、java序列化的目的

  • 网络传输
  • 对象持久化

2、java序列化的缺点

1、java了序列化从JDK1.1版本就已经提供,它不需要提供额外的类库,只需要实现java.io.Serializable,并生成序列ID即可,因此,它在诞生之初就得到了广发的应用

2、远程服务调用RPC时,很少直接使用java序列化进行消息的编解码和传输。

  • 无法跨语言(java序列化属于java语言内部的私有协议,其他语言不支持,对于java序列化后的字节数组,别的语言无法进行反序列化)
  • 序列化后的码流太大
  • 序列化的性能太低

3、如何评判一个编解码框架的优劣,需要考虑的因素

  • 是否支持跨语言,支持的语言种类是否丰富
  • 编码后的码流大小
  • 编解码的性能
  • 类库是否小巧,api使用是否方便
  • 使用者需要手工开发的工作量和难度

 

二、业界主流的编解码框架

名称 特点 备注
Google的ProtoBuf

将数据结构以.proto文件进行描述,通过代码生成工具可以生成对应数据结构的POJO对象和Protobuf相关的方法和属性

1、结构化数据存储格式(XML,JSON)

2、高效的编解码性能

3、语言无关,平台无关,扩展性好

4、官方支持java,c++,python三种语言

xml可读性和可扩展性好,但为了可读性而牺牲的空间开销非常大

protobuf 利用二进制编码,在空间额性能上具有更大优势

facebook的thrift

1、通用的二进制编解码

2、压缩二进制编解码

3、优化的可选字段压缩编解码

 
JBoss Marshalling

相比传统的java序列化机制,它的优点如下

1、可插拔的类解析器,提供更加便捷的类加载定制策略,通过一个接口即可实现定制

2、可插拔的对象替换技术,不需要通过集成方式

3、可插拔的预定义类缓存表,可以减小序列化的字节数组长度,提升常用类型的对象序列化性能

4、无需实现java.io.Serializable接口,即可实现java序列化

5、通过缓存技术提升对象的序列化性能

jboss内部使用,应用范围有限
MessagePack

高效的二进制序列化框架

1、粘包/半包支持

2、编解码高效,性能高

3、序列化之后的码流小

4、支持跨语言(java,python,ruby,haskell,c#,OCaml,Lua,Go,C,C++)

 

三、MessagePack实现编解码技术

1、引入MessagePack的jar

<!--messagePack-->
        <dependency>
            <groupId>org.msgpack</groupId>
            <artifactId>msgpack</artifactId>
            <version>0.6.12</version>
        </dependency>
        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.22.0-GA</version>
        </dependency>
View Code

2、声明消息体(消息体上要加注解@Message)

package com.spring.test.service.netty.codec.messagepack.niov1;

import org.msgpack.annotation.Message;

/**
 * 
 */
@Message
public class UserInfo {

    private String name;
    private int age;

    public String getName() {
        return name;
    }

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

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

    public int getAge() {
        return age;
    }

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


}
View Code

3、开始编写服务端客户端

MessagePack编码类

package com.spring.test.service.netty.codec.messagepack.niov1;

import org.msgpack.MessagePack;

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

/**
 * MessagePack的编码器
 */
public class MsgPackEncoder extends MessageToByteEncoder<Object> {
    @Override
    protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
        MessagePack messagePack = new MessagePack();
        byte[] raw = messagePack.write(msg);
        out.writeBytes(raw);
    }
}
View Code

MessagePack解码类

package com.spring.test.service.netty.codec.messagepack.niov1;

import org.msgpack.MessagePack;

import java.util.List;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;

/**
 * MessagePack的解码器
 */
public class MsgPackDecoder extends MessageToMessageDecoder<ByteBuf> {
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
        final byte[] array;
        final int length = msg.readableBytes();
        array = new byte[length];
        msg.getBytes(msg.readerIndex(), array, 0, length);
        MessagePack msgPack = new MessagePack();
        out.add(msgPack.read(array));
    }
}
View Code

第一种:不考虑半包读写

1、服务端代码+handler

package com.spring.test.service.netty.codec.messagepack.niov1;

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;

/**
 *
 */
public class EchoServer {

    public void bind(int port) {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //编码器
                            ch.pipeline().addLast("msgpack decoder", new MsgPackDecoder());
                            //解码器
                            ch.pipeline().addLast("msgpack encoder", new MsgPackEncoder());
                            //用户自定义的消息处理器
                            ch.pipeline().addLast(new MySelfEchoServerHandler());
                        }
                    });
            //绑定服务端端口,同步等待成功
            ChannelFuture f = serverBootstrap.bind(port).sync();
            //等待服务端监听端口关闭
            f.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //优雅退出,释放线程池资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        int port = 8080;
        new EchoServer().bind(port);
    }
}




package com.spring.test.service.netty.codec.messagepack.niov1;

import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

/**
 * @author <a href="mailto:shangxiaofei@meituan.com">尚晓飞</a>
 * @date 4:38 PM 2019/8/25
 */
public class  MySelfEchoServerHandler extends ChannelHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("this Server receive the msgpack message:"+msg);
        ctx.write(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();
    }
}
View Code

2、客户端+handler

package com.spring.test.service.netty.codec.messagepack.niov1;


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;

/**
 */
public class EchoClient {
    private final String host;
    private final int port;
    private final int sendNumber;

    EchoClient(String host, int port, int sendNumber) {
        this.host = host;
        this.port = port;
        this.sendNumber = sendNumber;
    }

    public void connect() {
        //配置客户端NIO线程组
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group).channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //编码器
                            ch.pipeline().addLast("msgpack decoder", new MsgPackDecoder());
                            //解码器
                            ch.pipeline().addLast("msgpack encoder", new MsgPackEncoder());
                            ch.pipeline().addLast(new MySelfEchoClientHandler(sendNumber));
                        }
                    });
            //发起异步连接操作
            ChannelFuture f = bootstrap.connect(host, port).sync();
            //等待客户端链路关闭
            f.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //优雅的退出,释放NIO线程组
            group.shutdownGracefully();
        }

    }


    public static void main(String[] args) {
        new EchoClient("127.0.0.1", 8080, 1000).connect();

    }
}


package com.spring.test.service.netty.codec.messagepack.niov1;

import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

/**
 *
 */
public class MySelfEchoClientHandler extends ChannelHandlerAdapter {

    private final int sendNumber;

    MySelfEchoClientHandler(int sendNumber) {
        this.sendNumber = sendNumber;
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        UserInfo[] userInfos=userInfos();
        for(UserInfo userInfo:userInfos){
            ctx.write(userInfo);
        }
        ctx.flush();
        System.out.println("=======client request finished========");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("Client receive the msgpage message:"+msg);
        ctx.write(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 UserInfo[] userInfos(){
        UserInfo[] userInfos=new UserInfo[sendNumber];
        UserInfo userInfo=null;
        for(int i=0;i<sendNumber;i++){
            userInfo=new UserInfo();
            userInfo.setAge(i);
            userInfo.setName("ABCDE==>"+i);
            userInfos[i]=userInfo;
        }
        return userInfos;
    }
}
View Code

第二种:考虑半包读写

1、服务端+handler

package com.spring.test.service.netty.codec.messagepack.niov2;

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 bind(int port) {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast("frameDecoder",new LengthFieldBasedFrameDecoder(65535,0,2,0,2));
                            //编码器
                            ch.pipeline().addLast("msgpack decoder", new MsgPackDecoder());

                            ch.pipeline().addLast("frameEncoder",new LengthFieldPrepender(2));

                            //解码器
                            ch.pipeline().addLast("msgpack encoder", new MsgPackEncoder());
                            //用户自定义的消息处理器
                            ch.pipeline().addLast(new MySelfEchoServerHandler());
                        }
                    });
            //绑定服务端端口,同步等待成功
            ChannelFuture f = serverBootstrap.bind(port).sync();
            //等待服务端监听端口关闭
            f.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //优雅退出,释放线程池资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        int port = 8080;
        new EchoServer().bind(port);
    }
}



package com.spring.test.service.netty.codec.messagepack.niov2;

import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

/**
 *
 */
public class  MySelfEchoServerHandler extends ChannelHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("this Server receive the msgpack message:"+msg);
        ctx.write(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();
    }
}
View Code

2、客户端+handler

package com.spring.test.service.netty.codec.messagepack.niov2;


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 {
    private final String host;
    private final int port;
    private final int sendNumber;

    EchoClient(String host, int port, int sendNumber) {
        this.host = host;
        this.port = port;
        this.sendNumber = sendNumber;
    }

    public void connect() {
        //配置客户端NIO线程组
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group).channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast("frameDecoder",new LengthFieldBasedFrameDecoder(65535,0,2,0,2));
                            //编码器
                            ch.pipeline().addLast("msgpack decoder", new MsgPackDecoder());
                            ch.pipeline().addLast("frameEncoder",new LengthFieldPrepender(2));
                            //解码器
                            ch.pipeline().addLast("msgpack encoder", new MsgPackEncoder());
                            ch.pipeline().addLast(new MySelfEchoClientHandler(sendNumber));
                        }
                    });
            //发起异步连接操作
            ChannelFuture f = bootstrap.connect(host, port).sync();
            //等待客户端链路关闭
            f.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //优雅的退出,释放NIO线程组
            group.shutdownGracefully();
        }

    }


    public static void main(String[] args) {
        new EchoClient("127.0.0.1", 8080, 1000).connect();

    }
}


package com.spring.test.service.netty.codec.messagepack.niov2;

import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

/**
 *
 */
public class MySelfEchoClientHandler extends ChannelHandlerAdapter {

    private final int sendNumber;

    MySelfEchoClientHandler(int sendNumber) {
        this.sendNumber = sendNumber;
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        UserInfo[] userInfos=userInfos();
        for(UserInfo userInfo:userInfos){
            ctx.write(userInfo);
        }
        ctx.flush();
        System.out.println("=======client request finished========");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("Client receive the msgpage message:"+msg);
        ctx.write(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 UserInfo[] userInfos(){
        UserInfo[] userInfos=new UserInfo[sendNumber];
        UserInfo userInfo=null;
        for(int i=0;i<sendNumber;i++){
            userInfo=new UserInfo();
            userInfo.setAge(i);
            userInfo.setName("ABCDE==>"+i);
            userInfos[i]=userInfo;
        }
        return userInfos;
    }
}
View Code

 

 

 

posted on 2019-08-25 17:48  无信不立  阅读(272)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3