idea 配置多个tomcat引发的血案

javax.management.InstanceNotFoundException: Catalina:type=Server
修改tomcat端口时却仍是8080
没有使用在idea tomcat中配置的端口的情况

查看堆栈错误原因

mvc跨服务器上传时,需要配置多个服务器,因为对idea启动服务器的流程不够清晰,所以两个tomcat server的configure中设置了同个tomcat 地址,引发了上面的三个同个根源的问题,之后是解决的思路与总结

  1. 直接复制粘贴相应的错误报告(javax.management.InstanceNotFoundException: Catalina:type=Server)到google和百度上查询
  • 出现的多为修改host和Connector port 后出现的无法匹配问题,为单个服务器的问题,而我单个服务器的配置时并无问题,也有查过两个服务器与报错两者相关联的报错信息,但是可能因为问题的描述不对,导致无论stackflow还是其他论坛中相同的问题,答案却并不符合,快速解决的路子不通了,只能自己翻看错误报告了

  • 首先,错误报告中最吸引人的字样是protocol中的 8080端口,因为idea的中无论 http port无论如何修改,到这里总是显示有关8080 端口的信息。

这里尝试过先启动idea中设置的8080 端口的服务器,再启动idea中设置为8089的同一个文件夹下的tomcat,服务器会开始部署,但到最后会出现javax.management.InstanceNotFoundException: Catalina:type=Server

而先开启8089的tomcat,再开启8080的,则会直接显示8080端口被占用。

idea 的tomcat组件的作用:如同start.up一样帮我们预处理,其中的http port 的设置作用是idea会先帮我检查这个端口有没有被使用,若有,则直接报错端口占用(若不一致此时他并不会检查你的tomcat的server.xml中有没有启动这个端口);若没有则,设定部署的一些参数,然后寻找tomcat服务器文件的位置以启动tomcat,启动application server中设置的tomcat服务器地址中的catalina.bat,这也是出现javax.management.InstanceNotFoundException: Catalina:type=Server该错误的地方,最后打开默认地址。

  • 很明显,多次更改了idea中的配置,仍然出现的是8080端口,证明应该是启动过程中引用的其它文件的问题(因为引用的是tomcat服务,所以自然而然地想到tomcat服务器的配置问题),向上翻找启动顺序。

  • 看到了\bin\catalina.bat,可以让我们很容易联想到刚学tomcat时,最原始的启动方式:startup.bat,他也是通过启动catalina.bat来启动tomcat服务器的配置的,从这我们可以看出idea的tomcat server配置相当于自己完成了startup.bat功能。

tomcat startup.bat

startup.bat详解**

if "%OS%" == "Windows_NT" setlocal //判断当前系统是否是window系统

rem --------------------------------------------------------------------------- //rem 是注释(下同)

rem Start script for the CATALINA Server

rem

rem $Id: startup.bat 302918 2004-05-27 18:25:11Z yoavs $

rem ---------------------------------------------------------------------------

rem Guess CATALINA_HOME if not defined

set CURRENT_DIR=%cd% //设置当前目录

if not "%CATALINA_HOME%" == "" gotogotHome //如果设置了CATALINA_HOME环境变量 ,就直接到下面的gotHome处

set CATALINA_HOME=%CURRENT_DIR% //如果没有设置CATALINA_HOME,就设置CATALINA_HOME为当前目录(其实这里她假设你进入tomcat的安装目录)

if exist "%CATALINA_HOME%\bin\catalina.bat" gotookHome//判断一下catalina.bat是否找到了,找到了就直接到下面的gotHome处

cd .. //这里他是假设你开始已经进入到了tomcat的bin目录,所以就退到上一级目录

set CATALINA_HOME=%cd%//现在再设置CATALINA_HOME为tomcat的安装目录

cd %CURRENT_DIR% //这里是进入dos的当前目录

:gotHome

if exist "%CATALINA_HOME%\bin\catalina.bat" gotookHome //再次判断catalina.bat是否找到了,找到了就直接到下面的okHome处,没有的话,就只能提示你啦!

echo The CATALINA_HOME environment variable is not defined correctly

echo This environment variable is needed to run this program

goto end

:okHome

set EXECUTABLE=%CATALINA_HOME%\bin\catalina.bat //设置要执行的文件

rem Check that target executable exists

if exist "%EXECUTABLE%" gotookExec //再次判断catalina.bat是否找到了,找到了就直接到下面的okExec处,没有的话,就提示。

echo Cannot find %EXECUTABLE%

echo This file is needed to run this program

goto end

:okExec

rem Get remaining unshifted command line arguments and save them in the

set CMD_LINE_ARGS= //这里是设置参数

:setArgs

if ""%1""=="""" gotodoneSetArgs //判断参数是否加入完成

set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1 //将参数组成一行,接在后面

shift

gotosetArgs

:doneSetArgs

call "%EXECUTABLE%" start %CMD_LINE_ARGS% //执行catalina.bat,最好将这行改为:echo "%EXECUTABLE%" start %CMD_LINE_ARGS% 以便阅读、理解本文件的作用

:end

Catalina.bat

  • 可以看出startup.bat 主要是进行一些环境的判断,然后启动catalina.bat,Catalina.bat是tomcat所有脚本中最重要的脚本,完成几乎所有的tomcat操作。如启动,关闭等等,都是由catalina.bat脚本来完成的。

Catalina.bat脚本解析

Catalina.bat是tomcat所有脚本中最重要的脚本,完成几乎所有的tomcat操作。如启动,关闭等等,都是由catalina.bat脚本来完成的。接下来,我将对Tomcat catalina.bat脚本进行分析。

首先省去catalina.bat开头诸多注解,这些注解主要是讲解各个变量是干什么的。

rem Guess CATALINA_HOME if not defined 查看是否在tomcat目录下,与startup.bat里相同,不解释了。
set CURRENT_DIR=%cd%
if not "%CATALINA_HOME%" == "" goto gotHome
set CATALINA_HOME=%CURRENT_DIR%
if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHome
cd ..
set CATALINA_HOME=%cd%
cd %CURRENT_DIR%
:gotHome
if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHome
echo The CATALINA_HOME environment variable is not defined correctly
echo This environment variable is needed to run this program
goto end
:okHome

rem Get standard environment variables
if exist "%CATALINA_HOME%\bin\setenv.bat" call "%CATALINA_HOME%\bin\setenv.bat" 如果存在setenv.bat脚本,调用它,我的tomcat 没有这个脚本

rem Get standard Java environment variables
if exist "%CATALINA_HOME%\bin\setclasspath.bat" goto okSetclasspath 查看是否存在setclasspath.bat脚本,如果存在,转到okSetclasspath位置
echo Cannot find %CATALINA_HOME%\bin\setclasspath.bat 否则输出下面两行,并退出
echo This file is needed to run this program
goto end
:okSetclasspath okSetclasspath位置

set BASEDIR=%CATALINA_HOME% 设定BASEDIR变量与CATALINA_HOME变量值相同
call "%CATALINA_HOME%\bin\setclasspath.bat" %1 调用setclasspath.bat脚本并加上参数
if errorlevel 1 goto end 如果存在错误 退出

rem Add on extra jar files to CLASSPATH 设定JSSE_HOME变量,如果存在加入CLASSPATH,不存在跳过

if "%JSSE_HOME%" == "" goto noJsse 检查是否存在JSSE_HOME变量
set CLASSPATH=%CLASSPATH%;%JSSE_HOME%\lib\jcert.jar;%JSSE_HOME%\lib\jnet.jar;%JSSE_HOME%\lib\jsse.jar 如果有加入到CLASSPATH变量后面
:noJsse
set CLASSPATH=%CLASSPATH%;%CATALINA_HOME%\bin\bootstrap.jar 将bootstrap.jar加入到CLASSPATH里

if not "%CATALINA_BASE%" == "" goto gotBase 如果CATALINA_BASE变量不为空,跳过,转到gotBase位置
set CATALINA_BASE=%CATALINA_HOME% 如果为空,将CATALINA_BASE设为CATALINA_HOME变量的值
:gotBase

if not "%CATALINA_TMPDIR%" == "" goto gotTmpdir CATALINA_TMPDIR不为空,跳过,转到gotTmpdir位置
set CATALINA_TMPDIR=%CATALINA_BASE%\temp 如果为空,将 CATALINA_TMPDIR设为%CATALINA_BASE%\temp变量的值(即tomcat\temp)
:gotTmpdir

if not exist "%CATALINA_HOME%\bin\tomcat-juli.jar" goto noJuli 如果不存在tomcat-juli.jar这个类,转到noJuli位置
set JAVA_OPTS=%JAVA_OPTS% -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager - Djava.util.logging.config.file="%CATALINA_BASE%\conf \logging.properties" 如果存在,将变量加入到JAVA_OPTS里
:noJuli

set JAVA_OPTS=%JAVA_OPTS% -Xms128m -Xmx512m -Dfile .encoding=UTF8 -Duser.timezone=GMT -Djava.security.auth.login.config=%CATALINA_HOME%/conf/jaas.config 设定JAVA_OPTS变量

echo Using CATALINA_BASE: %CATALINA_BASE% 输出CATALINA_BASE变量值
echo Using CATALINA_HOME: %CATALINA_HOME% 输出CATALINA_HOME变量值
echo Using CATALINA_TMPDIR: %CATALINA_TMPDIR% 输出CATALINA_TMPDIR变量值
if ""%1"" == ""debug"" goto use_jdk 如果变量%1里存在debug ,转到use_jdk位置
echo Using JRE_HOME: %JRE_HOME% 输出JRE_HOME变量值
goto java_dir_displayed 转到java_dir_displayed
:use_jdk
echo Using JAVA_HOME: %JAVA_HOME% 输出JAVA_HOME变量值
:java_dir_displayed
下面几行设定相应变量
set _EXECJAVA=%_RUNJAVA%
set MAINCLASS=org.apache.catalina.startup.Bootstrap
set ACTION=start
set SECURITY_POLICY_FILE=
set DEBUG_OPTS=
set JPDA=

if not ""%1"" == ""jpda"" goto noJpda
set JPDA=jpda
if not "%JPDA_TRANSPORT%" == "" goto gotJpdaTransport
set JPDA_TRANSPORT=dt_shmem
:gotJpdaTransport
if not "%JPDA_ADDRESS%" == "" goto gotJpdaAddress
set JPDA_ADDRESS=jdbconn
:gotJpdaAddress
if not "%JPDA_SUSPEND%" == "" goto gotJpdaSuspend
set JPDA_SUSPEND=n
:gotJpdaSuspend
if not "%JPDA_OPTS%" == "" goto gotJpdaOpts
set JPDA_OPTS=-Xdebug -Xrunjdwp:transport=%JPDA_TRANSPORT%,address=%JPDA_ADDRESS%,server=y,suspend=%JPDA_SUSPEND%
:gotJpdaOpts
shift
:noJpda

if ""%1"" == ""debug"" goto doDebug 如果%1为debug,转到doDebug,运行debug模式
if ""%1"" == ""run"" goto doRun 如果%1为run,转到doRun,运行正常模式
if ""%1"" == ""start"" goto doStart 如果%1为start,转到doStart,启动tomcat
if ""%1"" == ""stop"" goto doStop 如果%1为stop,转到doStop,关闭tocmat
if ""%1"" == ""version"" goto doVersion 如果%1为version,转到doVersion,显示tomcat的版本号

echo Usage: catalina ( commands ... ) 如果%1没有上述内容,输出下面几行,并结束
echo commands:
echo debug Start Catalina in a debugger
echo debug -security Debug Catalina with a security manager
echo jpda start Start Catalina under JPDA debugger
echo run Start Catalina in the current window

echo run -security Start in the current window with security manager
echo start Start Catalina in a separate window
echo start -security Start in a separate window with security manager
echo stop Stop Catalina
echo version What version of tomcat are you running?
goto end

:doDebug
shift 将%2里的值转到%1
set _EXECJAVA=%_RUNJDB% 将变量 _EXECJAVA设为_RUNJDB变量的值
set DEBUG_OPTS=-sourcepath "%CATALINA_HOME%....\jakarta-tomcat-catalina\catalina\src\share"
设定DEBUG_OPTS变量

if not ""%1"" == ""-security"" goto execCmd
如果%1不为-security,转到execCmd位置

shift 将%2里的值转到%1
echo Using Security Manager 输出该行
set SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy
设定SECURITY_POLICY_FILE变量的值

goto execCmd 转到execCmd位置

:doRun
shift 将%2里的值转到%1
if not ""%1"" == ""-security"" goto execCmd 如果%1不为-security,转到execCmd位置
shift 将%2里的值转到%1
echo Using Security Manager 输出该行
set SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy
设定SECURITY_POLICY_FILE变量的值

goto execCmd 转到execCmd位置

:doStart
shift 将%2里的值转到%1
if not "%OS%" == "Windows_NT" goto noTitle 如果OS变量不为Windows_NT,转到noTitle
set _EXECJAVA=start "Tomcat" %_RUNJAVA% 设定_EXECJAVA变量的值
goto gotTitle 转到gotTitle位置
:noTitle
set _EXECJAVA=start %_RUNJAVA% 设定 _EXECJAVA 变量的值
:gotTitle
if not ""%1"" == ""-security"" goto execCmd 如果%1不为-security,转到execCmd位置
shift 将%2里的值转到%1
echo Using Security Manager 输出该行
set SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy
设定SECURITY_POLICY_FILE变量的值

goto execCmd 转到execCmd位置

:doStop
shift 将%2里的值转到%1
set ACTION=stop 将ACTION的变量设为stop
set CATALINA_OPTS= 设CATALINA_OPTS为空

goto execCmd 转到execCmd位置

:doVersion 显示tomcat版本号
%_EXECJAVA% -classpath "%CATALINA_HOME%\server\lib\catalina.jar" org.apache.catalina.util.ServerInfo 执行该命令
goto end 结束该程序

:execCmd
rem Get remaining unshifted command line arguments and save them in the
以下几行将命令参数存入CMD_LINE_ARGS变量中

set CMD_LINE_ARGS=
:setArgs
if ""%1""=="""" goto doneSetArgs
set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
shift
goto setArgs
:doneSetArgs

rem Execute Java with the applicable properties
if not "%JPDA%" == "" goto doJpda 如果JPDA变量不为空,转到doJpda位置
if not "%SECURITY_POLICY_FILE%" == "" goto doSecurity
如果SECURITY_POLICY_FILE变量不为空,转到doSecurity位置

如果都没有执行下面命令,并结束该程序
%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end
:doSecurity 执行下面命令,并结束该程序
%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Djava.security.manager -Djava.security.policy=="%SECURITY_POLICY_FILE%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end
:doJpda
如果SECURITY_POLICY_FILE变量不为空,转到doSecurityJpda位置,为空执行下面命令,并结束该程序

if not "%SECURITY_POLICY_FILE%" == "" goto doSecurityJpda
%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %JPDA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end
:doSecurityJpda 执行下面命令,并结束该程序
%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %JPDA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Djava.security.manager -Djava.security.policy=="%SECURITY_POLICY_FILE%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end

:end

tomcat相关配置文件

  • 即使不细看,也可以知道其中会调用相关的配置文件以启动相关的服务,那么,相关的服务在哪里呢,我们需要知道tomcat服务器的总的存贮目录的功能

img

  • 出现端口相关的问题,肯定在配置文件中,其中配置文件中最重要的便是server.xml和web.xml

preview

tomcat整体架构

  • 要想理解这两个文件的作用,首先要认识tomcat的整体架构

img

Server 表示整个servlet容器,因此Tomcat容器中只有一个Server实例

  • Service 表示一个或多个Connector的集合。这些Connector共享同一个Container来处理其他请求。在一个Server中可以包含多个Service,这些Service相互独立

  • Connector Tomcat连接器,用于监听并转换为Socket请求,将该请求交由Container处理,支持不同的协议及不同IO方式

  • Container 表示能够接收请求并返回响应的一类对象。在Tomcat中存在不同级别的容器:Engine、Host、Context、Wrapper

  • Engine 表示整个Servlet引擎,Engine为最高级别的容器。尽量Engine不是直接处理请求的容器却是获得目标容器的入口

  • Host 表示Engine中的虚拟机,与一个服务器的网络名有关,如域名等。客户端可以使用这个网络名连接服务器,这个名称必须要在DNS服务器上注册

  • Context 用于表示ServletContext,在Servlet规范中,一个ServletContext表示一个Web应用

  • Wrapper 表示Web应用中定义的Servlet

  • Executor 表示Tomcat组件间可以共享的线程池

server.xml和web.xml

  • 因此我们可以大致猜到Server.xml的作用,我们出现问题的地方也在这里了,若是我们设置了不同http port,绕过了idea的端口检测,且启动同一个catalina.bat,我们将读取同一个Server.xml文件,那我们的server 端口将会重复,出现:InstanceNotFoundException: Catalina:type=Server(即catalina在server创建阶段出现错误,无法找到相应实例),其中server的识别是依据相应的端口号来识别的,而相同的catalina.bat的启动导致读取的server.xml仍是相同的server端口号,导致异常
  • 解决方案便是:启动时两个服务器各自应用两个独立的tomcat服务器文件,或者修改catalina.bat(并创建另外的server文件),使其第二次启动时跳过已被占用了的端口文件,读取其他的配置。所以我选择了前者。

server.xml的作用:

preview

  • 另外还有一个要注意的时这个web.xml,我们在idea或者eclipse中创建的web项目中也包含了web.xml,两者有什么关系呢
  • 这是他们的父类,它里面的配置,如果动态web工程没有覆盖,就会被“继承”下来。我们会发现,conf/web.xml里配置了默认的DefaultServlet(org.apache.catalina.servlets.DefaultServlet,以及一些cgi等)和filter (很多类org.apache.catalina.filters. *)

web.xml的作用:

preview

联想到SpringMVC

  • 这便是web项目中web.xml最原始的模样,springmvc中的dispatcherServlet也是如此,将匹配到的/路径下的拿到dispatcherServlet中去转发或重定位

Web.xml对servlet和Litsener等的读取方式

  • 而若想知道为什么单凭一个链接便可以接入到相应的servlet,你便需要了解整个流程
  • 我们通过catalina.bat开始创建服务器,将server.xml环境配置完后,tomcat是通过生成一个StandardServer将其与catalina实例绑定,进行注册等操作后,便会开始部署项目,创建StandardContext
  • 部署项目时一个StandardContext便是一个web项目,其会读取web.xml,最终解析是在ContextConfig中的webConfig 方法解析完生成一个WebXml 对象

web.xml加载过程(其中节点的加载顺序context-param -> listener -> filter -> servlet )
1.全局默认配置文件:tomcat目录下conf/web.xml解析为WebXml对象。
2.具体应用程序下的web.xml解析:例 /examples/WEB-INF/web.xml
3.扫描应用程序examples下的所有Jar,查找META-INF/web-fragment.xml文件并解析,保存到变量fragments中,格式<String,WebXml>。
4.已经检索到的WebXml配置进行排序。
5.基于SPI机制查找ServletContainerInitializer的实现。
6.扫描/WEB-INF/classes下面的类,处理Annotation:@ WebServlet,@ WebFilter,@ WebListener
7、扫描jar包中的Annotation:web-fragment.xml所在的jar包。
8、合并配置:所有web-fragment.xml中的配置,合并到应用程序examples中的配置web.xml。
9、合并配置:全局默认配置defaults 合并到应用程序的web.xml。

  • 接着用这个对象去创建ServletContext(单例共享)

10、将JSP转换为Servlet。
11、将合并后的最终配置web.xml对象webXml应用到servlet容器(ServletContext),装配servlet上下文;
12、将配置信息保存容器中,供其他组件访问,使得其他组件不需要再次重复上面的步骤去获取配置信息了。
13、查找jar包中的静态资源。
14、将ServletContainerInitializer应用到上下文。

  • 同时StandardContext在读取web.xml中过的**创建中的类实例,创建监听器。 ****
  • 有了监听器,我们便可以监听有到达我们的端口的消息(url-pattern);有了ServletContext,我们就可以依据相应的相应的servlet-class去执行(或者再经过disparcherServlet去转发),若是找不到相应的servlet,则会用defaultServlet(连出错处理的servlet都没有的情况下)去处理
  • 有这种servlet则加入到ServletContext的Map中
<servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>HelloServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>HelloServlet</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>
  • springmvc将我们配置servlet的步骤省略了,都靠DisparcherServlet接收,并由他进行后续的处理,而我们只需要依照他的应用规则相应的注释。
  • 需要相应实体类便由spring用工厂模式加载到的容器中去调用
  • Spring与myBatis的集成便需要细细述说了,但就是Spring的事务管理的基础上,改换一下sqlsession的创建流程,换一个不同种类的代理类(与.getmapper相似),并用专门的SpringManageTransaction用以获取保存的connection。
  • 而上面这个Spring与myBatis的集成就是真正的惨案了
    https://www.cnblogs.com/eternal-heathens/p/13391261.html
posted @ 2020-07-28 15:07  eternal_heathens  阅读(1721)  评论(0编辑  收藏  举报