当开发软件程序时,我们通常需要
异步
记录操作日志和用户信息。操作日志可以用于跟踪系统的运行情况、查询问题以及审计目标的,而使用用户信息记录则可以用于个人化服务、权限管理和统计分析等等方面。然而,在某种情况下,我们可能会遇到以下问题:在异常任务
中访问ThreadLocal
的信息时报错。
问题现象和产生的原因
异步任务在不同的线路中执行,而ThreadLocal
只在当前线路中可见。这意味着,当我们在异步任务中尝试访问之前在主线路或其它线路中设置的ThreadLocal
变化量时,就会发生错误。这种错误可能会导致无法正确获取用户信息,并影响到日志记录的准确性和完整性。
解决方案一:转发用户信息(推荐)
为了解决异常步骤中无法访问ThreadLocal
的问题,一种常见的解决方法是通过参数传递用户信息。具体而言,我们可以在登录验证通过滤器中,登录成功后将使用用户信息作为参数传递给异常步骤。这样,异步任务就可以直接使用传递的用户信息,而不依赖于ThreadLocal
。
在现实过程中,我们需要在登录验证过滤器中获取用户信息,并将其传递给异常任务。可以通过在异常任务的结构制造数中接收用户信息参数的方式来现实。这样,在异步任务中就可以使用该用户信息,进行日志记录或其他相关操作。
解决方案二:使用InheritableThreadLocal
另一种解决方案是使用InheritableThreadLocal
。InheritableThreadLocal
代替ThreadLocal
,它是ThreadLocal
的一个子类,它可以在子线中继承父线中设置的信息。通过使用InheritableThreadLocal
我们可以在主线中设置用户消息到ThreadLocal
,然后在异常任务的子线程序中获得这些信息。
需要注意的是,使用InheritableThreadLocal
可能会引入一些性能开发,并且需要小心处理线路之间的共享数据。在使用时,我们需要确保合适时机设置和清理中的价值,以防止内存泄漏或数据混乱InheritableThreadLocal
等问题。
解决方案三:使用单线程的线程池
如果你希望登录过滤器和异步任务在同一个线程中执行,可以使用单线程的线程池
来执行异步任务。这样,所有的异步任务都会在同一条线路
中按顺序执行,保证登录通过滤器和异步任务的执行在同一条线路上。
在这种方案中,我们需要创建一个单线程的线程池,然后将异常步骤提交给该线程池执行。这样,所有的异常步骤事务都会在同一条线路中执行,可以确保登录过过滤器中设置的ThreadLocal
中用户信息在异常操作执行期间可用。
需要注意的是,使用单线程的线程池可能会影响系统的并发性能
,因为所有的异常步骤都在同一个线程中顺序执行。因此,该方案适合用于对并发性能要求不高的场景。
方案一详细实现步骤
这里用一个简单的demo项目演示方案一的详细解决步骤
模拟记录操作日志,在查询方法加上Log自定义注解,前端发起请求执行该方法就会执行记录操作日志。
1、过滤器对请求进行过滤和放行,判断session
中是否有用户信息。
2、如果session
中有用户信息则查询数据库校验用户信息,校验通过后返回用户信息。
3、将用户信息存入session
中,下一次请求就会携带用户信息。
4、将用户信息存入ThreadLocal
中并放行。
5、进入AOP方法增强代码,处理日志信息,这里可以获取到ThreadLocal
信息。
6、处理日志信息方法也可以获取ThreadLocal
信息,通过参数传递
的方式将用户信息传递给异步任务方法。
7、进入异步任务方法,这里可以看到通过方法传参
可以获取到用户信息,而使用ThreadLocal
则获取不到用户信息。
8、执行SQL将操作日志信息写入到数据库中。
以上是针对该问题的几种解决方案。具体选择哪种解决方案需要根据具体的业务和需求进行相应的调整。