netty实战-手写一个Tomcat服务器

1、Request

public class DyyNettyRequest {

    private ChannelHandlerContext ctx;

    private HttpRequest httpRequest;


    public DyyNettyRequest(ChannelHandlerContext ctx, HttpRequest httpRequest) {
        this.ctx = ctx;
        this.httpRequest = httpRequest;
    }

    public String getMethod() {
        return httpRequest.getMethod().name();
    }

    public String getUrl() {
        return httpRequest.getUri().split("\\?")[0];
    }

    public String getUri() {
        return httpRequest.getUri();
    }

    public Map> getParameters(){
        QueryStringDecoder decoder = new QueryStringDecoder(httpRequest.getUri());
        return decoder.parameters();
    }

    public String getParameter(String paramName){
        List params = getParameters().get(paramName);
        if (paramName == null){
            return null;
        }else {
            return params.get(0);
        }
    }
}

2、Response

public class DyyNettyResponse {

    private ChannelHandlerContext ctx;

    public DyyNettyResponse(ChannelHandlerContext ctx) {
        this.ctx = ctx;
    }

    public void write(String outStr){
        try {
            if (StringUtils.isEmpty(outStr)){
                return;
            }
            FullHttpResponse response = new DefaultFullHttpResponse(
                    HttpVersion.HTTP_1_1,
                    HttpResponseStatus.OK,
                    Unpooled.wrappedBuffer(outStr.getBytes(StandardCharsets.UTF_8))
            );
            response.headers().set("Content-Type","text/html").set("charset",StandardCharsets.UTF_8.name());
            ctx.write(response);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            ctx.flush();
            ctx.close();
        }
    }
}

3、Servlet抽象类

public abstract class DyyNettyServlet{


    public void service(DyyNettyRequest request, DyyNettyResponse response) throws Exception {
            if ("GET".equalsIgnoreCase(request.getMethod())){
                System.out.println(String.format("获取到请求地址:%s,请求参数:%s", request.getUri(), request.getParameters()));
                doGet(request,response);
            }else if ("post".equalsIgnoreCase(request.getMethod())){
                System.out.println(String.format("获取到请求地址:%s,请求参数:%s", request.getUri(), request.getParameters()));
                doPost(request,response);
            }
    }

    public abstract void doGet(DyyNettyRequest request, DyyNettyResponse response) throws Exception;

    public abstract void doPost(DyyNettyRequest request, DyyNettyResponse response) throws Exception;
}

4、Tomcat

public class DyyNettyTomcat {

    private int port = 8080;
    //缓存Servlet
    private Map servletMapping = new HashMap<>();
    private Properties webxml = new Properties();

    private void init() {
        //加载web.xml 到servletMapping缓存中
        String webXml = "src/main/resources/web.properties";
        try (
                FileInputStream fin = new FileInputStream(webXml);
        ) {
            webxml.load(fin);
            for (Object k : webxml.keySet()) {
                String key = k.toString();
                if (key.endsWith(".url")) {
                    String servletName = key.replaceAll("\\.url$", "");
                    String url = webxml.getProperty(key);
                    String classNmae = webxml.getProperty(servletName + ".className");
                    //单线程 多实例
                    DyyNettyServlet servlet = (DyyNettyServlet) Class.forName(classNmae).newInstance();
                    servletMapping.put(url, servlet);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void start() {
        //加载web.xml 将各个servlet实例放入servletMapping缓存中
        init();
        //处理连接,主线程组
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        //工作线程组
        NioEventLoopGroup workGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap server = new ServerBootstrap();
            server.group(bossGroup, workGroup)
                    //NIO服务端通道,主线程处理类
                    .channel(NioServerSocketChannel.class)
                    //工作线程处理,类似NIO例子中的handle方法
                    .childHandler(new ChannelInitializer() {
                        //客户端连接后的处理
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            //netty对http的封装,对顺序有要求
                            socketChannel.pipeline()
                                    //责任链模式,双向链表 InBound、OutBound
                                    //InBound 从上至下执行
                                    //OutBound 由下至上执行
                                    .addLast(new HttpResponseEncoder())
                                    .addLast(new HttpRequestDecoder())
                                    .addLast(new DyyNettyTomcatHandler());
                        }
                    })
                    //初始化服务器连接队列大小 这里分配最大队列128
                    .option(ChannelOption.SO_BACKLOG, 128)
                    //工作线程配置,保持长连接
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            //启动服务器(并绑定端口),bind是异步操作,sync方法是等待异步操作执行完毕,通过isDone()等方法可以判断异步事件的执行情况
            ChannelFuture future = server.bind(port).sync();
            System.out.println(String.format("DyyNettyTomcat is started...,port:%s", port));
            //通过sync方法同步等待通道关闭处理完毕,这里会阻塞等待通道关闭完成
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }

    /**
     *
     */
    public class DyyNettyTomcatHandler extends ChannelHandlerAdapter {
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            super.exceptionCaught(ctx, cause);
        }

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            if (msg instanceof HttpRequest) {
                HttpRequest httpRequest = (HttpRequest) msg;
                DyyNettyRequest request = new DyyNettyRequest(ctx, httpRequest);
                DyyNettyResponse response = new DyyNettyResponse(ctx);
                String url = request.getUrl();
                if (servletMapping.containsKey(url)) {
                    servletMapping.get(url).service(request, response);
                } else {
                    response.write("404 - Not Found");
                }

            }
        }
    }

    /**
     * 启动类
     *
     * @param args
     */
    public static void main(String[] args) {
        new DyyNettyTomcat().start();
    }
}

5、FirstServlet

public class FirstServlet extends DyyNettyServlet {

    @Override
    public void doGet(DyyNettyRequest request, DyyNettyResponse response) throws Exception {
        doPost(request,response);
    }

    @Override
    public void doPost(DyyNettyRequest request, DyyNettyResponse response) throws Exception {
        response.write("Im First Servlet!");
    }
}

6、SecondServlet

public class SecondServlet extends DyyNettyServlet {
    @Override
    public void doGet(DyyNettyRequest request, DyyNettyResponse response) throws Exception {
        doPost(request,response);
    }

    @Override
    public void doPost(DyyNettyRequest request, DyyNettyResponse response) throws Exception {
        response.write("Im Second Servlet!");
    }
}

7、web.properties

servlet.one.url=/FirstServlet.do
servlet.one.className=com.dyy.demo.tomcat.netty.FirstServlet
servlet.two.url=/SecondServlet.do
servlet.two.className=com.dyy.demo.tomcat.netty.SecondServlet

8、启动tomcat中的main方法,浏览器调用地址显示


你可能感兴趣的:(netty实战-手写一个Tomcat服务器)