• 【Linux】写个脚本实现简单的程序单例运行功能


    需求

    写了个爬虫脚本放到服务器上定时运行,当脚本碰到耗时的任务阻塞太久的时候可能下一次定时调用会发生,这会导致同时运行的脚本数量大于一个,可能会造成数据库数据重复等问题。现在想解决这个问题,但是并不想更改爬虫脚本相关的代码。

    分析需求不难看出同时运行的爬虫脚本不能大于一个,由于脚本是通过定时任务启动(这里假设是 cron),我们可以在启动爬虫的指令上动手脚,在启动爬虫之前先判断是否有爬虫脚本运行,如果有则不启动爬虫。

    实现方法1:ps(失败)

    最直观的方法是通过 ps 查找指定名称的进程,然后判断进程是否存在,如果存在则不启动脚本:

    TASK=`ps aux | grep sh /root/run_crawler.sh | grep -v grep`
    if [ "$TASK" = "" ]; then
      # 执行一些耗时的任务
      python run.py 
    fi
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如示例代码所示,这个脚本的主要问题在 grep 查找进序上,因为通过 cron 启动这个脚本的时候也会启动同名的进程,所以在查找之前该进程就已经被创建,而根据脚本“进程不存在才执行”的逻辑,这段脚本永远不会执行 if 段里面的内容。

    实现方法2:判断指定文件是否存在

    我们可以在运行脚本之前判断一个指定的文件是否存在,如果存在表示脚本正在运行,否则运行脚本并创建该文件,脚本运行结束后删除该文件,伪代码如下:

    filename = 'LOCK'
    
    if filename 不存在:
      创建文件(filename)
      # 执行耗时操作
      python run.py
      删除文件(filename)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    示例 bash 代码如下:

    LOCK="LOCK.temp"
    if [ ! -f "$LOCK" ]; then
      touch $LOCK
      python run_crawlers.py
      sleep 2
      python push_message.py
      rm -rf $LOCK
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这种写法有两个主要的问题:

    1. 文件名可能会和本地文件冲突 - 可以通过生成时间戳文件名等方法解决
    2. 可能产生 if 内脚本永远无法运行的 bug
      第二个问题产生的原因是当我们执行 if 段的代码时,创建了 lock 文件,但是执行过程中脚本的运行被打断,导致 lock 文件无法被自动删除,除非手动删除否则该脚本永远不会执行。

    第二个问题的解决方法
    增加一个超时机制,即如果 lock 存在的时间超过指定时间,删除 lock,这里用到了 stat 指令用来获取文件创建的时间戳,date +%s 获取当前时间戳,完整代码如下:

    LOCK="LOCK.temp"
    if [ ! -f "$LOCK" ]; then
      touch $LOCK
      # 执行一些耗时操作
      python main.py
      rm -rf $LOCK
    else
      ts=`stat -c %Y $LOCK`
      now=`date +%s`
      # 超时时间为 1800s (30min)
      if [ $[ $now - $ts ] -gt 1800 ]; then
        rm -rf $LOCK
        echo "Lock expired, deleted"
      fi
    fi
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    总结

    以上提供了一种单例运行的思路,实际上还有很多其他的方法能够避免数据重复写的问题,可以根据自己的实际情况选择。

  • 相关阅读:
    从0开发一个Chrome插件:用户反馈与更新 Chrome 插件
    初识数据结构
    540 - Team Queue (UVA)
    Burpsuite介绍及2022.8.2版本超详细安装教程(图文版)
    商业合作保密协议 (4)
    变量声明方式的一些思考
    Java并发常见面试题(三)
    域对象共享数据
    Jenkins更换主目录
    AssetBundle检测服务使用指南
  • 原文地址:https://blog.csdn.net/qq_39559879/article/details/125526004