为何网站打不开,北京顺义网站建设,品牌策划书案例,做网站需要什么人最近不是在救火#xff0c;就是在救火的路上。 也没什么特别可写的#xff0c;今天记录下最近遇到的一个问题#xff0c;个人觉得挺有意思#xff0c; 待有缘人阅读 言归正传#xff0c;售后反馈#xff1a; 营业查询中付款方式为第三方支付的几条银行缴费#xff0c;创… 最近不是在救火就是在救火的路上。 也没什么特别可写的今天记录下最近遇到的一个问题个人觉得挺有意思 待有缘人阅读 言归正传售后反馈 营业查询中付款方式为第三方支付的几条银行缴费创建操作员和修改操作员为系统操作员系统操作员一般只用于系统配置不会用于处理业务 这类异常数据会导致月底与财务报表不正确 看到这个问题的时候第一感觉就是有点蒙 在我们的系统中的配置操作员和业务操作员是分开的。银行缴费的操作员记录的是指定的业务操作员 查看系统日志发现日志中有问题交易流水120231116205337hF544的记录信息 从日志分析问题数据不是通过客户充值产生的数据而是银行对账产生的数据。 所谓对账就是指本系统的缴费和银行系统的缴费做对比对方有我方没有则补数据对方没有我方有则冲正数据 双方都有则按照银行的数据修复成一直。 通过日志定位问题数据都是对方有我方没有而补录的数据。
2023-11-17 09:41:00,997 INFO [com.bank.server.checkAccount.CheckAccountJob] Checking detail account...
2023-11-17 09:41:01,001 INFO [com.bank.server.checkAccount.CheckSingleContext] boss中不存在关于流水号120231116205337hF544的付款记录重做对应交易
2023-11-17 09:41:01,004 INFO [com.bank.server.checkAccount.CheckSingleContext] there is not detail that flowNo is 120231116205337hF544,transaction again
查看补录缴费程序的实现公司内部框架中的持久化处理默认会设置表数据的创建操作员operator为登录session中的操作员信息。对账是后台任务处理的后台任务没有操作员登录没有操作员登录那么肯定就没有session. 处理到这儿我开始有点儿见鬼的感觉想不明白为什么一个后台任务突然有了session有了登录信息
private static void setCreateInfo(ApplicationSession session, AbstractSystemModel entity) {Operator createOperator null;if (session null || session.getValue(operator) null) {createOperator new Operator();}else {createOperator (Operator) session.getValue(operator);}if (entity.getCreateOperator() null || entity.getCreateOperator().getId() null) {entity.setCreateOperator(createOperator);}if (entity.getCreateDate() null) {entity.setCreateDate(new Timestamp(System.currentTimeMillis()));}}
统计稽核问题数据发现后台对账任务记录信息也很奇怪有修改操作员记录的数据表示对账任务有session,没有修改操作员的记录表示此时对账任务没有session, 这个现象说明当前问题是偶然现象 当操作员登录后会在线程变量里缓存session信息用于快速获取登录信息。从上面的数据库分析执行对账的后台任务有的时候有session有的时候没有session .这个现象很像是线程变量增加session后没有清空引起的。
我们知道Jboss是通过线程池记录来减少线程的开销 难道是现场复用引起的。 我有了一个大胆的猜测
系统管理员登录后办理业务使用A线程 登录时设置了session到A线程的线程变量中系统管理员完成业务操作后因某个原因退出登录没有清空线程A的线程变量信息也就是没有清空session线程A回归线程池后再次被对账任务使用,此事后台任务从线程变量里取值就能错误获取线程变量里的记录信息
为了验证猜测我写了下面的一段测试代码模拟猜测场景。
LocalThreadTest 实现Runable接口并在其中设置一个线程变量线程池有3个线程并分配10个随机10秒的任务。 将第2个任务的线程设置一个线程变量观察第2个任务的线程被再次使用的时候线程变量是否存在
package com.thread.localthread;import java.util.Date;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class LocalThreadTest implements Runnable {private static final ThreadLocalString LOCAL_SESSION new ThreadLocalString();private Integer index;public LocalThreadTest(Integer i){indexi;}public static void main(String[] args) {ExecutorService fixedThreadPool Executors.newFixedThreadPool(3);for (Integer i 0; i 10; i) {fixedThreadPool .execute(new LocalThreadTest(i));}fixedThreadPool.shutdown();}Overridepublic void run() {if(index2){LOCAL_SESSION.set(session isThread.currentThread().getName());}System.out.println(LOCAL_SESSION.get());Random random new Random();int randomNumber random.nextInt(4) 1;try {Thread.sleep(randomNumber*1000);} catch (InterruptedException e) {e.printStackTrace();}}
}执行结果发现和想的一样当线程回归线程池后。再次使用的时候线程变量仍会被线程持有。并不会被清除。
null
null
session ispool-1-thread-3
null
session ispool-1-thread-3
null
null查看代码还有一个疑问没有解决开启对账任务的时候是通过start()方法来启动线程的而不是run方法run()方法不会新创建线程。 如果主线程中使用ThreadLocal记录线程变量 当使用start()方法运行线程时是真正创建一个子线程。 对于线程变量ThreadLocal对象来说子线程不会持有主线程中ThreadLocal的信息。这么来看对账处理中ApplicationSession 应该也没有session才对。
public void billingServiceStart(int processInstanceId,long billingServiceInstanceId) {try {//.....略部分代码AbstractBillingServiceContext context (AbstractBillingServiceContext) BeanFactoryHolder.get().getBean(contextName);context.setBillingServiceInstance(billingServiceInstance);Thread t new Thread(context);t.start();} catch (Exception e) {logger.error(case:, e);BillingServiceInstance bsi billingServiceInstanceDao.queryBillingServiceInstance(billingServiceInstanceId);bsi.setProcessStatus(ServiceProcessStatus.ERROR_FINISHED);bsi.setInfoStr(e.toString() : e.getMessage());bsi.setEndDate(new Date());billingServiceInstanceDao.modifyBillingServiceInstance(bsi);}}
再进一步分析ApplicationSessionHolder的代码才解决了自己的疑惑。 ApplicationSessionHolder对象中使用的线程变量是InheritableThreadLocal对象InheritableThreadLocal是ThreadLocal 的子类两者的区别是InheritableThreadLocal创建时可以获取主线程的线程变量值
public final class ApplicationSessionHolder
{private static final InheritableThreadLocalApplicationSessionHolder LOCAL_SESSION new InheritableThreadLocal();private boolean clear;private ApplicationSession session;private ApplicationSessionHolder(ApplicationSession session){this.session session;}
} public class InheritableThreadLocalT extends ThreadLocalT {} 至此定位问题原因 第一次遇到session错乱的问题 算是涨经验了 前一篇线程池技术总结