《How Tomcat Works》读书笔记

Posted 默默的看雨下

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《How Tomcat Works》读书笔记相关的知识,希望对你有一定的参考价值。

《How Tomcat Works》读书笔记(二)

这是《How Tomcat Works》第一二章的读书笔记。第一张主要写了一个静态资源处理的web服务器,第二章加了对servlet的处理。

1. 概述


1.1 架构
图来自原书

  • HttpServer:表示Http服务器,与客户端通信,处理Http请求。
  • StaticResourceProcessor:对静态资源请求进行处理。
  • ServletProcessor:对Servlet资源请求进行处理。
  • Request:表示Http请求,实现了ServletRequest接口。
  • Response:表示Http响应,实现了ServletResponse接口。
  • RequestFacade/ResponseFacade:Request/Response的门面类。
  • PrimitiveServlet:表示一个Servlet类。

处理流程:

1.2 一些代码
HttpServer处理逻辑:

 1 // check if this is a request for a servlet or
 2 // a static resource
 3 // a request for a servlet begins with "/servlet/"
 4 if (request.getUri().startsWith("/servlet/")) {
 5     ServletProcessor1 processor = new ServletProcessor1();
 6     processor.process(request, response);
 7 }
 8 else {
 9     StaticResoureProcessor processor = new StaticResourceProcessor();
10     processor.process(request, response);
11 }

ServletProcessor中通过类加载器把.class文件动态的加载为Servlet对象:

 1 public void process(Request request, Response response) {
 2     String uri = request.getUri();
 3     String servletName = uri.substring(uri.lastIndexOf("/") + 1);
 4     URLClassLoader loader = null;
 5     try {
 6         // create a URLClassLoader
 7         URL[] urls = new URL[1];
 8         URLStreamHandler streamHandler = null;
 9         File classPath = new File(Constants.WEB_ROOT);
10         // the forming of repository is taken from the
11         // createClassLoader method in
12         // org.apache.catalina.startup.ClassLoaderFactory
13         String repository =(new URL("file", null, 
14               classPath.getCanonicalPath() + File.separator)).toString() ;
15         // the code for forming the URL is taken from
16         // the addRepository method in
17         // org.apache.catalina.loader.StandardClassLoader.
18         urls[0] = new URL(null, repository, streamHandler);
19         loader = new URLClassLoader(urls);
20     } catch (IOException e) {
21     }
22     Class myClass = null;
23     try {
24         myClass = loader.loadClass(servletName);
25     } catch (ClassNotFoundException e) {
26     }
27     Servlet servlet = null;
28     try {
29         servlet = (Servlet) myClass.newInstance();
30         // 使用门面设计模式
31         RequestFacade rquestFacade = new RequestFacade(request);
32         ResponseFacade responseFacade = new RespondeFacade(response);
33         // 交由Servlet进行处理
34         servlet.service(requestFacade, responseFacade);
35     }
36 }

1.3 设计模式
在这里使用了门面的设计模式,目的是安全性。在StaticResourceProcessor.processor()方法中会把Request、Response对象传给Servelt进行处理,在后面程序猿处理Servlet请求时就可以把ServletRequest向下转型为Request对象,然后使用不该在此使用的方法,如:parse()方法。所以,在这里使用了门面设计模式。

ResponseFacade类:

 1 public class ResponseFacade implements ServletResponse {
 2     private ServletResponse servletResponse = null;
 3 
 4     // 通过构造方法传入真正的Response,然后向上转型为ServletResponse
 5     public ResponseFacade(Response response) {
 6         this.servletResponse = response;
 7     }
 8     /**
 9      * 后面都是实现ServlerResponse接口要实现的方法,就不写完了。
10      */ 
11     @Override
12     public String getCharacterEncoding() {
13         return servletResponse.getCharacterEncoding();
14     }
15     @Override
16     public String getContentType() {
17         return servletResponse.getContentType();
18     }

2. 注意


2.1 HTTP协议
HTTP协议是应用层协议,它基于TCP/IP协议进行传输数据。在客户端与web服务器解析数据时,必须有相关的头部数据。所以web服务器给客户端发出响应,该响应必须加入头部数据。自己在写该代码时,就忘了加头部数据,出个bug,解决了一段时间才发现。
PrimitiveServlet的service()方法:

 1 public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
 2     String header = "HTTP/1.1 200 OK\\r\\n"
 3             + "Content-Type: text/html;charset=UTF-8\\r\\n"
 4             + "\\r\\n";
 5     String content = "<html>\\r\\n"
 6             + "<head>" 
 7             + "</head>"
 8             + "<body>" 
 9             + "Primitive Servlet" 
10             + "</body>" 
11             + "</html>";
12     PrintWriter writer = res.getWriter();
13     writer.write(header);
14     writer.write(content);
15     writer.flush();
16     writer.close();
17 }

我们平时使用Servlet开发时,直接继承HttpServlet,容器会自动的加上HTTP Response相关信息。

2.2 MIME类型
HTTP Respose header中有一个ContentType字段,它要求告诉客户端该Response正文的内容MIME类型,我们可以通过下面的API获得某一个文件的MIME类型。

String mimeType = URLConnection.getFileNameMap().getContentTypeFor(fileName)

2.2 .class文件
该系统使用到了URLClassLoader加载Class对象,ClassLoader把字节码(.class文件)加载为Class对象,所以需要编译好的.class文件。在Idea中Project中有个out目录,其中放的就是编译好的.class文件(只要你使用Idea运行过的.java文件)。

3. 代码


与原书逻辑基本一致,由于自己加入了多线程技术,所以新增了Servlet容器(使用了Map),并且该容器一开始就加载所有的Servlet。

HttpServer:

 1 package note1;
 2 
 3 import java.io.IOException;
 4 import java.net.ServerSocket;
 5 import java.net.Socket;
 6 import java.util.concurrent.ExecutorService;
 7 import java.util.concurrent.Executors;
 8 
 9 /**
10  * Created by kanyuxia on 2017/4/24.
11  * HttpServer是模拟HTTP服务器:接受HTTP请求,响应静态资源或者Servlet资源
12  */
13 public class HttpServer {
14     /**
15      * HttpServer端口号
16      */
17     public static final int PORT = 10086;
18     /**
19      * Http静态文件根目录
20      */
21     public static final String STATIC_RESOURCE_ROOT = "E:/java/HttpServer/staticresource";
22     /**
23      * Http中Servlet文件根目录
24      */
25     public static final String SERVLET_ROOT = "E:/java/HttpServer/servlet";
26     /**
27      * 线程池
28      */
29     private ExecutorService executorService;
30     /**
31      * 线程池大小
32      */
33     public static final int THREAD_POOL_SIZE = 50;
34 
35     HttpServer() {
36         executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
37     }
38 
39     /**
40      * 启动HttpServer服务器
41      * 使用了try-with-resource: since jdk1.7
42      */
43     public void start() {
44         // 创建ServerSocket
45         try (ServerSocket serverSocket = new ServerSocket(PORT)) {
46             while (true) {
47                 // 客户端连接
48                 Socket socket = serverSocket.accept();
49                 // 使用线程池处理该socket
50                 ServerHandle serverHandle = new ServerHandle(socket);
51                 executorService.execute(serverHandle);
52             }
53         } catch (IOException e) {
54             System.out.println(e);
55         }
56     }
57 }
View Code

Request:

  1 package note1;
  2 
  3 import javax.servlet.*;
  4 import java.io.*;
  5 import java.util.Enumeration;
  6 import java.util.Locale;
  7 import java.util.Map;
  8 
  9 /**
 10  * Created by kanyuxia on 2017/4/24
 11  * Request类:实现了ServletRequest接口,如果HTTP请求是Servlet,则服务器创建ServletRequest和ServletResponse对象,
 12  * 并传入Servlet的service()方法
 13  */
 14 public class Request implements ServletRequest {
 15     private final InputStream inputStream;
 16 
 17     private String url;
 18 
 19     public Request(InputStream inputStream) {
 20         this.inputStream = inputStream;
 21     }
 22 
 23     public String getUrl() {
 24         return url;
 25     }
 26 
 27     /**
 28      * 解析请求URL
 29      */
 30     public void parse() {
 31         BufferedInputStream in = new BufferedInputStream(inputStream);
 32         StringBuilder result = new StringBuilder(1024);
 33         byte[] buffer = new byte[1024];
 34         int readNum = 0;
 35         try {
 36             readNum = in.read(buffer);
 37         } catch (IOException e) {
 38             e.printStackTrace();
 39         }
 40         for (int i = 0; i < readNum; i++) {
 41             result.append((char) buffer[i]);
 42         }
 43         // 解析URL
 44         int start = result.toString().indexOf(" ") + 1;
 45         int end = result.toString().indexOf(" ", start);
 46         this.url = result.toString().substring(start, end);
 47     }
 48 
 49 
 50     @Override
 51     public Object getAttribute(String name) {
 52         return null;
 53     }
 54 
 55     @Override
 56     public Enumeration<String> getAttributeNames() {
 57         return null;
 58     }
 59 
 60     @Override
 61     public String getCharacterEncoding() {
 62         return null;
 63     }
 64 
 65     @Override
 66     public void setCharacterEncoding(String env) throws UnsupportedEncodingException {
 67 
 68     }
 69 
 70     @Override
 71     public int getContentLength() {
 72         return 0;
 73     }
 74 
 75     @Override
 76     public long getContentLengthLong() {
 77         return 0;
 78     }
 79 
 80     @Override
 81     public String getContentType() {
 82         return null;
 83     }
 84 
 85     @Override
 86     public ServletInputStream getInputStream() throws IOException {
 87         return null;
 88     }
 89 
 90     @Override
 91     public String getParameter(String name) {
 92         return null;
 93     }
 94 
 95     @Override
 96     public Enumeration<String> getParameterNames() {
 97         return null;
 98     }
 99 
100     @Override
101     public String[] getParameterValues(String name) {
102         return new String[0];
103     }
104 
105     @Override
106     public Map<String, String[]> getParameterMap() {
107         return null;
108     }
109 
110     @Override
111     public String getProtocol() {
112         return null;
113     }
114 
115     @Override
116     public String getScheme() {
117         return null;
118     }
119 
120     @Override
121     public String getServerName() {
122         return null;
123     }
124 
125     @Override
126     public int getServerPort() {
127         return 0;
128     }
129 
130     @Override
131     public BufferedReader getReader() throws IOException {
132         return null;
133     }
134 
135     @Override
136     public String getRemoteAddr() {
137         return null;
138     }
139 
140     @Override
141     public String getRemoteHost() {
142         return null;
143     }
144 
145     @Override
146     public void setAttribute(String name, Object o) {
147 
148     }
149 
150     @Override
151     public void removeAttribute(String name) {
152 
153     }
154 
155     @Override
156     public Locale getLocale() {
157         return null;
158     }
159 
160     @Override
161     public Enumeration<Locale> getLocales() {
162         return null;
163     }
164 
165     @Override
166     public boolean isSecure() {
167         return false;
168     }
169 
170     @Override
171     public RequestDispatcher getRequestDispatcher(String path) {
172         return null;
173     }
174 
175     @Override
176     public String getRealPath(String path) {
177         return null;
178     }
179 
180     @Override
181     public int getRemotePort() {
182         return 0;
183     }
184 
185     @Override
186     public String getLocalName() {
187         return null;
188     }
189 
190     @Override
191     public String getLocalAddr() {
192         return null;
193     }
194 
195     @Override
196     public int getLocalPort() {
197         return 0;
198     }
199 
200     @Override
201     public ServletContext getServletContext() {
202         return null;
203     }
204 
205     @Override
206     public AsyncContext startAsync() throws IllegalStateException {
207         return null;
208     }
209 
210     @Override
211     public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException {
212         return null;
213     }
214 
215     @Override
216     public boolean isAsyncStarted() {
217         return false;
218     }
219 
220     @Override
221     public boolean isAsyncSupported() {
222         return false;
223     }
224 
225     @Override
226     public AsyncContext getAsyncContext() {
227         return null;
228     }
229 
230     @Override
231     public DispatcherType getDispatcherType() {
232         return null;
233     }
234 }
View Code

Response:

  1 package note1;
  2 
  3 import javax.servlet.ServletOutputStream;
  4 import javax.servlet.ServletResponse;
  5 import java.io.*;
  6 import java.net.URLConnection;
  7 import java.util.Locale;
  8 
  9 /**
 10  * Created by kanyuxia on 2017/4/24.
 11  */
 12 public class Response implements ServletResponse {
 13     private final Request request;
 14 
 15     private final OutputStream outputStream;
 16 
 17     public Response(Request request, OutputStream outputStream) {
 18         this.request = request;
 19         this.outputStream = ou

以上是关于《How Tomcat Works》读书笔记的主要内容,如果未能解决你的问题,请参考以下文章

how tomcat works读书笔记 七 日志记录器

How tomcat works 读书笔记十七 启动tomcat 上

读书笔记《如何定义公司(How Google Works)》

的 How Tomcat Works

how_tomcat_works

How tomcat works(深入剖析tomcat) Logger