Weblogic81中HttpCompleteMessageTimeout相关的两个异常
在网络性能较差的环境中,weblogic server的日志中经常能看到如下的两种异常,
1:####<Mar 1, 2005 12:18:57 PM PST> <Error> <HTTP> <****> <****> <ExecuteThread: '3' for queue: 'weblogic.kernel.System'> <<WLS Kernel>> <> <BEA-101083> <Connection failure. java.io.IOException: A complete message could not be read on socket: 'weblogic.servlet.internal.MuxableSocketHTTP@115954e - idle timeout: '30000'
ms, socket timeout: '100' ms', in the configured timeout period of '60' secs
2:####<2008/11/14 09:18:23 PM PST> <Info> <HTTP> <****> <****> <ExecuteThread: '0' for queue: 'weblogic.kernel.System'> <<anonymous>> <> <BEA-101326> <The server could not send the HTTP message during the configured timeout value. The socket has been closed.>
我们可以看到上面的两个异常都是因为在指定的timeout时间内完成数据包的发送。BEA-101083出现在客户端请求往服务器端发送请求数据的时间内没有结束,而BEA-101326则是发生在weblogic回写response的时候在指定的timeout内没有完成数据包发送。HttpCompleteMessageTimeout默认值为60秒,最大值为480秒。如果没有设定,会取server---->Protocols---->General中的CompleteMessageTimout值(general中配置适合于所有协议,包括Http, T3, IIOP等)。如果两个都没有设定,那么weblogic server会将该值设定为:CompleteMessageTimeoutTrigger. CLEANUP_TRIGGER_TIMEPERIOD_LOW=2secs。
注意:CompleteMessageTimeout是数据包,而不是整个请求的timeout时间。
对于BEA-101326,假如客户端请求下载一个100M的文件,这100M的文件将会被分解成N多TCP/IP packets进行传送(weblogic内部为chunk,默认的chunk大小为4080bytes,chunk的大小可以通过-Dweblogic. ChunkSize设定)。100M的文件即使在网络性能很好的情况下,也很难在60秒(CompleteMessageTimout的默认值)内完成,所以这个timeout不是针对整个reponse而言的。在weblogic实现中,每次回写数据包(Chunk)的时候,会将当前的OutputStream register到CompleteMessageTimeoutTrigger中,这样trigger被触发的时候,它会负责检查这个数据报是否在指定的timeout内完成发送,如果没有,则BEA-101326会记入到日志中,同时会关闭到客户端的socket,剩余的数据将无法继续写入。如果数据包在timeout之前已经写完,该OutputStream会从tigger中移除,tigger不会继续检查该OutputStream
1
static
void
writeChunks(Chunk header,Chunk c, OutputStream os,
int
size,
boolean
chunkXfer)
throws
IOException {
2 try {
3 trigger.register(os);
4 if (chunkXfer)
5 writeChunkTransfer(header,c, os);
6 else
7 writeChunkNoTransfer(header, c, os, size);
8 } finally {
9 trigger.unregister(os);
10 }
11 }
2 try {
3 trigger.register(os);
4 if (chunkXfer)
5 writeChunkTransfer(header,c, os);
6 else
7 writeChunkNoTransfer(header, c, os, size);
8 } finally {
9 trigger.unregister(os);
10 }
11 }
BEA-101326通常跟网络性能有关系(60秒内,4k的数据无法发送完成),首要解决方法就是调优网络。从weblogic方面来看,我们可以通过调整如下两个参数来解决这个问题,但这不是解决问题的关键。
1:增加HttpCompleteMessageTimeout,最大值为480秒
2:减小weblogic.ChunkSize
至于BEA-101083,通常是因为客户端发送的请求数据不能在HttpCompleteMessageTimeout内完成,常见的两种情况是:性能很差的网络环境、黑客攻击(连续的向服务器写入数据,但每次写入很少的数据,例如,10bytes/secs或更少)。客户端数据读取超时是通过SocketMuxer.TimeoutTrigger实现的。这个trigger同时还负责IdleTimeout(KeepAlive的HttpConnection的Duration,默认为30秒)。
1
/*
package
*/
final
int
checkTimeout(
long
idleTimeout,
long
msgTimeout) {
2 int status = OKAY;
3 long interval;
4 synchronized ( this ) {
5
6 if (messagePending()) {
7 if (msgTimeout <= 0 ) return OKAY;
8 /*
9 *get time left for reading message from client. When SocketMuxer begins reading packets
10 *from http connection. System.currentTimeMills() is recorded info SocketInfo as lastMessageReadingStartedTimeMillis
11 *and in getMessageIntervalMillis(), it returns (System.currentTimeMills() - info.lastMessageReadingStartedTimeMillis)
12 *If reading finishes in time, SocketMuxer will set messagePending to false.
13 */
14 interval = getMessageIntervalMillis(msgTimeout);
15 if (interval <= msgTimeout) return OKAY;
16 status = MSG_TIMEOUT;
17 } else {
18 //idle timeout is checked here
19 }
20 // for MuxableSocketHttp, ms.requestTimeout() is always true, i.e., timeout affects such connection
21 if ( ! ms.requestTimeout()) return OKAY;
22 setCloseOnly();
23 }
24 return status;
25 }
2 int status = OKAY;
3 long interval;
4 synchronized ( this ) {
5
6 if (messagePending()) {
7 if (msgTimeout <= 0 ) return OKAY;
8 /*
9 *get time left for reading message from client. When SocketMuxer begins reading packets
10 *from http connection. System.currentTimeMills() is recorded info SocketInfo as lastMessageReadingStartedTimeMillis
11 *and in getMessageIntervalMillis(), it returns (System.currentTimeMills() - info.lastMessageReadingStartedTimeMillis)
12 *If reading finishes in time, SocketMuxer will set messagePending to false.
13 */
14 interval = getMessageIntervalMillis(msgTimeout);
15 if (interval <= msgTimeout) return OKAY;
16 status = MSG_TIMEOUT;
17 } else {
18 //idle timeout is checked here
19 }
20 // for MuxableSocketHttp, ms.requestTimeout() is always true, i.e., timeout affects such connection
21 if ( ! ms.requestTimeout()) return OKAY;
22 setCloseOnly();
23 }
24 return status;
25 }
对于IdleTimeout,可以参考SocketInfo.lastIoInitiatedTimeMillis
Last time we had some activity on socket, for enforcing idle timeout of a socket. A -1 value indicates that there is no pending IO.
用于控制keepAlive的HttpConnection空闲多长时间后将被weblogic关闭,所以空闲,是指Socket上没有IO操作。
要解决BEA-101083,基本和BEA-101326差不多,还是要解决网络性能问题,另外就是加强网络安全管理,防止黑客攻击。
最后在说一下CompleteMessageTimeout设定,weblogic admin console上提示改设定是动态的,即不需要重启,但实际并不是这样的。如果该设定是要解决BEA-101083,那么它是动态的,不要重启,而如果是要解决
BEA-101326,那么要使设定生效,重启是必须的。我们看一下两个trigger就知道了,
BEA-101083,SocketMuxer,
1
protected
class
TimeoutTrigger
implements
Triggerable {
2 public void trigger(Schedulable sched) {
3 MuxableSocket[] socks = getSockets();
4 for ( int i = 0 ; i < socks.length; i ++ ) {
5
6 MuxableSocket ms = socks[i];
7 SocketInfo info = ms.getSocketInfo();
8 if (info == null ) continue ;
9 long idleTimeout = ms.getIdleTimeoutMillis();
10 long msgTimeout = ms.getCompleteMessageTimeoutMillis();
11
12 switch (info.checkTimeout(idleTimeout, msgTimeout)) {
13
14 }
15
16 }
17 }
18 }
2 public void trigger(Schedulable sched) {
3 MuxableSocket[] socks = getSockets();
4 for ( int i = 0 ; i < socks.length; i ++ ) {
5
6 MuxableSocket ms = socks[i];
7 SocketInfo info = ms.getSocketInfo();
8 if (info == null ) continue ;
9 long idleTimeout = ms.getIdleTimeoutMillis();
10 long msgTimeout = ms.getCompleteMessageTimeoutMillis();
11
12 switch (info.checkTimeout(idleTimeout, msgTimeout)) {
13
14 }
15
16 }
17 }
18 }
BEA-101326, ChunkUtils
1
static
{
2 trigger = new CompleteMessageTimeoutTrigger();
3 }
2 trigger = new CompleteMessageTimeoutTrigger();
3 }
从上面代码可以看出,BEA-101083的trigger每次被触发的时候,都会重新读取配置参数,所以修改是动态的。而BEA-101326,tigger是静态变量,当ChunkUtils被load的时候实例化,实例化的时候,它会读取配置参数。但静态变量的初始化只有一次,除非ChunkUtils被重新装载,所以需要重启才能使该设定生效。