定时程序设计方案分享
定时程序设计分享
定时程序设计方案分享
1. 背景
本文所指的定时程序
并非狭义上的定时程序,而是泛指通过定时触发程序
的方式,并结合数据库来实现某些业务场景。
2. 方案分析
3.1 场景一
3.1.1 需求
我们需要将已完成
的订单数据推送给第三方系统。
3.1.2 SQL
DROP TABLE IF EXISTS order01;CREATE TABLE order01 (
order_id BIGINT UNSIGNED NOT NULL COMMENT "订单ID",
order_desc VARCHAR(256) NOT NULL DEFAULT "" COMMENT "订单描述",
order_state TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT "订单状态[0:初始, 10:已支付, ... 100:已完成]",
order_push_state TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT "订单推送状态[0:初始, 100:推送成功]",
create_time DATETIME NOT NULL COMMENT "创建时间",
update_time DATETIME NULL COMMENT "修改时间",
PRIMARY KEY (order_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT="订单表(示例01)";
3.1.3 服务
SQL服务
<!-- 获取一条带推送订单 --><selectOne id="getOrder01" dsKey="timerReadDB" txRef="tx_01">
select * from order01 where order_state = 100 AND order_push_state = 0 LIMIT 1
</selectOne>
<!-- 完成一条订单的推送 -->
<update id="completePushOrder01" dsKey="timerWriteDB" txRef="tx_02">
update order01 set
order_push_state = 100,
update_time = #{push_update_time|now()}
where
order_id = #{order_id} AND
order_push_state = 0
</update>
JAVA服务
/** * 方案3.1相关推送服务入口
*/
public void pushOrder01(XCO request) {
// 1. 获取一条待推送订单
XCO getRes = ServiceActuator.executeAlone("timer/getOrder01", new XCO());
if (0 != getRes.getCode()) {
log.error("获取一条待推送订单异常: " + getRes.getMessage());
return;
}
XCO order = getRes.getData();
if (null == order) {
return;// 暂无待推送订单
}
// 2. 推送给第三方
ServiceActuator.executeAlone("timerService/receiveOrder", order);
// 3. 标记此订单已经推送
ServiceActuator.executeAlone("timer/completePushOrder01", order);
}
3.1.4 Timer
<!-- 方案3.1相关Timer --><timer scheduled="/10 * * * * ?" service="timerService/pushOrder01" desc="推送订单(方案3.1)" sync="true"/>
3.2 场景二
3.2.1 需求
我们需要将已完成
的订单数据推送给第三方系统,如果推送失败,该订单需要延后10分钟,才可发起下次推送。
3.2.2 SQL
DROP TABLE IF EXISTS order02;CREATE TABLE order02 (
order_id BIGINT UNSIGNED NOT NULL COMMENT "订单ID",
order_desc VARCHAR(256) NOT NULL DEFAULT "" COMMENT "订单描述",
order_state TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT "订单状态[0:初始, 10:已支付, ... 100:已完成]",
order_push_state TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT "订单推送状态[0:初始, 90:推送失败, 100:推送成功]",
scheduled_push_time DATETIME NOT NULL COMMENT "计划推送时间",
create_time DATETIME NOT NULL COMMENT "创建时间",
update_time DATETIME NULL COMMENT "修改时间",
PRIMARY KEY (order_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT="订单表(示例02)";
3.2.3 服务
SQL服务
<selectOne id="getOrder02" dsKey="timerReadDB" txRef="tx_01"><![CDATA[ select * from order02 where order_state = 100 AND order_push_state = 0 AND #{current_push_time|now()} >= scheduled_push_time LIMIT 1
]]></selectOne>
<update id="completePushOrder02" dsKey="timerWriteDB" txRef="tx_02">
<if test="{flag} == 0">
update order02 set
order_push_state = 100,
update_time = #{time|now()}
where
order_id = #{order_id} AND
order_push_state = 0
</if>
<else>
update order02 set
scheduled_push_time = #{scheduled_push_time},
update_time = #{time|now()}
where
order_id = #{order_id} AND
order_push_state = 0
</else>
</update>
JAVA服务
/** * 方案3.2相关推送服务入口,如果推送失败,该订单需要延后10分钟,才可发起下次推送。
*/
public void pushOrder02(XCO request) {
// 1. 获取一条待推送订单
XCO getRes = ServiceActuator.executeAlone("timer/getOrder02", new XCO());
if (0 != getRes.getCode()) {
log.error("获取一条待推送订单异常: " + getRes.getMessage());
return;
}
XCO order = getRes.getData();
if (null == order) {
return;// 暂无待推送订单
}
// 2. 推送给第三方
XCO receiveRes = ServiceActuator.executeAlone("timerService/receiveOrder", order);
int flag = 0;
if (0 != receiveRes.getCode()) {
// 推送失败
Date scheduled_push_time = order.getDateTimeValue("scheduled_push_time");
// 增加10分钟
scheduled_push_time.setTime(scheduled_push_time.getTime() + 10L * 60L * 1000L);
flag = 1;
}
order.setIntegerValue("flag", flag);
// 3. 标记此订单已经推送
ServiceActuator.executeAlone("timer/completePushOrder02", order);
}
3.2.4 Timer
<!-- 方案3.2相关Timer --><timer scheduled="/10 * * * * ?" service="timerService/pushOrder02" desc="推送订单(方案3.2)" sync="true"/>
3.3 场景三
3.3.1 需求
我们需要将已完成
的订单数据推送给第三方系统,如果推送失败,该订单需要延后10分钟,才可发起下次推送,并且最多可尝试3次。
3.3.2 SQL
DROP TABLE IF EXISTS order03;CREATE TABLE order03 (
order_id BIGINT UNSIGNED NOT NULL COMMENT "订单ID",
order_desc VARCHAR(256) NOT NULL DEFAULT "" COMMENT "订单描述",
order_state TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT "订单状态[0:初始, 10:已支付, ... 100:已完成]",
order_push_state TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT "订单推送状态[0:初始, 90:推送失败, 100:推送成功]",
retry_count BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT "重试次数",
scheduled_push_time DATETIME NOT NULL COMMENT "计划推送时间",
create_time DATETIME NOT NULL COMMENT "创建时间",
update_time DATETIME NULL COMMENT "修改时间",
PRIMARY KEY (order_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT="订单表(示例03)";
3.3.3 服务
SQL服务
<selectOne id="getOrder03" dsKey="timerReadDB" txRef="tx_01"><![CDATA[ select * from order03 where order_state = 100 AND order_push_state = 0 AND #{current_push_time|now()} >= scheduled_push_time LIMIT 1
]]></selectOne>
<update id="completePushOrder03" dsKey="timerWriteDB" txRef="tx_02">
<if test="{flag} == 0">
update order03 set
order_push_state = 100,
update_time = #{time|now()}
where
order_id = #{order_id} AND
order_push_state = 0
</if>
<elseif test="{flag} == 1">
update order03 set
retry_count = retry_count + 1,
scheduled_push_time = #{scheduled_push_time},
update_time = #{time|now()}
where
order_id = #{order_id} AND
order_push_state = 0
</elseif>
<else>
update order03 set
order_push_state = 90,
update_time = #{time|now()}
where
order_id = #{order_id} AND
order_push_state = 0
</else>
</update>
JAVA服务
/** * 方案3.2相关推送服务入口,如果推送失败,该订单需要延后10分钟,才可发起下次推送,并且最多可尝试3次。
*/
public void pushOrder03(XCO request) {
// 1. 获取一条待推送订单
XCO getRes = ServiceActuator.executeAlone("timer/getOrder03", new XCO());
if (0 != getRes.getCode()) {
log.error("获取一条待推送订单异常: " + getRes.getMessage());
return;
}
XCO order = getRes.getData();
if (null == order) {
return;// 暂无待推送订单
}
// 2. 推送给第三方
XCO receiveRes = ServiceActuator.executeAlone("timerService/receiveOrder", order);
int flag = 0;
if (0 != receiveRes.getCode()) {
// 推送失败
Date scheduled_push_time = order.getDateTimeValue("scheduled_push_time");
// 增加10分钟
scheduled_push_time.setTime(scheduled_push_time.getTime() + 10L * 60L * 1000L);
flag = 1;
// 尝试次数判读
if (receiveRes.getIntegerValue("retry_count") > 2) {
flag = 2;
}
}
order.setIntegerValue("flag", flag);
// 3. 标记此订单已经推送
ServiceActuator.executeAlone("timer/completePushOrder02", order);
}
3.3.4 Timer
<!-- 方案3.3相关Timer --><timer scheduled="/10 * * * * ?" service="timerService/pushOrder03" desc="推送订单(方案3.3)" sync="true"/>
3.4 对于3.1的技术优化方案1
3.4.1 需求
需求同3.1.1,技术优化点:一次获取多条任务
3.4.2 SQL
同3.1.2
3.4.3 服务
SQL服务
<selectSet id="getOrderList01" dsKey="timerReadDB" txRef="tx_01"> select * from order01 where order_state = 100 AND order_push_state = 0 LIMIT 100
</selectSet>
JAVA服务
/** * 方案3.4相关推送服务入口,一次获取多条任务
*/
public void pushOrder04(XCO request) {
// 1. 获取多条待推送订单
XCO getRes = ServiceActuator.executeAlone("timer/getOrderList01", new XCO());
if (0 != getRes.getCode()) {
log.error("获取多条待推送订单异常: " + getRes.getMessage());
return;
}
List<XCO> orders = getRes.getData();
if (CollectionUtils.isEmpty(orders)) {
return;// 暂无待推送订单
}
for (XCO order : orders) {
// 2. 推送给第三方
ServiceActuator.executeAlone("timerService/receiveOrder", order);
// 3. 标记此订单已经推送
ServiceActuator.executeAlone("timer/completePushOrder01", order);
}
}
3.4.4 Timer
<!-- 方案3.4相关Timer --><timer scheduled="/10 * * * * ?" service="timerService/pushOrder04" desc="推送订单(方案3.4)" sync="true"/>
3.5 对于3.1的技术优化方案2
3.5.1 需求
需求同3.1.1,技术优化点:一次获取多条任务,并同时支持多个Timer
3.5.2 SQL
同3.1.2
3.5.3 服务
SQL服务
<selectSet id="getOrderList02" dsKey="timerReadDB" txRef="tx_01"> select * from order01 where order_state = 100 AND order_push_state = 0 AND (order_id % #{tt_total} = #{tt_index}) LIMIT 100
</selectSet>
JAVA服务
/** * 方案3.5相关推送服务入口,一次获取多条任务,并同时支持多个Timer
*/
public void pushOrder05(XCO request) {
// 1. 获取多条待推送订单
XCO getRes = ServiceActuator.executeAlone("timer/getOrderList02", request);
if (0 != getRes.getCode()) {
log.error("获取多条待推送订单异常: " + getRes.getMessage());
return;
}
List<XCO> orders = getRes.getData();
if (CollectionUtils.isEmpty(orders)) {
return;// 暂无待推送订单
}
for (XCO order : orders) {
// 2. 推送给第三方
ServiceActuator.executeAlone("timerService/receiveOrder", order);
// 3. 标记此订单已经推送
ServiceActuator.executeAlone("timer/completePushOrder01", order);
}
}
3.5.4 Timer
<!-- 方案3.5相关Timer --><timer scheduled="/10 * * * * ?" service="timerService/pushOrder05" desc="推送订单(方案3.5)[0]" sync="true">
<property name="tt_index" value="0"/>
<property name="tt_total" value="5"/>
</timer>
<timer scheduled="/10 * * * * ?" service="timerService/pushOrder05" desc="推送订单(方案3.5)[1]" sync="true">
<property name="tt_index" value="1"/>
<property name="tt_total" value="5"/>
</timer>
<timer scheduled="/10 * * * * ?" service="timerService/pushOrder05" desc="推送订单(方案3.5)[2]" sync="true">
<property name="tt_index" value="2"/>
<property name="tt_total" value="5"/>
</timer>
<timer scheduled="/10 * * * * ?" service="timerService/pushOrder05" desc="推送订单(方案3.5)[3]" sync="true">
<property name="tt_index" value="3"/>
<property name="tt_total" value="5"/>
</timer>
<timer scheduled="/10 * * * * ?" service="timerService/pushOrder05" desc="推送订单(方案3.5)[4]" sync="true">
<property name="tt_index" value="4"/>
<property name="tt_total" value="5"/>
</timer>
3. 方案总结
1
场景一
简单,不考虑失败
2
场景二
增加失败后延迟推送的支持
3
场景三
增加失败后的最大尝试次数控制
4
场景一优化
每次获取多条待推送任务
5
场景一优化
同时支持多个Timer进行并行处理
在实际的开发过程中,我们应该根据具体的业务场景和需求,选择合适的实现方案。
4. 附件
- SQL脚本和项目源码
以上是 定时程序设计方案分享 的全部内容, 来源链接: utcz.com/z/511816.html