1. 程式人生 > >十、編解碼技術--Java序列化

十、編解碼技術--Java序列化

Java自身的序列化機制,就是隻需要序列化的POJO物件實現Java.io.Serializable介面,根據實際情況生成序列ID,這個類就能夠通過java.io.ObjectInput和java.io.ObjectOutput序列化和反序列化。

服務端:Netty服務端接收到客戶端的使用者訂購請求訊息

訂購訊息

package NettySerialization;


import java.io.Serializable;

/**
 * Created by L_kanglin on 2017/6/23.
 * 訂購請求POJO類定義
 */

public class SubscribeReq
implements Serializable{
private static final long serialVersionUID = 1L; //訂購編號 private int subReqID; //使用者名稱 private String userName; //訂購的產品名稱 private String productName; //訂購者電話號碼 private String phoneNumber; //訂購者的家庭住址 private String address; public int getSubReqID
() { return subReqID; } public void setSubReqID(int subReqID) { this.subReqID = subReqID; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getProductName
() { return productName; } public void setProductName(String productName) { this.productName = productName; } public String getPhoneNumber() { return phoneNumber; } public void setPhoneNumber(String phoneNumber) { this.phoneNumber = phoneNumber; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "SubscribeReq{" + "subReqID=" + subReqID + ", userName='" + userName + '\'' + ", productName='" + productName + '\'' + ", phoneNumber='" + phoneNumber + '\'' + ", address='" + address + '\'' + '}'; } }

服務端接收到請求訊息,對使用者名稱進行合法性校驗。如果合法,則構造訂購成功的應答訊息返回給客戶端。訂購訊息如下:

package NettySerialization;


import java.io.Serializable;

/**
 * Created by L_kanglin on 2017/6/23.
 */
public class SubscribeResp implements Serializable{
    private static final long serialVersionUID = 1L;
    //訂購編號
    private int subReqID;
    //訂購結果:0 表示成功
    private int respCode;
    //可選的詳細描述資訊
    private String desc;

    public int getSubReqID() {
        return subReqID;
    }

    public void setSubReqID(int subReqID) {
        this.subReqID = subReqID;
    }

    public int getRespCode() {
        return respCode;
    }

    public void setRespCode(int respCode) {
        this.respCode = respCode;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    @Override
    public String toString() {
        return "SubscribeResp{" +
                "subReqID=" + subReqID +
                ", respCode=" + respCode +
                ", desc='" + desc + '\'' +
                '}';
    }
}

訂購服務主函式定義:

package NettySerialization;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

/**
 * Created by L_kanglin on 2017/6/13.
 */
public class SubReqServer {
    public void bind(int port) throws InterruptedException {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 100)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new ObjectDecoder(1024 * 1024, ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())));
                            ch.pipeline().addLast(new ObjectEncoder());
                            ch.pipeline().addLast(new SubReqServerHandler());
                        }
                    });
            //繫結埠,同步等待成功
            ChannelFuture f = b.bind(port).sync();
            //等待服務端監聽埠關閉
            f.channel().closeFuture().sync();
         } finally {
             //優雅退出,釋放執行緒池資源
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        int port=8080;
        if(args !=null&& args.length>0){
            port=Integer.valueOf(args[0]);
        }
        new SubReqServer().bind(port);
    }
}

ObjectDecoder,負責對實現Serializable的POJO物件進行解碼;
使用weakCachingConcurrentResolver建立執行緒安全的WeakReferenceMap對類載入器進行快取,它支援多執行緒併發訪問,當虛擬機器記憶體不足時,會釋放快取中的記憶體,防止記憶體洩漏。為了防止異常碼流和解碼錯位導致的記憶體溢位,這裡將單個物件最大序列化後的位元組陣列長度設定為1M。
ObjectEncoder可以在訊息傳送時自動將實現Serializable的POJO物件進行編碼,因此使用者無須親自對物件進行手工序列化,只需要關注自己的業務邏輯處理即可。物件序列化和反序列化都由Netty的物件編碼器解決。
將訂購處理handler SubReqServerHandler新增到ChannelPipeline的尾部用於業務邏輯處理。

package NettySerialization;

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

/**
 * Created by L_kanglin on 2017/6/23.
 */
public class SubReqServerHandler extends ChannelHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        SubscribeReq req= (SubscribeReq) msg;
        if("LiKanglin".equalsIgnoreCase(req.getUserName())){
            System.out.println("Service accept client subscribe req : [" + req.toString() + "]");
            ctx.writeAndFlush(resp(req.getSubReqID()));
        }
    }
    private SubscribeResp resp(int subReqID){
        SubscribeResp resp=new SubscribeResp();
        resp.setSubReqID(subReqID);
        resp.setRespCode(0);
        resp.setDesc("Netty is  lovaly");
        return resp;
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close(); //關閉異常,關閉鏈路
    }



}

客戶端設計思路:
(1)建立客戶端時,將Netty物件解碼器和編碼器新增到ChannelPipeline;
(2)鏈路被啟用的時候構造訂購請求訊息傳送,為了檢驗Netty的Java序列化功能是否支援TCP粘包和拆包,客戶端一次構造10條訂購請求,最後一次性發給服務端
(3)客戶端訂購處理handler將接收到的訂購響應訊息打印出來。

具體程式碼實現如下:

package NettySerializationClient;

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.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;

/**
 * Created by L_kanglin on 2017/6/23.
 */
public class SubReqClient {
    public void connnect(int port,String host) throws InterruptedException {
        //配置客戶端NIO執行緒組
        EventLoopGroup group =new NioEventLoopGroup();
        try{
            Bootstrap b = new Bootstrap();
            b.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(new ObjectDecoder(1024, ClassResolvers.cacheDisabled(this.getClass().getClassLoader())));
                            ch.pipeline().addLast(new ObjectEncoder());
                            ch.pipeline().addLast(new SubReqClientHandler());
                        }
                    });
            //發起非同步連線操作
            ChannelFuture f =b.connect(host,port).sync();
            //等待客戶端鏈路關閉
            f.channel().closeFuture().sync();
        }finally {
            //優雅退出,釋放NIO執行緒組
            group.shutdownGracefully();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        int port=8080;
        if(args!=null && args.length>0){
            port=Integer.valueOf(args[0]);
        }
        new SubReqClient().connnect(port,"127.0.0.1");
    }

}
package NettySerializationClient;

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

/**
 * Created by L_kanglin on 2017/6/23.
 */
public class SubReqClientHandler extends ChannelHandlerAdapter {
    public SubReqClientHandler() {
    }
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        for(int i=0;i<10;i++){
            ctx.write(subReq(i));
        }
        ctx.flush();
    }
    private SubscribeReq subReq(int i){
        SubscribeReq req = new SubscribeReq();
        req.setAddress("武漢市華中科技大學");
        req.setPhoneNumber("155XXXXXXXXXXXXXX");
        req.setProductName("Netty In Action");
        req.setSubReqID(i);
        req.setUserName("LiKanglin");
        return req;
    }
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("Receive server response : [" + 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();
    }

}

ClassResolvers.cacheDisabled(this.getClass().getClassLoader()))禁止對類載入器進行快取。

服務端執行如下:

Service accept client subscribe req : [SubscribeReq{subReqID=0, userName='LiKanglin', productName='Netty In Action', phoneNumber='155XXXXXXXXXXXXXX', address='武漢市華中科技大學'}]
Service accept client subscribe req : [SubscribeReq{subReqID=1, userName='LiKanglin', productName='Netty In Action', phoneNumber='155XXXXXXXXXXXXXX', address='武漢市華中科技大學'}]
Service accept client subscribe req : [SubscribeReq{subReqID=2, userName='LiKanglin', productName='Netty In Action', phoneNumber='155XXXXXXXXXXXXXX', address='武漢市華中科技大學'}]
Service accept client subscribe req : [SubscribeReq{subReqID=3, userName='LiKanglin', productName='Netty In Action', phoneNumber='155XXXXXXXXXXXXXX', address='武漢市華中科技大學'}]
Service accept client subscribe req : [SubscribeReq{subReqID=4, userName='LiKanglin', productName='Netty In Action', phoneNumber='155XXXXXXXXXXXXXX', address='武漢市華中科技大學'}]
Service accept client subscribe req : [SubscribeReq{subReqID=5, userName='LiKanglin', productName='Netty In Action', phoneNumber='155XXXXXXXXXXXXXX', address='武漢市華中科技大學'}]
Service accept client subscribe req : [SubscribeReq{subReqID=6, userName='LiKanglin', productName='Netty In Action', phoneNumber='155XXXXXXXXXXXXXX', address='武漢市華中科技大學'}]
Service accept client subscribe req : [SubscribeReq{subReqID=7, userName='LiKanglin', productName='Netty In Action', phoneNumber='155XXXXXXXXXXXXXX', address='武漢市華中科技大學'}]
Service accept client subscribe req : [SubscribeReq{subReqID=8, userName='LiKanglin', productName='Netty In Action', phoneNumber='155XXXXXXXXXXXXXX', address='武漢市華中科技大學'}]
Service accept client subscribe req : [SubscribeReq{subReqID=9, userName='LiKanglin', productName='Netty In Action', phoneNumber='155XXXXXXXXXXXXXX', address='武漢市華中科技大學'}]

客戶端執行如下:

Receive server response : [SubscribeResp{subReqID=0, respCode=0, desc='Netty is  lovaly'}]
Receive server response : [SubscribeResp{subReqID=1, respCode=0, desc='Netty is  lovaly'}]
Receive server response : [SubscribeResp{subReqID=2, respCode=0, desc='Netty is  lovaly'}]
Receive server response : [SubscribeResp{subReqID=3, respCode=0, desc='Netty is  lovaly'}]
Receive server response : [SubscribeResp{subReqID=4, respCode=0, desc='Netty is  lovaly'}]
Receive server response : [SubscribeResp{subReqID=5, respCode=0, desc='Netty is  lovaly'}]
Receive server response : [SubscribeResp{subReqID=6, respCode=0, desc='Netty is  lovaly'}]
Receive server response : [SubscribeResp{subReqID=7, respCode=0, desc='Netty is  lovaly'}]
Receive server response : [SubscribeResp{subReqID=8, respCode=0, desc='Netty is  lovaly'}]
Receive server response : [SubscribeResp{subReqID=9, respCode=0, desc='Netty is  lovaly'}]