最近处理的两个case

分享下最近处理了两个case,涉及到的主要是cpu sy高和依赖外网的dtd的问题。

Case I
有个应用换到了新的一批机器上,出现了同样的qps情况下cpu us和sy比原来机器高不少的现象,尤其是sy,高了非常多,按照以往的经验,cpu sy高主要是线程数多、线程竞争激烈,因为是同样的应用,同样的qps,也看过了应用的线程数和竞争状况,一切正常,然后想到新机器和原来的机器的不同点有一个是内核,原来的机器是2.6.18的,新的是2.6.32的,于是猜测可能是高精度定时器(关于高精度定时器之前的文章里有说过,简单来说就是2.6.32的内核支持纳秒级的调度,之前的版本只能支持到毫秒级)带来的“问题”(这个其实是提升,不是问题)。

按照这个思路,Java中如果要按比毫秒更小的单位去做唤醒等动作,通常最后都会调用到java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject.awaitNanos,于是用btrace去跟了下这里小于1ms(也就是1000000值)的调用,发现还真有,传入的值为100000,也就是100微秒,熟悉btrace的人自然知道这个地方可以直接获取到堆栈,于是按照堆栈翻了下业务代码,业务代码里有这么一行代码:
Object node = this.logQueue.poll(100L, TimeUnit.MICROSECONDS);

看到这一切就明了了,其实同样,这个地方压根就没有这么高精度的需求,否则在之前2.6.18内核跑的时候早就会懂了,因此要做的就是告诉开发,把这个值至少改到1ms以上…

哎,2.6.32内核经常中这种招,实在是悲催,明明是个好功能,但…

Case II
有个应用在发布的时候突然出现部分机器启动不了的现象,挖日志看到的是获取不到spring的dtd(貌似是因为最近spring网站不太稳定,不过暴露出来是好事),这个也算的上Java的经典问题,之前有篇专门说过这个问题。

于是开始查,看看是什么原因导致的,继续祭起btrace神器,跟踪的方法是跟踪什么地方调用了URL.openConnection,并且url是http开头,结尾是spring dtd的,很快就抓到了,发现竟然是jboss的SARDeployer在做parseDocument的时候调用到的,看的我当时真心傻眼了…

继续跟踪SARDeployer在parse哪些Document的时候会需要装载到spring dtd,看到SARDeployer在parse业务jar包里的几个*-service.xml的文件,这个时候才想起以前看过的jboss代码,SARDeployer是会扫描所有的deploy目录下的文件,包括jar包里的文件,并会尝试解析*-service.xml/*-ds.xml文件,看看是不是SARDeployer的描述文件…

这里用到的btrace脚本如下:
[code]
import static com.sun.btrace.BTraceUtils.*;
import com.sun.btrace.annotations.*;
import org.jboss.deployment.DeploymentInfo;

@BTrace public class Trace{
@OnMethod(
clazz=”org.jboss.deployment.SARDeployer”,
method=”parseDocument”
)
public static void traceExecute(DeploymentInfo di){
printFields(di);
}

@OnMethod(
clazz=”java.net.URL”,
method=”openConnection”,
location=@Location(Kind.RETURN)
)
public static void resolveEntity(@Self Object instance){
String protocol = str(get(field(“java.net.URL”, “protocol”),instance));
String file = str(get(field(“java.net.URL”, “file”),instance));
if(startsWith(protocol,”http”) && (endsWith(file,”.xsd”) || endsWith(file,”.dtd”))){
String authority = str(get(field(“java.net.URL”, “authority”),instance));
String path = str(get(field(“java.net.URL”, “path”),instance));
println(“=====================================”);
print(protocol);
print(“://”);
print(authority);
print(path);
println(” not found!”);
println(“who call:”);
jstack();
}
}
}
[/code]

扫描到的业务jar包里的*-service.xml文件里确实写了
[code]

[/code]
而jboss里显然是获取不到这个dtd的,于是就导致了连到外网去获取这个dtd文件了,一个简单办法是把上面这段改成:
[code]

[/code]
然后把spring-beans.dtd放到对应的jar里,就万事大吉了。

ps: 对于跑在jboss里的应用,更安全的是不要给自己的xml取名为*-service.xml的文件,一般来说没人会取成*-ds.xml文件,以免出现一些这种乱七八糟的问题。

Case的解决通常回头看是不太复杂的,解决Case通常最需要的是两点:
1. 对背后原理的理解,否则其实即使看到现象,也不知道如何定位到造成问题的具体代码;
2. 经验…这个真心只能靠实战,有些时候经验够丰富的话不懂原理也是能解决问题的,这个确实见过。

我有个很好奇的问题,我很久都没推广过自己的这个公众账号了,但为啥还是每天都偶尔会有零星的几个新增的关注的人,有点奇怪,不知道大家能回复下我是从哪知道的这个公众账号吗? 谢谢。

=============================
欢迎关注微信公众号:hellojavacases

关于此微信号:
分享Java问题排查的Case、Java业界的动态和新技术、Java的一些小知识点Test,以及和大家一起讨论一些Java问题或场景,这里只有Java细节的分享,没有大道理、大架构和大框架。

公众号上发布的消息都存放在http://hellojava.info上。

《最近处理的两个case》有1个想法

  1. get(field(“java.net.URL”, “protocol”),instance) 这句是field(“java.net.URL”, “protocol”)是取得URL对象的protocol属性,那后面的get(…,instance)是什么意思,谢谢!

发表评论

电子邮件地址不会被公开。 必填项已用*标注


*