手寫tomcat——netty版
阿新 • • 發佈:2021-10-03
點選檢視程式碼
package com.grady.diytomcat; import com.grady.diytomcat.handler.DiyNettyTomcatHandler; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.http.HttpRequestDecoder; import io.netty.handler.codec.http.HttpResponseEncoder; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.util.HashMap; import java.util.List; public class DiyTomcat { private int port = 8080; public static final HashMap<String, DiyNettyServlet> SERVLET_MAPPING = new HashMap<>(); public static final HashMap<String,String> URL_MAPPING = new HashMap<>(); static { loadServlet(); } private static void loadServlet() { try { //獲取web.xml目錄地址 String path = DiyTomcat.class.getResource("/").getPath(); SAXReader reader = new SAXReader(); //讀取web.xml檔案 Document document = reader.read(new File(path + "web.xml")); //獲取根標籤(servlet和servlet-mapping),放在一個List中 Element rootElement = document.getRootElement(); List<Element> elements = rootElement.elements(); //迴圈將對映寫進map對映裡 for(Element element : elements){ if ("servlet".equalsIgnoreCase(element.getName())){ Element servletName = element.element("servlet-name"); Element servletClass = element.element("servlet-class"); //需要注意的是servletMapping對映的第二個引數,要通過反射的方式進行例項化 SERVLET_MAPPING.put(servletName.getText(), (DiyNettyServlet) Class.forName(servletClass.getText().trim()).newInstance()); }else if ("servlet-mapping".equalsIgnoreCase(element.getName())){ Element servletName = element.element("servlet-name"); Element urlPattern = element.element("url-pattern"); URL_MAPPING.put(urlPattern.getText(), servletName.getText()); } } } catch (DocumentException e) { e.printStackTrace(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } } public void start() throws IOException { // Boss執行緒 NioEventLoopGroup bossGroup = new NioEventLoopGroup(); // worker執行緒 NioEventLoopGroup workerGroup = new NioEventLoopGroup(); // 1.建立物件 ServerBootstrap server = new ServerBootstrap(); //2. 配置引數 server.group(bossGroup,workerGroup) // 主執行緒處理類 .channel(NioServerSocketChannel.class) // 子執行緒處理類 Handler .childHandler(new ChannelInitializer<SocketChannel>() { protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(new HttpResponseEncoder()); socketChannel.pipeline().addLast(new HttpRequestDecoder()); socketChannel.pipeline().addLast(new DiyNettyTomcatHandler()); } }) //針對主執行緒的配置,最大執行緒數128 .option(ChannelOption.SO_BACKLOG,128) // 針對子執行緒的配置,保持長連線 .childOption(ChannelOption.SO_KEEPALIVE,true); try { // 3 啟動伺服器 ChannelFuture f = server.bind(port).sync(); System.out.println("DiyTomcat啟動成功,監聽的埠是:" + port); f.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } } }
1. netty版會稍有些不同,因為netty內部的核心是channel
(對socket進行了一層封裝處理),所以我們不能直接拿到socket
對應的InputStream
和OutputStream
2. 我們的核心處理邏輯在DiyNettyTomcatHandler
中
package com.grady.diytomcat.handler; import com.grady.diytomcat.DiyNettyRequest; import com.grady.diytomcat.DiyNettyResponse; import com.grady.diytomcat.DiyNettyServlet; import com.grady.diytomcat.DiyTomcat; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.handler.codec.http.HttpRequest; public class DiyNettyTomcatHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof HttpRequest){ HttpRequest req = (HttpRequest) msg; DiyNettyRequest request = new DiyNettyRequest(ctx, req); DiyNettyResponse response = new DiyNettyResponse(ctx, req); // 實際業務處理 String url = request.getUrl(); if(DiyTomcat.URL_MAPPING.containsKey(url)) { String servletName = DiyTomcat.URL_MAPPING.get(url); DiyNettyServlet servlet= DiyTomcat.SERVLET_MAPPING.get(servletName); servlet.service(request, response); } else { response.write("404 - Not Found"); } } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { } }
在這裡,將ChannelHandlerContext
封裝到DiyNettyRequest
和DiyNettyResponse
中,這樣我們的request
和response
就擁有了讀資料和寫資料的能力
<br/>
原始碼地址:
https://github.com/ZhongJinHacker/diy-tomcat/tree/netty-tomcat