MessagePack 序列化框架在netty中的簡單實現。

weixin_33872660發表於2017-03-16

MessagePack 序列化框架的簡單實現。

1.匯入相關jar包

 如果使用maven的話,直接新增依賴即可:
        <properties>
            <msgpack.version>0.6.12</msgpack.version>
        </properties>

        <!-- MessagePack dependency -->
        <dependency>
            <groupId>org.msgpack</groupId>
            <artifactId>msgpack</artifactId>
            <version>${msgpack.version}</version>
        </dependency>
 如果是普通java專案,去[maven中央倉庫](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22ch.dissem.msgpack%22)下載即可

2.編寫Encoder(編碼器)

 繼承MessageToByteEncoder
public class MsgpackEncoder extends MessageToByteEncoder {

    @Override
    protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
        // TODO Auto-generated method stub
        MessagePack msgpack = new MessagePack();
        out.writeBytes(msgpack.write(msg));
    }

}

3.編寫Decoder(編碼器)

 繼承MessageToMessageDecoder,設定型別為ByteBuf
public class MsgpackDecoder extends MessageToMessageDecoder<ByteBuf> {

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
        // TODO Auto-generated method stub
        final int length = msg.readableBytes();
        byte[] b = new byte[length];
        msg.getBytes(msg.readerIndex(), b,0,length);
        MessagePack msgpack = new MessagePack();
        out.add(msgpack.read(b));
    }

}

4.在Client端和Server端增加編碼器和解碼器,順便增加粘包/拆包支援:

 客戶端程式碼:
public class NettyClient {

    
    private void bind(int port,String host){
        EventLoopGroup group = new NioEventLoopGroup();
        Bootstrap b = new Bootstrap();
        b.group(group).channel(NioSocketChannel.class)
        .option(ChannelOption.TCP_NODELAY, true).handler(new ClientHandlerInit());
        
        try {
            ChannelFuture f = b.connect(host, port).sync();
            f.channel().closeFuture().sync();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally{
            group.shutdownGracefully();
        }
        
        
        
    }
    /*
    *這裡是增加相應的解碼編碼器。
    */
    private class ClientHandlerInit extends ChannelInitializer<SocketChannel>{

        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            // TODO Auto-generated method stub
            //這裡設定通過增加包頭表示報文長度來避免粘包
            ch.pipeline().addLast("frameDecoder",new LengthFieldBasedFrameDecoder(1024, 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 ClientHandler());
        }
        
    }

    public static void main(String[] args) throws UnknownHostException {
        // TODO Auto-generated method stub
        NettyClient client = new NettyClient();
        client.bind(12580,"localhost");
    }

}
Server端程式碼,和client端大同小異,不再贅述
public class TimeServer {
    public void bind(int port) throws Exception {
        EventLoopGroup bossGruop = new NioEventLoopGroup();
        EventLoopGroup workGroup = new NioEventLoopGroup();
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(bossGruop, workGroup).channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 1024).childHandler(new ChildChannelHandler());

        try {
            ChannelFuture future = bootstrap.bind(port).sync();
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            bossGruop.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }

    private class ChildChannelHandler extends ChannelInitializer<SocketChannel> {

        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            // TODO Auto-generated method stub
            ch.pipeline().addLast("frameDecoder",new LengthFieldBasedFrameDecoder(1024, 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 TimeServerHandler());
        }
    }

    public static void main(String[] args) {
        int port = 12580;
        try {
            new TimeServer().bind(port);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

5.接下來自定義一個model類來實作為訊息類:

/*
*這裡出現了兩個坑,一個是需要在訊息類上加上註解Message,另一個就是必須要有預設的無參構造器,不然就會報如下的錯誤:
*org.msgpack.template.builder.BuildContext build
*SEVERE: builder: 這個問題在github上有個issue解釋了
*/

@Message
public class UserInfo {
    private String username;
    private String age;
    public String getUsername() {
        return username;
    }
    public String getAge() {
        return age;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public void setAge(String age) {
        this.age = age;
    }
    public UserInfo(String username, String age) {
        super();
        this.username = username;
        this.age = age;
    }
    
    public UserInfo(){
        
    }
    
    
}

github關於錯誤的issue

6.對應的ServerHandler和ClientHandler程式碼:

serverhandler:

public class TimeServerHandler extends ChannelHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // TODO Auto-generated method stub
        try {
        //直接輸出msg
            System.out.println(msg.toString());
            String remsg = new String("has receive");
        //回覆has receive 給客戶端
            ctx.write(remsg);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
        }
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        // TODO Auto-generated method stub
        ctx.flush();
    }
}

clientHanlder:

public class ClientHandler extends ChannelHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // TODO Auto-generated method stub
        System.out.println(msg);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // TODO Auto-generated method stub
        //傳送50個UserInfo給伺服器,由於啟用了粘包/拆包支援,所以這裡連續傳送多個也不會出現粘包的現象。
        for (int i = 0; i < 50; i++) {
            UserInfo userInfo = new UserInfo();
            userInfo.setAge(i + "year");
            userInfo.setUsername("senninha");
            ctx.write(userInfo);
        }
        ctx.flush();
        System.out.println("-----------------send over-----------------");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        // TODO Auto-generated method stub
        System.out.println("error");
    }
}

客戶端控制檯:

-----------------send over-----------------
"has receive"
"has receive"
"has receive"
"has receive"
"has receive"
"has receive"
........

伺服器控制檯:

["senninha","0year"]
["senninha","1year"]
["senninha","2year"]
["senninha","3year"]
["senninha","4year"]
["senninha","5year"]
["senninha","6year"]

至此,一個MessagePack序列化框架的入門就搭建好了,參考了《Netty權威指南》。

相關文章