上海高端做网站武义县网站建设公司

当前位置: 首页 > news >正文

上海高端做网站,武义县网站建设公司,企业名录软件,夏邑县百城建设提质网站一、启动脚本 当我们在服务启动Tomcat时#xff0c;都是通过执行startup.sh脚本启动。 在Tomcat的启动脚本startup.sh中#xff0c;最终会去执行catalina.sh脚本#xff0c;传递的参数是start。 在catalina.sh脚本中#xff0c;前面是环境判断和初始化参数#xff0c;最终… 一、启动脚本 当我们在服务启动Tomcat时都是通过执行startup.sh脚本启动。 在Tomcat的启动脚本startup.sh中最终会去执行catalina.sh脚本传递的参数是start。 在catalina.sh脚本中前面是环境判断和初始化参数最终根据传递的start来执行上图的代码最终会调用Tomcat的Bootstrap启动类的main方法传递的参数是start。 二、源码解析 为了更容易的理解源码整个系列中Tomcat的运行模式采用BIO的方式。 public static void main(String args[]) {if (daemon null) {Bootstrap bootstrap new Bootstrap();try {//初始化bootstrap.init();} catch (Throwable t) {handleThrowable(t);t.printStackTrace();return;}daemon bootstrap;} else {Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);}try {String command start;if (args.length 0) {command args[args.length - 1];}//脚本传递的是startif (command.equals(startd)) {args[args.length - 1] start;daemon.load(args);daemon.start();} else if (command.equals(stopd)) {args[args.length - 1] stop;daemon.stop();} else if (command.equals(start)) {//加载和启动整个Tomcatdaemon.setAwait(true);daemon.load(args);daemon.start();} else if (command.equals(stop)) {daemon.stopServer(args);} else if (command.equals(configtest)) {daemon.load(args);if (nulldaemon.getServer()) {System.exit(1);}System.exit(0);} else {log.warn(Bootstrap: command \ command \ does not exist.);}} catch (Throwable t) {if (t instanceof InvocationTargetException t.getCause() ! null) {t t.getCause();}handleThrowable(t);t.printStackTrace();System.exit(1);}} 在启动类的main方法中创建了一个启动类的实例然后进行初始化最后再根据启动命令传递进来的参数也就是上文中的 start 来执行对应的逻辑即加载和启动整个Tomcat。 public void init() throws Exception {//设置容器的路径和脚本中的基本信息setCatalinaHome();setCatalinaBase();//初始化Tomcat的三大类加载器initClassLoaders();//该类加载器用于加载这个源码的类Thread.currentThread().setContextClassLoader(catalinaLoader);SecurityClassLoad.securityClassLoad(catalinaLoader);//创建一个容器的启动实例待会用于加载server.xmlClass? startupClass catalinaLoader.loadClass(org.apache.catalina.startup.Catalina);Object startupInstance startupClass.newInstance();String methodName setParentClassLoader;Class? paramTypes[] new Class[1];paramTypes[0] Class.forName(java.lang.ClassLoader);Object paramValues[] new Object[1];paramValues[0] sharedLoader;Method method startupInstance.getClass().getMethod(methodName, paramTypes);method.invoke(startupInstance, paramValues);catalinaDaemon startupInstance;} 在初始化启动器是主要是创建三大类加载器commonLoader、catalinaLoader、sharedLoader并设置好父子关系最后再创建一个启动的实例主要是用来解析server.xml文件。 在Tomcat中一共有四大类加载器如下图 类加载器作用父加载器 commonLoader共同类加载器 加载$CATALINA_HOME/lib下的类加载器应用类加载器 catalinaLoader容器类加载器 加载Tomcat应用服务器的类加载器可以理解为加载Tomcat源码中的类共同类加载器 sharedLoader共享类加载器 加载应用类加载器的共享的类加载器例如相同版本的mysql驱动等共同类加载器webappLoader应用类加载加载web应用下的类类加载每个web应用之间是相互隔离的共享类加载器 commonLoader、catalinaLoader、sharedLoader可以在tomcat下的conf/catalina.properties文件中修改。 初始化完成之后会根据启动参数 start然后执行daemon.load(args)即加载。 private void load(String[] arguments)throws Exception {String methodName load;Object param[];Class? paramTypes[];if (argumentsnull || arguments.length0) {paramTypes null;param null;} else {paramTypes new Class[1];paramTypes[0] arguments.getClass();param new Object[1];param[0] arguments;}Method method catalinaDaemon.getClass().getMethod(methodName, paramTypes);method.invoke(catalinaDaemon, param);} 加载就是通过反射去调用Catalina的load方法。 public void load() {//找到脚本中设置的路径initDirs();initNaming();//创建一个解析器Digester digester createStartDigester();InputSource inputSource null;InputStream inputStream null;File file null;try {try {//读取server.xml文件file configFile();inputStream new FileInputStream(file);inputSource new InputSource(file.toURI().toURL().toString());} catch (Exception e) {if (log.isDebugEnabled()) {log.debug(sm.getString(catalina.configFail, file), e);}}if (inputStream null) {try {inputStream getClass().getClassLoader().getResourceAsStream(getConfigFile());inputSource new InputSource(getClass().getClassLoader().getResource(getConfigFile()).toString());} catch (Exception e) {if (log.isDebugEnabled()) {log.debug(sm.getString(catalina.configFail,getConfigFile()), e);}}}if (inputStream null) {try {inputStream getClass().getClassLoader().getResourceAsStream(server-embed.xml);inputSource new InputSource(getClass().getClassLoader().getResource(server-embed.xml).toString());} catch (Exception e) {if (log.isDebugEnabled()) {log.debug(sm.getString(catalina.configFail,server-embed.xml), e);}}}if (inputStream null || inputSource null) {if (file null) {log.warn(sm.getString(catalina.configFail,getConfigFile() ] or [server-embed.xml]));} else {log.warn(sm.getString(catalina.configFail,file.getAbsolutePath()));if (file.exists() !file.canRead()) {log.warn(Permissions incorrect, read permission is not allowed on the file.);}}return;}try {//解析server.xml文件把server.xml中的每一个标签都转换成对应的实例对象inputSource.setByteStream(inputStream);digester.push(this);digester.parse(inputSource);} catch (SAXParseException spe) {log.warn(Catalina.start using getConfigFile() : spe.getMessage());return;} catch (Exception e) {log.warn(Catalina.start using getConfigFile() : , e);return;}} finally {if (inputStream ! null) {try {inputStream.close();} catch (IOException e) {// Ignore}}}getServer().setCatalina(this);initStreams();try {//初始化最顶层的组件,会导致Service组件和Connector组件也跟着初始化getServer().init();} catch (LifecycleException e) {if (Boolean.getBoolean(org.apache.catalina.startup.EXIT_ON_INIT_FAILURE)) {throw new java.lang.Error(e);} else {log.error(Catalina.start, e);}}} 在Catalina的load方法中会找到server.xml文件然后解析标签并创建出对应的实例对象最终在调用最顶层的Server组件的init方法会调用Service组件的初始化而Service组件的会调用Connector组件的初始化容器的初始化是懒加载的即有请求达到时才开始初始化。 在上面3个组件的初始化中最值得关注的是Connector组件的初始化因为它会绑定一个端口并且添加对应的协议处理器从而等待请求。 Connectorprotected void initInternal() throws LifecycleException {super.initInternal();//创建一个适配器adapter new CoyoteAdapter(this);//协议处理器在连接器创建时也跟着创建了protocolHandler.setAdapter(adapter);if( null parseBodyMethodsSet ) {setParseBodyMethods(getParseBodyMethods());}//…省略try {//协议处理器初始化protocolHandler.init();} catch (Exception e) {throw new LifecycleException(sm.getString(coyoteConnector.protocolHandlerInitializationFailed), e);}mapperListener.init();}Http11Protocolpublic void init() throws Exception {//…省略try {endpoint.init();} catch (Exception ex) {getLog().error(sm.getString(abstractProtocolHandler.initError,getName()), ex);throw ex;}}JIoEndpoint:public final void init() throws Exception {testServerCipherSuitesOrderSupport();if (bindOnInit) {bind();bindState BindState.BOUND_ON_INIT;}}public void bind() throws Exception {if (acceptorThreadCount 0) {acceptorThreadCount 1;}if (getMaxConnections() 0) {setMaxConnections(getMaxThreadsExecutor(true));}if (serverSocketFactory null) {if (isSSLEnabled()) {serverSocketFactory handler.getSslImplementation().getServerSocketFactory(this);} else {serverSocketFactory new DefaultServerSocketFactory(this);}}//创建ServerSocket绑定监听端口if (serverSocket null) {try {if (getAddress() null) {serverSocket serverSocketFactory.createSocket(getPort(),getBacklog());} else {serverSocket serverSocketFactory.createSocket(getPort(),getBacklog(), getAddress());}} catch (BindException orig) {String msg;if (getAddress() null)msg orig.getMessage() null: getPort();elsemsg orig.getMessage() getAddress().toString() : getPort();BindException be new BindException(msg);be.initCause(orig);throw be;}}} 在连接器的初始化方法中会调用协议处理器的初始化方法协议处理器会调用Endpoint的初始化方法最终在Endpoint中完成了ServerSocket的创建并且绑定了端口此时还不能接受处理HTTP请求 当Catalina的load方法调用完成后除了懒加载的容器组件还未创建其它组件都已经创建出来了下一步就是启动这些组件上面只是初始化组件还没开始工作。 public void start()throws Exception {if( catalinaDaemonnull ) init();Method method catalinaDaemon.getClass().getMethod(start, (Class [] )null);method.invoke(catalinaDaemon, (Object [])null);} 跟调用load方法一样也是通过反射去调用Catalina的start方法。 public void start() {//…省略try {//调用Server的启动方法getServer().start();} catch (LifecycleException e) {log.fatal(sm.getString(catalina.serverStartFail), e);try {getServer().destroy();} catch (LifecycleException e1) {log.debug(destroy() failed for failed Server , e1);}return;}//…省略} 通过调用最顶层Server组件的start方法会导致Service组件的start被调用而Service组件的会调用Connector组件的start方法。 此处值得关注的还是Connector的start方法在上文中ServerSocket已经被创建并且绑定了端口但是还没有去执行接受连接的方法。   Connectorprotected void startInternal() throws LifecycleException {//…省略try {//调用协议处理器的startprotocolHandler.start();} catch (Exception e) {//…省略}mapperListener.start();}Http11Protocolpublic void start() throws Exception {try {endpoint.start();} catch (Exception ex) {}}JIoEndpointpublic final void start() throws Exception {if (bindState BindState.UNBOUND) {bind();bindState BindState.BOUND_ON_START;}startInternal();}public void startInternal() throws Exception {if (!running) {running true;paused false;//创建一个线程池if (getExecutor() null) {createExecutor();}initializeConnectionLatch();//创建连接接收器并放入线程池中接受连接startAcceptorThreads();//创建一个超时处理线程Thread timeoutThread new Thread(new AsyncTimeout(),getName() -AsyncTimeout);timeoutThread.setPriority(threadPriority);timeoutThread.setDaemon(true);timeoutThread.start();}}protected final void startAcceptorThreads() {int count getAcceptorThreadCount();//创建连接接收器acceptors new Acceptor[count];for (int i 0; i count; i) {acceptors[i] createAcceptor();String threadName getName() -Acceptor- i;acceptors[i].setThreadName(threadName);//一个连接接收器对应一个线程Thread t new Thread(acceptors[i], threadName);t.setPriority(getAcceptorThreadPriority());t.setDaemon(getDaemon());t.start();}}Acceptor public void run() {//…省略try {countUpOrAwaitConnection();Socket socket null;try {//此处调用ServerSocket的阻塞接受连接方法socket serverSocketFactory.acceptSocket(serverSocket);} catch (IOException ioe) {countDownConnection();// Introduce delay if necessaryerrorDelay handleExceptionWithDelay(errorDelay);// re-throwthrow ioe;}//…省略} 连接器start中调用协议处理器的start协议处理器最终调用Endpoint的start最终在Endpoint中创建了Acceptor(接收器)并且将Acceptor放入一个线程中异步处理因为BIO的socket会阻塞此刻整个tomcat的启动流程大致完成Tomcat启动完成之后就是在Acceptor中接受请求并处理了。 Tomcat启动时序图