Tomcat使用Netty实现

  • 定义配置文件,指定 url,与对应的执行类
servlet.one.url=/firstServlet.do
servlet.one.className=com.rt.netty.tomcat.servlet.FirstServlet

servlet.two.url=/secondServlet.do
servlet.two.className=com.rt.netty.tomcat.servlet.SecondServlet
  • 读取配置文件,初始化 Servlet 容器
private Map<String, RTServlet> servletMapping = new HashMap<String, RTServlet>();
private Properties webxml = new Properties();
private void init(){
    
    
	//加载web.xml文件,同时初始化 ServletMapping对象
	try{
    
    
		String WEB_INF = this.getClass().getResource("/").getPath();
		FileInputStream fis = new FileInputStream(WEB_INF + "web.properties");
		webxml.load(fis);
		for (Object k : webxml.keySet()) {
    
    
			String key = k.toString();
			if(key.endsWith(".url")){
    
    
				String servletName = key.replaceAll("\\.url$", "");
				String url = webxml.getProperty(key);
				String className = webxml.getProperty(servletName + ".className");
				RTServlet obj = (RTServlet)Class.forName(className).newInstance();
				servletMapping.put(url, obj);
			}
		}
	}catch(Exception e){
    
    
		e.printStackTrace();
	}
}	
  • Netty 启动对本地IP指定端口的监听
//Netty封装了NIO,Reactor模型,Boss,worker
// Boss线程
EventLoopGroup bossGroup = new NioEventLoopGroup();
// Worker线程
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
    
    
	// Netty服务
	//ServetBootstrap   ServerSocketChannel
	ServerBootstrap server = new ServerBootstrap();
	// 链路式编程
	server.group(bossGroup, workerGroup)
			// 主线程处理类,看到这样的写法,底层就是用反射
			.channel(NioServerSocketChannel.class)
			// 子线程处理类 , Handler
			.childHandler(new ChannelInitializer<SocketChannel>() {
    
    
				// 客户端初始化处理
				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 RTTomcatHandler());
				}
			})
			// 针对主线程的配置 分配线程最大数量 128
			.option(ChannelOption.SO_BACKLOG, 128)
			// 针对子线程的配置 保持长连接
			.childOption(ChannelOption.SO_KEEPALIVE, true);
	// 启动服务器
	ChannelFuture f = server.bind(port).sync();
	System.out.println("Tomcat 已启动,监听的端口是:" + port);
	f.channel().closeFuture().sync();
}catch (Exception e){
    
    
	e.printStackTrace();
}finally {
    
    
	// 关闭线程池
	bossGroup.shutdownGracefully();
	workerGroup.shutdownGracefully();
}
  • 处理对指定端口的网络连接
public class RTTomcatHandler extends ChannelInboundHandlerAdapter {
    
    
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    
    
		if (msg instanceof HttpRequest){
    
    
			HttpRequest req = (HttpRequest) msg;
			// 转交给我们自己的request实现
			RTRequest request = new RTRequest(ctx,req);
			// 转交给我们自己的response实现
			RTResponse response = new RTResponse(ctx,req);
			// 实际业务处理
			String url = request.getUrl();
			if(servletMapping.containsKey(url)){
    
    
				servletMapping.get(url).service(request, response);
			}else{
    
    
				response.write("404 - Not Found");
			}
		}
	}
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    
    
	}
}
  • 封装请求与响应对象
private ChannelHandlerContext ctx;
private HttpRequest req;
public RTRequest(ChannelHandlerContext ctx, HttpRequest r
	this.ctx = ctx;
	this.req = req;
}
public String getUrl() {
    
    
	return req.uri();
}
public String getMethod() {
    
    
	return req.method().name();
}
public class RTResponse {
    
    

	//SocketChannel的封装
	private ChannelHandlerContext ctx;

	private HttpRequest req;

	public RTResponse(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;");
			// 当前是否支持长连接
//            if (HttpUtil.isKeepAlive(r)) {
    
    
//                // 设置连接内容为长连接
//                response.headers().set(CONNECTION, HttpHeaderValues.KEEP_ALIVE);
//            }
			ctx.write(response);
		} finally {
    
    
			ctx.flush();
			ctx.close();
		}
	}
}
  • RTServlet 请求分发
public abstract class RTServlet {
    
    
	
	public void service(RTRequest request, RTResponse response) throws Exception{
    
    
		
		//由service方法来决定,是调用doGet或者调用doPost
		if("GET".equalsIgnoreCase(request.getMethod())){
    
    
			doGet(request, response);
		}else{
    
    
			doPost(request, response);
		}

	}
	
	public abstract void doGet(RTRequest request, RTResponse response) throws Exception;
	
	public abstract void doPost(RTRequest request, RTResponse response) throws Exception;
}
  • servlet 执行类

	public void doGet(RTRequest request, RTResponse response) throws Exception {
    
    
		this.doPost(request, response);
	}

	public void doPost(RTRequest request, RTResponse response) throws Exception {
    
    
		response.write("This is rt First Serlvet");
	}

}
public class SecondServlet extends RTServlet {
    
    

	public void doGet(RTRequest request, RTResponse response) throws Exception {
    
    
		this.doPost(request, response);
	}

	public void doPost(RTRequest request, RTResponse response) throws Exception {
    
    
		response.write("This is rt Second Serlvet");
	}

}

猜你喜欢

转载自blog.csdn.net/weixin_44971379/article/details/121369728