定时程序设计方案分享

编程

定时程序设计分享

定时程序设计方案分享

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. 方案总结

SN

方案名称

描述

1

场景一

简单,不考虑失败

2

场景二

增加失败后延迟推送的支持

3

场景三

增加失败后的最大尝试次数控制

4

场景一优化

每次获取多条待推送任务

5

场景一优化

同时支持多个Timer进行并行处理

在实际的开发过程中,我们应该根据具体的业务场景和需求,选择合适的实现方案。

4. 附件

  1. SQL脚本和项目源码

以上是 定时程序设计方案分享 的全部内容, 来源链接: utcz.com/z/511816.html

回到顶部