關鍵字:Netty解編碼,JBoss Marshalling,
代碼在 https://github.com/zhaikaishun/NettyTutorial 在SocketIO_02 kaishun.netty.serial下
Netty解編碼技術
解編碼技術,說白了就是java序列化技術,序列化的目的就兩個,第一進行網絡傳輸,第二對象持久化
雖然我們可以使用java進行對象序列化,netty去傳輸,但是java序列化的硬傷太多,比如java序列化沒法跨語言、序列化夠碼流太大、序列化性能太低等等…
主流的編碼解碼框架:
JBoss 的 Marshalling包
google的Protobuf
給予Protobuf的Kyro
MessagePack框架
JBoss Marshalling
Jboss Marshalling是一個java對象序列化包,對jdk默認的序列化框架進行了優化,但又保持跟java.io.Serializable接口的兼容,同時增加了一些可調的參數和附加特性,
類庫 jboss-marshalling-1.3.0、jboss-marshalling-serial-1.3.0
Jboss Marshalling與Netty結合後進行序列化對象的代碼編寫非常簡單,我們一起來看一下
首先有個MarshallingCodeCFactory類
這個類不需要看懂,就要有一個這個類就好了
/**
* Marshalling工廠
* @author(alienware)
* @since 2014-12-16
*/
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和單個消息序列化後的最大長度
MarshallingDecoder decoder = new MarshallingDecoder(provider, 1024 * 1024 * 1);
return decoder;
}
/**
* 創建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對象序列化爲二進制數組
MarshallingEncoder encoder = new MarshallingEncoder(provider);
return encoder;
}
}
Server端其他基本不變,就這裏
.childHandler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel sc) throws Exception {
sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
sc.pipeline().addLast(new ServerHandler());
}
});
Client端也是這裏有變化
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws Exception {
sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
sc.pipeline().addLast(new ClientHandler());
}
});
for(int i = 0; i < 5; i++ ){
Req req = new Req();
req.setId("" + i);
req.setName("pro" + i);
req.setRequestMessage("數據信息" + i);
// String path = System.getProperty("user.dir") + File.separatorChar + "sources" + File.separatorChar + "001.jpg";
// File file = new File(path);
// FileInputStream in = new FileInputStream(file);
// byte[] data = new byte[in.available()];
// in.read(data);
// in.close();
// req.setAttachment(GzipUtils.gzip(data));
cf.channel().writeAndFlush(req);
}
Req類
public class Req implements Serializable{
private static final long SerialVersionUID = 1L;
private String id ;
private String name ;
private String requestMessage ;
private byte[] attachment;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRequestMessage() {
return requestMessage;
}
public void setRequestMessage(String requestMessage) {
this.requestMessage = requestMessage;
}
public byte[] getAttachment() {
return attachment;
}
public void setAttachment(byte[] attachment) {
this.attachment = attachment;
}
}
Resp類
public class Resp implements Serializable{
private static final long serialVersionUID = 1L;
private String id;
private String name;
private String responseMessage;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getResponseMessage() {
return responseMessage;
}
public void setResponseMessage(String responseMessage) {
this.responseMessage = responseMessage;
}
}
ClientHandler
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
Resp resp = (Resp)msg;
System.out.println("Client : " + resp.getId() + ", " + resp.getName() + ", " + resp.getResponseMessage());
} finally {
ReferenceCountUtil.release(msg);
}
}
ServerHandler
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Req req = (Req)msg;
System.out.println("Server : " + req.getId() + ", " + req.getName() + ", " + req.getRequestMessage());
// byte[] attachment = GzipUtils.ungzip(req.getAttachment());
//
// String path = System.getProperty("user.dir") + File.separatorChar + "receive" + File.separatorChar + "001.jpg";
// FileOutputStream fos = new FileOutputStream(path);
// fos.write(attachment);
// fos.close();
Resp resp = new Resp();
resp.setId(req.getId());
resp.setName("resp" + req.getId());
resp.setResponseMessage("響應內容" + req.getId());
ctx.writeAndFlush(resp);//.addListener(ChannelFutureListener.CLOSE);
}
總結上面的代碼:
其實只需要在Server和Client端加上這兩個,其他方法還是一樣的。
sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
補充: 如果還想進行大文件(例如圖片視頻等傳輸),將上面的註釋的代碼打開即可,這塊代碼還使用了Gzip進行壓縮,壓縮之後再進行傳輸,這是需要注意的。
特別感謝互聯網架構師白鶴翔老師,本文大多出自他的視頻講解。
筆者主要是記錄筆記,以便之後翻閱,正所謂好記性不如爛筆頭,爛筆頭不如雲筆記