最近项目中要从之前的spring task框架改成用elasticjob,经过一番探索,总算成功搞定。现在做下记录:
1、引入maven 的jar包依赖:
<!-- 引入elastic-job 模块 -->
<dependency>
<groupId>org.apache.shardingsphere.elasticjob</groupId>
<artifactId>elasticjob-lite-spring-boot-starter</artifactId>
<version>3.0.0-RC1</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>5.1.0</version>
</dependency>
2、配置定时任务的相关参数,以及注册中心的地址(需要提前配置好zookeeper,本地的话也可以用来学习,搭建好的话配置127.0.0.1:2181):
3、编写定时任务类:
@Slf4j
@Component
public class InnerActivityJob implements SimpleJob {
@Autowired
private InnerbuyActivityService innerbuyActivityService;
/**
* 自动修改内购活动状态
*
* @throws InterruptedException
*/
@Override
public void execute(ShardingContext shardingContext) {
System.out.println(String.format("------Thread ID: %s, 任务总片数: %s, 当前分片项: %s",
Thread.currentThread().getId(), shardingContext.getShardingTotalCount(), shardingContext.getShardingItem()));
//结束内购活动
List<InnerbuyActivity> willEndActivities = innerbuyActivityService.list(new LambdaQueryWrapper<InnerbuyActivity>()
.le(InnerbuyActivity::getEndTime, System.currentTimeMillis())
.ne(InnerbuyActivity::getStatus, InnerbuyActivityStatus.FINISHED.getValue()));
willEndActivities.forEach(willEndActivity -> {
innerbuyActivityService.end(willEndActivity.getId());
});
//启动内购活动
List<InnerbuyActivity> willStartActivities = innerbuyActivityService.list(new LambdaQueryWrapper<InnerbuyActivity>()
.le(InnerbuyActivity::getStartTime, System.currentTimeMillis())
.gt(InnerbuyActivity::getEndTime, System.currentTimeMillis())
.eq(InnerbuyActivity::getStatus, InnerbuyActivityStatus.WAIT.getValue()));
willStartActivities.forEach(willStartActivity -> {
innerbuyActivityService.start(willStartActivity.getId());
});
}
}
4、此时启动项目基本就可以了,看到任务正常执行就OK。不过,由于我们项目中的定时任务使用了代理,导致项目重启的时候会报任务名冲突,提示任务已经在注册中心存在。于是,我们加了以下的spring spi配置,让定时任务类在项目启动的时候读取到原本的class类名,就不会提示冲突了:
/**lol.redScarf.yjdf.infrastructure.elasticjob
* 需要使用这个类兼容获取spring cglib的bean,否则注册到注册中心会报错
* 需要使用SPI META-INF.services注册
*/
public class SpringProxyJobClassNameProvider implements JobClassNameProvider {
@Override
public String getJobClassName(final ElasticJob elasticJob) {
return AopUtils.isAopProxy(elasticJob) ? AopTargetUtils.getTarget(elasticJob).getClass().getName() : elasticJob.getClass().getName();
}
}
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.apache.shardingsphere.elasticjob.infra.exception.JobSystemException;
import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.support.AopUtils;
import java.lang.reflect.Field;
/**
* Aop target Utility.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class AopTargetUtils {
/**
* Get target object.
*
* @param proxy proxy object
* @return target object
*/
public static Object getTarget(final Object proxy) {
if (!AopUtils.isAopProxy(proxy)) {
return proxy;
}
if (AopUtils.isJdkDynamicProxy(proxy)) {
return getProxyTargetObject(proxy, "h");
} else {
return getProxyTargetObject(proxy, "CGLIB$CALLBACK_0");
}
}
private static Object getProxyTargetObject(final Object proxy, final String proxyType) {
Field h;
try {
h = proxy.getClass().getSuperclass().getDeclaredField(proxyType);
} catch (final NoSuchFieldException ex) {
return getProxyTargetObjectForCglibAndSpring4(proxy);
}
h.setAccessible(true);
try {
return getTargetObject(h.get(proxy));
} catch (final IllegalAccessException ex) {
throw new JobSystemException(ex);
}
}
private static Object getProxyTargetObjectForCglibAndSpring4(final Object proxy) {
Field h;
try {
h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
h.setAccessible(true);
Object dynamicAdvisedInterceptor = h.get(proxy);
Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
advised.setAccessible(true);
return ((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
// CHECKSTYLE:OFF
} catch (final Exception ex) {
// CHECKSTYLE:ON
throw new JobSystemException(ex);
}
}
private static Object getTargetObject(final Object object) {
try {
Field advised = object.getClass().getDeclaredField("advised");
advised.setAccessible(true);
return ((AdvisedSupport) advised.get(object)).getTargetSource().getTarget();
// CHECKSTYLE:OFF
} catch (final Exception ex) {
// CHECKSTYLE:ON
throw new JobSystemException(ex);
}
}
}
至此,可以完美跑起来了。