当前位置: 首页 > 知识库问答 >
问题:

java - 集群怎么保证脚本线程唯一,用户怎么知道脚本线程运行状态?

束俊英
2023-09-22

有一个出库的任务,有多个用户,只要一个用户的出库参数是true,这个任务就要一直执行
目前有两台后端服务器做负载均衡,为了保证任务唯一,用redis做状态控制,用一个key来表示任务的运行状态,任务在用户前端点击启动的时候通过判断对应key的状态来选择是否要新启一个线程执行任务。

现在有问题,有时候出错误了会直接将这个线程中断掉,这时候只能把所有用户的参数设置为关闭再打开,才可以重新执行,除了现在的redis怎么设计可以让用户知道这个线程现在是运行,还是停止的状态

一个脚本任务
两台服务器
用户前端点击按钮开启/关闭脚本任务——只要一个用户是开启的,脚本任务就不停止。全部关闭脚本任务关闭
redis做脚本任务状态控制
脚本任务目前是另起一个线程,然后死循环执行,当用户都关闭的时候跳出死循环


你们是怎么设计的,这样设计好像问题很多,项目启动的时候怎么自动启动脚本任务并且保证集群中只有一个,怎么让用户知道当前脚本任务确实在执行

共有2个答案

公冶龙野
2023-09-22

可以使用redisson分布式锁来保证只有一个后端服务在运行该脚本任务,由于其创建的分布式锁是有过期时间(默认30秒)的且有锁续期机制(默认每隔10秒续期),所以尽管当执行脚本任务的服务崩溃,锁也会在一定时间之后过期,另一个服务则可以拿到锁执行任务。

用分布式锁的存在与否来判断当前脚本任务状态,锁存在表示脚本任务正在执行,反之则不在执行(当然如果服务崩溃后,锁没有被及时释放,这里会存在一定的延迟,可以通过调整锁的默认过期时间来缩短延迟时间)。

大致流程:

  1. 服务拿到分布式锁,启动任务(客户端通过锁是否存在来判断任务状态)
  2. 任务执行中
  3. 任务执行完成,释放锁
耿弘阔
2023-09-22

要解决这个问题,可以考虑使用一个更健壮的状态管理方案,例如使用分布式锁或者使用消息队列。

分布式锁解决方案
使用一个分布式锁,如基于Redis的Redlock,Zookeeper等,可以解决单点故障问题。当一个线程获取到锁时,就执行出库任务,并在Redis中保存执行状态。如果因为某种原因线程被中断,用户可以尝试再次获取锁来继续执行任务。

消息队列解决方案
使用一个消息队列(如RabbitMQ,Kafka等)来处理任务也是一种常见的做法。用户将出库任务发送到消息队列,然后有一个或多个消费者线程从队列中获取任务并执行。消费者线程在执行任务前会检查任务状态,如果任务已停止,则不会尝试执行。

对于用户如何知道脚本线程运行状态的问题,你可以设计一个API接口,用户可以通过这个接口查询任务的执行状态。例如,当用户启动出库任务时,可以通过这个接口得到任务的状态。如果任务被中止,用户可以通过这个接口重新启动任务。

以下是一个简单的示例代码片段,用于说明如何使用Redis分布式锁来管理任务的执行状态:

import redisimport timedef start_task(user_id, is_active):    # Connect to Redis server    r = redis.StrictRedis(host='localhost', port=6379, db=0)    lock_key = f'task_lock:{user_id}'    # Try to acquire the lock    while True:        if r.set(lock_key, 1, nx=True, ex=3600):  # Set lock for 1 hour            break        time.sleep(1)  # Wait for 1 second and try again    # Check if task is active    task_status = r.get(f'task_status:{user_id}')    if task_status and task_status == 'active':        print(f'Task is already active for user {user_id}')        r.delete(lock_key)  # Release the lock        return    # Start the task    print(f'Starting task for user {user_id}')    # TODO: Start your task here...    # Update task status    r.set(f'task_status:{user_id}', 'active')    r.expire(f'task_status:{user_id}', 3600)  # Set expiration time for 1 hourdef stop_task(user_id):    # Connect to Redis server    r = redis.StrictRedis(host='localhost', port=6379, db=0)    lock_key = f'task_lock:{user_id}'    # Acquire the lock (this will fail if the lock is already set)    if r.set(lock_key, 1, nx=True, ex=3600):  # Set lock for 1 hour        print(f'Acquired lock for user {user_id}')        try:            # Update task status            r.set(f'task_status:{user_id}', 'stopped')  # Update task status to stopped            r.expire(f'task_status:{user_id}', 3600)  # Set expiration time for 1 hour            print(f'Stopped task for user {user_id}')        finally:            r.delete(lock_key)  # Release the lock regardless of success or failure of the task stop operation
 类似资料:
  • 本文向大家介绍在 Java 程序中怎么保证多线程的运行安全?相关面试题,主要包含被问及在 Java 程序中怎么保证多线程的运行安全?时的应答技巧和注意事项,需要的朋友参考一下 方法一:使用安全类,比如 Java. util. concurrent 下的类。 方法二:使用自动锁 synchronized。 方法三:使用手动锁 Lock。 手动锁 Java 示例代码如下:  

  • 在下面的代码中,javascript运行在与主线程不同的单独线程中。该脚本是一个无限循环,因此需要以某种方式终止。怎样 使命感脚本开始运行后,cancel()不起作用。但如果我打电话。cancel()在线程初始化之后,它将终止它(注释掉的行)。

  • 问题内容: 在下面的代码中,我使javascript在与主要线程不同的线程中运行。该脚本是一个无限循环,因此需要以某种方式终止。怎么样? 脚本开始运行后,调用.cancel()无效。但是,如果我在线程初始化之后立即调用.cancel(),它将终止它(注释行)。 问题答案: 与Java一样,JavaScript(在Nashorn下)也不会像Java那样在紧密循环中响应中断。该脚本需要轮询中断并自动终

  • Redisson的看门狗实现中,对于续期失败的异常处理就是释放锁: 见红框 也就是说主线程不会知道当前锁是否还有效,可能锁因为续期失败已经过期了,但主线程还在处理业务。 这种情况应该怎么处理呢?

  • 问题内容: 我有这个线程可以从服务器下载一些图像。因此,一旦下载了图像,我就调用处理程序并进行UI更新。因此,由于不建议使用stop()线程,因此无法使用它。我在这里有两个问题。 最终该线程会发生什么?(意味着我调用处理程序方法后会发生什么)。 或者如何不使用stop()停止该线程? 这是我的代码。 问题答案: 线程将结束并自行死亡。您不必自己结束。如果不创建新对象,将无法重新启动它。垃圾收集器将

  • 本文向大家介绍测试脚本的维护成本是怎么样的?相关面试题,主要包含被问及测试脚本的维护成本是怎么样的?时的应答技巧和注意事项,需要的朋友参考一下 业务不变的情况下,一般脚本都是不坏不动的