Tomcat源码解析(八):Host
来源:互联网 发布:移动网络代理服务器 编辑:程序博客网 时间:2024/06/04 19:57
host的作用是啥?
之前我们学习了context,它表示一个独立的web应用。当我们一台机器上只有一个web应用的时候,只需要把context当做最上层的容器就可以了。可是通常我们希望在一个机器上部署多个应用,就需要有多个context。这时候怎么将不同的请求匹配到不同的context呢?
就由host来解决这个问题啦。
context管理wrapper,同样地,host来管理context。
主流程
host的主要内容
看了host、StandardHost的代码之后,感觉跟context的内容差别不大。区别是实现了Deployer接口。
public class StandardHostextends ContainerBaseimplements Deployer, Host {// ----------------------------------------------------------- Constructors/** * Create a new StandardHost component with the default basic Valve. */public StandardHost() { super(); pipeline.setBasic(new StandardHostValve());}/** * start()中利用它创建mapper * The Java class name of the default Mapper class for this Container. */private String mapperClass = "org.apache.catalina.core.StandardHostMapper";/** * Start this host. * * start()比较简单,创建errorReportValve之后,就调用ContainerBase的start()。ContainerBase的start()中,会根据mapperClass创建mapper * @exception LifecycleException if this component detects a fatal error * that prevents it from being started */public synchronized void start() throws LifecycleException { // Set error report valve if ((errorReportValveClass != null) && (!errorReportValveClass.equals(""))) { try { Valve valve = (Valve) Class.forName(errorReportValveClass) .newInstance(); addValve(valve); } catch (Throwable t) { log(sm.getString ("standardHost.invalidErrorReportValveClass", errorReportValveClass)); } } // Set dispatcher valve addValve(new ErrorDispatcherValve()); super.start();}
host的主要功能是根据请求匹配到对应的context,那么map方法就是最重要的一个方法啦。我们看下主流程吧。
StandardHost的basic valve是StandardHostValve。request进入后,调用StandardHostValve的invoke方法:
public void invoke(Request request, Response response, ValveContext valveContext) throws IOException, ServletException { System.out.println("[StandardHostValve] invoke"); // Validate the request and response object types if (!(request.getRequest() instanceof HttpServletRequest) || !(response.getResponse() instanceof HttpServletResponse)) { return; // NOTE - Not much else we can do generically } // Select the Context to be used for this Request //调用host的map(request, true)方法,实际来自于ContainerBase StandardHost host = (StandardHost) getContainer(); Context context = (Context) host.map(request, true); if (context == null) { ((HttpServletResponse) response.getResponse()).sendError (HttpServletResponse.SC_INTERNAL_SERVER_ERROR, sm.getString("standardHost.noContext")); return; } // Bind the context CL to the current thread Thread.currentThread().setContextClassLoader (context.getLoader().getClassLoader()); // Update the session last access time for our session (if any) HttpServletRequest hreq = (HttpServletRequest) request.getRequest(); String sessionId = hreq.getRequestedSessionId(); if (sessionId != null) { Manager manager = context.getManager(); if (manager != null) { Session session = manager.findSession(sessionId); if ((session != null) && session.isValid()) session.access(); } } // Ask this Context to process this request context.invoke(request, response);}
StandardHostValve.invoke()调用ContainerBase.map(request, true)方法,ContainerBase.map(request, true)获取mapper,调用mapper的map(Request request, boolean update)。
public Container map(Request request, boolean update) { // Select the Mapper we will use //先获取StandardHostMapper Mapper mapper = findMapper(request.getRequest().getProtocol()); if (mapper == null) return (null); // Use this Mapper to perform this mapping //调用StandardHostMapper.map(request, update) return (mapper.map(request, update));}
StandardHostMapper.map调用的是host的map方法,囧~
public Container map(Request request, boolean update) { System.out.println("[StandardHostMapper] map"); // Has this request already been mapped? if (update && (request.getContext() != null)) return (request.getContext()); // Perform mapping on our request URI String uri = ((HttpRequest) request).getDecodedRequestURI(); Context context = host.map(uri); // Update the request (if requested) and return the selected Context if (update) { request.setContext(context); if (context != null) ((HttpRequest) request).setContextPath(context.getPath()); else ((HttpRequest) request).setContextPath(null); } return (context);}
那么host的map()方法长啥样呢?
public Context map(String uri) { if (debug > 0) log("Mapping request URI '" + uri + "'"); if (uri == null) return (null); // Match on the longest possible context path prefix if (debug > 1) log(" Trying the longest context path prefix"); Context context = null; String mapuri = uri; while (true) { context = (Context) findChild(mapuri); if (context != null) break; int slash = mapuri.lastIndexOf('/'); if (slash < 0) break; mapuri = mapuri.substring(0, slash); } // If no Context matches, select the default Context if (context == null) { if (debug > 1) log(" Trying the default context"); context = (Context) findChild(""); } // Complain if no Context has been selected if (context == null) { log(sm.getString("standardHost.mappingError", uri)); return (null); } // Return the mapped Context (if any) if (debug > 0) log(" Mapped to context '" + context.getPath() + "'"); return (context);}/** * host根据name匹配context * Return the child Container, associated with this Container, with * the specified name (if any); otherwise, return <code>null</code> * * @param name Name of the child Container to be retrieved */public Container findChild(String name) { if (name == null) return (null); synchronized (children) { // Required by post-start changes return ((Container) children.get(name)); }}
实际就是在children这个map中,根据request的uri来匹配。children在初始化的时候就有了,可是我都没有在Bootstrap中看到给context设置name呢。。
别急,原来在这里:
/** * Set the context path for this Context. * <p> * <b>IMPLEMENTATION NOTE</b>: The context path is used as the "name" of * a Context, because it must be unique. * * @param path The new context path */public void setPath(String path) { setName(RequestUtil.URLDecode(path));}
所以Bootstrap.class中调用context.setPath,就给context设置name了。
host配置小例子
之前都是一个context,看到可以放两个应用,当然要试一试啦~看下我的小例子吧~
首先写一个自己的servlet:
/** * Created by zhangguixian on 8/14/16 */public class MyServlet implements Servlet { @Override public void init(ServletConfig servletConfig) throws ServletException { } @Override public ServletConfig getServletConfig() { return null; } @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { PrintWriter writer = servletResponse.getWriter(); writer.println("hello app2~"); writer.println("sunny day @@"); } @Override public String getServletInfo() { return null; } @Override public void destroy() { }}
然后在webapps路径下,建两个子路径:
webapps/app1/WEB-INF/classes下放ModernServlet.class和PrimitiveServlet.class,webapps/app2/WEB-INF/classes下放MyServlet.class。
最后看Bootstrap类的测试代码:
public final class Bootstrap1 { public static void main(String[] args) { //invoke: http://localhost:8080/app1/Primitive or http://localhost:8080/app1/Modern System.setProperty("catalina.base", System.getProperty("user.dir")); Connector connector = new HttpConnector(); //app1 Wrapper wrapper1 = new StandardWrapper(); wrapper1.setName("Primitive"); wrapper1.setServletClass("PrimitiveServlet"); Wrapper wrapper2 = new StandardWrapper(); wrapper2.setName("Modern"); wrapper2.setServletClass("ModernServlet"); Context context1 = new StandardContext(); // StandardContext's start method adds a default mapper context1.setPath("/app1"); context1.setDocBase("app1"); context1.addChild(wrapper1); context1.addChild(wrapper2); Loader loader1 = new WebappLoader(); context1.setLoader(loader1); //app2 Wrapper MyServletWrapper = new StandardWrapper(); MyServletWrapper.setName("MyServlet"); MyServletWrapper.setServletClass("MyServlet"); Context context2 = new StandardContext(); // StandardContext's start method adds a default mapper context2.setPath("/app2"); context2.setDocBase("app2");//context2下servlet的路径 context2.addChild(MyServletWrapper); LifecycleListener listener = new SimpleContextConfig(); ((Lifecycle) context1).addLifecycleListener(listener); ((Lifecycle) context2).addLifecycleListener(listener); //host Host host = new StandardHost(); host.addChild(context1); host.addChild(context2); host.setName("localhost"); host.setAppBase("webapps");//host下servlet的逻辑。那么context2的路径就是/webapps/app2,它包括的servlet都在这个路径下。 Loader loader2 = new WebappLoader(); context2.setLoader(loader2); // context.addServletMapping(pattern, name); context1.addServletMapping("/Primitive", "Primitive"); context1.addServletMapping("/Modern", "Modern"); context2.addServletMapping("/MyServlet", "MyServlet"); connector.setContainer(host); try { connector.initialize(); ((Lifecycle) connector).start(); ((Lifecycle) host).start(); // make the application wait until we press a key. System.in.read(); ((Lifecycle) host).stop(); } catch (Exception e) { e.printStackTrace(); } }}
启动项目后,在浏览器输入http://localhost:8080/app1/Primitive或http://localhost:8080/app2/MyServlet就可以看到不同的效果啦~
Engine
host之上还有engine,看了下是用来管理多个虚拟主机,没太看懂~
在网上查了下资料,大致明白了一点。一个host表示一个虚拟主机,表示一个域名。一个engine下配置多个host,就表示可以在一台机器上配置多个域名(localhost、www.helloworld.com),而且完全独立互不干扰。
主要内容在StandardEngine的默认mapper StandardEngineMapper的map()中:
public Container map(Request request, boolean update) { int debug = engine.getDebug(); // Extract the requested server name String server = request.getRequest().getServerName(); if (server == null) { server = engine.getDefaultHost(); if (update) request.setServerName(server); } if (server == null) return (null); server = server.toLowerCase(); if (debug >= 1) engine.log("Mapping server name '" + server + "'"); // Find the matching child Host directly //根据request中的servername即服务器主机名匹配host if (debug >= 2) engine.log(" Trying a direct match"); Host host = (Host) engine.findChild(server); // Find a matching Host by alias. FIXME - Optimize this! if (host == null) { if (debug >= 2) engine.log(" Trying an alias match"); Container children[] = engine.findChildren(); for (int i = 0; i < children.length; i++) { String aliases[] = ((Host) children[i]).findAliases(); for (int j = 0; j < aliases.length; j++) { if (server.equals(aliases[j])) { host = (Host) children[i]; break; } } if (host != null) break; } } // Trying the "default" host if any if (host == null) { if (debug >= 2) engine.log(" Trying the default host"); host = (Host) engine.findChild(engine.getDefaultHost()); } // Update the Request if requested, and return the selected Host ; // No update to the Request is required return (host);}
- Tomcat源码解析(八):Host
- (八)Tomcat源码解析 - Tomcat类加载器原理
- Tomcat 源码阅读(八)Lifecycle
- TFS源码解析八
- tomcat源码解析(一)
- Tomcat 源码解析(1)
- Tomcat源码解析(1)
- Tomcat源码解析(2)
- Tomcat源码解析(3)
- Tomcat源码解析(4)
- Tomcat源码解析(5)
- Tomcat源码解析(6)
- Tomcat源码解析(7)
- Tomcat源码解析(8)
- Tomcat源码解析(9)
- Tomcat源码解析(9)
- Tomcat源码解析(10)
- Tomcat源码解析(11)
- Maven在Mac OS环境配置
- android 实现定时拍照功能
- eclispse项目导入android studio的坑(E/AndroidRuntime: FATAL EXCEPTION: main ...findLibrary returned null)
- c++ 多态性
- c# 委托与异步调用
- Tomcat源码解析(八):Host
- pandas数据处理常用函数demo之创建/行列操作/查看/文件操作
- PAT B 1011
- ios developer tiny share-20160824
- ELK在linux部署安装(一)
- C++继承和派生
- Spring scope属性详解
- freemarker的使用,转自http://blog.sina.com.cn/s/blog_64c505480100sxuh.html
- Java反射机制实例详解