设为首页 收藏本站
查看: 394|回复: 0

[经验分享] 《how tomcat works》读书笔记

[复制链接]

尚未签到

发表于 2017-1-26 13:38:07 | 显示全部楼层 |阅读模式
  一 简单web服务
  客户端socket和服务端socket。
  客户端socket:
  Socket socket = new Socket("127.0.0.1", "8080");
  socket有输入和输出流 socket.getInputstream(), socket.getOutputStream()
  服务端socket:
  监听服务端口,收到客户端socket请求,会创建一个socket。
  serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
  socket = serverSocket.accept(); 这个socket和客户端的对应,它的输出端对应客户端输入端,输入端对于客户端的输出端。
  操作系统管理一个先进先出的队列(一般大小为50)来存放客户端请求,超过限制就拒绝连接。每个request对应一个socket,
  所以加载一个页面可能需要多次连接socket请求,比如图片等会有很多请求,可以使用http1.1协议的长连接新特性来避免多次连接请求。
  在同一个线程里面serverSocket.accept()只有等待前一个socket处理完成之后,才能出来队列里面的下一个socket请求。
  可以使用多线程提交效率,参见第四章HttpProcessor内容。
  Http 1.1 新功能(书里面第四章,笔记里放在第一章)
  Persistent Connections 长连接
  不开启长连接时,请求一个page,里面的每个资源(图片、applets等)都需要建一个request,很耗资源,
  开启长连接后,服务器可以不关闭连接,而等待一个page的所有内容加载完后才关闭,这样一个page和它引用的
  相关资源可以共用一个连接。
  request header 添加 connection: keep-alive即可。主流服务器软件都支持,可以设置长连接超时时间,apache
  默认开启长连接功能,超时时间默认是5秒
  Chunked Encoding
  表明response消息体的长度,如:
  9\r\n 
  p a tree. 
  0\r\n
  100 (Continue) 状态
  客户端先发送一个request 头包含:Expect:100-Continue,表明接下来需要发送一个内容很长的request,等待服务端
  回应,是否接受,接受就发送,否则就不发了。
  如果response返回100表示接受,HTTP/1.1 100 Continue。
  
  二 简单servlet容器
  servlet容器用于处理用户的请求,可以是静态的或者动态内容。
  servlet容器需要实现javax.servlet.Servlet接口,3个方法:
  // 类实例化后调用,只做一次
  public void init(ServletConfig config) throws ServletException 
  // 服务方法,每次处理都调用
  public void service(ServletRequest request, ServletResponse response) throws ServletException, java.io.IOException 
  // 容器关闭时调用,清理一些内容
  public void destroy() 
  request一般分为获取静态文件或者动态内容,如果是静态文件就从web——root目录下去读取文件写入response返回。
  动态内容则利用URLClassLoader加载相关servlet进行处理。具体代码参见ex02.pyrmont.ServletProcessor1;
  
  三 连接器connector
  Catalina主要的两个模块:container和connector。连接器主要是用于提供HttpServletRequest和HttpServletResponse实例。
  connector解析http请求获取header,cookies和参数。最后拼装response返回。
  StringManager 类: 用于错误信息。需要支持国际化。本身是单例模式。
  每个package下面的都会存放这个包下类需要的错误信息,所以这么实现:
  private static Hashtable managers = new Hashtable();
  public synchronized static StringManager getManager(String packageName) {
  StringManager mgr = (StringManager) managers.get(packageName);
  if (mgr == null) {
  mgr = new StringManager(packageName);
  managers.put(packageName, mgr);
  }
  return mgr;
  }
  构造函数是private保证单例,使用ResourceBundle支持国际化。
  private ResourceBundle bundle;
  private StringManager(String packageName) {
  String bundleName = packageName + ".LocalStrings";
  bundle = ResourceBundle.getBundle(bundleName);
  }
  错误消息体的拼装使用的是java.text.MessageFormat。
  HttpConnector:实现Runnable接口。在run方法里面:
  serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
  socket = serverSocket.accept();   // 等待一个请求
  // Hand this socket off to an HttpProcessor 
  HttpProcessor processor = new HttpProcessor(this);  // 生成一个HttpProcessor处理请求
  processor.process(socket);                          
  connetor支持多线程,调用它的start方法就好:
  public void start() {
  Thread thread = new Thread(this);
  thread.start();
  }
  HttpProcessor: 对于每个请求,processor做下面的工作:
  创建一个HttpRequest对象.
  创建一个HttpResponse对象.
  解析http request的第一行(请求方法、资源),消息头header,然后填充HttpRequest对象,消息体在servlet service时
  才解析,因为只在使用时解析,提高效率。
  分静态资源StaticResourceProcessor和动态内容ServletProcessor分别处理。
  public void process(Socket socket)
  // SocketInputStream是对inputStream的封装。主要有两个方法readRequestLine和readHeader,这部分代码挺复杂,可以细细研究
  SocketInputStream input = new SocketInputStream(socket.getInputStream(), 2048); 
  parseRequest(input, output); // 解析命令
  parseHeaders(input);         // 解析头信息
  // 最后分静态和动态内容进行处理
  if (request.getRequestURI().startsWith("/servlet/")) {
  ServletProcessor processor = new ServletProcessor();
  processor.process(request, response);
  } else {
  StaticResourceProcessor processor = new StaticResourceProcessor();
  processor.process(request, response);
  }
  HttpRequest: 继承javax.servlet.http.HttpServletRequest
  主要用于存放请求消息的header,cookies,request uri,消息体等。存放的结构各不相同:
  protected HashMap headers = new HashMap(); 
  protected ArrayList cookies = new ArrayList(); 
  protected ParameterMap parameters = null;
  解析和填充HttpRequest对象是个复杂的工作,可以分为下面几个步骤:
  Reading the socket's input stream
  Parsing the request line
  Parsing headers
  Parsing cookies
  Obtaining parameters
  四 Tomcat默认Connector
  Connector被设计成一个模块,目前有多个实现(Coyote, mod_jk, mod_jk2, and mod_webapp),必须满足:
  1. It must implement the org.apache.catalina.Connector interface.
  2. It must create request objects whose class implements the org.apache.catalina.Request interface.
  3. It must create response objects whose class implements the org.apache.catalina.Response interface.
  HttpConnector: 一个Tomcat connector里面的主要方法为:getContainer,createRequest,createResponse
  HttpConnector同时实现了Lifecycle这个接口,主要实现initialize 和 start方法。
  initialize里面主要创建一个server socket,采用工厂模式(ServerSocketFactory->DefaultServerSocketFactory)。
  start里面则是启动一个线程等待请求并填充HttpProcessor实例
  // Start our background thread
  threadStart(); // 里面主要起个Daemon线程,里面socket = serverSocket.accept(); processor.assign(socket);
  // Create the specified minimum number of processors
  while (curProcessors < minProcessors) {
  if ((maxProcessors > 0) && (curProcessors >= maxProcessors))
  break;
  HttpProcessor processor = newProcessor();
  recycle(processor);
  }
  
  HttpConnector用一个栈维护 HttpProcessor 实例:private Stack processors = new Stack(); 这样重复利用,提高效率。
  最开始初始化最少(5个)processors,不够就增加,直到最大值(20个).如果请求超过maxProcessors(20),就会被忽略,把maxProcessors设置为负值
  则不限制。
  HttpProcessor 相对于前一章节的HttpProcessor,它实现了Runnable接口,支持多线程。多线程没写过,这里是重点!!!
  public void run() { 
  // Process requests until we receive a shutdown signal 
  while (!stopped) { 
  //先获取一个socket
  Socket socket = await(); 
  if (socket == null) continue; 
  // process处理
  try { 
  process(socket);  
  } catch (Throwable t) { 
  log("process.invoke", t); 
  } 
  // 把processor归还pool
  connector.recycle(this); 
  } 
  // Tell threadStop() we have shut ourselves down successfully
  synchronized (threadSync) { 
  threadSync.notifyAll(); 
  } 
  }
  这里await()方法控制processor线程只在获取到socket后才能执行后面的操作。
  而processor线程在HttpConnector的newProcessor方法里面生成时,线程就已经开始执行了
  private HttpProcessor newProcessor() {
  HttpProcessor processor = new HttpProcessor(this, curProcessors++);
  if (processor instanceof Lifecycle) {
  try {
  ((Lifecycle) processor).start();
  } catch (LifecycleException e) {
  log("newProcessor", e);
  return (null);
  }
  }
  created.addElement(processor);
  return (processor);
  }
  HttpConnector线程里面调用assign后,await才能得到socket。
  assign和await是在不同的线程里面执行的,assign是由HttpConnector线程run方法调用的,而await由HttpProcessor线程run方法调用。
  它们之间的交流是靠HttpProcessor线程变量available和Object wait和notifyAll方法进行的。
  synchronized void assign(Socket socket) { 
  // Wait for the processor to get the previous socket 
  while (available) { 
  try { 
  wait(); 
  } catch (InterruptedException e) {
  } 
  } 
  // Store the newly available Socket and notify our thread 
  this.socket = socket; 
  available = true; 
  notifyAll(); 
  ... 
  }
  private synchronized Socket await() { 
  // Wait for the Connector to provide a new Socket
  while (!available) { 
  try { 
  wait(); 
  } catch (InterruptedException e) { 
  } 
  } 
  // Notify the Connector that we have received this Socket Socket 
  socket = this.socket; 
  available = false; 
  notifyAll(); 
  if ((debug >= 1) && (socket != null)) 
  log(" The incoming request has been awaited"); 
  return (socket); 
  }
  线程对比
  The processor thread (the await method)            The connector thread (the assign method)
  while (!available) {    while (available) {
  wait(); wait();
  }                                                  }
  Socket socket = this.socket;                       this.socket = socket;
  available = false;                                 available = true;
  notifyAll();                                       notifyAll();
  return socket;                                     ...
  processor线程启动后available初始化为false,所以processor线程就卡在wait(); 直到其他线程调用notify或者notifyAll.
  (线程内容补充,这里面HttpProcessor实现了Runnable,生成线程时先要实例化一个HttpProcessor对象,然后用new Thread(processor)来
  创建线程,根据前面newProcessor代码来看,本章的程序里面一个HttpProcessor实例就对应一个HttpProcessor线程而不是多个。)
  
  process方法:真正处理请求的方法,主要:
  parse the connection
  parse the request
  parse headers
  程序里面一个特点是ok的使用,用ok来控制最终的response(处理时发生异常错误仍然需要返回,只是返回错误信息)。
  解析请求中为提高效率,在Chapter3里面的header字段比较是直接的String比较,本章节都改为了字符数组比较。
  if (name.equals("cookie"))              if (header.equals(DefaultHeaders.COOKIE_NAME))
  // 字符串直接比较                       //  DefaultHeaders.COOKIE_NAME是定义好的数组,equals被重写了比较字符数组
  这个我试了下在jdk1.6下面,毫秒级都差不多。可能jdk本身做了优化,书是tomcat4的,估计jdk的版本也不高。
  五 容器Container
  容器的作用就是处理请求,填充response返回给客户端。容器需要实现org.apache.catalina.Container接口,Tomcat一共有4种容器:
  Engine, Host, Context, and Wrapper。本章只讨论Context和Wrapper。
  Engine. Represents the entire Catalina servlet engine.
  Host. Represents a virtual host with a number of contexts.
  Context. Represents a web application. A context contains one or more wrappers.
  Wrapper. Represents an individual servlet.
  Wrapper是最底层的实现,不能再包含任务子容器了,其他的容器都可以有0或者多个子容器:
  public void addChild(Container child);
  public void removeChild(Container child);
  public Container findChild(String name); 
  public Container[] findChildren();
  容器里面还可以包含其他的支持组件如:Loader, Logger, Manager, Realm, and Resources。
  Pipelining Tasks
  pipeline(管道)和valve(阀门),它们类似于servlet的filter chain和filter概念。valve处理一个request和response,完成后
  将request、response传给pipeline上的下一个valve,pipeline必须有个basicValve。
  一个container有一个pipeline,当connector调用container的invoke方法处理request时,容器交给管道,然后pipeline上的阀门一个个处理。
  实现方式上,不是采用:
  for (int n=0; n<valves.length; n++) { valve[n].invoke( ... ); } // then, invoke the basic valve basicValve.invoke( ... );
  这种方法。方式如下:
  ContainerBase 里面的invoke如下:
  public void invoke(Request request, Response response) throws IOException, ServletException { 
  pipeline.invoke(request, response); 
  }
  而pipeline里面创建一个实现ValveContext接口的内部类(内部类可以访问pipeline的所有内容),内部类主要实现
  public void invokeNext(Request request, Response response)这个方法。
  pipeline内部ValveContext实现:
  protected class StandardPipelineValveContext implements ValveContext { 
  protected int stage = 0; 
  public String getInfo() { return info; } 
  public void invokeNext(Request request, Response response) throws IOException, ServletException { 
  int subscript = stage; stage = stage + 1;
  // Invoke the requested Valve for the current request thread 
  if (subscript < valves.length) { 
  valves[subscript].invoke(request, response, this); 
  } else if ((subscript == valves.length) && (basic != null)) { 
  basic.invoke(request, response, this);  // 最后执行默认的valve
  } else { 
  throw new ServletException (sm.getString("standardPipeline.noValve")); 
  } 
  } 
  }
  valveContext对象作为valve的invoke方法的参数,这样每个valve也都持有ValveContext对象,先由pipeline调用ValveContext的invokeNext调用第一个valve,
  valve处理后再调用ValveContext对象的invokeNext传递给下一个valve.
  以ClientIPLoggerValve的invoke方法为例:
  public void invoke(Request request, Response response,
  ValveContext valveContext) throws IOException, ServletException {
  // Pass this request on to the next valve in our pipeline
  valveContext.invokeNext(request, response);
  System.out.println("Client IP Logger Valve");
  ServletRequest sreq = request.getRequest();
  System.out.println(sreq.getRemoteAddr());
  System.out.println("------------------------------------");
  }
  先调用valveContext的invokeNext,然后才处理下日志,类似递归的过程(和servlet的filter机制类似)。这种设计的优势在于在调用valveContext.invokeNext(request, response);
  前面和后面都可以添加代码,起到“包裹”的作用,简单的循环依次调用是做不到这点的.
  The Contained Interface
  valve可以实现这个接口,主要是为了和container关联。看它的定义就知道了:
  public interface Contained { 
  public Container getContainer(); 
  public void setContainer(Container container);
  The Wrapper Interface
  wapper是最底层的container,代表一个servlet,接口继承了Container接口另外新增了一些方法。它主要是处理servlet class的生命周期,
  如call ini、service、destroy方法等。
  wapper里面两个重要的方法是allocate and load。allocate用于分配一个初始化好的servlet实例,load加载和初始化servlet实例。
  The Context Interface
  代表一个web application的Container,一般都有一个或者多个Wrapper作为子container,重要的方法有addWrapper, createWrapper等.
  The Wrapper Application
  这个应用包含的类:
  SimpleWrapper、SimplePipeline、SimpeLoader、SimpleWrapperValve(basic value)、ClientIPLoggerValve、HeaderLoggerValve
  相比较第四章,加载servlet在httpprocessor里面,这边加载servlet都放到SimpeLoader里面操作了。
  SimpeLoadr: 构造函数返回一个类加载器给SimpleWrapper
  public SimpleLoader() {
  try {
  URL[] urls = new URL[1];
  URLStreamHandler streamHandler = null;
  File classPath = new File(WEB_ROOT);
  String repository = (new URL("file", null,
  classPath.getCanonicalPath() + File.separator)).toString();
  urls[0] = new URL(null, repository, streamHandler);
  classLoader = new URLClassLoader(urls);
  } catch (IOException e) {
  System.out.println(e.toString());
  }
  }
  SimplePipeline:这个参考Pipelining Tasks描述
  SimpleWrapper: 
  里面定义了loader和父container变量:
  private Loader loader; 
  protected Container parent = null;
  获取loader时先从自己的类里面取,没有就从父container里面取:
  public Loader getLoader() { 
  if (loader != null) return (loader); 
  if (parent != null) return (parent.getLoader()); 
  return (null); 
  }
  在构造函数里面设置默认的valve:
  public SimpleWrapper() {
  pipeline.setBasic(new SimpleWrapperValve()); 
  }
  wrapper里面的allocate and load方法:
  load用于加载和初始化servlet:
  public void load() throws ServletException {
  instance = loadServlet();
  }
  而loadServlet()代码如下:
  private Servlet loadServlet() throws ServletException {
  if (instance != null)
  return instance;
  Servlet servlet = null;
  String actualClass = servletClass;
  if (actualClass == null) {
  throw new ServletException("servlet class has not been specified");
  }
  Loader loader = getLoader();
  // Acquire an instance of the class loader to be used
  if (loader == null) {
  throw new ServletException("No loader.");
  }
  ClassLoader classLoader = loader.getClassLoader();
  // Load the specified servlet class from the appropriate class loader
  Class classClass = null;
  try {
  if (classLoader != null) {
  classClass = classLoader.loadClass(actualClass);
  }
  } catch (ClassNotFoundException e) {
  throw new ServletException("Servlet class not found");
  }
  // Instantiate and initialize an instance of the servlet class itself
  try {
  servlet = (Servlet) classClass.newInstance();
  } catch (Throwable e) {
  throw new ServletException("Failed to instantiate servlet");
  }
  // Call the initialization method of this servlet
  try {
  servlet.init(null);  // 初始化servlet
  } catch (Throwable f) {
  throw new ServletException("Failed initialize servlet.");
  }
  return servlet;
  }
  allocate则用于获取已经实例化的servlet
  public Servlet allocate() throws ServletException {
  // Load and initialize our instance if necessary
  if (instance == null) {
  try {
  instance = loadServlet();
  } catch (ServletException e) {
  throw e;
  } catch (Throwable e) {
  throw new ServletException(
  "Cannot allocate a servlet instance", e);
  }
  }
  return instance;
  }
  SimpleWrapperValve
  那么默认的valve做什么呢:
  public void invoke(Request request, Response response,
  ValveContext valveContext) throws IOException, ServletException {
  SimpleWrapper wrapper = (SimpleWrapper) getContainer();
  ServletRequest sreq = request.getRequest();
  ServletResponse sres = response.getResponse();
  Servlet servlet = null;
  HttpServletRequest hreq = null;
  if (sreq instanceof HttpServletRequest)
  hreq = (HttpServletRequest) sreq;
  HttpServletResponse hres = null;
  if (sres instanceof HttpServletResponse)
  hres = (HttpServletResponse) sres;
  // Allocate a servlet instance to process this request
  try {
  servlet = wrapper.allocate();  // wrapper获取已经实例化完成的servlet,然后调用servlet的service处理请求返回response
  if (hres != null && hreq != null) {
  servlet.service(hreq, hres);
  } else {
  servlet.service(sreq, sres);
  }
  } catch (ServletException e) {
  }
  }
  ClientIPLoggerValve、HeaderLoggerValve做些酱油操作。
  启动服务:
  HttpConnector connector = new HttpConnector();
  Wrapper wrapper = new SimpleWrapper();
  wrapper.setServletClass("ModernServlet");  // 指明servlet名称
  Loader loader = new SimpleLoader();
  Valve valve1 = new HeaderLoggerValve();
  Valve valve2 = new ClientIPLoggerValve();
  wrapper.setLoader(loader); // 设置类加载器
  ((Pipeline) wrapper).addValve(valve1);  // 设置额外的valve
  ((Pipeline) wrapper).addValve(valve2);
  connector.setContainer(wrapper);  // 与Connector关联,wrapper作为connector的容器
  try {
  connector.initialize();
  connector.start();
  The Context Application
  之前的只是一个wrapper不算一个完整的服务器,只能处理一个servlet,Context里面使用mapper接口保存子container(wrapper,Tomcat5不再使用mapper).
  Mapper:
  package org.apache.catalina; 
  public interface Mapper {
  public Container getContainer(); 
  public void setContainer(Container container); 
  public String getProtocol(); 
  public void setProtocol(String protocol); 
  public Container map(Request request, boolean update); // 获取处理request的子container(这里是wrapper)
  }
  启动服务:
  HttpConnector connector = new HttpConnector();
  Wrapper wrapper1 = new SimpleWrapper();  // 生成两个wrapper
  wrapper1.setName("Primitive");
  wrapper1.setServletClass("PrimitiveServlet");
  Wrapper wrapper2 = new SimpleWrapper();
  wrapper2.setName("Modern");
  wrapper2.setServletClass("ModernServlet");
  Context context = new SimpleContext(); // 生成context并包含wrapper
  context.addChild(wrapper1);
  context.addChild(wrapper2);
  Valve valve1 = new HeaderLoggerValve();
  Valve valve2 = new ClientIPLoggerValve();
  ((Pipeline) context).addValve(valve1);  // 添加valve
  ((Pipeline) context).addValve(valve2);
  Mapper mapper = new SimpleContextMapper(); // mapper
  mapper.setProtocol("http");
  context.addMapper(mapper);
  Loader loader = new SimpleLoader();
  context.setLoader(loader);   //类loader交给context关联,wrapper也能用
  context.addServletMapping("/Primitive", "Primitive");  // mapper设置对应关系,处理时取相应的wrapper处理请求
  context.addServletMapping("/Modern", "Modern");        // 应相对uri和wrapper名称建关联 
  connector.setContainer(context);    // 与Connector关联,context作为connector的容器
  try {
  connector.initialize();
  connector.start();
  处理过程是这样的:
  connector调用container(Context)的invoke处理请求,Context invoke里面调用Context的SimplePipeline 的invoke来处理:
  public void invoke(Request request, Response response) throws IOException, ServletException {
  pipeline.invoke(request, response);
  }
  pipeline自然是先调用辅助的valve,最后调用basic valve(pipeline.setBasic(new SimpleContextValve()); 就是SimpleContextValve)处理,
  Wrapper wrapper = null;
  try {
  wrapper = (Wrapper) context.map(request, true);  // 根据request从mapper获取对应的wrapper
  } catch (IllegalArgumentException e) {
  badRequest(requestURI, (HttpServletResponse) response.getResponse());
  return;
  }
  if (wrapper == null) {
  notFound(requestURI, (HttpServletResponse) response.getResponse());
  return;
  }
  // Ask this Wrapper to process this Request
  response.setContext(context);
  wrapper.invoke(request, response);  // wrapper最终处理(wrapper里面也是有pipeline的,本例里面没有添加酱油valve
  所以直接就到basic valve调用servlet service处理请求)
  最后看看mapper是如何找到request和wrapper的对应关系的:
  public Container map(Request request, boolean update) {
  // Identify the context-relative URI to be mapped
  String contextPath = ((HttpServletRequest) request.getRequest()).getContextPath();
  String requestURI = ((HttpRequest) request).getDecodedRequestURI();
  String relativeURI = requestURI.substring(contextPath.length());
  // Apply the standard request URI mapping rules from the specification
  Wrapper wrapper = null;
  String servletPath = relativeURI;
  String pathInfo = null;
  String name = context.findServletMapping(relativeURI);  // 利用相对uri找到wrapper的名字
  if (name != null)
  wrapper = (Wrapper) context.findChild(name);
  return (wrapper);
  }
  六 Lifecycle 生命周期
  Catalina有很多组件构成,Catalina启动时他们也需要启动,Catalina停止时,组件需要释放资源等。
  实现Lifecycle接口的组件可以下面的事件:
  BEFORE_START_EVENT, START_EVENT, AFTER_START_EVENT, BEFORE_STOP_EVENT, STOP_EVENT, and AFTER_STOP_EVENT
  有事件就有监听器接口:org.apache.catalina.LifecycleListener
  The Lifecycle Interface
  public interface Lifecycle { 
  public static final String START_EVENT = "start"; 
  public static final String BEFORE_START_EVENT = "before_start"; 
  public static final String AFTER_START_EVENT = "after_start"; 
  public static final String STOP_EVENT = "stop"; 
  public static final String BEFORE_STOP_EVENT = "before_stop"; 
  public static final String AFTER_STOP_EVENT = "after_stop"; 
  public void addLifecycleListener(LifecycleListener listener); 
  public LifecycleListener[] findLifecycleListeners(); 
  public void removeLifecycleListener(LifecycleListener listener); 
  public void start() throws LifecycleException; 
  public void stop() throws LifecycleException; 
  }
  The LifecycleEvent Class
  它继承了EventObject,本身持有lifecycle变量.
  The LifecycleListener Interface
  他只有一个方法:public void lifecycleEvent(LifecycleEvent event); 当一个事件触发时,对该事件感兴趣的listener执行这个方法.
  The LifecycleSupport Class
  这个是个Lifecycle辅助类,有事件有监听器就得注册监听器和事件关系吧,这个类就能帮忙了。
  public LifecycleSupport(Lifecycle lifecycle) { // 构造函数 持有一个 lifecycle对象
  super();
  this.lifecycle = lifecycle;
  }
  然后实现了Lifecycle里面的addLifecycleListener,findLifecycleListeners,removeLifecycleListener三个方法。
  额外有个触发事件的方法:
  public void fireLifecycleEvent(String type, Object data) {
  LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
  LifecycleListener interested[] = null;
  synchronized (listeners) {
  interested = (LifecycleListener[]) listeners.clone(); // 拷贝可能是怕listeners数组有新增或者减少会引起异常
  }
  for (int i = 0; i < interested.length; i++)
  interested.lifecycleEvent(event);  // 对这个事件感兴趣的监听都执行lifecycleEvent
  }
  下面看看Lifecycle机制具体的使用:
  参考第五章SimpleContext、SimpleWrapper、SimplePipeline、SimpleLoader、SimpleContextMapper都实现了Lifecycle.
  SimpleContext: 代码和第5章类似
  protected LifecycleSupport lifecycle = new LifecycleSupport(this);
  然后addLifecycleListener什么的就不说了。
  关键是start()和stop()方法:
  public synchronized void start() throws LifecycleException {
  if (started) // 是个变量用于标识对象是否启动了
  throw new LifecycleException("SimpleContext has already started");
  lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null); // 触发“启动之前”事件
  started = true;
  try { // context下面相关的组件也都启动下
  if ((loader != null) && (loader instanceof Lifecycle)) // loader组件
  ((Lifecycle) loader).start();
  Container children[] = findChildren();            // 所有的wrapper
  for (int i = 0; i < children.length; i++) {
  if (children instanceof Lifecycle)
  ((Lifecycle) children).start();
  }
  if (pipeline instanceof Lifecycle)         // 启动所有valve包括 basic valve  
  ((Lifecycle) pipeline).start();
  lifecycle.fireLifecycleEvent(START_EVENT, null); // 触发“启动”事件
  } catch (Exception e) {
  e.printStackTrace();
  }
  lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null); // 触发“启动完成”事件
  }
  public void stop() throws LifecycleException {  // 和start类似,需要停止context下面相关的组件
  if (!started)
  throw new LifecycleException("SimpleContext has not been started");
  // Notify our interested LifecycleListeners
  lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
  lifecycle.fireLifecycleEvent(STOP_EVENT, null);
  started = false;
  try {
  // Stop the Valves in our pipeline (including the basic), if any
  if (pipeline instanceof Lifecycle) {
  ((Lifecycle) pipeline).stop();
  }
  // Stop our child containers, if any
  Container children[] = findChildren();
  for (int i = 0; i < children.length; i++) {
  if (children instanceof Lifecycle)
  ((Lifecycle) children).stop();
  }
  if ((loader != null) && (loader instanceof Lifecycle)) {
  ((Lifecycle) loader).stop();
  }
  } catch (Exception e) {
  e.printStackTrace();
  }
  // Notify our interested LifecycleListeners
  lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
  }
  可以看如果需要启动或者停止所有组件只需要启动和停止组件上层的container,本例里面是Context.
  代码里面有触发事件,当然就得有监听器了。如SimpleContextLifecycleListener
  public void lifecycleEvent(LifecycleEvent event) {
  Lifecycle lifecycle = event.getLifecycle();
  System.out.println("SimpleContextLifecycleListener's event "
  + event.getType().toString());
  if (Lifecycle.START_EVENT.equals(event.getType())) {
  System.out.println("Starting context.");
  } else if (Lifecycle.STOP_EVENT.equals(event.getType())) {
  System.out.println("Stopping context.");
  }
  }
  本例中写的比较简单,就是打印一些信息.
  总结下Lifecycle机制就是为了提供event和eventListener,这样便于不影响流程的情况下新增监听器添加新功能.
  七 Logger
  先看看org.apache.catalina.Logger接口的定义:
  public static final int FATAL = Integer.MIN_VALUE;  // 5种verbosity日志级别
  public static final int ERROR = 1;
  public static final int WARNING = 2;
  public static final int INFORMATION = 3;
  public static final int DEBUG = 4;
  public Container getContainer();
  public void setContainer(Container container);
  public String getInfo();
  public int getVerbosity();
  public void setVerbosity(int verbosity);
  public void addPropertyChangeListener(PropertyChangeListener listener);
  public void log(String message);
  public void log(Exception exception, String msg);
  public void log(String message, Throwable throwable);
  public void log(String message, int verbosity);
  public void log(String message, Throwable throwable, int verbosity);
  public void removePropertyChangeListener(PropertyChangeListener listener);
  在实现上tomcat提供了三种实现FileLogger、SystemErrLogger、SystemOutLogger这三种又有很多同样的代码,所以提供了一个父类:LoggerBase
  LoggerBase: 是个抽象类
  设置日志级别的方法:默认是ERROR(protected int verbosity = ERROR;)
  public void setVerbosityLevel(String verbosity) {
  if ("FATAL".equalsIgnoreCase(verbosity))
  this.verbosity = FATAL;
  else if ("ERROR".equalsIgnoreCase(verbosity))
  this.verbosity = ERROR;
  else if ("WARNING".equalsIgnoreCase(verbosity))
  this.verbosity = WARNING;
  else if ("INFORMATION".equalsIgnoreCase(verbosity))
  this.verbosity = INFORMATION;
  else if ("DEBUG".equalsIgnoreCase(verbosity))
  this.verbosity = DEBUG;
  }
  log方法里面只有public abstract void log(String msg);是没有实现,由子类实现,其他的都提供了实现类.
  public void log(String message, int verbosity) {
  if (this.verbosity >= verbosity)
  log(message); // 具体的实现由子类完成
  }
  The SystemOutLogger Class
  看看的它的实现:
  public class SystemOutLogger extends LoggerBase {
  protected static final String info = "org.apache.catalina.logger.SystemOutLogger/1.0";
  public void log(String msg) {
  System.out.println(msg);
  }
  }
  SystemErrLogger class: System.err.println(msg); 
  The FileLogger Class
  FileLogger实现了Lifecycle接口,可以和容器中的其他组件一起根据生命周期启动,停止。
  start方法不看了。stop里面关闭输出流:
  public void stop() throws LifecycleException {
  // Validate and update our current component state
  if (!started)
  throw new LifecycleException(sm.getString("fileLogger.notStarted"));
  lifecycle.fireLifecycleEvent(STOP_EVENT, null);
  started = false;
  close(); // 关闭文件输出
  }
  log方法,看来是以日期分割日志文件的,每天一个日志文件,open的时候用了锁,防止生成多个日志文件.
  public void log(String msg) {
  // Construct the timestamp we will use, if requested
  Timestamp ts = new Timestamp(System.currentTimeMillis());
  String tsString = ts.toString().substring(0, 19);
  String tsDate = tsString.substring(0, 10);
  // If the date has changed, switch log files
  if (!date.equals(tsDate)) {
  synchronized (this) {
  if (!date.equals(tsDate)) {
  close();
  date = tsDate;
  open();
  }
  }
  }
  // Log this message, timestamped if necessary
  if (writer != null) {
  if (timestamp) {
  writer.println(tsString + " " + msg);
  } else {
  writer.println(msg);
  }
  }
  }
  open 用于打开文件:
  private void open() {
  // Create the directory if necessary
  File dir = new File(directory);
  if (!dir.isAbsolute())
  dir = new File(System.getProperty("catalina.base"), directory);
  dir.mkdirs();
  // Open the current log file
  try {
  String pathname = dir.getAbsolutePath() + File.separator + prefix
  + date + suffix;
  writer = new PrintWriter(new FileWriter(pathname, true), true);
  } catch (IOException e) {
  writer = null;
  }
  }
  八 Loader
  前面的章节提到过SimpleLoader,用于加载和初始化servlet实例,之前用的system loader,这样不安全,需要自己定制一个loader,防止不良企图的servlet.
  一个servlet只能有权限访问/WEB-INF/classes下面的类和/WEB-INF/lib下面的jar里面的类.而且定制的loader可以检查类是否有修改,如果有可以重新加载,这个
  就是热部署。
  java有三种类加载器:bootstrap class loader, extension class loader, and system class loader, 他们之间存在父子关系。
  bootstrap 处于最高级,system处于最低级。
  bootstrap class loader用于引导jvm,它加载的是java核心的类(java.lang或者java.io packages),所以不是用java语言实现的(C语言).
  extension class loader 各个jvm实现的不一样,sun的主要加载 /jdk/jre/lib/ext 里面的类或者jar。
  system class loader 则是从CLASSPATH里面定义的目录中加载类或者jar.
  一个类加载时,先交给system class loader,但是它不加载,交给它的父loader(extension class loader)加载,extension class loader同理也先交给它的父loader,
  也就是bootstrap class loader来加载。如果bootstrap class loader能找到类就加载,找不到就让它的子loader加载,就这样。。。如果最底层的loader也加载不了就报
  ClassNotFoundException. 这种机制主要是为了安全性,防止用户用非法的类替换标准的类。
  先看看Loader接口:
  public interface Loader {
  public ClassLoader getClassLoader();
  public Container getContainer();
  public void setContainer(Container container);
  public DefaultContext getDefaultContext();
  public void setDefaultContext(DefaultContext defaultContext);
  public boolean getDelegate();
  public void setDelegate(boolean delegate);
  public String getInfo();
  public boolean getReloadable();
  public void setReloadable(boolean reloadable);
  public void addPropertyChangeListener(PropertyChangeListener listener);
  public void addRepository(String repository);
  public String[] findRepositories();
  public boolean modified();
  public void removePropertyChangeListener(PropertyChangeListener listener);
  }
  整体结构如下:
  Loader                  loader.Reloader          java.net.URLClassLoader
     |                          |__________________________|
  loader.WebappLoader                      |
          |_______use_________________loader.WebappClassLoader

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-333754-1-1.html 上篇帖子: Android与Tomcat远程访问 下篇帖子: 读取tomcat jndi配置
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表