netty 簡介
Netty一個基于NIO的客戶、服務器端的編程框架
1.環境準備
maven依賴
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.42.Final</version>
</dependency>
12345
RequestMethodEnum 請求方式
public enum RequestMethodEnum {
GET("GET"),
POST("POST");
public String code;
RequestMethodEnum(String code) {
this.code=code;
}}12345678
ParentServlet 父類servlet
public abstract class ParentServlet {
public void service(ParentRequest request, ParentResponse response) throws Exception {
//service 方法決定調用doGet、doPost;
if (RequestMethodEnum.GET.code.equalsIgnoreCase(request.getMethod())) {
doGet(request, response);
} else {
doPost(request, response);
}
}
protected abstract void doPost(ParentRequest request, ParentResponse response) throws Exception;
protected abstract void doGet(ParentRequest request, ParentResponse response) throws Exception;
}
12345678910111213141516
- FirstServlet
public class FirstServlet extends ParentServlet {
@Override
protected void doPost(ParentRequest request, ParentResponse response) throws Exception {
response.write("This is the first");
} @Override
protected void doGet(ParentRequest request, ParentResponse response) throws Exception {
this.doPost(request,response);
}}1234567891011
- SecondServlet
public class SecondServlet extends ParentServlet {
@Override
protected void doPost(ParentRequest request, ParentResponse response) throws Exception {
response.write("this is the second");
} @Override
protected void doGet(ParentRequest request, ParentResponse response) throws Exception {
this.doPost(request,response);
}}1234567891011
- ParentRequest
public class ParentRequest {
private String method;
private String url;
public String getUrl() {
return url;
} public String getMethod() {
return method;
}}1234567891011121314
- ParentResponse
public class ParentResponse {
private OutputStream out;
public ParentResponse (OutputStream out) {
this.out = out;
} public void write(String s) throws Exception{
//輸出也要遵循HTTP
//狀態碼為200
StringBuilder sb = new StringBuilder();
sb.Append("HTTP/1.1 200 OK n")
.append("Content-Type: text/html;n")
.append("rn")
.append(s);
out.write(sb.toString().getBytes());
}
}
1234567891011121314151617
- web.properties
servlet.first.url=/first
servlet.first.className=com.aiden.servlet.FirstServletservlet.second.url=/secondservlet.second.className=com.aiden.servlet.SecondServlet1234
2.基于傳統I/O手寫Tomcat
- 修改ParentRequest
public class ParentRequest {
private String method;
private String url;
public ParentRequest(InputStream in) {
try {
String content = "";
byte[] buff = new byte[1024];
int len = 0;
if ((len = in.read(buff)) > 0) {
content = new String(buff,0,len);
} String line = content.split("\n")[0];
String [] arr = line.split("\s");
this.method = arr[0];
System.out.println(method);
this.url = arr[1].split("\?")[0];
} catch (IOException e) {
e.printStackTrace(); } } public String getUrl() {
return url;
} public String getMethod() {
return method;
}}12345678910111213141516171819202122232425262728293031
- 編寫tomcatStart類
public class TomcatStart {
private int port = 8080;
private ServerSocket server;
private Map<String, ParentServlet> servletMapping = new HashMap<String, ParentServlet>();
private Properties webProperties = new Properties();
private void init() {
try {
String WEB_INF = this.getClass().getResource("/").getPath();
FileInputStream fis = new FileInputStream(WEB_INF + "web.properties");
webProperties.load(fis); for (Object k : webProperties.keySet()) {
String key = k.toString();
if (key.endsWith(".url")) {
String servletName = key.replaceAll("\.url$", "");
String url = webProperties.getProperty(key);
String className = webProperties.getProperty(servletName + ".className");
//單實例 多線程
ParentServlet obj = (ParentServlet) Class.forName(className).newInstance();
servletMapping.put(url, obj);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void start() {
//1.加載配置類,初始化servletMapping
init();
try {
//2.綁定端口啟動
server = new ServerSocket(this.port);
System.out.println("Tomcat 已啟動,監聽端口是:" + this.port);
//3.等待用戶請求,用一個死循環
while (true) {
Socket client = server.accept();
//4.http 請求
process(client);
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void process(Socket client) throws IOException {
InputStream is = null;
OutputStream os = null;
try {
is = client.getInputStream();
os = client.getOutputStream();
//5.Request(inputstream) Response (outputstream)
ParentRequest request = new ParentRequest(is);
ParentResponse response = new ParentResponse(os);
//6.從協議內容中獲取url 映射相應的servlet
String url = request.getUrl();
if (servletMapping.containsKey(url)) {
//7.調用實例化對象的service方法
servletMapping.get(url).service(request, response);
} else {
response.write("404 - Not Found");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (os != null) {
os.flush();
os.close();
}
if (is != null) {
is.close();
}
client.close();
}
}
public static void main(String[] args) {
//啟動
new TomcatStart().start();
}
}
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
3.基于netty手寫Tomcat
- 修改ParentRequest
public class ParentRequest {
private ChannelHandlerContext ctx;
private HttpRequest req;
public ParentRequest(ChannelHandlerContext ctx, HttpRequest req) {
this.ctx = ctx;
this.req = req;
} public String getUrl() {
return req.uri();
} public String getMethod() {
return req.method().name();
} public Map<String, List<String>> getParameters() {
QueryStringDecoder decoder = new QueryStringDecoder(req.uri());
return decoder.parameters();
} public String getParameter(String name) {
Map<String, List<String>> params = getParameters();
List<String> param = params.get(name);
if (null == param) {
return null;
} else {
return param.get(0);
} }}123456789101112131415161718192021222324252627282930313233
- 修改ParentResponse
public class ParentResponse {
//SocketChannel的封裝 private ChannelHandlerContext ctx; private HttpRequest req; public ParentResponse(ChannelHandlerContext ctx, HttpRequest req) { this.ctx = ctx; this.req = req; } public void write(String out) throws Exception {
try { if (out == null || out.length() == 0) {
return;
} // 設置 http協議及請求頭信息 FullHttpResponse response = new DefaultFullHttpResponse( // 設置http版本為1.1
HttpVersion.HTTP_1_1, // 設置響應狀態碼 HttpResponseStatus.OK, // 將輸出值寫出 編碼為UTF-8
Unpooled.wrappedBuffer(out.getBytes("UTF-8")));
response.headers().set("Content-Type", "text/html;");
ctx.write(response);
} finally { ctx.flush();
ctx.close();
} }}12345678910111213141516171819202122232425262728293031323334
- 修改TomcatStart
public class TomcatStart {
private int port = 8080;
private Map<String, ParentServlet> servletMapping = new HashMap<String, ParentServlet>();
private Properties webProperties = new Properties();
private void init() {
try {
String WEB_INF = this.getClass().getResource("/").getPath();
FileInputStream fis = new FileInputStream(WEB_INF + "web.properties");
webProperties.load(fis); for (Object k : webProperties.keySet()) {
String key = k.toString();
if (key.endsWith(".url")) {
String servletName = key.replaceAll("\.url$", "");
String url = webProperties.getProperty(key);
String className = webProperties.getProperty(servletName + ".className");
//單實例 多線程
ParentServlet obj = (ParentServlet) Class.forName(className).newInstance();
servletMapping.put(url, obj);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void start() {
//1.加載配置類,初始化servletMapping
init();
// Netty NIO Reactor模型 Boss Worker
//Boss 線程
EventLoopGroup bossGroup = new NioEventLoopGroup();
//Work線程
EventLoopGroup workGroup = new NioEventLoopGroup();
ServerBootstrap server = null;
try {
//創建對象
server = new ServerBootstrap();
//配置參數
//鏈式編程
server.group(bossGroup, workGroup)
//主線程處理類,
.channel(NIOServerSocketChannel.class)
//子線程處理類
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel client) throws Exception {
//無鎖化串行編程
//netty對http的封裝 對順序有要求
//httpResponseEncoder 編碼器
client.pipeline().addLast(new HttpResponseEncoder());
//httprequestDecoder 解碼器
client.pipeline().addLast(new HttpRequestDecoder());
//業務處理器
client.pipeline().addLast(new TomcatHandler());
}
})
//主線程 線程最大數量128
.option(ChannelOption.SO_BACKLOG, 128)
//子線程配置 保存長連接
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture f = server.bind(port).sync();
System.out.println("Tomcat 已啟動,監聽端口是:" + this.port);
f.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
public class TomcatHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpRequest) {
System.out.println("hello request");
HttpRequest req = (HttpRequest) msg;
ParentRequest request = new ParentRequest(ctx, req);
ParentResponse response = new ParentResponse(ctx, req);
String url = request.getUrl();
if (servletMapping.containsKey(url)) {
//7.調用實例化對象的service方法
servletMapping.get(url).service(request, response);
} else {
response.write("404 - Not Found");
}
}
}
}
public static void main(String[] args) {
//啟動
new TomcatStart().start();
}
}
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
4.訪問
http://localhost:8080/first