Tomcat学习
1.3 Tomcat目录
- bin下,startup.bat, startup.sh, shutdown.bat, shutdown.sh
- conf logging.properties 日志相关配置
- conf server.xml 需要配置,部署应用端口指定
- conf tomcat-users.xml 用户,角色以及功能
- conf web.xml 全局的web.xml,局部覆盖全局
1.4 浏览器请求服务器流程
http 只定义了数据的组织各市,数据考TCP/IP 传输
1.5 Tomcat处理请求
- http服务器
- Tomcat是一个Servlet服务器
1.6 Servlet容器处理请求
Servlet 完成业务逻辑处理
1)Tomcat是一个http服务器
2)Tomcat是一个Servlet容器
加载servlet 就是反射
1.7 总体架构概述
1)和客户端浏览器交互,进行socket通信,将字节流和Request,Response 进行转换
2)Servlet容器处理业务逻辑
1.8 Connector,Coyote
1)封装了网络通信
2)使得容器组件与容器解耦
3)里面有各种IO模型(NIO) 和应用层协议 (HTTP)
1.10 容器组件Catalina
本质是servlet容器
一个Catalina实例:
- 一个Server实例
- 多个Service实例
- 每个Service有多个Connector 和一个Container
1.11 配置
Connector 可以监听端口
Executor 增加共享线程池
一个服务器可以当做多个虚拟主机用,配置多个站点
1.16 context标签
配置一个Web应用
2.1 手写实现mini版Tomcat。思路分析
1)作为一个服务器,提供服务。接收请求(Socket 通信)。Minicat 1.0版本
package server; import java.io.IOException; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class Bootstrap { //端口号 private int port = 8080; public int getPort() { return this.port; } public void setPort(int port) { this.port = port; } //初始化操作 public void start() throws IOException { // 浏览请求 http://localhost:8080, 返回一个固定的字符串到页面 "Hello Minicat" // 必须写好请求体,响应头header ServerSocket serverSocket = new ServerSocket(this.port); System.out.println("Mini cat start on : " + this.port); while (true) { Socket socket = serverSocket.accept(); // 接收到请求 OutputStream outputStream = socket.getOutputStream(); String responseText = HttpProtocolUtil.getHttpHeader200("Hello Minicat".getBytes().length) + "Hello Minicat"; outputStream.write(responseText.getBytes()); socket.close(); } } public static void main(String[] args) { Bootstrap bootstrap = new Bootstrap(); try { bootstrap.start(); } catch (IOException e) { e.printStackTrace(); } } }
package server; //提供响应头信息,这里提供200 和 400的情况 public class HttpProtocolUtil { public static String getHttpHeader200(long length) { return "HTTP/1.1 200 OK\n" + "Content-Type: text/html \n" + "Content-Length: " + length + "\n" + "\r\n"; } public static String getHttpHeader404() { String str404 = "<h1> 404 not found</h1>"; return "HTTP/1.1 400 NOT Found\n" + "Content-Type: text/html \n" + "Content-Length: " + str404.getBytes().length + "\n" + "\r\n" + str404; } }
2)请求信息封装成Request对象(Resonpse 对象)Minicat 2.0 版本
返回html静态资源文件
启动:
package server; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class Bootstrap { //端口号 private int port = 8080; public int getPort() { return this.port; } public void setPort(int port) { this.port = port; } //初始化操作 public void start() throws IOException { // 浏览请求 http://localhost:8080, 返回一个固定的字符串到页面 "Hello Minicat" // 必须写好请求体,响应头header ServerSocket serverSocket = new ServerSocket(this.port); System.out.println("Mini cat start on : " + this.port); while (true) { Socket socket = serverSocket.accept(); InputStream inputStream = socket.getInputStream(); //从输入流获取请求信息, 网络存在延迟 Request request = new Request(inputStream); Response response = new Response(socket.getOutputStream()); response.outputHtml(request.getUrl()); socket.close(); } } public static void main(String[] args) { Bootstrap bootstrap = new Bootstrap(); try { bootstrap.start(); } catch (IOException e) { e.printStackTrace(); } } }
package server; import java.io.IOException; import java.io.InputStream; //根据input stream public class Request { private String method; // 请求方式 比如GET/POST private String url; // 例如 /,/index.html. 代表资源路径 读出来通过OutputStream输出 private InputStream inputStream; // 输入流,其他属性,从输入流中解析出来 public String getMethod() { return method; } public String getUrl() { return url; } public InputStream getInputStream() { return inputStream; } public void setMethod(String method) { this.method = method; } public void setUrl(String url) { this.url = url; } public void setInputStream(InputStream inputStream) { this.inputStream = inputStream; } 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); String inputStr = new String(bytes); String [] requests = inputStr.split("\\n"); String firstLine = requests[0]; String [] firstSplit = firstLine.split(" "); this.method = firstSplit[0]; this.url = firstSplit[1]; // 可以看见请求头 System.out.println("请求信息 method=" + this.method); System.out.println("请求信息 url=" + this.url); } }
package server; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; //Output Stream public class Response { private OutputStream outPutStream; public Response() { } public Response(OutputStream outputStream) { this.outPutStream = outputStream; } //path url, 根据url 获取静态资源绝对路径 进一步根据绝对路径读取该路径资源文件,最终通过输出流输出 public void outputHtml(String path) throws IOException { // 获取静态资源文件的绝对路径 String absoluteResourcePath = StaticResourceUtil.getAbsolutePath(path); File file = new File(absoluteResourcePath); System.out.println("outputHtml absoluteResourcePath=" + absoluteResourcePath); if (file.exists() && file.isFile()) { System.out.println("here"); // 读取敬爱资源,输出静态资源, 输出静态资源 StaticResourceUtil.outputStaticResource(new FileInputStream(file), outPutStream); } else { // 输出404 output(HttpProtocolUtil.getHttpHeader404()); } } //使用输出流 public void output(String content) throws IOException { outPutStream.write(content.getBytes()); } }
package server; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public class StaticResourceUtil { public static String getAbsolutePath(String path) { String absolutePath = StaticResourceUtil.class.getResource("/").getPath(); System.out.println("absolute " + absolutePath); return absolutePath.replaceAll("\\\\", "/") + path; } //读取输入流, 通过输出流 public static void outputStaticResource(InputStream inputStream, OutputStream outputStream) throws IOException { int count = 0; while (count == 0) { count = inputStream.available(); } int resourceSize = count; //输出请求头和内容 outputStream.write(HttpProtocolUtil.getHttpHeader200(resourceSize).getBytes()); //读取内容输出 long written = 0; // 已经读取了内容长度 while (written < resourceSize) { int byteSize = 1024; if (written + byteSize > resourceSize) { byteSize = (int) (resourceSize - written); } byte[] bytes = new byte[byteSize]; inputStream.read(bytes); outputStream.write(bytes); outputStream.flush(); written += byteSize; } } }
3)客户端请求资源,资源分为静态资源(html)和动态资源 (Servlet)
package server; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.HashMap; import java.util.List; import java.util.Map; public class Bootstrap { //端口号 private int port = 8080; private Map<String, HttpServlet> servletMap = new HashMap<>(); public int getPort() { return this.port; } public void setPort(int port) { this.port = port; } // 加载解析web.xml, 初始化Servlet private void loadServlet() { InputStream resourcesAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml"); SAXReader saxReader = new SAXReader(); try { Document document = saxReader.read(resourcesAsStream); Element rootElement = document.getRootElement(); List<Element> selectNodes = rootElement.selectNodes("//servlet"); for (Element element : selectNodes) { Element servletnameElement = (Element) element.selectSingleNode("servlet-name"); String servletName = servletnameElement.getStringValue(); Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']"); String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue(); Element servletclassElement = (Element) element.selectSingleNode("servlet-class"); String servletClass = servletclassElement.getStringValue(); servletMap.put(urlPattern, (HttpServlet) Class.forName(servletClass).newInstance()); } } catch (Exception e) { e.printStackTrace(); } } //初始化操作 public void start() throws Exception { // 浏览请求 http://localhost:8080, 返回一个固定的字符串到页面 "Hello Minicat" // 必须写好请求体,响应头header loadServlet(); ServerSocket serverSocket = new ServerSocket(this.port); System.out.println("Mini cat start on : " + this.port); while (true) { Socket socket = serverSocket.accept(); InputStream inputStream = socket.getInputStream(); //从输入流获取请求信息, 网络存在延迟 Request request = new Request(inputStream); Response response = new Response(socket.getOutputStream()); if (servletMap.get(request.getUrl()) == null) { response.outputHtml(request.getUrl()); } else { // 动态资源servlet请求 HttpServlet httpServlet = servletMap.get(request.getUrl()); httpServlet.service(request, response); } socket.close(); } } public static void main(String[] args) throws Exception { Bootstrap bootstrap = new Bootstrap(); try { bootstrap.start(); } catch (IOException e) { e.printStackTrace(); } } }
package server; public class LagouServlet extends HttpServlet { @Override public void doGet(Request request, Response response) { String content = "<h1>LagouServeltGet</h1>"; try { response.output( HttpProtocolUtil.getHttpHeader200(content.getBytes().length) + content ); } catch (Exception e) { e.printStackTrace(); } } @Override public void doPost(Request request, Response response) { } @Override public void init() throws Exception { } @Override public void destroy() throws Exception { } }
package server; public abstract class HttpServlet implements Servlet { public abstract void doGet(Request request, Response response); public abstract void doPost(Request request, Response response); @Override public void service(Request request, Response response) throws Exception { if ("GET".equalsIgnoreCase(request.getMethod())) doGet(request, response); else doPost(request, response); } }
package server; interface Servlet { void init() throws Exception; void destroy() throws Exception; void service(Request request, Response response) throws Exception; }
4) 多线程改造
如果你请求了一个资源阻塞住,那么再有一个请求到你服务器,也同样会阻塞住。可以使用多线程进行改造. 来一个请求,给一个socket处理. 用线程池
package server; import java.io.InputStream; import java.net.Socket; import java.util.Map; public class RequestProcessor extends Thread { private Socket socket; private Map<String, HttpServlet> servletMap; public RequestProcessor(Socket socket, Map<String, HttpServlet> servletMap) { this.socket = socket; this.servletMap = servletMap; } @Override public void run() { try { InputStream inputStream = socket.getInputStream(); Request request = new Request(inputStream); Response response = new Response(socket.getOutputStream()); // 静态资源处理 if (servletMap.get(request.getUrl()) == null) { response.outputHtml(request.getUrl()); } else { // 动态资源servlet请求 HttpServlet httpServlet = servletMap.get(request.getUrl()); httpServlet.service(request, response); } socket.close(); } catch (Exception e) { e.printStackTrace(); } } }
package server; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; public class Bootstrap { //端口号 private int port = 8080; private Map<String, HttpServlet> servletMap = new HashMap<>(); public int getPort() { return this.port; } public void setPort(int port) { this.port = port; } // 加载解析web.xml, 初始化Servlet private void loadServlet() { InputStream resourcesAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml"); SAXReader saxReader = new SAXReader(); try { Document document = saxReader.read(resourcesAsStream); Element rootElement = document.getRootElement(); List<Element> selectNodes = rootElement.selectNodes("//servlet"); for (Element element : selectNodes) { Element servletnameElement = (Element) element.selectSingleNode("servlet-name"); String servletName = servletnameElement.getStringValue(); Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']"); String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue(); Element servletclassElement = (Element) element.selectSingleNode("servlet-class"); String servletClass = servletclassElement.getStringValue(); servletMap.put(urlPattern, (HttpServlet) Class.forName(servletClass).newInstance()); } } catch (Exception e) { e.printStackTrace(); } } //初始化操作 public void start() throws Exception { // 浏览请求 http://localhost:8080, 返回一个固定的字符串到页面 "Hello Minicat" // 必须写好请求体,响应头header loadServlet(); ServerSocket serverSocket = new ServerSocket(this.port); System.out.println("Mini cat start on : " + this.port); ExecutorService service = Executors.newFixedThreadPool(10); while (true) { Socket socket = serverSocket.accept(); RequestProcessor requestProcessor = new RequestProcessor(socket, servletMap); service.execute(requestProcessor); } } public static void main(String[] args) throws Exception { Bootstrap bootstrap = new Bootstrap(); try { bootstrap.start(); } catch (IOException e) { e.printStackTrace(); } } }
4)资源返回给客户端浏览器
2,8
谢谢!