tomcat学习笔记手写tomcat
Posted 拐柒
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了tomcat学习笔记手写tomcat相关的知识,希望对你有一定的参考价值。
tomcat学习笔记(二)手写tomcat
手写实现迷你版tomcat
名称:Minicat
Minicat:作为服务器软件提供服务,我们通过浏览器客户端发送HTTP请求,Minicat可以接受请求进行处理,处理之后的结果可以返回浏览器客户端。
1、提供服务,接受请求(socket通信)
2、请求信息封装成request/response对象
3、客户端请求资源,资源分为静态资源(html)和动态资源(servlet)
4、资源返回给客户端浏览器
Bootstrap
迷你tomcta启动类。
public class Bootstrap {
/**
* 定义socket监听端口号
*/
private int port=8080;
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
/**
*Minicat启动需要初始化展开的一些操作
*/
public void start() throws Exception {
//加载解析配置文件web.xml
loadServlet();
//定义线程池
//初始大小
int corePoolSize=10;
//最大大小
int maximumPoolSize=50;
//存活时长
long keepAliveTime=100L;
//时长单位
TimeUnit unit= TimeUnit.SECONDS;
//请求队列
BlockingQueue<Runnable> workQueue=new ArrayBlockingQueue<>(50);
//线程工厂
ThreadFactory threadFactory=Executors.defaultThreadFactory();
//拒绝策略
RejectedExecutionHandler handler=new ThreadPoolExecutor.AbortPolicy();
ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory,handler);
/**
* 1.0
* 完成minicat1.0版本(浏览器请求localhost:8080,返回固定的字符串到页面"hellow Minicat!")
*/
ServerSocket serverSocket=new ServerSocket(port);
System.out.println("Minicat start on port:"+port);
// while (true) {
// Socket socket = serverSocket.accept();
// //有了socket以后,接收到请求,获取输出流
// OutputStream outputStream = socket.getOutputStream();
// String data="Hello Minicat!";
// String responseText=HttpProtocolUtil.getHttpHeader200(data.getBytes().length)+data;
// outputStream.write(responseText.getBytes());
// socket.close();
// }
/**
* 2.0
* 完成minicat2.0版本
* 封装Request和Response对象,返回html静态资源文件
*/
// while (true) {
// Socket socket = serverSocket.accept();
// InputStream inputStream = socket.getInputStream();
// //封装request/response对象
// Request request=new Request(inputStream);
// Response response=new Response(socket.getOutputStream());
// response.outPutHtml(request.getUrl());
// socket.close();
// }
/**
* 3.0
* 请求动态资源servlet
*/
// while (true) {
// Socket socket = serverSocket.accept();
// InputStream inputStream = socket.getInputStream();
// //封装request/response对象
// Request request=new Request(inputStream);
// Response response=new Response(socket.getOutputStream());
// if(servletMap.get(request.getUrl())==null){
// response.outPutHtml(request.getUrl());
// }else {
// HttpServlet httpServlet = servletMap.get(request.getUrl());
// httpServlet.service(request,response);
// }
// socket.close();
// }
/**
* 不适用线程池
* 多线程改造
*/
// while (true) {
// Socket socket = serverSocket.accept();
// RequestProcessor requestProcessor=new RequestProcessor(socket,servletMap);
// requestProcessor.start();
// }
System.out.println("使用线程池进行多线程改造");
/**
* 使用线程池
* 多线程改造
*/
while (true) {
Socket socket = serverSocket.accept();
RequestProcessor requestProcessor=new RequestProcessor(socket,servletMap);
threadPoolExecutor.execute(requestProcessor);
}
}
private Map<String,HttpServlet> servletMap=new HashMap<>();
//加载解析web.xml初始化servlet
private void loadServlet() {
InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml");
SAXReader saxReader=new SAXReader();
try {
Document document = saxReader.read(resourceAsStream);
Element rootElement = document.getRootElement();
List<Element> list = rootElement.selectNodes("//servlet");
for (int i = 0; i < list.size(); i++) {
Element element = list.get(i);
Element servletNameElement = (Element) element.selectSingleNode("servlet-name");
//servlet-name>lagouServlet</servlet-name>
String servletName=servletNameElement.getStringValue();
Element servletClassElement = (Element) element.selectSingleNode("servlet-class");
//<servlet-class>server.LagouServlet</servlet-class>
String servletClass=servletClassElement.getStringValue();
//根据servletName的值找到url-pattern
Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
//<url-pattern>/lagou</url-pattern>
String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();
try {
servletMap.put(urlPattern, (HttpServlet) Class.forName(servletClass).newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
} catch (DocumentException e) {
e.printStackTrace();
}
}
/**
* Minicat启动入口
* @param args
*/
public static void main(String[] args) {
Bootstrap bootstrap=new Bootstrap();
try {
//启动Minicat
bootstrap.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
HttpProtocolUtil
http协议工具类,
/**
* http协议工具类,主要是提供响应头信息,这里我们提供200和404的情况
*/
public class HttpProtocolUtil {
/**
* 为响应码200提供请求头信息
* @return
*/
public static String getHttpHeader200(long contentLength){
return "HTTP/1.1 200 OK \\n" +
"Content-Type: text/html;charset=utf-8 \\n" +
"Content-Length: " +contentLength+" \\n" +
"\\r\\n";
}
/**
* 为响应码404提供请求头信息(此处也包含了数据内容)
* @return
*/
public static String getHttpHeader404(){
String str="<h1>404 not found</h1>";
return "HTTP/1.1 404 NOT Found \\n" +
"Content-Type: text/html;charset=utf-8 \\n" +
"Content-Length: " +str.getBytes().length+" \\n" +
"\\r\\n" + str;
}
}
Servlet
servlet接口,定义servlet规范
public interface Servlet {
void init() throws Exception;
void destroy() throws Exception;
void service(Request request,Response response) throws Exception;
}
HttpServlet
处理请求的httpservlet
public abstract class HttpServlet implements Servlet{
public abstract void doGet(Response response,Request request);
public abstract void doPost(Request request,Response response);
@Override
public void service(Request request, Response response) throws Exception {
if("GET".equals(request.getMethod())){
doGet(response,request);
}else {
doPost(request,response);
}
}
}
LagouServlet
自定义servlet
public class LagouServlet extends HttpServlet{
@Override
public void doGet(Response response, Request request) {
try {
Thread.sleep(200000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String string="<h1>LagouServlet get</h1>";
try {
response.outPut(HttpProtocolUtil.getHttpHeader200(string.getBytes().length)+string);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void doPost(Request request, Response response) {
String string="<h1>LagouServlet post</h1>";
try {
response.outPut(HttpProtocolUtil.getHttpHeader200(string.getBytes().length)+string);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void init() throws Exception {
}
@Override
public void destroy() throws Exception {
}
}
Request
请求信息封装类
/**
* 把请求信息封装为Request对象(根据inputStream封装)
*/
public class Request {
//请求方式get/post
private String method;
//url
private String url;
//输入流,其他属性从输入流解析出来
private InputStream inputStream;
public Request() {
}
//构造器,输入流传入,解析出url和method
public Request(InputStream inputStream) throws IOException {
this.inputStream = inputStream;
int count=0;
while (count==0){
count=inputStream.available();
}
byte[] bytes=new byte[count];
inputStream.read(bytes);
System.out.println("请求信息:"+new String(bytes));
String string=new String(bytes);
//获取第一行请求头信息 GET / HTTP/1.1
String firstLine=string.split("\\\\n")[0];
String[] s = firstLine.split(" ");
this.method=s[0];
this.url=s[1];
System.out.println("method:"+this.method);
System.out.println("url:"+this.url);
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public InputStream getInputStream() {
return inputStream;
}
public void setInputStream(InputStream inputStream) {
this.inputStream = inputStream;
}
}
RequestProcessor
请求处理器
public class RequestProcessor extends Thread{
private Socket socket;
private Map<String,HttpServlet> servletMap;
public RequestProcessor() {
}
public RequestProcessor(Socket socket, Map<String, HttpServlet> servletMap) {
this.socket = socket;
this.servletMap = servletMap;
}
@Override
public void run() {
try {
InputStream inputStream = socket.getInputStream();
//封装request/response对象
Request request=new Request(inputStream);
Response response=new Response(socket.getOutputStream());
if(servletMap.get(request.getUrl())==null){
response.outPutHtml(request.getUrl());
}else {
HttpServlet httpServlet = servletMap.get(request.getUrl());
httpServlet.service(request,response);
}
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Response
返回信息封装类
/**
* 把相应信息封装为Response对象(根据outputStream封装)
* 该对象需要提供核心对象,输出静态资源(html)
*/
public class Response {
private OutputStream outputStream;
public Response() {
}
public Response(OutputStream outputStream) {
this.outputStream = outputStream;
}
//使用输出流输出指定字符串
public void outPut(String content) throws IOException {
this.outputStream.write(content.getBytes());
}
/**
*
* @param path url ,根据url获取到静态资源的绝对路径,根据绝对路径,进一步读取该静态文件,最终通过输出流输出
*/
public void outPutHtml(String path) throws IOException {
//获取静态资源的绝对路径
String absoluteResourcePath=StaticResourceUtil.getAbsolutePath(path);
//输出静态资源
File file=new File(absoluteResourcePath);
if(file.exists()&& file.isFile()){
//读取静态资源文件,输出静态资源
StaticResourceUtil.outputStaticResource(new FileInputStream(file),outputStream);
}else {
//输出404
outPut(HttpProtocolUtil.getHttpHeader404());
}
}
}
StaticResourceUtil
静态资源工具类
public class StaticResourceUtil {
/**
* 获取静态资源的绝对路径
* @param path
* @return
*/
public static String getAbsolutePath(String path){
String absolutePath=StaticResourceUtil.class.getResource("/").getPath();
return absolutePath.replaceAll("\\\\\\\\","/")+path;
}
/**
* 读取静态资源文件输入流,通过输出流输出
* @param inputStream
* @param outputStream
*/
public static void outputStaticResource(InputStream inputStream, OutputStream outputStream) throws IOException {
int count=0;
while (count==0){
count = inputStream.available();
}
int resourceSize=count;
//http请求头输出,然后输出具体内容
outputStream.write(HttpProtocolUtil.getHttpHeader200(resourceSize).getBytes());
//读取输出内容
long written=0;//已经读取的内容长度
int byteSize=1024;//计划每次缓冲长度
byte[] bytes=new byte[byteSize];
while (written<resourceSize){
if(written+byteSize>resourceSize){//剩余未读取大小不足1024,那就按照真实长度处理
byteSize= (int) (resourceSize-written);
bytes=new byte[byteSize];
}
inputStream.read(bytes);
outputStream.write(bytes);
outputStream.flush();
written+=byteSize;
}
}
}
以上是关于tomcat学习笔记手写tomcat的主要内容,如果未能解决你的问题,请参考以下文章
阿里P8架构师手写笔记:Spring源码+JVM+MySQL+Kafka+Tomcat
阿里P8架构师手写笔记:Spring源码+JVM+MySQL+Kafka+Tomcat