使用Netty進行編解碼的操作過程詳解
前言
何為編解碼,通俗的來說,我們需要將一串文本信息從A發(fā)送到B并且將這段文本進行加工處理,如:A將信息文本信息編碼為2進制信息進行傳輸。B接受到的消息是一串2進制信息,需要將其解碼為文本信息才能正常進行處理。
上章我們介紹的Netty如何解決拆包和粘包問題,就是運用了解碼的這一功能。
java默認(rèn)的序列化機制
使用Netty大多是java程序猿,我們基于一切都是對象的原則,經(jīng)常會將對象進行網(wǎng)絡(luò)傳輸,那么對于序列化操作肯定大家都是非常熟悉的。
一個對象是不能直接進行網(wǎng)絡(luò)I/O傳輸?shù)?,jdk默認(rèn)是將對象轉(zhuǎn)換為可存儲的字節(jié)數(shù)組來進行網(wǎng)絡(luò)操作。基于JDK默認(rèn)的序列化機制可以避免操作底層的字節(jié)數(shù)組,從而提升開發(fā)效率。
jdk默認(rèn)的序列化機制雖然能給程序猿帶來極大的方便,但是它也帶來了許多問題:
- 無法跨語言。
- 序列化后的碼流太大,會給網(wǎng)絡(luò)傳輸帶來極大的開銷。
- 序列化的性能太低,對于高性能的網(wǎng)絡(luò)架構(gòu)是極其不友好的。
主流的編解碼框架
- Google的Protobuf。
- Facebok的Thrift。
- Jboss Marshalling
- MessagePack
這幾類編解碼框架都有各自的特點,有興趣的童鞋可以自己對其進行研究。
我們這里主要對MessagePack進行講解。
MessagePack簡介
MessagePack是一個高效的二進制序列化框架,它像JSON一樣支持不同的語言間的數(shù)據(jù)交換,并且它的性能更快,序列化之后的碼流也更小。
它的特點如下:
- 編解碼高效,性能高
- 序列化之后的碼流小,利于網(wǎng)絡(luò)傳輸或存儲
- 支持跨語言
MessagePack Java Api的使用
首先導(dǎo)包
<!-- https://mvnrepository.com/artifact/org.msgpack/msgpack --> <dependency> <groupId>org.msgpack</groupId> <artifactId>msgpack</artifactId> <version>0.6.12</version> </dependency>
使用API進行編碼和解碼
List<String> nameList = new ArrayList<String>(); nameList.add("Tom"); nameList.add("Jack"); MessagePack messagePack = new MessagePack(); //開始序列化 byte[] raw = messagePack.write(nameList); //使用MessagePack的模版,來接序列化后的字節(jié)數(shù)組轉(zhuǎn)換為List List<String> deNameList = messagePack.read(raw,Templates.tList(Templates.TString)); System.out.println(deNameList.get(0)); System.out.println(deNameList.get(1)); System.out.println(deNameList.get(2));
Netty中如何使用MessagePack
編碼器的實現(xiàn)
public class MsgpackEncoder extends MessageToByteEncoder { @Override protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception { MessagePack msgpack = new MessagePack(); //使用MessagePack對要發(fā)送的數(shù)據(jù)進行序列化 byte[] raw = msgpack.write(msg); out.writeBytes(raw); } }
解碼器的實現(xiàn)
public class MsgpackDecoder extends MessageToMessageDecoder<ByteBuf> { @Override protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception { //從msg中獲取需要解碼的byte數(shù)組 final int length = msg.readableBytes(); byte[] b = new byte[length]; msg.getBytes(msg.readerIndex(), b,0,length); //使用MessagePack的read方法將其反序列化成Object對象,并加入到解碼列表out中 MessagePack msgpack = new MessagePack(); out.add(msgpack.read(b)); } }
實現(xiàn)該編碼器和解碼器的Netty服務(wù)端
public class NettyServer { 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 ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { // TODO Auto-generated method stub socketChannel.pipeline() //添加支持粘包、拆包解碼器,意義:從頭兩個字節(jié)解析出數(shù)據(jù)的長度,并且長度不超過1024個字節(jié) .addLast("frameDecoder",new LengthFieldBasedFrameDecoder(1024, 0, 2,0,2)) //反序列化解碼器 .addLast("msgpack decoder",new MsgpackDecoder()) //添加支持粘包、拆包編碼器,發(fā)送的每個數(shù)據(jù)都在頭部增加兩個字節(jié)表消息長度 .addLast("frameEncoder",new LengthFieldPrepender(2)) //序列化編碼器 .addLast("msgpack encoder",new MsgpackEncoder() //后續(xù)自己的業(yè)務(wù)邏輯 .addLast(new ServerHandler()); } }); try { ChannelFuture future = bootstrap.bind(port).sync(); future.channel().closeFuture().sync(); } catch (Exception e) { e.printStackTrace(); } finally { bossGruop.shutdownGracefully(); workGroup.shutdownGracefully(); } } }
實現(xiàn)該編碼器和解碼器的Netty客戶端
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 ChannelInitializer<SocketChannel>(){ @Override protected void initChannel(SocketChannel socketChannel) throws Exception { // TODO Auto-generated method stub socketChannel.pipeline() .addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 0, 2, 0, 2)) .addLast("msgpack decoder", new MsgpackDecoder()) .addLast("frameEncoder", new LengthFieldPrepender(2)) .addLast("msgpack encoder", new MsgpackEncoder()) .addLast(new ClientHandler()); } }); try { ChannelFuture f = b.connect(host, port).sync(); f.channel().closeFuture().sync(); } catch (Exception e) { e.printStackTrace(); } finally { group.shutdownGracefully(); } } }
可以看出客戶端的代碼與服務(wù)端基本相同,所以啊,如果能熟練掌握Netty,今后在自己的項目中運用上定制化編解碼的傳輸,將會是一件十分簡單的活路。
總結(jié)
無論是之前解決粘包拆包問題,還是這里的使用序列化框架來進行編解碼。我相信讀者學(xué)習(xí)到這里,對于Netty的使用都有了較為全面的了解。其實Netty幫我們解決了很多底層棘手問題,如客戶端斷連、句柄泄漏和消息丟失等等。所以我們才能十分簡單開發(fā)出一個穩(wěn)定的網(wǎng)絡(luò)通訊項目。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java中String的JdbcTemplate連接SQLServer數(shù)據(jù)庫的方法
這篇文章主要介紹了Java中String的JdbcTemplate連接SQLServer數(shù)據(jù)庫的方法,在研發(fā)過程中我們需要與其他系統(tǒng)對接的場景,連接SQLServer拉取數(shù)據(jù),所以就用jdbc連接數(shù)據(jù)庫的方式連接外部數(shù)據(jù)源,需要的朋友可以參考下2021-10-10Java實現(xiàn)學(xué)生成績管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java實現(xiàn)學(xué)生成績管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-04-04xxl-job定時任務(wù)配置應(yīng)用及添加到springboot項目中實現(xiàn)動態(tài)API調(diào)用
XXL-JOB是一個分布式任務(wù)調(diào)度平臺,其核心設(shè)計目標(biāo)是開發(fā)迅速、學(xué)習(xí)簡單、輕量級、易擴展,本篇文章主要是對xuxueli的xxl-job做一個簡單的配置,以及將其添加到自己已有的項目中進行api調(diào)用,感興趣的朋友跟隨小編一起看看吧2024-04-04Spring?Boot中的過濾器攔截器監(jiān)聽器使用技巧匯總
本文將介紹在Spring?Boot應(yīng)用程序中使用過濾器、攔截器和監(jiān)聽器的使用技巧,我們將討論它們之間的區(qū)別,以及何時使用它們,我們還將提供代碼示例,以幫助您在自己的應(yīng)用程序中使用它們2023-12-12SpringBoot整合Retry實現(xiàn)錯誤重試過程逐步介紹
重試的使用場景比較多,比如調(diào)用遠(yuǎn)程服務(wù)時,由于網(wǎng)絡(luò)或者服務(wù)端響應(yīng)慢導(dǎo)致調(diào)用超時,此時可以多重試幾次。用定時任務(wù)也可以實現(xiàn)重試的效果,但比較麻煩,用Spring Retry的話一個注解搞定所有,感興趣的可以了解一下2023-02-02Springboot Thymeleaf實現(xiàn)HTML屬性設(shè)置
這篇文章主要介紹了Springboot Thymeleaf實現(xiàn)HTML屬性設(shè)置,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2007-11-11