新版本加了动态业务标识配置从 nacos 取的功能后,发现新增业务标识后创建任务的请求发不进去了,大概代码如下:
代码
controller
@Slf4j
@RestController
@RequestMapping({"/split"})
@Validated
public class PdfSplitController {
@Resource
private SplitPdfCommandApplication commandApplication;
@PostMapping("/createPdfJob")
public Result<String> createPdfJob(@Validated @RequestBody PdfSplitRequest pdfSplitRequest) {
return commandApplication.create(pdfSplitRequest);
}
}
SplitTask 定时任务
@Component
public class PdfSplitJobTask {
@Resource
private SplitPdfCommandServiceImpl splitPdfCommandService;
/**
* 拆分定时任务
*/
@Scheduled(fixedDelay = 1000)
public void splitPdfCommand() {
splitPdfCommandService.splitPdfCommand();
}
}
SplitPdfCommandApplication
@Service
public class SplitPdfCommandApplication {
@Resource
private SplitPdfCommandService splitPdfCommandService;
// 业务标识的集合,里面是个集合,nacos 动态刷新
@Resource
private BusinessProperties businessProperties;
/**
* 保存拆分请求
*/
public Result<String> create(PdfSplitRequest pdfSplitRequest) {
// 构建领域模型
CreatPdfJob creatPdfJob = new CreatPdfJob(
pdfSplitRequest.getBusinessId(),
pdfSplitRequest.getFileUrl()
);
// 校验,其实就是判断业务标识集合是否存在于list里
creatPdfJob.check(businessProperties.getBusinessList());
// 调用领域层
splitPdfCommandService.createCommand(creatPdfJob);
return Result.succeed(creatPdfJob.getPdfSplitJobId().getJobId());
}
}
SplitPdfCommandServiceImpl
@Service
@Slf4j
@RefreshScope
public class SplitPdfCommandServiceImpl implements SplitPdfCommandService {
/**
* 最大页码限制
*/
@Value("${split.maxPageNumber}")
private Integer maxPageNumber;
/**
* 最大文件大小
*/
@Value("${split.maxFileSize}")
private Long maxFileSize;
@Override
public void createCommand(CreatPdfJob creatPdfJob) {
try {
pdfContext.getSplitPdfRepository().save(creatPdfJob);
} catch (Exception e) {
throw new BizException(ErrorEnum.BIZ_ADD_JOB_ERROR.getCode(), ErrorEnum.BIZ_ADD_JOB_ERROR.getMessage());
}
}
BusinessProperties
@Configuration
@ConfigurationProperties(prefix = "business")
public class BusinessProperties {
public List<String> businessList;
public List<String> getBusinessList() {
return businessList;
}
public void setBusinessList(List<String> businessList) {
this.businessList = businessList;
}
}
第一时间重启,拆分服务恢复正常,但是后续发现按以下步骤操作,总会导致线程 park(但是这块代码新建一个项目也无法实现线程 park,不太清楚为什么)
- 正常请求,没什么问题
- 修改 nacos 配置,新增一个业务标识,发布,BusinessProperties 收到新集合,生效
- 用新增的业务标识请求,卡死
排查过程
- 找到 Java 进程的 PID
- jstack 打印线程信息,信息如下:
"http-nio-8080-exec-1@11566" daemon prio=5 tid=0x62 nid=NA waiting
java.lang.Thread.State: WAITING
at sun.misc.Unsafe.park(Unsafe.java:-1)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireShared(AbstractQueuedSynchronizer.java:967)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireShared(AbstractQueuedSynchronizer.java:1283)
at java.util.concurrent.locks.ReentrantReadWriteLock$ReadLock.lock(ReentrantReadWriteLock.java:727)
at org.springframework.cloud.context.scope.GenericScope$LockedScopedProxyFactoryBean.invoke(GenericScope.java:494)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
at com.rongda.split.domain.service.pdf.split.impl.SplitPdfCommandServiceImpl$$EnhancerBySpringCGLIB$$cf626698.createCommand(<generated>:-1)
at com.rongda.split.application.service.pdf.split.SplitPdfCommandApplication.create(SplitPdfCommandApplication.java:57)
at com.rongda.split.web.api.controller.pdf.PdfSplitController.createPdfJob(PdfSplitController.java:39)
at com.rongda.split.web.api.controller.pdf.PdfSplitController$$FastClassBySpringCGLIB$$2962b742.invoke(<generated>:-1)
- 在百度输入:LockedScopedProxyFactoryBean park,相关的文章较多,并且没找到相关的问题,但是大部分回答都指向一个注解:@RefreshScope
- 由于 Service 里的 @Value 注解一直都有,并且能动态刷新,所以猜测是因为这次加入了动态业务标识导致,也就是 Application 里的 BusinessProperties
- 由于动态添加业务标识的情况不多,所以时间比较多,既然有动态添加业务标识这个需求,那就想办法实现,目前有两种方式:
1. 动态获取改成从数据库或 redis 获取,但是这样的话需要编写新增业务标识的接口
2. 想办法解决这个问题 - 因为刚才百度查的结果都是 @RefreshScope 相关的文章,所以就怀疑是 @RefreshScope + @Value 的使用在目前项目里执行 lock 方法时和什么冲突了
- 所以尝试:新建一个 Properties 文件,service 中引用该 Properties,动态刷新的配置统一都用 Properties 文件,不再使用 @RefreshScope + @Value 实现动态刷新,最终解决
总结
最终也没有发现具体是什么问题,但是大概就是加锁的时候冲突了,哪里没释放锁。
这里只是记一下临时解决问题的思路。
以后使用 nacos 动态刷新都是用 Properties 方式来实现,避免未知的异常问题