Netty是由JBOSS提供的一個java開源框架。java
Netty提供異步的、事件驅動的網絡應用程序框架和工具,用以快速開發高性能、高可靠性的網絡服務器和客戶端程序。算法
也就是說,Netty 是一個基於NIO的客戶、服務器端編程框架,使用Netty能夠確保你快速和簡單的開發出一個網絡應用,例如實現了某種協議的客戶、服務端應用。spring
Netty至關於簡化和流線化了網絡應用的編程開發過程,例如:基於TCP和UDP的socket服務開發。編程
「快速」和「簡單」並不用產生維護性或性能上的問題。Netty是一個吸取了多種協議(包括FTP、SMTP、HTTP等各類二進制文本協議)的實現經驗,並通過至關精心設計的項目。bootstrap
最終,Netty 成功的找到了一種方式,在保證易於開發的同時還保證了其應用的性能,穩定性和伸縮性服務器
本文講解SpringBoot如何使用Netty服務端和客戶端的簡單案例以及相關參數解釋網絡
1、Netty服務端框架
一、導入依賴異步
< dependency>socket
< 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>
二、編寫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(); } } 三、編寫Netty服務端初始化器
/**
/**
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();
}
} }
五、啓動類
@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));
}
} 六、啓動結果
< 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>
二、編寫客戶端處理器
/**
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();
}
}
三、編寫客戶端初始化器
/**
@author Gjing
客戶端初始化器
**/ public class NettyClientInitializer extends ChannelInitializer {
@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());
}
}
四、編寫客戶端
/**
**/ @Component
@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();
}
}
}
五、啓動類
@SpringBootApplication
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
//啓動netty客戶端
NettyClient nettyClient = new NettyClient();
nettyClient.start();
}
} 六、啓動結果
客戶端
3、ChannelOption參數詳解 一、ChannelOption.SO_BACKLOG
ChannelOption.SO_BACKLOG對應的是tcp/ip協議listen函數中的backlog參數,函數listen(int socketfd,int backlog)用來初始化服務端可鏈接隊列,服務端處理客戶端鏈接請求是順序處理的,因此同一時間只能處理一個客戶端鏈接,多個客戶端來的時候,服務端將不能處理的客戶端鏈接請求放在隊列中等待處理,backlog參數指定了隊列的大小
二、ChannelOption.SO_REUSEADDR
ChanneOption.SO_REUSEADDR對應於套接字選項中的SO_REUSEADDR,這個參數表示容許重複使用本地地址和端口,好比,某個服務器進程佔用了TCP的80端口進行監聽,此時再次監聽該端口就會返回錯誤,使用該參數就能夠解決問題,該參數容許共用該端口,這個在服務器程序中比較常使用,好比某個進程非正常退出,該程序佔用的端口可能要被佔用一段時間才能容許其餘進程使用,並且程序死掉之後,內核一須要必定的時間纔可以釋放此端口,不設置SO_REUSEADDR就沒法正常使用該端口。
三、ChannelOption.SO_KEEPALIVE
Channeloption.SO_KEEPALIVE參數對應於套接字選項中的SO_KEEPALIVE,該參數用於設置TCP鏈接,當設置該選項之後,鏈接會測試連接的狀態,這個選項用於可能長時間沒有數據交流的鏈接。當設置該選項之後,若是在兩小時內沒有數據的通訊時,TCP會自動發送一個活動探測數據報文
四、ChannelOption.SO_SNDBUF和ChannelOption.SO_RCVBUF
ChannelOption.SO_SNDBUF參數對應於套接字選項中的SO_SNDBUF,ChannelOption.SO_RCVBUF參數對應於套接字選項中的SO_RCVBUF這兩個參數用於操做接收緩衝區和發送緩衝區的大小,接收緩衝區用於保存網絡協議站內收到的數據,直到應用程序讀取成功,發送緩衝區用於保存發送數據,直到發送成功。
五、ChannelOption.SO_LINGER
ChannelOption.SO_LINGER參數對應於套接字選項中的SO_LINGER,Linux內核默認的處理方式是當用戶調用close()方法的時候,函數返回,在可能的狀況下,儘可能發送數據,不必定保證會發生剩餘的數據,形成了數據的不肯定性,使用SO_LINGER能夠阻塞close()的調用時間,直到數據徹底發送
六、ChannelOption.TCP_NODELAY
ChannelOption.TCP_NODELAY參數對應於套接字選項中的TCP_NODELAY,該參數的使用與Nagle算法有關,Nagle算法是將小的數據包組裝爲更大的幀而後進行發送,而不是輸入一次發送一次,所以在數據包不足的時候會等待其餘數據的到了,組裝成大的數據包進行發送,雖然該方式有效提升網絡的有效負載,可是卻形成了延時,而該參數的做用就是禁止使用Nagle算法,使用於小數據即時傳輸,於TCP_NODELAY相對應的是TCP_CORK,該選項是須要等到發送的數據量最大的時候,一次性發送數據,適用於文件傳輸。
七、IP_TOS
IP參數,設置IP頭部的Type-of-Service字段,用於描述IP包的優先級和QoS選項。
八、ALLOW_HALF_CLOSURE
Netty參數,一個鏈接的遠端關閉時本地端是否關閉,默認值爲False。值爲False時,鏈接自動關閉;爲True時,觸發ChannelInboundHandler的userEventTriggered()方法,事件爲ChannelInputShutdownEvent。
4、Netty的future.channel().closeFuture().sync();到底有什麼用?
主線程執行到這裏就 wait 子線程結束,子線程纔是真正監聽和接受請求的,closeFuture()是開啓了一個channel的監聽器,負責監聽channel是否關閉的狀態,若是監聽到channel關閉了,子線程纔會釋放,syncUninterruptibly()讓主線程同步等待子線程結果