Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache、Sun 和其他一些公司及个人共同开发而成。由于有了Sun 的参与和支持,最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现。现在我们通过netty来实现一些Tomcat的简单功能。
//1、启动端口(IP),默认8080 , ServerSocket localhost:IP //2、配置web.xml 自己写的Servlet需要继承HttpServlet //servlet-url //servlet-class //servlet-name //3、读取配置url-pattern 和 Servlet建立一个映射关系 //4、HTTP请求,发送的就是一些有规律的字符串,(HTTP协议) //5、从协议中拿到URL,通过反射实例化Servlet对象 //6、最后条用doGet或者doPost进行逻辑处理 //7、Request()、Response()
废话不多说,直接上代码:
package com.lh.study.rpc.io.tomcat.v1;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
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;
/**
* @description:
* @author: lianghao
* @create: 8/5/2019 5:52 PM
**/
public class TomcatServer {
public void start(int port){
EventLoopGroup workGroup = new NioEventLoopGroup();
EventLoopGroup bossGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup,workGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer() {
protected void initChannel(SocketChannel socketChannel) throws Exception {
// 无锁化串行编程
// HttpResponseEncoder 编码器
socketChannel.pipeline().addLast(new HttpResponseEncoder());
// HttpRequestDecoder 解码器
socketChannel.pipeline().addLast(new HttpRequestDecoder());
// 业务逻辑处理
socketChannel.pipeline().addLast(new LhTomcatHandler());
}
})
// 针对主线程的配置 分配线程最大数量 128
.option(ChannelOption.SO_BACKLOG, 128)
// 针对子线程的配置 保持长连接
.childOption(ChannelOption.SO_KEEPALIVE, true);
try {
ChannelFuture future = serverBootstrap.bind(port).sync();
System.out.println("Tomcat start port:"+port);
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
if(null != workGroup){
workGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
public static void main(String[] args) {
new TomcatServer().start(8088);
}
}
package com.lh.study.rpc.io.tomcat.v1;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
import java.lang.reflect.Method;
import java.util.List;
/**
* @description:
* @author: lianghao
* @create: 8/6/2019 9:17 AM
* @email: [email protected]
**/
public class LhTomcatHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if(msg instanceof HttpRequest){
HttpRequest httpRequest = (HttpRequest)msg;
LhResponse response = new LhResponse(ctx , httpRequest);
LhRequest request = new LhRequest(ctx , httpRequest);
String uri = request.getUri();
if(uri.indexOf("?") >=0){
uri = request.getUri().substring(0 , uri.indexOf("?"));
}
ServletMappingHandle servletMappingHandle = patternUrl(uri);
if(null == servletMappingHandle){
response.write("404 Not Found");
return;
}
Class> clazz = Class.forName(servletMappingHandle.getServletClassName());
Method method = null;
HttpMethod httpMethod = request.getMethod();
if(httpMethod.equals(HttpMethod.GET)){
method = clazz.getMethod("doGet" , new Class>[]{LhRequest.class , LhResponse.class});
}else{
method = clazz.getMethod("doPost" , new Class>[]{LhRequest.class , LhResponse.class});
}
method.invoke(clazz.newInstance() , new Object[]{request , response});
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
}
private ServletMappingHandle patternUrl(String uri){
List servletMappingHandles = ServletMappingHandle.servletMappingHandleList;
for(ServletMappingHandle servletMappingHandle : servletMappingHandles){
if(servletMappingHandle.getServletUrl().equals(uri)){
return servletMappingHandle;
}
}
return null;
}
}
package com.lh.study.rpc.io.tomcat.v1;
import java.util.ArrayList;
import java.util.List;
/**
* @description:
* @author: lianghao
* @create: 8/14/2019 5:13 PM
**/
public class ServletMappingHandle {
private String servletUrl;
private String servletClassName;
public static List servletMappingHandleList = new ArrayList<>();
public ServletMappingHandle(){
}
public ServletMappingHandle(String servletUrl, String servletClassName) {
this.servletUrl = servletUrl;
this.servletClassName = servletClassName;
}
public String getServletUrl() {
return servletUrl;
}
public void setServletUrl(String servletUrl) {
this.servletUrl = servletUrl;
}
public String getServletClassName() {
return servletClassName;
}
public void setServletClassName(String servletClassName) {
this.servletClassName = servletClassName;
}
static {
/**这里简化了读取web.xml的,直接手动封装了访问URl和servlet的对应关系*/
servletMappingHandleList.add(new ServletMappingHandle("/test" , "com.lh.study.rpc.io.tomcat.v1.LhServletImpl"));
}
}
package com.lh.study.rpc.io.tomcat.v1;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.QueryStringDecoder;
import java.util.List;
import java.util.Map;
/**
* @description:
* @author: lianghao
* @create: 8/6/2019 9:21 AM
**/
public class LhRequest {
private ChannelHandlerContext ctx;
private HttpRequest httpRequest;
public LhRequest(ChannelHandlerContext ctx, HttpRequest httpRequest) {
this.ctx = ctx;
this.httpRequest = httpRequest;
}
/**
* 获取请求的uri
* @return 返回请求路径
*/
public String getUri(){
return httpRequest.uri();
}
public HttpMethod getMethod(){
return httpRequest.method();
}
public Map> getParams(){
QueryStringDecoder queryStringDecoder = new QueryStringDecoder(getUri());
return queryStringDecoder.parameters();
}
public List getParamByName(String name){
return getParams().get(name);
}
public ChannelHandlerContext getCtx() {
return ctx;
}
public void setCtx(ChannelHandlerContext ctx) {
this.ctx = ctx;
}
public HttpRequest getHttpRequest() {
return httpRequest;
}
public void setHttpRequest(HttpRequest httpRequest) {
this.httpRequest = httpRequest;
}
}
package com.lh.study.rpc.io.tomcat.v1;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.*;
import static io.netty.handler.codec.http.HttpHeaderNames.*;
/**
* @description:
* @author: lianghao
* @create: 8/6/2019 9:34 AM
**/
public class LhResponse {
private ChannelHandlerContext ctx;
private HttpRequest httpRequest;
public LhResponse(ChannelHandlerContext ctx, HttpRequest httpRequest) {
this.ctx = ctx;
this.httpRequest = httpRequest;
}
public void write(String out){
try {
if(null == out || out.length()==0){
return;
}
FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(
// 设置http版本为1.1
HttpVersion.HTTP_1_1,
// 设置响应状态码
HttpResponseStatus.OK,
// 将输出值写出 编码为UTF-8
Unpooled.wrappedBuffer(out.getBytes("UTF-8")));
// 设置连接类型 为 JSON
fullHttpResponse.headers().set(CONTENT_TYPE, "text/json");
// 设置请求头长度
fullHttpResponse.headers().set(CONTENT_LANGUAGE, fullHttpResponse.content().readableBytes());
// 设置超时时间为5000ms
fullHttpResponse.headers().set(EXPIRES, 5000);
ctx.write(fullHttpResponse);
}catch (Exception e){
e.printStackTrace();
}finally {
ctx.flush();
ctx.close();
}
}
public ChannelHandlerContext getCtx() {
return ctx;
}
public void setCtx(ChannelHandlerContext ctx) {
this.ctx = ctx;
}
public HttpRequest getHttpRequest() {
return httpRequest;
}
public void setHttpRequest(HttpRequest httpRequest) {
this.httpRequest = httpRequest;
}
}
package com.lh.study.rpc.io.tomcat.v1;
/**
* @description:
* @author: lianghao
* @create: 8/6/2019 10:44 AM
**/
public abstract class LhServlet {
abstract void doGet(LhRequest request , LhResponse response);
abstract void doPost(LhRequest request , LhResponse response);
}
package com.lh.study.rpc.io.tomcat.v1;
import java.util.List;
import java.util.Map;
/**
* @description:
* @author: lianghao
* @create: 8/6/2019 10:45 AM
**/
public class LhServletImpl extends LhServlet {
@Override
public void doGet(LhRequest request, LhResponse response) {
try {
// 获取 name 参数 并返回
Map> params = request.getParams();
response.write(params.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void doPost(LhRequest request, LhResponse response) {
doGet(request , response);
}
}