基于netty手写Tomcat
Posted 程序员闪充宝
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于netty手写Tomcat相关的知识,希望对你有一定的参考价值。
原文:http://suo.im/5Ar7t8
netty 简介
Netty一个基于NIO的客户、服务器端的编程框架
1.环境准备
maven依赖
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.42.Final</version>
</dependency>
12345
RequestMethodEnum 请求方式
public enum RequestMethodEnum {
GET("GET"),
POST("POST");
public String code;
RequestMethodEnum(String code) {
this.code=code;
}}12345678
ParentServlet 父类servlet
public abstract class ParentServlet {
public void service(ParentRequest request, ParentResponse response) throws Exception {
//service 方法决定调用doGet、doPost;
if (RequestMethodEnum.GET.code.equalsIgnoreCase(request.getMethod())) {
doGet(request, response);
} else {
doPost(request, response);
}
}
protected abstract void doPost(ParentRequest request, ParentResponse response) throws Exception;
protected abstract void doGet(ParentRequest request, ParentResponse response) throws Exception;
}
12345678910111213141516
-
FirstServlet
public class FirstServlet extends ParentServlet {
@Override
protected void doPost(ParentRequest request, ParentResponse response) throws Exception {
response.write("This is the first");
} @Override
protected void doGet(ParentRequest request, ParentResponse response) throws Exception {
this.doPost(request,response);
}}1234567891011
-
SecondServlet
public class SecondServlet extends ParentServlet {
@Override
protected void doPost(ParentRequest request, ParentResponse response) throws Exception {
response.write("this is the second");
} @Override
protected void doGet(ParentRequest request, ParentResponse response) throws Exception {
this.doPost(request,response);
}}1234567891011
-
ParentRequest
public class ParentRequest {
private String method;
private String url;
public String getUrl() {
return url;
} public String getMethod() {
return method;
}}1234567891011121314
-
ParentResponse
public class ParentResponse {
private OutputStream out;
public ParentResponse (OutputStream out) {
this.out = out;
} public void write(String s) throws Exception{
//输出也要遵循HTTP
//状态码为200
StringBuilder sb = new StringBuilder();
sb.append("HTTP/1.1 200 OK
")
.append("Content-Type: text/html;
")
.append("
")
.append(s);
out.write(sb.toString().getBytes());
}
}
1234567891011121314151617
-
web.properties
servlet.first.url=/first
servlet.first.className=com.aiden.servlet.FirstServletservlet.second.url=/secondservlet.second.className=com.aiden.servlet.SecondServlet1234
2.基于传统I/O手写Tomcat
-
修改ParentRequest
public class ParentRequest {
private String method;
private String url;
public ParentRequest(InputStream in) {
try {
String content = "";
byte[] buff = new byte[1024];
int len = 0;
if ((len = in.read(buff)) > 0) {
content = new String(buff,0,len);
} String line = content.split("\n")[0];
String [] arr = line.split("\s");
this.method = arr[0];
System.out.println(method);
this.url = arr[1].split("\?")[0];
} catch (IOException e) {
e.printStackTrace(); } } public String getUrl() {
return url;
} public String getMethod() {
return method;
}}12345678910111213141516171819202122232425262728293031
-
编写tomcatStart类
public class TomcatStart {
private int port = 8080;
private ServerSocket server;
private Map<String, ParentServlet> servletMapping = new HashMap<String, ParentServlet>();
private Properties webProperties = new Properties();
private void init() {
try {
String WEB_INF = this.getClass().getResource("/").getPath();
FileInputStream fis = new FileInputStream(WEB_INF + "web.properties");
webProperties.load(fis); for (Object k : webProperties.keySet()) {
String key = k.toString();
if (key.endsWith(".url")) {
String servletName = key.replaceAll("\.url$", "");
String url = webProperties.getProperty(key);
String className = webProperties.getProperty(servletName + ".className");
//单实例 多线程
ParentServlet obj = (ParentServlet) Class.forName(className).newInstance();
servletMapping.put(url, obj);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void start() {
//1.加载配置类,初始化servletMapping
init();
try {
//2.绑定端口启动
server = new ServerSocket(this.port);
System.out.println("Tomcat 已启动,监听端口是:" + this.port);
//3.等待用户请求,用一个死循环
while (true) {
Socket client = server.accept();
//4.http 请求
process(client);
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void process(Socket client) throws IOException {
InputStream is = null;
OutputStream os = null;
try {
is = client.getInputStream();
os = client.getOutputStream();
//5.Request(inputstream) Response (outputstream)
ParentRequest request = new ParentRequest(is);
ParentResponse response = new ParentResponse(os);
//6.从协议内容中获取url 映射相应的servlet
String url = request.getUrl();
if (servletMapping.containsKey(url)) {
//7.调用实例化对象的service方法
servletMapping.get(url).service(request, response);
} else {
response.write("404 - Not Found");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (os != null) {
os.flush();
os.close();
}
if (is != null) {
is.close();
}
client.close();
}
}
public static void main(String[] args) {
//启动
new TomcatStart().start();
}
}
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
3.基于netty手写Tomcat
-
修改ParentRequest
public class ParentRequest {
private ChannelHandlerContext ctx;
private HttpRequest req;
public ParentRequest(ChannelHandlerContext ctx, HttpRequest req) {
this.ctx = ctx;
this.req = req;
} public String getUrl() {
return req.uri();
} public String getMethod() {
return req.method().name();
} public Map<String, List<String>> getParameters() {
QueryStringDecoder decoder = new QueryStringDecoder(req.uri());
return decoder.parameters();
} public String getParameter(String name) {
Map<String, List<String>> params = getParameters();
List<String> param = params.get(name);
if (null == param) {
return null;
} else {
return param.get(0);
} }}123456789101112131415161718192021222324252627282930313233
-
修改ParentResponse
public class ParentResponse {
//SocketChannel的封装 private ChannelHandlerContext ctx; private HttpRequest req; public ParentResponse(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;");
ctx.write(response);
} finally { ctx.flush();
ctx.close();
} }}12345678910111213141516171819202122232425262728293031323334
-
修改TomcatStart
public class TomcatStart {
private int port = 8080;
private Map<String, ParentServlet> servletMapping = new HashMap<String, ParentServlet>();
private Properties webProperties = new Properties();
private void init() {
try {
String WEB_INF = this.getClass().getResource("/").getPath();
FileInputStream fis = new FileInputStream(WEB_INF + "web.properties");
webProperties.load(fis); for (Object k : webProperties.keySet()) {
String key = k.toString();
if (key.endsWith(".url")) {
String servletName = key.replaceAll("\.url$", "");
String url = webProperties.getProperty(key);
String className = webProperties.getProperty(servletName + ".className");
//单实例 多线程
ParentServlet obj = (ParentServlet) Class.forName(className).newInstance();
servletMapping.put(url, obj);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void start() {
//1.加载配置类,初始化servletMapping
init();
// Netty NIO Reactor模型 Boss Worker
//Boss 线程
EventLoopGroup bossGroup = new NioEventLoopGroup();
//Work线程
EventLoopGroup workGroup = new NioEventLoopGroup();
ServerBootstrap server = null;
try {
//创建对象
server = new ServerBootstrap();
//配置参数
//链式编程
server.group(bossGroup, workGroup)
//主线程处理类,
.channel(NioserverSocketChannel.class)
//子线程处理类
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
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 TomcatHandler());
}
})
//主线程 线程最大数量128
.option(ChannelOption.SO_BACKLOG, 128)
//子线程配置 保存长连接
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture f = server.bind(port).sync();
System.out.println("Tomcat 已启动,监听端口是:" + this.port);
f.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
public class TomcatHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpRequest) {
System.out.println("hello request");
HttpRequest req = (HttpRequest) msg;
ParentRequest request = new ParentRequest(ctx, req);
ParentResponse response = new ParentResponse(ctx, req);
String url = request.getUrl();
if (servletMapping.containsKey(url)) {
//7.调用实例化对象的service方法
servletMapping.get(url).service(request, response);
} else {
response.write("404 - Not Found");
}
}
}
}
public static void main(String[] args) {
//启动
new TomcatStart().start();
}
}
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
4.访问
http://localhost:8080/first
好文章,我在看
好文章,我在看
以上是关于基于netty手写Tomcat的主要内容,如果未能解决你的问题,请参考以下文章
java 从零开始手写 RPC (02)-netty4 实现客户端和服务端
[年薪60W分水岭]基于Netty手写实现Apache Dubbo(带注册中心和注解)