Netty 實現簡單的HTTP服務
超文字傳輸協議(HTTP,HyperText Transfer Protocol)是網際網路上應用最為廣泛的一種網路協議。
在後端開發中接觸HTTP協議的比較多,目前大部分都是基於Servlet容器實現的Http服務,往往有一些核心子系統對效能的要求非常高,這個時候我們可以考慮採用NIO的網路模型來實現HTTP服務,以此提高效能和吞吐量,Netty除了開發網路應用非常方便,還內建了HTTP相關的編解碼器,讓使用者可以很方便的開發出高效能的HTTP協議的服務,Spring Webflux預設是使用的Netty。
接下來我們簡單的介紹下如何使用Netty來構建一個簡單的Http服務
- 建立一個NettyHttpServer來啟動服務
public static void main(String[] args) {
int port = 2222;
new NettyHttpServer().run(port);
}
public void run(int port) {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
new HttpResponseEncoder(),
new HttpRequestDecoder(),
new NettyHttpServerHandler());
}
}).option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
try {
ChannelFuture f = bootstrap.bind(port).sync();
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
需要關注的是下面的這行程式碼:
ch.pipeline().addLast(
new HttpResponseEncoder(),
new HttpRequestDecoder(),
new NettyHttpServerHandler());
HttpResponseEncoder: 服務端往客戶端傳送資料的行為是Response,所以這邊要使用HttpResponseEncoder將資料進行編碼操作
HttpRequestDecoder:服務端接收到資料的行為是Request,所以要使用HttpRequestDecoder進行解碼操作
NettyHttpServerHandler:自定義的資料處理類
public class NettyHttpServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
Unpooled.wrappedBuffer("歡迎來到猿天地".getBytes("utf-8")));
response.headers().set(Names.CONTENT_TYPE, "text/plain;charset=UTF-8");
response.headers().set(Names.CONTENT_LENGTH, response.content().readableBytes());
response.headers().set(Names.CONNECTION, Values.KEEP_ALIVE);
ctx.write(response);
ctx.flush();
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
cause.printStackTrace();
}
}
通過DefaultFullHttpResponse構建了返回的物件,設定了HTTP版本,返回的狀態碼,返回的內容。
返回的響應頭通過response.headers().set()進行設定。
到此為止,一個簡單的HTTP服務就實現好了,我們啟動服務,在瀏覽器中輸入http://localhost:2222/ 就可以看到頁面中顯示的內容是:歡迎來到猿天地
上面演示的是一個典型的請求響應模式,一般我們開發介面的時候通常都是需要根據請求的引數進行對應的資料返回,如何在Netty中獲取請求的引數呢?
channelRead方法中的msg引數就是請求資訊,通過msg可以獲取到請求的所有資訊,有請求頭資訊(包括請求的地址,GET請求的引數),請求體(POST請求的資料)。
下面已GET請求的方式來獲取請求的引數資訊,程式碼如下:
if (msg instanceof HttpRequest) {
DefaultHttpRequest request = (DefaultHttpRequest) msg;
System.out.println("URI:" + request.getUri());
System.err.println(msg);
}
if (msg instanceof HttpContent) {
LastHttpContent httpContent = (LastHttpContent) msg;
ByteBuf byteData = httpContent.content();
if (byteData instanceof EmptyByteBuf) {
System.out.println("Content:無資料");
} else {
String content = new String(ByteUtils.objectToByte(byteData));
System.out.println("Content:" + content);
}
}
可以看到控制檯輸出的內容就是一個完整的HTTP請求包含的資訊:
URI:/?name=yjh
DefaultHttpRequest(decodeResult: success, version: HTTP/1.1)
GET /?name=yjh HTTP/1.1
Host: localhost:2222
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: _ga=GA1.1.939107719.1520393952; JSESSIONID=EE205236911D5BBA145E3021DB472D90
Content:無資料
本文只是簡單的介紹瞭如何在Netty中去實現HTTP服務,如果想要做成Spring MVC這樣的框架那後面的路還很長,請求響應Netty內建了編解碼器,還是有很多工作需要自己去做的。比如引數的獲取,請求的路由,引數對映成物件等….
更多技術分享請關注微信公眾號:猿天地