粗汉手撕Tomcat核心原理-Netty版

粗汉手撕Tomcat核心原理-Netty版


源码地址: https://gitee.com/kylin1991_admin/mini-tomcat/tree/master/nio-tomcat

环境准备

1、maven

    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.10</version>
      <scope>provided</scope>
    </dependency>
    
    <!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
    <dependency>
      <groupId>io.netty</groupId>
      <artifactId>netty-all</artifactId>
      <version>4.1.48.Final</version>
    </dependency>

2、插件:Lombok

相关类图展示

在这里插入图片描述

1、配置文件web.properties
servlet.one.url=/firstServlet.do
servlet.one.className=org.example.servlet.FirstServlet
2、顶级抽象类NServlet
public abstract class NServlet {
    public void service(NRequest request, NResponse response) throws IOException {
        if ("GET".equalsIgnoreCase(request.getMethod())) {
            doGet(request, response);
        } else {
            doPost(request, response);
        }
    }

    protected abstract void doGet(NRequest request, NResponse response) throws IOException;

    protected abstract void doPost(NRequest request, NResponse response) throws IOException;
}
3、包装NRequst
@Data
public class NRequest {
    private String url;
    private String method;
    private Map<String, Object> parametersMap = new HashMap<>();

    private ChannelHandlerContext ctx;
    private HttpRequest req;

    public NRequest(ChannelHandlerContext ctx, HttpRequest req) {
        this.ctx = ctx;
        this.req = req;
        init();
    }

    private void init() {
        this.method = req.method().toString();
        String[] requestUrl = req.uri().split("\\?");
        this.setUrl(requestUrl[0]);

        if (requestUrl.length > 1) {
            String[] paramKeyValue = requestUrl[1].split("&");
            for (String keyValue : paramKeyValue) {
                String[] params = keyValue.split("=");
                this.getParametersMap().put(params[0], params[1]);
            }
        }
    }
}
4、包装NResponse
public class NResponse {

    private ChannelHandlerContext ctx;
    private HttpRequest req;

    public NResponse(ChannelHandlerContext ctx, HttpRequest req) {
        this.ctx = ctx;
        this.req = req;
    }

    public void write(String content) throws IOException {
        if (content == null || content.length() == 0) {
            return;
        }
        FullHttpResponse response = new DefaultFullHttpResponse(
                // 设置http版本为1.1
                HttpVersion.HTTP_1_1,
                // 设置响应状态码
                HttpResponseStatus.OK,
                // 设置输出值写出 编码为UTF-8
                Unpooled.wrappedBuffer(content.getBytes("UTF-8")));
        // 设置响应头
        response.headers().set("Content-Type", "text/html");
        ctx.writeAndFlush(response);

        ctx.close();
    }
}
5、NTomcat启动类
public class NTomcat {
    // 打开 Tomcat 源码,全局搜索ServerSocket
    private static final int PORT = 8080;
    private static Map<String, NServlet> serviceMap = new HashMap<>();
    private static String WEB_URL = "web.properties";

    private void init() {
        try {

            FileInputStream fileInputStream = new FileInputStream(this.getClass().getResource("/").getPath() + WEB_URL);
            Properties properties = new Properties();
            properties.load(fileInputStream);
            for (Object k : properties.keySet()) {
                String key = k.toString();
                if (key.endsWith(".url")) {
                    String keyPrefix = key.replaceAll("\\.url$", "");
                    String url = properties.getProperty(key);
                    String className = properties.getProperty(keyPrefix + ".className");
                    NServlet servlet = (NServlet) Class.forName(className).newInstance();
                    serviceMap.put(url, servlet);
                }
            }
        } catch (IOException | IllegalAccessException | InstantiationException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public void start() {
        init();
        // Netty 封装了NIO,Reactor模型,Boss,worker线程

        EventLoopGroup bossGroup = new NioEventLoopGroup();

        EventLoopGroup workerGroup = new NioEventLoopGroup();

        // netty 服务    ==>> nio中是ServerSocketChannel
        ServerBootstrap server = new ServerBootstrap();
        // 链路式编程
        server.group(bossGroup, workerGroup)
                // 主线程处理类,以后看到这种写法,底层就是用的反射
                .channel(NioServerSocketChannel.class)
                // 主线程的配置 分配线程最大数量 128
                .option(ChannelOption.SO_BACKLOG, 128)

                // 子线程处理类,handler
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    // 客户端初始化处理
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        // 无锁化串行编程,
                        // Netty 对 HTTP 协议的封装,顺序有要求的
                        // HttpResponseEncoder 编码器
                        ch.pipeline().addLast(new HttpResponseEncoder());
                        // HttpRequestDecoder 解码器
                        ch.pipeline().addLast(new HttpRequestDecoder());
                        // 业务处理逻辑
                        ch.pipeline().addLast(new NTomcatHandler());
                    }
                })
                // 针对子线程的配置 保持长连接
                .childOption(ChannelOption.SO_KEEPALIVE, true);

        // 启动服务器
        try {
            ChannelFuture f = server.bind(this.PORT).sync();
            System.out.println("N Tomcat 已启动,监听端口是:" + this.PORT);

            f.channel().closeFuture().sync();

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }

    public static class NTomcatHandler extends ChannelInboundHandlerAdapter {
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            if (msg instanceof HttpRequest) {
                HttpRequest req = (HttpRequest) msg;

                // 转交给我们自己的request 实现
                NRequest request = new NRequest(ctx, req);
                NResponse response = new NResponse(ctx, req);

                // 实际的业务处理
                if (serviceMap.containsKey(request.getUrl())) {
                    serviceMap.get(request.getUrl()).service(request, response);
                } else {
                    response.write("404 - Not Found");
                }
            }
        }
    }

    public static void main(String[] args) {
        new NTomcat().start();
    }
}
6、测试服务类
public class FirstServlet extends NServlet {
    @Override
    protected void doGet(NRequest request, NResponse response) throws IOException {
        doPost(request, response);
    }

    @Override
    protected void doPost(NRequest request, NResponse response) throws IOException {
        response.write("test first servlet success");
    }
}

好了。至此。直接启动BTomcat 就可以访问了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值