resin常见有关问题admin管理员组文章数量:1646314
1.1. Resin停止响应 ●可能是一个线程死锁的问题,应该进行线程转储。 ●启用完全调试日志模式,检查日志最后的纪录看看发生了什么。 1.2. Resin不停重启动 ●启用完全调试日志模式,检查记录看看Resin为什么不停的重启它。 1.3. java.lang.OutOfMemoryError错误,应用程序内存溢出 ●使用JVM启动参数增加堆(heap)内存。 ●转储堆,看看那个对象无法被垃圾回收器无法回收。 ●转储线程,检查占用着对象的不能释放的线程 一个OutOfMemoryError错误通常意味着堆(heap)内存被用尽。一般是应用程序代码保持了对不在使用的对象的引用,垃圾回收器无法对其进行回收。转储堆,能够查到什么代码和什么种类的对象被占用了。如果对转储或者其它监视工具显示服务器和你的程序实际没有超出堆内存,那么 OutOfMemoryError意味着JVM超出了虚拟内存,也就是底层的malloc()调用失败。通常这种情况,通过使用操作系统工具显示内存使用,JVM自己能够显示其自己的堆内存,但是操作系统工具确显示进程占用了大量的内存。在Windows下使用任务管理器,Unix下使用top或者ps 命令。 JVM无法进行堆内存分配可能有如下原因: ●线程,特别是线程堆占用虚拟内存。 ●JNI库可能调用malloc或者nmap占用虚拟内存。这包括很多数据库驱动,也包含一些Resin使用的JNI代码。 ●对于.jar/.zip文件,JDK要分配虚拟内存。如果你打开了大量的jar文件,你可能会遇到问题。可以想到用于打开jar的getResourceAsStream没有关闭将会耗尽.jar内存。 1.4. 运行一会儿,服务器开始变得非常慢 ● 这可能是一个垃圾回收问题。如果你的内存缺乏,然后又创建了大量的对象,这导致垃圾回收器耗尽CPU。如果你内存溢出,JVM将会慢慢停止(连续地进行垃圾收集)直到它死亡。 ○ 监视垃圾收集。 ○ 转储堆,看看是否是有对象无法被回收。 ○ 参看JVM垃圾回收参数调整的文档获得更多垃圾回收的信息。 ● 可能有一个死循环的线程或者一个请求耗尽资源。回应一个请求的线程如果不能返回,Resin就没法再次利用它,那么可用来服务的线程就会越来越少。 ○ 进行线程转储,检查可能占用对象的无法释放的线程。 1.5. CPU尖峰,高的CPU使用率 ● 转储线程,检查那些线程在无限循环。 ● 检查垃圾收集的部分。 1.6. 会话(sessions)变成null,会话丢失 1.6.1. 调试日志 首先启用调试日志。特别是浏览器请求提交的头信息能够显示一个客户端的JSESSIONID状态,日志也能说明Resin什么时候识别、创建和失效一个会话。 1.6.2. Resin会话配置 另一个可能是session-max设置过低,导致当前用户建立会话的数量大于你设置的这个值。另一个可能是会话超时,你可以通过session-timeout标签来配置它。 <web-app id='/'> ... <session-config> <!-- timeout after 120 minutes --> <session-timeout>120</session-timeout> <!-- up to 4096 sessions at once --> <session-max>4096</session-max> </session-config> ... </web-app> 1.6.3. 应用程序重载 无论何时,一个java源文件、web.xml或者resin.xml改变,Resin都会重启应用程序。如果这个情况发生,你当前的会话就会丢失,除非你配置了一个持久性会话存储。 1.6.4. 浏览器cookie的局限 一些用户报告,如果他们的应用程序使用大量的cookie,浏览器将会丢弃旧的cookie为新的腾出空间。这就会出现浏览器丢失了Resin 用来跟踪会话的cookie。IE浏览器用户特别容易遇到这个问题。如果你的应用程序使用大量的cookie,最好的解决方案就是减少cookie数量和 cookie数据的大小。Resin使用一个单一的cookie其存储相对很少的数据用拉跟踪用户的会话ID。应用程序存储在cookie中的信息可以使用HttpSession对象来存储。作为最后的手段,你可以配置Resin总是使用URL重写,这需要把enable-cookies设置成 false。由于安全的原因URL重写式不推荐的,因为重写URL增加了重写某些页面丢失调用的高可能性。 <web-app id='/'> ... <session-config> <enable-cookies>false</enable-cookies> <enable-url-rewriting>true</enable-url-rewriting> </session-config> ... </web-app> 1.6.5. cookie域名的问题 如果你的cookie域名不兼容也可能丢失会话。例如,如果你有一个服务器使用cookie域名"hogwarts",另一个使用 "qa.hogwarts",在浏览器中"hogwarts"的cookie会干扰在"qa.hogwarts"上的会话。方法是改变cookie域名"hogwarts"为"www.hogwarts"。 你可以在session-config标签中设置 cookie域名。 1.6.6. cookie名称冲突 如果你使用Resin和另一个应用服务器(例如Tomcat),你可能遇到这个冲突,因为它们使用相同的cookie名称(他通常是 JSESSIONID) 来跟踪会话。Resin提供session-cookie 和 ssl-session-cookie让你可以改变Resin使用的cookie名称。 改变用来跟踪会话的cookie名称的片断: <cluster> ... <session-cookie>RJESSESSIONID</session-cookie> 1.6.7. URL重写 如果你忘记了重写一个URL,一个需要重写的用户当访问到这个URL时将丢失他们的会话。Resin在一个用户浏览器和一个会话 (session)之间建立一个关联,是通过为每一个新请求返回一个惟一的id。这可通过两种方式之一来完成:使用cookie或者URL重写。 Resin首先尝试向用户浏览器发送一个包含惟一会话ID的cookie来跟踪一个用户的会话。有时Resin不能建立cookie,不是因为用户在其浏览器禁用了cookies就是因为某些浏览器不支持它们(例如一些HDML和WML浏览器)。如果cookie不能建立那么就会使用URL重写。在这种情况下,Resin重写每一个它提交给用户的URL,让其包含一个名称为_jsessionid的参数。然后为每一个新来的请求做的第一件事就是查找这个参数,如果这个参数存在那么就知道一个会话已经建立,它移出参数并使用它来查找用户会话对象。URL重写需要开发者的协同合作。开发者必须编码每一个URL 引用让Resin有一个合适的机会放置_jsessionid参数。 使用JSTL实现URL重写 <%@ taglib prefix='c' uri='http://java.sun/jstl/core' %> Time to go <a href="<c:url _fcksavedurl="<c:url _fcksavedurl="<c:url value='home.jsp' />">Home</a>! 使用Java scriptlet实现URL重写 <% String homeUrl = response.encodeURL("home.jsp"); %> <%-- the presentation --%> Time to go <a href="<%= homeUrl %>">Home</a>! 1.7. J2EE规范,javax.servlet包规范1.3和Resin不兼容 参看清除classpath环境变量。 1.8. Unsupported major.minor version 48.0 这个错误经常在发现一个冲突的jar时发生,参看清除classpath环境变量。 如果环境变量classpath被完全清除,然而一个JDK或者旧Resin的一个jar或者一些其它组件出现在的什么地方,如果你已经在那些地方添加了,在你的JAVA_HOME树里的一些jar可能有一个问题,那里可有一个和你的web程序WEB-INF/lib/目录下冲突的jar。另一种可能是你还没设置JAVA_HOME,或者你使用了一个冲突的JDK的一些组件。 如果在Windows上,检查JAVA_HOME之外的java.exe的拷贝,例如C:/WINDOWS/java.exe或者在你PATH路径里其它地方的java.exe。 1.9. 读取POST数据的问题 首先启用调试日志。调试日志会显示发送到Resin的请求,提供一些Resin如何处理这些数据的信息。最重要的是确保在读取POST参数之前编码设置正确。浏览器总是发回和输出页面编码相同的参数。因为请求不包含编码,应用程序代码需要确保编码匹配。因此第一件事就是确定发送到浏览器的表单的编码。你的应用程序总应该指定它。一旦你指定了它,你就知道浏览器POST使用编码。(这里UTF-8是个自然的编码选择,我不能确信你为什么使用其它的编码)。在读取POST参数之前确保设置了正确的编码,你可以调用request.setCharacterEncoding(encoding)来设置编码。 2. 技巧方法 2.1. 启用调试日志 Resin使用JDK日志工具提供了大量的诊断信息。通过使用一个空名称(匹配所有名字)可以启用完全调试日志,调试级别为“全部”。因为将会产生大量信息,把这些信息放在一个单独的文件中比较好。 2.1.1. 服务器和所有应用程序的完全调试日志 下面的配置每天创建一个日志,当一个问题出现时用来查找问题出现在什么地方。因为日志配置在resin.xml中,日志的捕捉是服务器和其上所有应用程序的。日志输出的信息在 $RESIN_HOME/log/debug.log。 <!-- resin.xml --> <resin xmlns="http://caucho/ns/resin"> <log-handler name="" level="all" path="log/debug.log" timestamp="[%H:%M:%S.%s] {%{thread}} " /> <logger name="" level="finer" /> </resin> 有其它一些的日志配置选项,请参看Resin日志文档。 2.1.2. 一个web应用程序的完全调试日志 通常你一般仅需要一个程序输出的调试日志。日志配置记录放在<web-app-root>/WEB-INF/web.xml中,那么仅这个web应用程序的日志信息被输出到日志文件中。下面的配置每天创建一个调试日志,位置<web-app-root>/WEB-INF /work/debug.log。 <!-- <web-app-root>/WEB-INF/web.xml --> <web-app> ... <log name="" path="WEB-INF/work/debug.log" timestamp="[%H:%M:%S.%s] {%{thread}} " /> <logger name="" level="finer" /> ... </web-app> 2.2. 线程转储 如果应用程序好像有问题或者超出资源泄露,线程转储能够显示服务器的状态。对于服务器调试Java的县城转储是一个重要的工具。因为 Servlet是多线程的,没有处理好的话很可能出现死锁,或者出现死循环和导致内存溢出错误。特别是你使用了第三方软件例如数据库、EJB和Corba ORBs。 2.2.1. 使用JDK5工具转储线程 在JDK5里可以使用jps和jstack,一个快捷的命令行方法获得当前所有线程的堆栈跟踪信息。 # jps 12903 Jps 20087 Resin # jstack 20087 Attaching to process ID 20087, please wait... Debugger attached successfully. Client compiler detected. JVM version is 1.5.0-beta2-b51 Thread 12691: (state = BLOCKED) - java.lang.Object.wait(long) (Compiled frame; information may be imprecise) - com.caucho.util.ThreadPool.runTasks() @bci=111, line=474 (Compiled frame) - com.caucho.util.ThreadPool.run() @bci=85, line=423 (Interpreted frame) - java.lang.Thread.run() @bci=11, line=595 (Interpreted frame) Thread 12689: (state = BLOCKED) - java.lang.Object.wait(long) (Compiled frame; information may be imprecise) - com.caucho.util.ThreadPool.runTasks() @bci=111, line=474 (Compiled frame) - com.caucho.util.ThreadPool.run() @bci=85, line=423 (Interpreted frame) - java.lang.Thread.run() @bci=11, line=595 (Interpreted frame) ... 2.2.2. 通过发送一个信号转储线程 在 Windows, ctrl-break会产生线程转储。 在Unix, "kill -QUIT" 会产生线程转储。 2.2.3. 如果发送信号无效时的线程转储 你可以在启动JVM时指定附加的参数允许附加一个调试器而不是发送信号来转储线程。你然后在任何时候附加调试器来得到线程转储。 这种方法在所有的操作系统上得到支持。 下面是是逐步的指导: 1. 使用附加的参数启动Resin来允许一个调试器附加: resin.xml for debugging <resin xmlns="http://caucho/ns/resin"> <cluster id=""> <server-default> <jvm-arg>-Xdebug</jvm-arg> <jvm-arg>-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5432</jvm-arg> </server-default> <server id="" address="127.0.0.1" port="6800" /> </cluster> </resin> 2. 等待,直到你认为应用程序出现了死锁或者失去控制。 3. 打开另一个终端 (window), 使用jdb连接正在运行的Resin实例: $JAVA_HOME/bin/jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=5432 jdb会显示类似如下信息: Set uncaught java.lang.Throwable Set deferred uncaught java.lang.Throwable Initializing jdb ... > 4. 使用 "suspend" 命令, 然后 "where all"命令获得一个线程转储: 例子: jdbc suspend > suspend All threads suspended. > where all tcpConnection-6862-3: [1] java.lang.Object.wait (native method) [2] com.caucho.server.TcpServer.accept (TcpServer.java:650) [3] com.caucho.server.TcpConnection.accept (TcpConnection.java:208) [4] com.caucho.server.TcpConnection.run (TcpConnection.java:131) [5] java.lang.Thread.run (Thread.java:536) tcpConnection-543-2: [1] java.lang.Object.wait (native method) [2] com.caucho.server.TcpServer.accept (TcpServer.java:650) [3] com.caucho.server.TcpConnection.accept (TcpConnection.java:208) [4] com.caucho.server.TcpConnection.run (TcpConnection.java:131) [5] java.lang.Thread.run (Thread.java:536) .. 5. 使用 "resume" 命令来恢复进程 > resume Unix 用户(和Windows上的Cygwin用户)可以使用一个脚本: resin-thread-dump.sh #!/bin/sh echo -e "suspend\nwhere all\nresume\nquit" | $JAVA_HOME/bin/jdb -connect \ com.sun.jdi.SocketAttach:hostname=localhost,port=5432 虽然没有进行过严格基准测试,好像使用线程转储参数启动的JVM在性能上影响不大。 2.2.4. 理解线程转储 在任何情况下,你会最终得到类似如下的跟踪调试信息(不同的JDK有稍微的差别): Full thread dump: "tcpConnection-8080-2" daemon waiting on monitor [0xbddff000..0xbddff8c4] at java.lang.Object.wait(Native Method) at com.caucho.server.TcpServer.accept(TcpServer.java:525) at com.caucho.server.TcpConnection.accept(TcpConnection.java:190) at com.caucho.server.TcpConnection.run(TcpConnection.java:136) at java.lang.Thread.run(Thread.java:484) "tcpConnection-8080-1" daemon waiting on monitor [0xbdfff000..0xbdfff8c4] at java.lang.Object.wait(Native Method) at com.caucho.server.TcpServer.accept(TcpServer.java:525) at com.caucho.server.TcpConnection.accept(TcpConnection.java:190) at com.caucho.server.TcpConnection.run(TcpConnection.java:136) at java.lang.Thread.run(Thread.java:484) "tcpConnection-8080-0" daemon waiting on monitor [0xbe1ff000..0xbe1ff8c4] at java.lang.Object.wait(Native Method) at com.caucho.server.TcpServer.accept(TcpServer.java:525) at com.caucho.server.TcpConnection.accept(TcpConnection.java:190) at com.caucho.server.TcpConnection.run(TcpConnection.java:136) at java.lang.Thread.run(Thread.java:484) "tcp-accept-8080" runnable [0xbe7ff000..0xbe7ff8c4] at java.PlainSocketImpl.socketAccept(Native Method) at java.PlainSocketImpl.accept(PlainSocketImpl.java:413) at java.ServerSocket.implAccept(ServerSocket.java:243) at java.ServerSocket.accept(ServerSocket.java:222) at com.caucho.server.TcpServer.run(TcpServer.java:415) at java.lang.Thread.run(Thread.java:484) "resin-cron" daemon waiting on monitor [0xbe9ff000..0xbe9ff8c4] at java.lang.Thread.sleep(Native Method) at com.caucho.util.Cron$CronThread.run(Cron.java:195) "resin-alarm" daemon waiting on monitor [0xbebff000..0xbebff8c4] at java.lang.Thread.sleep(Native Method) at com.caucho.util.Alarm$AlarmThread.run(Alarm.java:268) "Signal Dispatcher" runnable [0..0] "Finalizer" daemon waiting on monitor [0xbf3ff000..0xbf3ff8c4] at java.lang.Object.wait(Native Method) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:108) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:123) at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:162) "Reference Handler" daemon waiting on monitor [0xbf5ff000..0xbf5ff8c4] at java.lang.Object.wait(Native Method) at java.lang.Object.wait(Object.java:420) at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:110) "main" waiting on monitor [0xbfffd000..0xbfffd210] at java.lang.Thread.sleep(Native Method) at com.caucho.server.http.ResinServer.waitForExit(ResinServer.java:674) at com.caucho.server.http.ResinServer.main(ResinServer.java:821) at com.caucho.server.http.HttpServer.main(HttpServer.java:95) 每个线程都被命名了。这里有一些通用的名称:
线程名称 | 描述 |
tcp-accept-8080 | 在8080端口监听新连接的线程 |
tcpConnection-8080-3 | 处理从8080端口连接的servlet线程 |
tcp-cron | Resin的run-at线程 |
tcp-alarm | Resin的警告线程 |
<resin xmlns="http://caucho/ns/resin">
...
<log-handler name='com.caucho.server.http' level='finer'
path='log/http.log' />
<log-handler name='com.caucho.server.connection' level='finer'
path='log/http.log' />
...
</resin>
侦听和监视一个web浏览器和Resin之间传递的原始数据能够提供很有价值的信息。这个原始数据包括浏览器提交的信息头和内容,及Resin返回给浏览器的信息头和内容。 Apache Axis jar包含了一个工具"tcpmon",它可以用来侦听和监视浏览器和Resin之间的传输。使用tcpmon, 你要指定一个"listen port" 、一个 "target host" 、一个"target port"。例如,如果你通常运行Resin在8080端口上,你可以启动tcpmon使用"listen port"9090端口,一个localhost的目标主机和一个目标端口8080。现在你可以在浏览器中使用一个url de>http://localhost:9090。这时浏览器就会使用tcpmon。tcpmon会纪录发送的请求,同时转发内容到8080端口上的Resin,也会纪录Resin返回的数据并把它也发送回浏览器。de> 2.6. 使用一个外部编译器 Resin默认使用内部(internal)编译器,因为它是很容易使用的。有时内部编译器会导致错误,抛出错误或者简单挂起和占用一个线程。解决方法是在resin.xml中改变编译器为"javac"。 <javac compiler="javac" args="" /> 当然也可以使用Jikes等编译器。 2.7. 调整栈内存避免线程限制 每一个线程分配了一个栈,如果栈尺寸太大,当线程数量增大时可能内存溢出。请参看JVM参数调整的文章。 2.8. 使用操作系统的 netstat 命令获得当前 TCP/IP 端口的使用 netstat命令可用来获取当前系统的网络状态。 unix$ netstat -anp win> netstat -an -a 指示侦听的和非侦听的套接字都显示。-n 指示显示端口号而不是端口名(例 http)。-p 显示正在使用套接字的进程。因为Windows下的netstat命令和UNIX下的有些不同,-p选项在Winodws系统上无效。 2.8.1. 连接状态 连接状态可能是最重要的信息。可查看netstat命令帮助获得相关状态的详细描述。 "LISTEN" or "LISTENING" 表示,进程在套接字上等待连接。 "TIME_WAIT"表示包处理结束之后套接字仍在等待的状态。连接关闭之后,套接字会被操作系统保持在打开状态一个短期的时间。即使连接完全关闭了,在网络上也可能有些偏离的包需要连接。TIME_WAIT就是保持套接字足够长的打开时间来捕捉这些偏离的包,以至于这些偏离的包不会传输到在同一个套接字上的新连接上。 2.8.2. 端口使用 如果Resin显示不能绑定到一个端口,这意味着可能令一个其它进程在使用这个端口,netstat可以查出那个程序在使用这个端口。因为netstat产生了很多信息,应该滤掉那些没用的信息。下面的例子是查找使用80端口的程序:
版权声明:本文标题:resin常见有关问题 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/dianzi/1729425752a1200861.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论