Netty4+SpringBoot實現http server
一.Netty是什麼?
Netty:Netty是 一個非同步事件驅動的網路應用程式框架, 用於快速開發可維護的高效能協議伺服器和客戶端。Netty是一個NIO客戶端伺服器框架,可以快速輕鬆地開發協議伺服器和客戶端等網路應用程式。它極大地簡化並簡化了TCP和UDP套接字伺服器等網路程式設計。“快速簡便”並不意味著最終的應用程式會受到可維護性或效能問題的影響。Netty經過精心設計,具有豐富的協議,如FTP,SMTP,HTTP以及各種二進位制和基於文字的傳統協議。因此,Netty成功地找到了一種在不妥協的情況下實現易於開發,效能,穩定性和靈活性的方法。來自於官網的描述。
個人理解:是建立在客戶端和伺服器之間,通過某個指定協議實現網路資料傳輸,高效能,非同步,事件驅動,的java nio框架(協議的容器)。
二.Netty執行流程
圖片來源部落格:圖解Netty5.0:https://blog.csdn.net/KouLouYiMaSi/article/details/80589095,侵權刪。
三.SpringBoot整合Netty,實現http協議的server伺服器端
1.http協議下的客戶端和伺服器端的請求執行流程圖,圖片來源:https://blog.csdn.net/wangshuang1631/article/details/73251180/
2.maven的pom.xml中引入Netty的jar包,新增以下依賴:
<dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.31.Final</version> </dependency>
3.Netty服務端啟動器類NettyServer的編碼:
package demo.netty; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; /** * description: * author: * date: 2018-11-28 12:07 **/ @Component public class NettyServer { private static final Logger log = LoggerFactory.getLogger(NettyServer.class); //boss事件輪詢執行緒組 private EventLoopGroup boss = new NioEventLoopGroup(); //worker事件輪詢執行緒組 private EventLoopGroup worker = new NioEventLoopGroup(); private Channel channel; @Autowired ServerChannelInitializer serverChannelInitializer; @Value("${n.port}") private Integer port; /** * 開啟Netty服務 * * @return */ public ChannelFuture start() { //啟動類 ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(boss, worker)//設定引數,組配置 .option(ChannelOption.SO_BACKLOG, 128)//socket引數,當伺服器請求處理程全滿時,用於臨時存放已完成三次握手的請求的佇列的最大長度。如果未設定或所設定的值小於1,Java將使用預設值50。 .channel(NioServerSocketChannel.class)///構造channel通道工廠//bossGroup的通道,只是負責連線 .childHandler(serverChannelInitializer);//設定通道處理者ChannelHandler////workerGroup的處理器 //Future:非同步操作的結果 ChannelFuture channelFuture = serverBootstrap.bind(port);//繫結埠 ChannelFuture channelFuture1 = channelFuture.syncUninterruptibly();//接收連線 channel = channelFuture1.channel();//獲取通道 if (channelFuture1 != null && channelFuture1.isSuccess()) { log.info("Netty server 服務啟動成功,埠port = {}", port); } else { log.info("Netty server start fail"); } return channelFuture1; } /** * 停止Netty服務 */ public void destroy() { if (channel != null) { channel.close(); } worker.shutdownGracefully(); boss.shutdownGracefully(); log.info("Netty server shutdown success"); } }
其中@Value("${n.port}")取的是application.properties配置檔案中的n.port=7000。
4.通道初始化類ServerChannelInitializer的編碼,主要用於設定各種Handler:
package demo.netty;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* description: 通道初始化,主要用於設定各種Handler
* author:
* date: 2018-11-28 14:55
**/
@Component
public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {
@Autowired
ServerChannelHandler serverChannelHandler;
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//編碼解碼
ch.pipeline().addLast(new HttpRequestDecoder());
ch.pipeline().addLast(new HttpResponseEncoder());
ch.pipeline().addLast(serverChannelHandler);//ChannelHandler
}
}
5.通道處理者ServerChannelHandler的編碼,就是用來處理請求的:
package demo.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import org.springframework.stereotype.Component;
/**
* description:
* author:
* date: 2018-11-28 15:49
**/
@Component
@ChannelHandler.Sharable
public class ServerChannelHandler extends SimpleChannelInboundHandler<Object> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("Netty Server receive msg : " + msg);
if (msg instanceof HttpRequest) {
//要返回的內容, Channel可以理解為連線,而連線中傳輸的資訊要為ByteBuf
ByteBuf content = Unpooled.copiedBuffer("Hello World", CharsetUtil.UTF_8);
//構造響應
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);
//設定頭資訊的的MIME型別
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain"); //內容型別
//設定要返回的內容長度
response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes()); //內容長度
//將響應物件返回
ctx.channel().writeAndFlush(response);
ctx.close();
} else {
System.out.println("the request is not HttpRequest");
}
}
}
6.最後,SpringBoot啟動類中新增Netty的命令列啟動:
package demo;
import demo.netty.NettyServer;
import io.netty.channel.ChannelFuture;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
/**
* ClassName: SpringBootApplication
* description:
* author:
* date: 2018-09-30 09:15
**/
@org.springframework.boot.autoconfigure.SpringBootApplication//@EnableAutoConfiguration @ComponentScan
public class SpringBootApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(SpringBootApplication.class,args);
}
@Autowired
NettyServer nettyServer;
@Override
public void run(String... args) throws Exception {
ChannelFuture start = nettyServer.start();
Runtime.getRuntime().addShutdownHook(new Thread(){
@Override
public void run() {
nettyServer.destroy();
}
});
start.channel().closeFuture().syncUninterruptibly();
}
}
四.啟動程式,瀏覽器訪問
1.啟動程式後的日誌:
2.瀏覽器訪問:127.0.0.1:7000,會看見hello world顯示在頁面,後端輸出:
可以發現,瀏覽器一次http請求向後端傳送了4次請求,原因是一個完整的http請求DefaultFullHttpRequest包含了所有的http請求資訊,但實際http請求的時候好像會將http請求行、請求頭、請求體分開發送,詳細可百度FullHttpRequest(推薦一篇:netty對http協議解析原理解析)。