使用quartz实现多线程定时任务(极光推送),通过订单号尾数去处理会重复推送问题,如果需要更多的线程,可以使用分布式锁。
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.demo</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<mybatis-plus.version>3.3.2</mybatis-plus.version>
<oracle.driver.version>11.2.0.3</oracle.driver.version>
<java.version>1.8</java.version>
<lib.path>${basedir}/lib</lib.path>
<framework.version>1.7</framework.version>
<quartz.version>2.3.2</quartz.version>
<slf4j.version>1.7.25</slf4j.version>
</properties>
<dependencies>
<!--全栈式Web开发 包括Tomcat和spring-webmvc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--添加freemarker启动器依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!--引入JDBC-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- 引入Oracle驱动包 -->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>${oracle.driver.version}</version>
<scope>runtime</scope>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!--通过简单的注解的形式 简化java bean代码-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--aop面向切面编程-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!--字符串处理与校验的工具包-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
<!--极光提供https推送操作的相关依赖包-->
<dependency>
<groupId>cn.jpush.api</groupId>
<artifactId>jiguang-common</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>cn.jpush.api</groupId>
<artifactId>jpush-client</artifactId>
<version>3.3.4</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>${quartz.version}</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>${quartz.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.yml配置
以下有两个key的配置,如果只需一个则写一个appKey和secret即可。
hm:
appKey: app申请的key
secret: app申请的密钥
salesmanAppKey: app申请的密钥
salesmanSecret: app申请的密钥
threadId: 0
application-quartz.yml配置
quartz:
# jobGroup名称一致的情况下,不可出现相同jobName
jobs[0]:
jobName: 推送订单消息到APP-尾数01
# 以服务名为组名
jobGroup: hm
# 业务逻辑处理类的包名
jobClassName: com.zhitengda.job.PushMessageToAppJobOne
# cron表达式 每10秒执行一次
cronExpression: 0/10 * * * * ?
# 任务状态 1 正常 0 暂停
triggerState: 1
# 排序
sort: 1
jobs[1]:
jobName: 推送订单消息到APP-尾数23
# 以服务名为组名
jobGroup: hm
# 业务逻辑处理类的包名
jobClassName: com.zhitengda.job.PushMessageToAppJobTwo
# cron表达式 每10秒执行一次
cronExpression: 0/10 * * * * ?
# 任务状态 1 正常 0 暂停
triggerState: 1
# 排序
sort: 2
jobs[2]:
jobName: 推送订单消息到APP-尾数45
# 以服务名为组名
jobGroup: hm
# 业务逻辑处理类的包名
jobClassName: com.zhitengda.job.PushMessageToAppJobThree
# cron表达式 每10秒执行一次
cronExpression: 0/10 * * * * ?
# 任务状态 1 正常 0 暂停
triggerState: 1
# 排序
sort: 3
jobs[3]:
jobName: 推送订单消息到APP-尾数67
# 以服务名为组名
jobGroup: hm
# 业务逻辑处理类的包名
jobClassName: com.zhitengda.job.PushMessageToAppJobFour
# cron表达式 每10秒执行一次
cronExpression: 0/10 * * * * ?
# 任务状态 1 正常 0 暂停
triggerState: 1
# 排序
sort: 4
jobs[4]:
jobName: 推送订单消息到APP-尾数89
# 以服务名为组名
jobGroup: hm
# 业务逻辑处理类的包名
jobClassName: com.zhitengda.job.PushMessageToAppJobFive
# cron表达式 每10秒执行一次
cronExpression: 0/10 * * * * ?
# 任务状态 1 正常 0 暂停
triggerState: 1
# 排序
sort: 5
对应的处理类
这里只提供一个PushMessageToAppJobOne,其它一样的
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class PushMessageToAppJobOne implements Job, Serializable {
@Autowired
private TabAppMessagesServie tabAppMessagesServie;
@Override
public void execute(JobExecutionContext jobExecutionContext) {
try {
List<String> mantissa = new ArrayList<>();
mantissa.add("0");
mantissa.add("1");
tabAppMessagesServie.pushMessageToApp(mantissa);
} catch (Exception e) {
e.printStackTrace();
}
}
}
服务实现类
@Slf4j
@Service
public class TabAppMessagesServiceImpl extends ServiceImpl<TabAppMessagesMapper, AppMessage> implements TabAppMessagesServie {
@Value("${hm.appKey}")
private String appKey;
@Value("${hm.secret}")
private String secret;
@Value("${hm.salesmanAppKey}")
private String salesmanAppKey;
@Value("${hm.salesmanSecret}")
private String salesmanSecret;
@Value("${hm.threadId}")
private Long threadId;
@Autowired
private TabAppMessagesMapper tabAppMessagesMapper;
@Override
public List<AppMessage> getNeedPushMessage(AppMessageQry messageQry) {
return null;
}
@Override
public void pushMessageToApp(List<String> mantissa) {
try {
AppMessageQry messageQry = new AppMessageQry();
messageQry.setThreadId(threadId);
messageQry.setMantissa(mantissa);
List<AppMessage> messages = tabAppMessagesMapper.getNeedPushMessage(messageQry);
if (CollectionUtils.isNotEmpty(messages)) {
for (AppMessage message : messages) {
AppMessage result = pushMessage(message);
tabAppMessagesMapper.updateAppMessage(result);
}
}
} catch (Exception e) {
log.error(null, e);
}
}
public AppMessage pushMessage(AppMessage message) {
AppMessage result = new AppMessage();
try {
result.setGuid(message.getGuid());
result.setPushCount(message.getPushCount() == null ? 1 : message.getPushCount() + 1);
JPushClient jpushClient;
if (MessageTypeEnum.DISPATCH.getCode().equals(message.getMessageType())) {
jpushClient = new JPushClient(salesmanSecret, salesmanAppKey, null, ClientConfig.getInstance());
} else {
jpushClient = new JPushClient(secret, appKey, null, ClientConfig.getInstance());
}
log.info("<<<<<<<<< 推送消息类型: {} >>>>>>>>>", message.getMessageType());
Map<String, String> map = new HashMap<String, String>();
map.put("content", message.getContent());
map.put("messageType", message.getMessageType());
// 由于运单号会为空,原封装运单号message.getBillCode()变更为使用订单号
map.put("messageId", message.getOrderCode());
String alias = message.getEmpCode();
PushPayload pushPayload = PushPayload.newBuilder().setPlatform(Platform.android())
.setAudience(Audience.alias(alias))
.setNotification(Notification.android(message.getContent(), message.getMessageTitle(), map)).build();
Long start = System.currentTimeMillis();
PushResult pushResult = jpushClient.sendPush(pushPayload);
log.info("<<<<<<<<< 极光推送耗时:{}秒 >>>>>>>>>", (System.currentTimeMillis() - start));
if (pushResult.isResultOK()) {
result.setPushRemark("消息id:" + pushResult.msg_id + "推送成功");
result.setStatus(1L);
result.setSendSuccessDate(new Date());
log.info("<<<<<<<<< 极光推送成功 >>>>>>>>>");
}
} catch (APIRequestException e) {
Gson gson = new Gson();
String msg = e.getMessage();
PushResult pushResult = gson.fromJson(msg, PushResult.class);
result.setPushRemark(pushResult.error.getMessage());
result.setStatus(-1L);
log.error("极光推送失败API异常信息: {}", msg);
} catch (Exception e) {
String msg = e.getMessage();
if (msg.length() > 0) {
msg = msg.substring(0, 200);
}
result.setPushRemark(msg);
result.setStatus(-1L);
log.error("极光推送失败Exception异常信息: {}", e.getMessage());
}
return result;
}
}
最后就可以实现推送到app了。