基于bio/netty分别实现简易tomcat

2022-07-26  本文已影响0人  奋斗的韭菜汪

源码请移步->
reference:
1、http与tcp的关系
2、netty

1、基于bio实现的一个简易tomcat(部分代码)

package com.grayson.netty.tomcat.bio;

import com.grayson.netty.tomcat.bio.http.GraysonRequest;
import com.grayson.netty.tomcat.bio.http.GraysonResponse;
import com.grayson.netty.tomcat.bio.servlet.GraysonAbstarctServlet;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 基于bio实现的一个简易tomcat
 */
public class GraysonTomcat {

    //tomcat默认端口
    private int port = 8080;
    private ServerSocket server;
    private Properties properties = new Properties();
    private Map<String, GraysonAbstarctServlet> servletMapping = new ConcurrentHashMap<String, GraysonAbstarctServlet>(8);

    //tomcat启动入口
    public static void main(String[] args) {
        new GraysonTomcat().start();
    }

    //1、初始化方法:加载web.properties参数,
    //2、解析配置:将servlet.*.className与servlet.*.url建立映射关系,并缓存,
    //3、解析浏览器请求的url映射到对应的servlet,通过反射创建servlet实例,访问对应的servlet的doget或dopost方法
    private void start() {
        //1、初始化方法:加载web.properties参数,
        init();

        try {
            //2、启动服务端socket等待用户请求(java中操作tcp的方法:ServerSocket/Socket)
            server = new ServerSocket(port);
            System.out.println("启动中...,监听端口是:" + this.port);
            //一直等待用户请求
            while (true){
                Socket client = server.accept();
                //socket一般会携带两个对象InputStream/OutputStream.request和response就是对InputStream/OutputStream的封装。
                //获得请求信息,解析http内容
                process(client);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void init(){
        //获取resources文件路径
        String WEB_INF = this.getClass().getResource("/").getPath();
        //获取文件输入流
        try {
            FileInputStream is = new FileInputStream(WEB_INF + "web.properties");
            //加载web.properties文件输入流
            properties.load(is);

            for(Object k : properties.keySet()){
                String key = k.toString();
                //是否key以url结尾
                if(key.endsWith(".url")){
                    //获取servlet.*
                    String servletName = key.replaceAll("\\.url$", "");
                    //key -> servlet.*.className, value -> className
                    String className = properties.getProperty(servletName + ".className");
                    String url = properties.getProperty(key);

                    //通过className反射获取servlet对象, java9之后推荐的写法.getDeclaredConstructor()
                    GraysonAbstarctServlet obj = (GraysonAbstarctServlet)Class.forName(className).getDeclaredConstructor().newInstance();
                    //将url和对应的Servlet对象缓存(建立映射关系)
                    servletMapping.put(url, obj);
                    //根据j2ee规范,web.xml中还有<load-on-startup>值需要配置
                    //(大于等于1,表示sevlet在web容器启动时初始化,否则,在请求时初始化,这里直接默认启动时初始化,忽略了<load-on-startup>)
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void process(Socket client) throws Exception{

            InputStream is = client.getInputStream();
            OutputStream os = client.getOutputStream();

            GraysonRequest request = new GraysonRequest(is);
            GraysonResponse response = new GraysonResponse(os);

            String url = request.getUrl();
            if(servletMapping.containsKey(url)){
                servletMapping.get(url).service(request, response);
            } else {
                response.write("404 - Not Found!!");
            }
            os.flush();
            os.close();
            is.close();
            client.close();
    }
}

二、基于netty实现的一个简易tomcat(部分代码)

netty最为基础的通信框架,封装了传输层(tcp&udp)相关的协议,在通信领域,适应范围广,几乎无所不能,性能高,详见netty官网。

package com.grayson.netty.tomcat.nio;

import com.grayson.netty.tomcat.nio.http.GraysonRequest;
import com.grayson.netty.tomcat.nio.http.GraysonResponse;
import com.grayson.netty.tomcat.nio.servlet.GraysonAbstarctServlet;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;

import java.io.FileInputStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 前提:先了解bio
 * 基于nio实现的一个简易tomcat
 * netty封装了http解析的方法
 */
public class GraysonTomcat {

    //tomcat默认端口
    private int port = 8080;
    private Properties properties = new Properties();
    private Map<String, GraysonAbstarctServlet> servletMapping = new HashMap<String, GraysonAbstarctServlet>(8);

    //tomcat启动入口
    public static void main(String[] args) {
        new GraysonTomcat().start();
    }

    //1、初始化方法:加载web.properties参数,
    //2、解析配置:将servlet.*.className与servlet.*.url建立映射关系,并缓存,
    //3、解析浏览器请求的url映射到对应的servlet,通过反射创建servlet实例,访问对应的servlet的doget或dopost方法
    private void start() {
        //1、初始化方法:加载web.properties参数,
        init();

        //Boss线程
        EventLoopGroup boosGroup = new NioEventLoopGroup();
        //Worker线程
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        //2、创建Netty服务端对象(ServerBootstrap 这里作用类似于bio中ServerSocket)
        ServerBootstrap server = new ServerBootstrap();

        try {
            //3、配置服务端参数
            server.group(boosGroup, workerGroup)
                    //配置主线程的处理逻辑,基于nio的socket
                    .channel(NioServerSocketChannel.class)
                    //子线程的回调处理
                    .childHandler(new ChannelInitializer() {
                        @Override
                        protected void initChannel(Channel client) throws Exception {
                            //处理回调的逻辑
                            //netty内部逻辑处理是链式编程,每一个处理逻辑都是一个hander

                            //处理请求的encoder,即处理响应结果的封装
                            client.pipeline().addLast(new HttpResponseEncoder());
                            //处理请求的decoder,即按http格式解码
                            client.pipeline().addLast(new HttpRequestDecoder());
                            //用户自己的业务逻辑
                            client.pipeline().addLast(new GraysonTomcatHandler());
                        }
                    })
                    //配置主线程分配的最大的线程数
                    .option(ChannelOption.SO_BACKLOG, 128)
                    //保持长连接
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            //启动服务
            ChannelFuture f = server.bind(this.port).sync();
            System.out.println("GraysonTomcat 已启动,监听端口:" + this.port);

            f.channel().closeFuture().sync();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            boosGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public void init(){
        //获取resources文件路径
        String WEB_INF = this.getClass().getResource("/").getPath();
        //获取文件输入流
        try {
            FileInputStream is = new FileInputStream(WEB_INF + "web-nio.properties");
            //加载web.properties文件输入流
            properties.load(is);

            for(Object k : properties.keySet()){
                String key = k.toString();
                //是否key以url结尾
                if(key.endsWith(".url")){
                    //获取servlet.*
                    String servletName = key.replaceAll("\\.url$", "");
                    //key -> servlet.*.className, value -> className
                    String className = properties.getProperty(servletName + ".className");
                    String url = properties.getProperty(key);

                    //通过className反射获取servlet对象, java9之后推荐的写法.getDeclaredConstructor()
                    GraysonAbstarctServlet obj = (GraysonAbstarctServlet)Class.forName(className).getDeclaredConstructor().newInstance();
                    //将url和对应的Servlet对象缓存(建立映射关系)
                    servletMapping.put(url, obj);
                    //根据j2ee规范,web.xml中还有<load-on-startup>值需要配置
                    //(大于等于1,表示sevlet在web容器启动时初始化,否则,在请求时初始化,这里直接默认启动时初始化,忽略了<load-on-startup>)
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void process(Socket client) throws Exception{


    }

    private class GraysonTomcatHandler extends ChannelInboundHandlerAdapter {
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            if(msg instanceof HttpRequest){
                HttpRequest req = (HttpRequest)msg;
                GraysonRequest request =  new GraysonRequest(ctx, req);
                GraysonResponse response =  new GraysonResponse(ctx, req);

                String url = request.getUrl();
                if(servletMapping.containsKey(url)){
                    servletMapping.get(url).service(request, response);
                }else {
                    response.write("404 - not found");
                }

            }
        }
    }
}

上一篇下一篇

猜你喜欢

热点阅读