繁体   English   中英

如何在 spring 中扩展 1 个以上的实例并处理计划任务?

[英]How to scale more than 1 instance and deal with scheduled task in spring?

我每天早上 8 点在欧洲/巴黎通过 spring 启动向 android 和 ios 应用程序发送推送通知。

如果我运行多个实例,通知将发送多次。 我正在考虑在数据库上发送每天发送的通知,并检查它们,但我担心它仍然会运行多次,这就是我正在做的事情:

@Component
public class ScheduledTasks {

    private static final Logger log = LoggerFactory.getLogger(ScheduledTasks.class);

    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");

    @Autowired
    private ExpoPushTokenRepository expoPushTokenRepository;

    @Autowired
    private ExpoPushNotificationService expoPushNotificationService;

    @Autowired
    private MessageSource messageSource;

    // TODO: if instances > 1, this will run multiple times, save to database the notifications send and prevent multiple sending.
    @Scheduled(cron = "${cron.promotions.notification}", zone = "Europe/Paris")
    public void sendNewPromotionsNotification() {
        List<ExpoPushToken> expoPushTokenList = expoPushTokenRepository.findAll();
        ArrayList<NotifyRequest> notifyRequestList = new ArrayList<>();
        for (ExpoPushToken expoPushToken : expoPushTokenList) {
            NotifyRequest notifyRequest = new NotifyRequest(
                    expoPushToken.getToken(),
                    "This is a test title",
                    "This is a test subtitle",
                    "This is a test body"
            );
            notifyRequestList.add(notifyRequest);
        }

        expoPushNotificationService.sendPushNotificationToList(notifyRequestList);
        log.info("{} Send push notification to " + expoPushTokenList.size() + " userse", dateFormat.format(new Date()));
    }
}

有人知道如何安全地防止这种情况吗?

Quartz将是我手头任务的主要与数据库无关的解决方案,但被排除在外,所以我们不打算讨论它。

我们将要探索的解决方案做了以下假设:

在这种情况下,我们可以通过以下查询从运行的应用程序的多个实例中检索批量通知:

SELECT * FROM expo_push_token FOR UPDATE SKIP LOCKED LIMIT 100;

这将从表expose_push_token中检索并锁定最多100个条目。 如果应用程序的两个实例同时执行此查询,则接收到的结果将是不相交的。 100只是一些样本值。 您可能希望针对您的用例微调此值。 锁保持活动状态,直到当前事务结束。

在一个实例获取了一批通知之后,它还必须从表中删除它锁定的条目,或者以其他方式标记该条目已被处理(如果我们沿着这条路线走 go,我们必须修改上面的查询以过滤掉已处理的整体)并关闭当前事务以释放锁。 然后应用程序的每个实例将重复此查询,直到查询返回零条目。

还有一种替代方法:一个实例首先获取要发送的批量通知,使数据库的事务保持打开状态(从而继续保持数据库的锁定),发出它的通知,然后删除/更新条目并关闭交易。

这两种解决方案具有不同的优势/劣势:

  • 第一个解决方案使交易保持简短。 但是,如果应用程序在发送通知的过程中崩溃,则未发送的批处理部分将在此运行中丢失。
  • 第二种解决方案使事务保持打开状态,可能会持续很长时间。 如果它在发送通知中途崩溃,所有条目将被解锁并重新处理其批次,可能导致某些通知被发送两次。

为了使这个解决方案起作用,我们还需要某种工作,用我们需要的数据填充表expo_push_token 该作业应预先运行,即其执行不应与通知发送过程重叠。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM