使用greys找到泄漏的本地线程变量值

2018/03/12 12:17:54 No Comments

在程序代码中,出现过这样一种情况,在一个新的线程组中,尝试去获取一个threadLocal的值,按照相应的程序代码,应该是不能获取到的。但是在实际的运行过程中,却发现能够获取到相应的变量值。
在我们的程序代码中,使用了shiro来存储相应的用户信息,即一个简化版的的权限管理程序。其中,在标准的web程序中,shiro拦截器会把相应的会话信息存储在session中,并且在应用代码中,可以通过一个threadLocal的ThreadContext来获取相应的subject,进而拿到相应的会话值.相应的代码如下所示:

    public static UserId getUserId() {
        Optional<Subject> optional = Optional.ofNullable(ThreadContext.getSubject());
        return optional.map(t -> t.getSession(false))
                .map(t -> t.getAttribute("userId"))
                .orElse(null);
    }

因为相应的数据为web调用时才会注入到sessionId,而如果是一个定时类的任务,那么理论上应该是不会有相应的session对象存在,那么即不会有相应的subject存在,在调用此方法时即会返回在调用getSubject时失败。但是在实际上时,却发生调用subject不为null,进而在调用getSession方法时出现了错误信息。

相应的调用链看起来应该像是这样

  1. 线程1调用了 ThreadContext.setSubject 方法,设置了session信息
  2. 线程1完成整个业务方法的运行,相应的session信息被销毁,但相应的removeSubject方法并没有被调用
  3. 线程1重新以任务的形式执行任务代码,其中调用了getSubject,但不能拿到相应的数据

在这种调用链中,我们只看到了最终的现场,但原始的设置值的现场即不能复现,即不清楚数据是什么时候,哪个调用任务进行处理的。整个问题称之为数据泄漏,即数据在不应该出现的地方出现了。
处理这种问题,一种作法即是异常标记法。详细的步骤如下所示:

  1. 在调用 ThreadContext.setSubject 时,设置一个异常线程栈(Exception),此里面封装了当前调用的整个路径
  2. 将异常线程栈(字符串形式)存储在一个threadLocal变量中,方便后面获取
  3. 在调用 getSubject 时,如果发生了相应的错误信息,即表示复现了相应的错误场景
  4. 这时候即将之前存储到threadLocal中的字符串获取出来,打印出原始的setSubject调用路径

整个作法参考于alibaba druid的数据库连接泄漏检测。

本文介绍了在不修改源代码的情况下,并且在线上环境,使用greys,来监控相应的调用,当出现了满足条件的场景时,自动打印出相应的调用路径。

(更多…)

安全有效地提升simpleDateFormat性能

2011/08/21 22:02:50 No Comments

    以下为转http://www.thedwick.com/blog/2008/04/simpledateformat-performance-pig/的一篇关于simpleDateformat中使用的一些误区以及怎么样来安全有效地使用simpleDateFormat。在本文中,描述了创造simpleDateFormat的昂贵代价,以及使用静态实例的多线程错误以及同步时的低效问题,并提出一个提升的解决办法。
    全文如下:

    Just yesterday I came across this problem “in the wild” for the third time in my career so far: an application with performance problems creating tons of java.text.SimpleDateFormat instances. So, I have to get this out there: creating a new instance of SimpleDateFormat is incredibly expensive and should be minimized. In the case that prompted this post, I was using JProfiler to profile this code that parses a CSV file and discovered that 50% of the time it took to suck in the file and make 55,000 objects out of it was spent solely in the constructor of SimpleDateFormat. It created and then threw away a new one every time it had to parse a date. Whew!

  “Great,” you think, “I’ll just create one, static instance, slap it in a field in a DateUtils helper class and life will be good.”
    Well, more precisely, life will be good about 97% of the time. A few days after you roll that code into production you’ll discover the second cool fact that’s good to know: SimpleDateFormat is not thread safe. Your code will work just fine most of the time and all of your regression tests will probably pass, but once your system gets under a production load you’ll see the occasional exception.
    “Fine,” you think, “I’ll just slap a ‘synchronized’ around my use of that one, static instance.”
    Ok, fine, you could do that and you’d be more or less ok, but the problem is that you’ve now taken a very common operation (date formatting and parsing) and crammed all of your otherwise-lovely, super-parallel application through a single pipe to get it done.
    What would be better is to use a ThreadLocal variable so you can have your cake and eat it, too:

(更多…)