關於netty
netty 是一個非阻塞IO框架,用於Java網絡應用開發,特點是異步處理,併發處理能力,netty裏面包含有reactor框架的實現,是一個非常高級的框架體系。
netty特性
netty 處理快,更少的資源需求,響應快,可以作爲高併發場景服務器的一個選擇
reactor 個人理解是 react:響應式, or :對象,就是響應式框架,netty 就是運用reactor 的核心設計思想編寫的高性能高併發網絡請求處理器框架。
以下來自維基百科,自由的百科全書
注意:本條目主題可能尚無中文譯名,因而使用原文或其拉丁字母轉寫作爲標題。如果您在可靠來源中找到本主題的中文名稱,請勇於將其移動至中文標題。(2019年2月)
開發者 | Netty項目社區 |
穩定版本 | 4.1.31.Final[1](2018年10月30日,2年前) |
預覽版本 | 5.0.0.Alpha3(2016年1月14日,5年前) |
源代碼庫 | github.com/netty/netty |
編程語言 | Java |
類型 | Enterprise Integration Patterns Message Oriented Middleware |
許可協議 | Apache許可證 2.0 |
網站 | netty.io |
Netty是一個非阻塞I/O客戶端-服務器框架,主要用於開發Java網絡應用程序,如協議服務器和客戶端。異步事件驅動的網絡應用程序框架和工具用於簡化網絡編程,例如TCP和UDP套接字服務器。[2]Netty包括了反應器編程模式的實現。Netty最初由JBoss開發,現在由Netty項目社區開發和維護。
除了作爲異步網絡應用程序框架,Netty還包括了對HTTP、HTTP2、DNS及其他協議的支持,涵蓋了在Servlet容器內運行的能力、對WebSockets的支持、與Google Protocol Buffers的集成、對SSL/TLS的支持以及對用於SPDY協議和消息壓縮的支持。自2004年以來,Netty一直在被積極開發。[3]
從版本4.0.0開始,Netty在支持NIO和阻塞Java套接字的同時,還支持使用NIO.2作爲後端。
用netty寫一個簡單非阻塞請求響應服務器
簡單示意代碼
服務端
服務器 HttpServer
package net.narule.jnetty.httpdemo;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import java.net.InetSocketAddress;
/**
* netty server
*/
public class HttpServer {
int port ;
public HttpServer(int port){
this.port = port;
}
public void start() throws Exception{
ServerBootstrap bootstrap = new ServerBootstrap();
// 事件監聽相關對象配置 netty 循環監聽是否有新事件
// 如果有 馬上將事件交給處理器,處理器層層循環處理,會循環到自定義消息解析器 HttpRequestHandler
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup work = new NioEventLoopGroup();
bootstrap.group(boss,work)
.handler(new LoggingHandler(LogLevel.DEBUG))
.channel(NioServerSocketChannel.class)
.childHandler(new HttpServerInitializer());
ChannelFuture f = bootstrap.bind(new InetSocketAddress(port)).sync();
System.out.println("server start... port : " + port);
f.channel().closeFuture().sync();
}
}
請求處理器初始化 HttpServerInitializer
initChannel 處理器配置。可以配置多個
package net.narule.jnetty.httpdemo;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
public class HttpServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
// http 編解碼
pipeline.addLast(new HttpServerCodec());
// http 消息聚合器 512*1024爲接收的最大contentlength
pipeline.addLast("httpAggregator",new HttpObjectAggregator(512*1024));
// 請求處理器
pipeline.addLast(new HttpRequestHandler());
}
}
消息處理器 HttpRequestHandler
重寫channelRead0 方法處理請求的消息並返回
package net.narule.jnetty.httpdemo;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.CharsetUtil;
public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
// 處理數據
Date date = new Date(System.currentTimeMillis());
String uri = req.uri();
Map<String,String> data = new HashMap<>();
String protocal = req.protocolVersion().text(); //獲取HTTP協議版本
String content = req.content().toString(CharsetUtil.UTF_8); //獲取HTTP協議版本
System.out.println("request uri:" + uri);
System.out.println("request content:" + content);
boolean match = false;
if(uri.contains("J")) {
match = true;
}
data.put("match", String.valueOf(match));
data.put("protocal", protocal);
data.put("uri", uri);
data.put("time", date.toString());
data.put("content", content);
System.getProperty("HOST");
String responsedata = "{";
Set<String> keySet = data.keySet();
int size = data.size();
for (String key : keySet) {
size --;
responsedata = responsedata + "\"" + key + "\":" + "\"" + data.get(key) + (size == 0? "\"}" : "\",");
}
// 創建http響應
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
Unpooled.copiedBuffer(responsedata, CharsetUtil.UTF_8));
// 設置頭信息
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json; charset=UTF-8");
// 將處理後的數據 write到客戶端、
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
}
啓動
main方法啓動服務
public static void main(String[] args) throws Exception{
HttpServer server = new HttpServer(80);// 80爲啓動端口
server.start();
}
server start... port : 80
客戶端測試
瀏覽器發送請求
直接url欄輸入get請求:
http://localhost/test?name=J&time=-1
返回結果
{
"match":"true",
"time":"Sun Jul 11 21:52:22 CST 2021",
"protocal":"HTTP/1.1",
"uri":"/test?name=J&time=-1",
"content":""
}
發送請求
http://localhost/test?name=nos&time=-1
返回結果
{
"match":"false",
"time":"Sun Jul 11 21:52:22 CST 2021",
"protocal":"HTTP/1.1",
"uri":"/test?name=nos&time=-1",
"content":""
}