javascript
Spring Boot 整合 Netty(附源码)
前言
本篇文章主要介紹的是SpringBoot整合Netty以及使用Protobuf進(jìn)行數(shù)據(jù)傳輸?shù)南嚓P(guān)內(nèi)容。Protobuf會(huì)簡(jiǎn)單的介紹下用法,至于Netty在之前的文章中已經(jīng)簡(jiǎn)單的介紹過(guò)了,這里就不再過(guò)多細(xì)說(shuō)了。
Protobuf
介紹
protocolbuffer(以下簡(jiǎn)稱PB)是google 的一種數(shù)據(jù)交換的格式,它獨(dú)立于語(yǔ)言,獨(dú)立于平臺(tái)。google 提供了多種語(yǔ)言的實(shí)現(xiàn):java、c#、c++、go 和python,每一種實(shí)現(xiàn)都包含了相應(yīng)語(yǔ)言的編譯器以及庫(kù)文件。
由于它是一種二進(jìn)制的格式,比使用 xml進(jìn)行數(shù)據(jù)交換快許多。可以把它用于分布式應(yīng)用之間的數(shù)據(jù)通信或者異構(gòu)環(huán)境下的數(shù)據(jù)交換。作為一種效率和兼容性都很優(yōu)秀的二進(jìn)制數(shù)據(jù)傳輸格式,可以用于諸如網(wǎng)絡(luò)傳輸、配置文件、數(shù)據(jù)存儲(chǔ)等諸多領(lǐng)域。
官方地址:
https://github.com/google/protobuf
使用
這里的使用就只介紹Java相關(guān)的使用。首先我們需要建立一個(gè)proto文件,在該文件定義我們需要傳輸?shù)奈募?/p>
例如我們需要定義一個(gè)用戶的信息,包含的字段主要有編號(hào)、名稱、年齡。
那么該protobuf文件的格式如下:
注:這里使用的是proto3,相關(guān)的注釋我已寫了,這里便不再過(guò)多講述了。需要注意一點(diǎn)的是proto文件和生成的Java文件名稱不能一致!
syntax?=?"proto3"; //?生成的包名 option?java_package="com.pancm.protobuf"; //生成的java名 option?java_outer_classname?=?"UserInfo";message?UserMsg?{//?IDint32?id?=?1;//?姓名string?name?=?2;//?年齡int32?age?=?3;//?狀態(tài)int32?state?=?4; }創(chuàng)建好該文件之后,我們把該文件和protoc.exe(生成Java文件的軟件)放到E盤目錄下的protobuf文件夾下,然后再到該目錄的dos界面下輸入:protoc.exe --java_out=文件絕對(duì)路徑名稱。
例如:
protoc.exe?--java_out=E:\protobuf?User.proto輸入完之后,回車即可在同級(jí)目錄看到已經(jīng)生成好的Java文件,然后將該文件放到項(xiàng)目中該文件指定的路徑下即可。
注:生成protobuf的文件軟件和測(cè)試的protobuf文件我也整合到該項(xiàng)目中了,可以直接獲取的。
Java文件生成好之后,我們?cè)賮?lái)看怎么使用。
這里我就直接貼代碼了,并且將注釋寫在代碼中,應(yīng)該更容易理解些。
代碼示例:
//?按照定義的數(shù)據(jù)結(jié)構(gòu),創(chuàng)建一個(gè)對(duì)象UserInfo.UserMsg.Builder?userInfo?=?UserInfo.UserMsg.newBuilder();userInfo.setId(1);userInfo.setName("xuwujing");userInfo.setAge(18);UserInfo.UserMsg?userMsg?=?userInfo.build();//?將數(shù)據(jù)寫到輸出流ByteArrayOutputStream?output?=?new?ByteArrayOutputStream();userMsg.writeTo(output);//?將數(shù)據(jù)序列化后發(fā)送byte[]?byteArray?=?output.toByteArray();//?接收到流并讀取ByteArrayInputStream?input?=?new?ByteArrayInputStream(byteArray);//?反序列化UserInfo.UserMsg?userInfo2?=?UserInfo.UserMsg.parseFrom(input);System.out.println("id:"?+?userInfo2.getId());System.out.println("name:"?+?userInfo2.getName());System.out.println("age:"?+?userInfo2.getAge());注:這里說(shuō)明一點(diǎn),因?yàn)閜rotobuf是通過(guò)二進(jìn)制進(jìn)行傳輸,所以需要注意下相應(yīng)的編碼。還有使用protobuf也需要注意一下一次傳輸?shù)淖畲笞止?jié)長(zhǎng)度。
輸出結(jié)果:
id:1 name:xuwujing age:18 SpringBoot整合Netty說(shuō)明:如果想直接獲取工程那么可以直接跳到底部,通過(guò)鏈接下載工程代碼。
-
開發(fā)準(zhǔn)備
-
環(huán)境要求
-
JDK:1.8
-
Netty: 4.0或以上(不包括5)
-
Protobuf:3.0或以上
如果對(duì)Netty不熟的話,可以看看這些文章。大神請(qǐng)無(wú)視~。~
https://blog.csdn.net/column/details/17640.html
首先還是Maven的相關(guān)依賴:
<properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><java.version>1.8</java.version><netty.version>4.1.22.Final</netty.version><protobuf.version>3.5.1</protobuf.version><springboot>1.5.9.RELEASE</springboot><fastjson>1.2.41</fastjson><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><version>${springboot}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><version>${springboot}</version><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><version>${springboot}</version><optional>true</optional></dependency><dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>${netty.version}</version></dependency><dependency><groupId>com.google.protobuf</groupId><artifactId>protobuf-java</artifactId><version>${protobuf.version}</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>${fastjson}</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency> </dependencies>添加了相應(yīng)的maven依賴之后,配置文件這塊暫時(shí)沒(méi)有什么可以添加的,因?yàn)闀簳r(shí)就一個(gè)監(jiān)聽(tīng)的端口而已。
代碼編寫
代碼模塊主要分為服務(wù)端和客戶端。
主要實(shí)現(xiàn)的業(yè)務(wù)邏輯:
服務(wù)端啟動(dòng)成功之后,客戶端也啟動(dòng)成功,這時(shí)服務(wù)端會(huì)發(fā)送一條protobuf格式的信息給客戶端,然后客戶端給予相應(yīng)的應(yīng)答。客戶端與服務(wù)端連接成功之后,客戶端每個(gè)一段時(shí)間會(huì)發(fā)送心跳指令給服務(wù)端,告訴服務(wù)端該客戶端還存過(guò)中,如果客戶端沒(méi)有在指定的時(shí)間發(fā)送信息,服務(wù)端會(huì)關(guān)閉與該客戶端的連接。當(dāng)客戶端無(wú)法連接到服務(wù)端之后,會(huì)每隔一段時(shí)間去嘗試重連,只到重連成功!
服務(wù)端
首先是編寫服務(wù)端的啟動(dòng)類,相應(yīng)的注釋在代碼中寫得很詳細(xì)了,這里也不再過(guò)多講述了。不過(guò)需要注意的是,在之前的我寫的Netty文章中,是通過(guò)main方法直接啟動(dòng)服務(wù)端,因此是直接new一個(gè)對(duì)象的。而在和SpringBoot整合之后,我們需要將Netty交給springBoot去管理,所以這里就用了相應(yīng)的注解。
代碼如下:
@Service("nettyServer") public?class?NettyServer?{private?static?final?int?port?=?9876;?//?設(shè)置服務(wù)端端口private?static?EventLoopGroup?boss?=?new?NioEventLoopGroup();?//?通過(guò)nio方式來(lái)接收連接和處理連接private?static?EventLoopGroup?work?=?new?NioEventLoopGroup();?//?通過(guò)nio方式來(lái)接收連接和處理連接private?static?ServerBootstrap?b?=?new?ServerBootstrap();@Autowiredprivate?NettyServerFilter?nettyServerFilter;public?void?run()?{try?{b.group(boss,?work);b.channel(NioServerSocketChannel.class);b.childHandler(nettyServerFilter);?//?設(shè)置過(guò)濾器//?服務(wù)器綁定端口監(jiān)聽(tīng)ChannelFuture?f?=?b.bind(port).sync();System.out.println("服務(wù)端啟動(dòng)成功,端口是:"?+?port);//?監(jiān)聽(tīng)服務(wù)器關(guān)閉監(jiān)聽(tīng)f.channel().closeFuture().sync();}?catch?(InterruptedException?e)?{e.printStackTrace();}?finally?{//?關(guān)閉EventLoopGroup,釋放掉所有資源包括創(chuàng)建的線程work.shutdownGracefully();boss.shutdownGracefully();}} }服務(wù)端主類編寫完畢之后,我們?cè)賮?lái)設(shè)置下相應(yīng)的過(guò)濾條件。
這里需要繼承Netty中ChannelInitializer類,然后重寫initChannel該方法,進(jìn)行添加相應(yīng)的設(shè)置,如心跳超時(shí)設(shè)置,傳輸協(xié)議設(shè)置,以及相應(yīng)的業(yè)務(wù)實(shí)現(xiàn)類。
代碼如下:
????@Componentpublic?class?NettyServerFilter?extends?ChannelInitializer<SocketChannel>?{@Autowiredprivate?NettyServerHandler?nettyServerHandler;@Overrideprotected?void?initChannel(SocketChannel?ch)?throws?Exception?{ChannelPipeline?ph?=?ch.pipeline();//入?yún)⒄f(shuō)明:?讀超時(shí)時(shí)間、寫超時(shí)時(shí)間、所有類型的超時(shí)時(shí)間、時(shí)間格式ph.addLast(new?IdleStateHandler(5,?0,?0,?TimeUnit.SECONDS));//?解碼和編碼,應(yīng)和客戶端一致//傳輸?shù)膮f(xié)議?Protobufph.addLast(new?ProtobufVarint32FrameDecoder());ph.addLast(new?ProtobufDecoder(UserMsg.getDefaultInstance()));ph.addLast(new?ProtobufVarint32LengthFieldPrepender());ph.addLast(new?ProtobufEncoder());//業(yè)務(wù)邏輯實(shí)現(xiàn)類ph.addLast("nettyServerHandler",?nettyServerHandler);}}服務(wù)相關(guān)的設(shè)置的代碼寫完之后,我們?cè)賮?lái)編寫主要的業(yè)務(wù)代碼。
使用Netty編寫業(yè)務(wù)層的代碼,我們需要繼承ChannelInboundHandlerAdapter 或SimpleChannelInboundHandler類,在這里順便說(shuō)下它們兩的區(qū)別吧。
繼承SimpleChannelInboundHandler類之后,會(huì)在接收到數(shù)據(jù)后會(huì)自動(dòng)release掉數(shù)據(jù)占用的Bytebuffer資源。并且繼承該類需要指定數(shù)據(jù)格式。
而繼承ChannelInboundHandlerAdapter則不會(huì)自動(dòng)釋放,需要手動(dòng)調(diào)用ReferenceCountUtil.release()等方法進(jìn)行釋放。繼承該類不需要指定數(shù)據(jù)格式。所以在這里,個(gè)人推薦服務(wù)端繼承ChannelInboundHandlerAdapter,手動(dòng)進(jìn)行釋放,防止數(shù)據(jù)未處理完就自動(dòng)釋放了。而且服務(wù)端可能有多個(gè)客戶端進(jìn)行連接,并且每一個(gè)客戶端請(qǐng)求的數(shù)據(jù)格式都不一致,這時(shí)便可以進(jìn)行相應(yīng)的處理。
客戶端根據(jù)情況可以繼承SimpleChannelInboundHandler類。好處是直接指定好傳輸?shù)臄?shù)據(jù)格式,就不需要再進(jìn)行格式的轉(zhuǎn)換了。
代碼如下:
@Service("nettyServerHandler") public?class?NettyServerHandler?extends?ChannelInboundHandlerAdapter?{/**?空閑次數(shù)?*/private?int?idle_count?=?1;/**?發(fā)送次數(shù)?*/private?int?count?=?1;/***?建立連接時(shí),發(fā)送一條消息*/@Overridepublic?void?channelActive(ChannelHandlerContext?ctx)?throws?Exception?{System.out.println("連接的客戶端地址:"?+?ctx.channel().remoteAddress());UserInfo.UserMsg?userMsg?=?UserInfo.UserMsg.newBuilder().setId(1).setAge(18).setName("xuwujing").setState(0).build();ctx.writeAndFlush(userMsg);super.channelActive(ctx);}/***?超時(shí)處理?如果5秒沒(méi)有接受客戶端的心跳,就觸發(fā);?如果超過(guò)兩次,則直接關(guān)閉;*/@Overridepublic?void?userEventTriggered(ChannelHandlerContext?ctx,?Object?obj)?throws?Exception?{if?(obj?instanceof?IdleStateEvent)?{IdleStateEvent?event?=?(IdleStateEvent)?obj;if?(IdleState.READER_IDLE.equals(event.state()))?{?//?如果讀通道處于空閑狀態(tài),說(shuō)明沒(méi)有接收到心跳命令System.out.println("已經(jīng)5秒沒(méi)有接收到客戶端的信息了");if?(idle_count?>?1)?{System.out.println("關(guān)閉這個(gè)不活躍的channel");ctx.channel().close();}idle_count++;}}?else?{super.userEventTriggered(ctx,?obj);}}/***?業(yè)務(wù)邏輯處理*/@Overridepublic?void?channelRead(ChannelHandlerContext?ctx,?Object?msg)?throws?Exception?{System.out.println("第"?+?count?+?"次"?+?",服務(wù)端接受的消息:"?+?msg);try?{//?如果是protobuf類型的數(shù)據(jù)if?(msg?instanceof?UserMsg)?{UserInfo.UserMsg?userState?=?(UserInfo.UserMsg)?msg;if?(userState.getState()?==?1)?{System.out.println("客戶端業(yè)務(wù)處理成功!");}?else?if(userState.getState()?==?2){System.out.println("接受到客戶端發(fā)送的心跳!");}else{System.out.println("未知命令!");}}?else?{System.out.println("未知數(shù)據(jù)!"?+?msg);return;}}?catch?(Exception?e)?{e.printStackTrace();}?finally?{ReferenceCountUtil.release(msg);}count++;}/***?異常處理*/@Overridepublic?void?exceptionCaught(ChannelHandlerContext?ctx,?Throwable?cause)?throws?Exception?{cause.printStackTrace();ctx.close();} }還有個(gè)服務(wù)端的啟動(dòng)類,之前是通過(guò)main方法直接啟動(dòng), 不過(guò)這里改成了通過(guò)springBoot進(jìn)行啟動(dòng),差別不大。
代碼如下:
@SpringBootApplication public?class?NettyServerApp?{public?static?void?main(String[]?args)?{//?啟動(dòng)嵌入式的?Tomcat?并初始化?Spring?環(huán)境及其各?Spring?組件ApplicationContext?context?=?SpringApplication.run(NettyServerApp.class,?args);NettyServer?nettyServer?=?context.getBean(NettyServer.class);nettyServer.run();}}到這里服務(wù)端相應(yīng)的代碼就編寫完畢了。
客戶端
客戶端這邊的代碼和服務(wù)端的很多地方都類似,我就不再過(guò)多細(xì)說(shuō)了,主要將一些不同的代碼拿出來(lái)簡(jiǎn)單的講述下。
首先是客戶端的主類,基本和服務(wù)端的差不多,也就是多了監(jiān)聽(tīng)的端口和一個(gè)監(jiān)聽(tīng)器(用來(lái)監(jiān)聽(tīng)是否和服務(wù)端斷開連接,用于重連)。
主要實(shí)現(xiàn)的代碼邏輯如下:
????public?void?doConnect(Bootstrap?bootstrap,?EventLoopGroup?eventLoopGroup)?{ChannelFuture?f?=?null;try?{if?(bootstrap?!=?null)?{bootstrap.group(eventLoopGroup);bootstrap.channel(NioSocketChannel.class);bootstrap.option(ChannelOption.SO_KEEPALIVE,?true);bootstrap.handler(nettyClientFilter);bootstrap.remoteAddress(host,?port);f?=?bootstrap.connect().addListener((ChannelFuture?futureListener)?->?{final?EventLoop?eventLoop?=?futureListener.channel().eventLoop();if?(!futureListener.isSuccess())?{System.out.println("與服務(wù)端斷開連接!在10s之后準(zhǔn)備嘗試重連!");eventLoop.schedule(()?->?doConnect(new?Bootstrap(),?eventLoop),?10,?TimeUnit.SECONDS);}});if(initFalg){System.out.println("Netty客戶端啟動(dòng)成功!");initFalg=false;}//?阻塞f.channel().closeFuture().sync();}}?catch?(Exception?e)?{System.out.println("客戶端連接失敗!"+e.getMessage());}}注:監(jiān)聽(tīng)器這塊的實(shí)現(xiàn)用的是JDK1.8的寫法。
客戶端過(guò)濾其這塊基本和服務(wù)端一直。不過(guò)需要注意的是,傳輸協(xié)議、編碼和解碼應(yīng)該一致,還有心跳的讀寫時(shí)間應(yīng)該小于服務(wù)端所設(shè)置的時(shí)間。
改動(dòng)的代碼如下:
???ChannelPipeline?ph?=?ch.pipeline();/**?解碼和編碼,應(yīng)和服務(wù)端一致*?*///入?yún)⒄f(shuō)明:?讀超時(shí)時(shí)間、寫超時(shí)時(shí)間、所有類型的超時(shí)時(shí)間、時(shí)間格式ph.addLast(new?IdleStateHandler(0,?4,?0,?TimeUnit.SECONDS));客戶端的業(yè)務(wù)代碼邏輯。
主要實(shí)現(xiàn)的幾點(diǎn)邏輯是心跳按時(shí)發(fā)送以及解析服務(wù)發(fā)送的protobuf格式的數(shù)據(jù)。
這里比服務(wù)端多個(gè)個(gè)注解, 該注解Sharable主要是為了多個(gè)handler可以被多個(gè)channel安全地共享,也就是保證線程安全。
廢話就不多說(shuō)了,代碼如下:
????@Service("nettyClientHandler")@ChannelHandler.Sharablepublic?class?NettyClientHandler?extends?ChannelInboundHandlerAdapter?{@Autowiredprivate?NettyClient?nettyClient;/**?循環(huán)次數(shù)?*/private?int?fcount?=?1;/***?建立連接時(shí)*/@Overridepublic?void?channelActive(ChannelHandlerContext?ctx)?throws?Exception?{System.out.println("建立連接時(shí):"?+?new?Date());ctx.fireChannelActive();}/***?關(guān)閉連接時(shí)*/@Overridepublic?void?channelInactive(ChannelHandlerContext?ctx)?throws?Exception?{System.out.println("關(guān)閉連接時(shí):"?+?new?Date());final?EventLoop?eventLoop?=?ctx.channel().eventLoop();nettyClient.doConnect(new?Bootstrap(),?eventLoop);super.channelInactive(ctx);}/***?心跳請(qǐng)求處理?每4秒發(fā)送一次心跳請(qǐng)求;**/@Overridepublic?void?userEventTriggered(ChannelHandlerContext?ctx,?Object?obj)?throws?Exception?{System.out.println("循環(huán)請(qǐng)求的時(shí)間:"?+?new?Date()?+?",次數(shù)"?+?fcount);if?(obj?instanceof?IdleStateEvent)?{IdleStateEvent?event?=?(IdleStateEvent)?obj;if?(IdleState.WRITER_IDLE.equals(event.state()))?{?//?如果寫通道處于空閑狀態(tài),就發(fā)送心跳命令UserMsg.Builder?userState?=?UserMsg.newBuilder().setState(2);ctx.channel().writeAndFlush(userState);fcount++;}}}/***?業(yè)務(wù)邏輯處理*/@Overridepublic?void?channelRead(ChannelHandlerContext?ctx,?Object?msg)?throws?Exception?{//?如果不是protobuf類型的數(shù)據(jù)if?(!(msg?instanceof?UserMsg))?{System.out.println("未知數(shù)據(jù)!"?+?msg);return;}try?{//?得到protobuf的數(shù)據(jù)UserInfo.UserMsg?userMsg?=?(UserInfo.UserMsg)?msg;//?進(jìn)行相應(yīng)的業(yè)務(wù)處理。。。//?這里就從簡(jiǎn)了,只是打印而已System.out.println("客戶端接受到的用戶信息。編號(hào):"?+?userMsg.getId()?+?",姓名:"?+?userMsg.getName()?+?",年齡:"?+?userMsg.getAge());//?這里返回一個(gè)已經(jīng)接受到數(shù)據(jù)的狀態(tài)UserMsg.Builder?userState?=?UserMsg.newBuilder().setState(1);ctx.writeAndFlush(userState);System.out.println("成功發(fā)送給服務(wù)端!");}?catch?(Exception?e)?{e.printStackTrace();}?finally?{ReferenceCountUtil.release(msg);}}}那么到這里客戶端的代碼也編寫完畢了。
功能測(cè)試
首先啟動(dòng)服務(wù)端,然后再啟動(dòng)客戶端。
我們來(lái)看看結(jié)果是否如上述所說(shuō)。
服務(wù)端輸出結(jié)果:
服務(wù)端啟動(dòng)成功,端口是:9876 連接的客戶端地址:/127.0.0.1:53319 第1次,服務(wù)端接受的消息:state:?1客戶端業(yè)務(wù)處理成功! 第2次,服務(wù)端接受的消息:state:?2接受到客戶端發(fā)送的心跳! 第3次,服務(wù)端接受的消息:state:?2接受到客戶端發(fā)送的心跳! 第4次,服務(wù)端接受的消息:state:?2接受到客戶端發(fā)送的心跳!客戶端輸入結(jié)果:
Netty客戶端啟動(dòng)成功! 建立連接時(shí):Mon Jul 16?23:31:58?CST?2018 客戶端接受到的用戶信息。編號(hào):1,姓名:xuwujing,年齡:18 成功發(fā)送給服務(wù)端! 循環(huán)請(qǐng)求的時(shí)間:Mon Jul 16?23:32:02?CST?2018,次數(shù)1 循環(huán)請(qǐng)求的時(shí)間:Mon Jul 16?23:32:06?CST?2018,次數(shù)2 循環(huán)請(qǐng)求的時(shí)間:Mon Jul 16?23:32:10?CST?2018,次數(shù)3 循環(huán)請(qǐng)求的時(shí)間:Mon Jul 16?23:32:14?CST?2018,次數(shù)4通過(guò)打印信息可以看出如上述所說(shuō)。
接下來(lái)我們?cè)賮?lái)看看客戶端是否能夠?qū)崿F(xiàn)重連。
先啟動(dòng)客戶端,再啟動(dòng)服務(wù)端。
客戶端輸入結(jié)果:
Netty客戶端啟動(dòng)成功! 與服務(wù)端斷開連接!在10s之后準(zhǔn)備嘗試重連! 客戶端連接失敗!AbstractChannel$CloseFuture@1fbaa3ac(incomplete) 建立連接時(shí):Mon Jul 16?23:41:33?CST?2018 客戶端接受到的用戶信息。編號(hào):1,姓名:xuwujing,年齡:18 成功發(fā)送給服務(wù)端! 循環(huán)請(qǐng)求的時(shí)間:Mon Jul 16?23:41:38?CST?2018,次數(shù)1 循環(huán)請(qǐng)求的時(shí)間:Mon Jul 16?23:41:42?CST?2018,次數(shù)2 循環(huán)請(qǐng)求的時(shí)間:Mon Jul 16?23:41:46?CST?2018,次數(shù)3服務(wù)端輸出結(jié)果:
服務(wù)端啟動(dòng)成功,端口是:9876 連接的客戶端地址:/127.0.0.1:53492 第1次,服務(wù)端接受的消息:state:?1客戶端業(yè)務(wù)處理成功! 第2次,服務(wù)端接受的消息:state:?2接受到客戶端發(fā)送的心跳! 第3次,服務(wù)端接受的消息:state:?2接受到客戶端發(fā)送的心跳! 第4次,服務(wù)端接受的消息:state:?2結(jié)果也如上述所說(shuō)!
其它
關(guān)于SpringBoot整合Netty使用Protobuf進(jìn)行數(shù)據(jù)傳輸?shù)竭@里就結(jié)束了。
SpringBoot整合Netty使用Protobuf進(jìn)行數(shù)據(jù)傳輸?shù)捻?xiàng)目工程地址:
https://github.com/xuwujing/springBoot-study/tree/master/springboot-netty-protobuf
對(duì)了,也有不使用springBoot整合的Netty項(xiàng)目工程地址:
https://github.com/xuwujing/Netty-study/tree/master/Netty-protobuf
總結(jié)
以上是生活随笔為你收集整理的Spring Boot 整合 Netty(附源码)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 一本彻底搞懂MySQL索引优化EXPLA
- 下一篇: Spring Boot MongoDB