在SpringBoot中,如何使用Netty實(shí)現(xiàn)遠(yuǎn)程調(diào)用方法總結(jié)
Netty
Netty是一個NIO客戶端服務(wù)器框架:
- 它可快速輕松地開發(fā)網(wǎng)絡(luò)應(yīng)用程序,例如協(xié)議服務(wù)器和客戶端。
- 它極大地簡化和簡化了網(wǎng)絡(luò)編程,例如TCP和UDP套接字服務(wù)器。
NIO是一種非阻塞IO ,它具有以下的特點(diǎn)
- 單線程可以連接多個客戶端。
- 選擇器可以實(shí)現(xiàn)單線程管理多個Channel,新建的通道都要向選擇器注冊。
- 一個SelectionKey鍵表示了一個特定的通道對象和一個特定的選擇器對象之間的注冊關(guān)系。
- selector進(jìn)行select()操作可能會產(chǎn)生阻塞,但是可以設(shè)置阻塞時間,并且可以用wakeup()喚醒selector,所以NIO是非阻塞IO。
Netty模型selector模式
它相對普通NIO的在性能上有了提升,采用了:
- NIO采用多線程的方式可以同時使用多個selector
- 通過綁定多個端口的方式,使得一個selector可以同時注冊多個ServerSocketServer
- 單個線程下只能有一個selector,用來實(shí)現(xiàn)Channel的匹配及復(fù)用
半包問題
TCP/IP在發(fā)送消息的時候,可能會拆包,這就導(dǎo)致接收端無法知道什么時候收到的數(shù)據(jù)是一個完整的數(shù)據(jù)。在傳統(tǒng)的BIO中在讀取不到數(shù)據(jù)時會發(fā)生阻塞,但是NIO不會。為了解決NIO的半包問題,Netty在Selector模型的基礎(chǔ)上,提出了reactor模式,從而解決客戶端請求在服務(wù)端不完整的問題。
netty模型reactor模式
在selector的基礎(chǔ)上解決了半包問題。
上圖,簡單地可以描述為"boss接活,讓work干":manReactor用來接收請求(會與客戶端進(jìn)行握手驗(yàn)證),而subReactor用來處理請求(不與客戶端直接連接)。
SpringBoot使用Netty實(shí)現(xiàn)遠(yuǎn)程調(diào)用
maven依賴
<!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.2</version> <optional>true</optional> </dependency> <!--netty--> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.17.Final</version> </dependency>
服務(wù)端部分
NettyServer.java:服務(wù)啟動監(jiān)聽器
@Slf4j public class NettyServer { public void start() { InetSocketAddress socketAddress = new InetSocketAddress("127.0.0.1", 8082); //new 一個主線程組 EventLoopGroup bossGroup = new NioEventLoopGroup(1); //new 一個工作線程組 EventLoopGroup workGroup = new NioEventLoopGroup(200); ServerBootstrap bootstrap = new ServerBootstrap() .group(bossGroup, workGroup) .channel(NioServerSocketChannel.class) .childHandler(new ServerChannelInitializer()) .localAddress(socketAddress) //設(shè)置隊(duì)列大小 .option(ChannelOption.SO_BACKLOG, 1024) // 兩小時內(nèi)沒有數(shù)據(jù)的通信時,TCP會自動發(fā)送一個活動探測數(shù)據(jù)報(bào)文 .childOption(ChannelOption.SO_KEEPALIVE, true); //綁定端口,開始接收進(jìn)來的連接 try { ChannelFuture future = bootstrap.bind(socketAddress).sync(); log.info("服務(wù)器啟動開始監(jiān)聽端口: {}", socketAddress.getPort()); future.channel().closeFuture().sync(); } catch (InterruptedException e) { log.error("服務(wù)器開啟失敗", e); } finally { //關(guān)閉主線程組 bossGroup.shutdownGracefully(); //關(guān)閉工作線程組 workGroup.shutdownGracefully(); } } }
ServerChannelInitializer.java:netty服務(wù)初始化器
/** * netty服務(wù)初始化器 **/ public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { //添加編解碼 socketChannel.pipeline().addLast("decoder", new StringDecoder(CharsetUtil.UTF_8)); socketChannel.pipeline().addLast("encoder", new StringEncoder(CharsetUtil.UTF_8)); socketChannel.pipeline().addLast(new NettyServerHandler()); } }
NettyServerHandler.java:netty服務(wù)端處理器
/** * netty服務(wù)端處理器 **/ @Slf4j public class NettyServerHandler extends ChannelInboundHandlerAdapter { /** * 客戶端連接會觸發(fā) */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { log.info("Channel active......"); } /** * 客戶端發(fā)消息會觸發(fā) */ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { log.info("服務(wù)器收到消息: {}", msg.toString()); ctx.write("你也好哦"); ctx.flush(); } /** * 發(fā)生異常觸發(fā) */ @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }
RpcServerApp.java:SpringBoot啟動類
/** * 啟動類 * */ @Slf4j @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) public class RpcServerApp extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(RpcServerApp.class); } /** * 項(xiàng)目的啟動方法 * * @param args */ public static void main(String[] args) { SpringApplication.run(RpcServerApp.class, args); //開啟Netty服務(wù) NettyServer nettyServer =new NettyServer (); nettyServer.start(); log.info("======服務(wù)已經(jīng)啟動========"); } }
客戶端部分
NettyClientUtil.java:NettyClient工具類
/** * Netty客戶端 **/ @Slf4j public class NettyClientUtil { public static ResponseResult helloNetty(String msg) { NettyClientHandler nettyClientHandler = new NettyClientHandler(); EventLoopGroup group = new NioEventLoopGroup(); Bootstrap bootstrap = new Bootstrap() .group(group) //該參數(shù)的作用就是禁止使用Nagle算法,使用于小數(shù)據(jù)即時傳輸 .option(ChannelOption.TCP_NODELAY, true) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast("decoder", new StringDecoder()); socketChannel.pipeline().addLast("encoder", new StringEncoder()); socketChannel.pipeline().addLast(nettyClientHandler); } }); try { ChannelFuture future = bootstrap.connect("127.0.0.1", 8082).sync(); log.info("客戶端發(fā)送成功...."); //發(fā)送消息 future.channel().writeAndFlush(msg); // 等待連接被關(guān)閉 future.channel().closeFuture().sync(); return nettyClientHandler.getResponseResult(); } catch (Exception e) { log.error("客戶端Netty失敗", e); throw new BusinessException(CouponTypeEnum.OPERATE_ERROR); } finally { //以一種優(yōu)雅的方式進(jìn)行線程退出 group.shutdownGracefully(); } } }
NettyClientHandler.java:客戶端處理器
/** * 客戶端處理器 **/ @Slf4j @Setter @Getter public class NettyClientHandler extends ChannelInboundHandlerAdapter { private ResponseResult responseResult; @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { log.info("客戶端Active ....."); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { log.info("客戶端收到消息: {}", msg.toString()); this.responseResult = ResponseResult.success(msg.toString(), CouponTypeEnum.OPERATE_SUCCESS.getCouponTypeDesc()); ctx.close(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }
驗(yàn)證
測試接口
@RestController @Slf4j public class UserController { @PostMapping("/helloNetty") @MethodLogPrint public ResponseResult helloNetty(@RequestParam String msg) { return NettyClientUtil.helloNetty(msg); } }
訪問測試接口
服務(wù)端打印信息
客戶端打印信息
到此這篇關(guān)于在SpringBoot中,如何使用Netty實(shí)現(xiàn)遠(yuǎn)程調(diào)用方法總結(jié)的文章就介紹到這了,更多相關(guān)Netty實(shí)現(xiàn)遠(yuǎn)程調(diào)用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java Lombok簡介、使用、工作原理、優(yōu)缺點(diǎn)
這篇文章主要介紹了Java Lombok簡介、使用、工作原理、優(yōu)缺點(diǎn)的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)使用Java Lombok,感興趣的朋友可以了解下2021-03-03SpringBoot整合RabbitMQ的5種模式實(shí)戰(zhàn)
本文主要介紹了SpringBoot整合RabbitMQ的5種模式實(shí)戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-08-08java:無法訪問org.springframework.boot.SpringApplication的解決方法
這篇文章主要給大家介紹了關(guān)于java:無法訪問org.springframework.boot.SpringApplication的解決方法,文中通過實(shí)例代碼將解決的辦法介紹的非常詳細(xì),需要的朋友可以參考下2023-01-01java中string.trim()函數(shù)的作用實(shí)例及源碼
這篇文章主要介紹了java中string.trim()函數(shù)的作用實(shí)例及源碼,具有一定借鑒價值,需要的朋友可以參考下2018-01-01基于Java匯總Spock框架Mock靜態(tài)資源經(jīng)驗(yàn)
這篇文章主要介紹了基于Java匯總Spock框架Mock靜態(tài)資源經(jīng)驗(yàn),前面講了?Spock框架Mock對象、方法經(jīng)驗(yàn)總結(jié),今天分享一下Spock框架中Mock靜態(tài)資源的實(shí)踐經(jīng)驗(yàn)匯總。分成靜態(tài)資源和混合場景,需要的朋友可以參考一下2022-02-02java設(shè)計(jì)模式之實(shí)現(xiàn)對象池模式示例分享
對象池模式經(jīng)常用在頻繁創(chuàng)建、銷毀對象(并且對象創(chuàng)建、銷毀開銷很大)的場景,比如數(shù)據(jù)庫連接池、線程池、任務(wù)隊(duì)列池等。本代碼簡單,沒有限制對象池大小2014-02-02java 定義長度為0的數(shù)組/空數(shù)組案例
這篇文章主要介紹了java 定義長度為0的數(shù)組/空數(shù)組案例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-03-03