SpringBoot使用netty


Netty是由JBOSS提供的一個java開源框架。Netty提供異步的、事件驅動的網絡應用程序框架和工具,用以快速開發高性能、高可靠性的網絡服務器和客戶端程序。也就是說,Netty 是一個基於NIO的客戶、服務器端編程框架,使用Netty 可以確保你快速和簡單的開發出一個網絡應用,例如實現了某種協議的客戶、服務端應用。Netty相當於簡化和流線化了網絡應用的編程開發過程,例如:基於TCP和UDP的socket服務開發。“快速”和“簡單”並不用產生維護性或性能上的問題。Netty 是一個吸收了多種協議(包括FTP、SMTP、HTTP等各種二進制文本協議)的實現經驗,並經過相當精心設計的項目。最終,Netty 成功的找到了一種方式,在保證易於開發的同時還保證了其應用的性能,穩定性和伸縮性

本文講解SpringBoot如何使用Netty服務端和客戶端的簡單案例以及相關參數解釋
一、Netty服務端
1、導入依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.36.Final</version>
</dependency>
2、編寫Netty服務端處理器
/**
* @author Gjing
*
* netty服務端處理器
**/
@Slf4j
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
/**
* 客戶端連接會觸發
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
log.info("Channel active......");
}

/**
* 客戶端發消息會觸發
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
log.info("服務器收到消息: {}", msg.toString());
ctx.write("你也好哦");
ctx.flush();
}

/**
* 發生異常觸發
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
3、編寫Netty服務端初始化器
/**
* @author Gjing
*
* netty服務初始化器
**/
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());
}
}
4、編寫Netty服務啟動
/**
* @author Gjing
* <p>
* 服務啟動監聽器
**/
@Slf4j
public class NettyServer {

public void start(InetSocketAddress socketAddress) {
//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)
//設置隊列大小
.option(ChannelOption.SO_BACKLOG, 1024)
// 兩小時內沒有數據的通信時,TCP會自動發送一個活動探測數據報文
.childOption(ChannelOption.SO_KEEPALIVE, true);
//綁定端口,開始接收進來的連接
try {
ChannelFuture future = bootstrap.bind(socketAddress).sync();
log.info("服務器啟動開始監聽端口: {}", socketAddress.getPort());
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//關閉主線程組
bossGroup.shutdownGracefully();
//關閉工作線程組
workGroup.shutdownGracefully();
}
}
}
5、啟動類
@SpringBootApplication
public class ServerApplication {

public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
//啟動服務端
NettyServer nettyServer = new NettyServer();
nettyServer.start(new InetSocketAddress("127.0.0.1", 8090));
}

}
6、啟動結果
![](https://img2020.cnblogs.com/blog/2210127/202101/2210127-20210107163715062-965402079.png)

1.png
二、Netty客戶端
1、添加依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.36.Final</version>
</dependency>
2、編寫客戶端處理器
/**
* @author Gjing
*
* 客戶端處理器
**/
@Slf4j
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
@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());
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
3、編寫客戶端初始化器
/**
* @author Gjing
* 客戶端初始化器
**/
public class NettyClientInitializer extends 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(new NettyClientHandler());
}
}
4、編寫客戶端
/**
* @author Gjing
**/
@Slf4j
public class NettyClient {

public void start() {
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap()
.group(group)
//該參數的作用就是禁止使用Nagle算法,使用於小數據即時傳輸
.option(ChannelOption.TCP_NODELAY, true)
.channel(NioSocketChannel.class)
.handler(new NettyClientInitializer());

try {
ChannelFuture future = bootstrap.connect("127.0.0.1", 8090).sync();
log.info("客戶端成功....");
//發送消息
future.channel().writeAndFlush("你好啊");
// 等待連接被關閉
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
group.shutdownGracefully();
}
}
}
5、啟動類
@SpringBootApplication
public class ClientApplication {

public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
//啟動netty客戶端
NettyClient nettyClient = new NettyClient();
nettyClient.start();
}
}
6、啟動結果
客戶端
![](https://img2020.cnblogs.com/blog/2210127/202101/2210127-20210107163733082-2127617451.png)

服務端
![](https://img2020.cnblogs.com/blog/2210127/202101/2210127-20210107163738506-2127045216.png)

三、ChannelOption參數詳解
1、ChannelOption.SO_BACKLOG
ChannelOption.SO_BACKLOG對應的是tcp/ip協議listen函數中的backlog參數,函數listen(int socketfd,int backlog)用來初始化服務端可連接隊列,服務端處理客戶端連接請求是順序處理的,所以同一時間只能處理一個客戶端連接,多個客戶端來的時候,服務端將不能處理的客戶端連接請求放在隊列中等待處理,backlog參數指定了隊列的大小

2、ChannelOption.SO_REUSEADDR
ChanneOption.SO_REUSEADDR對應於套接字選項中的SO_REUSEADDR,這個參數表示允許重復使用本地地址和端口,比如,某個服務器進程占用了TCP的80端口進行監聽,此時再次監聽該端口就會返回錯誤,使用該參數就可以解決問題,該參數允許共用該端口,這個在服務器程序中比較常使用,比如某個進程非正常退出,該程序占用的端口可能要被占用一段時間才能允許其他進程使用,而且程序死掉以后,內核一需要一定的時間才能夠釋放此端口,不設置SO_REUSEADDR就無法正常使用該端口。

3、ChannelOption.SO_KEEPALIVE
Channeloption.SO_KEEPALIVE參數對應於套接字選項中的SO_KEEPALIVE,該參數用於設置TCP連接,當設置該選項以后,連接會測試鏈接的狀態,這個選項用於可能長時間沒有數據交流的連接。當設置該選項以后,如果在兩小時內沒有數據的通信時,TCP會自動發送一個活動探測數據報文

4、ChannelOption.SO_SNDBUF和ChannelOption.SO_RCVBUF
ChannelOption.SO_SNDBUF參數對應於套接字選項中的SO_SNDBUF,ChannelOption.SO_RCVBUF參數對應於套接字選項中的SO_RCVBUF這兩個參數用於操作接收緩沖區和發送緩沖區的大小,接收緩沖區用於保存網絡協議站內收到的數據,直到應用程序讀取成功,發送緩沖區用於保存發送數據,直到發送成功。

5、ChannelOption.SO_LINGER
ChannelOption.SO_LINGER參數對應於套接字選項中的SO_LINGER,Linux內核默認的處理方式是當用戶調用close()方法的時候,函數返回,在可能的情況下,盡量發送數據,不一定保證會發生剩余的數據,造成了數據的不確定性,使用SO_LINGER可以阻塞close()的調用時間,直到數據完全發送

6、ChannelOption.TCP_NODELAY
ChannelOption.TCP_NODELAY參數對應於套接字選項中的TCP_NODELAY,該參數的使用與Nagle算法有關,Nagle算法是將小的數據包組裝為更大的幀然后進行發送,而不是輸入一次發送一次,因此在數據包不足的時候會等待其他數據的到了,組裝成大的數據包進行發送,雖然該方式有效提高網絡的有效負載,但是卻造成了延時,而該參數的作用就是禁止使用Nagle算法,使用於小數據即時傳輸,於TCP_NODELAY相對應的是TCP_CORK,該選項是需要等到發送的數據量最大的時候,一次性發送數據,適用於文件傳輸。

7、IP_TOS
IP參數,設置IP頭部的Type-of-Service字段,用於描述IP包的優先級和QoS選項。

8、ALLOW_HALF_CLOSURE
Netty參數,一個連接的遠端關閉時本地端是否關閉,默認值為False。值為False時,連接自動關閉;為True時,觸發ChannelInboundHandler的userEventTriggered()方法,事件為ChannelInputShutdownEvent。

四、Netty的future.channel().closeFuture().sync();到底有什么用?
主線程執行到這里就 wait 子線程結束,子線程才是真正監聽和接受請求的,closeFuture()是開啟了一個channel的監聽器,負責監聽channel是否關閉的狀態,如果監聽到channel關閉了,子線程才會釋放,syncUninterruptibly()讓主線程同步等待子線程結果

 

作者:阿靖哦
鏈接:https://www.jianshu.com/p/b60180a0a0e6
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM