feat: 2.4.0
1. 阻塞条件
This commit is contained in:
		
							parent
							
								
									304118216b
								
							
						
					
					
						commit
						409d7e42e6
					
				@ -0,0 +1,12 @@
 | 
			
		||||
package com.aizuda.easy.retry.server.job.task;
 | 
			
		||||
 | 
			
		||||
import com.aizuda.easy.retry.server.job.task.strategy.BlockStrategies.BlockStrategyContext;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author: www.byteblogs.com
 | 
			
		||||
 * @date : 2023-09-25 17:53
 | 
			
		||||
 */
 | 
			
		||||
public interface BlockStrategy {
 | 
			
		||||
 | 
			
		||||
    boolean block(BlockStrategyContext context);
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,15 @@
 | 
			
		||||
package com.aizuda.easy.retry.server.job.task.scan;
 | 
			
		||||
 | 
			
		||||
import com.aizuda.easy.retry.server.common.dto.RegisterNodeInfo;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author: www.byteblogs.com
 | 
			
		||||
 * @date : 2023-09-25 17:35
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
public class JobContext {
 | 
			
		||||
 | 
			
		||||
    private RegisterNodeInfo registerNodeInfo;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,36 @@
 | 
			
		||||
package com.aizuda.easy.retry.server.job.task.scan;
 | 
			
		||||
 | 
			
		||||
import akka.actor.AbstractActor;
 | 
			
		||||
import com.aizuda.easy.retry.common.core.log.LogUtils;
 | 
			
		||||
import com.aizuda.easy.retry.server.common.akka.ActorGenerator;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
 | 
			
		||||
import org.springframework.context.annotation.Scope;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author: www.byteblogs.com
 | 
			
		||||
 * @date : 2023-09-25 17:41
 | 
			
		||||
 */
 | 
			
		||||
@Component(ActorGenerator.SCAN_JOB_ACTOR)
 | 
			
		||||
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class JobExecutorActor extends AbstractActor {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Receive createReceive() {
 | 
			
		||||
        return receiveBuilder().match(JobContext.class, jobContext -> {
 | 
			
		||||
            try {
 | 
			
		||||
                doExecute(jobContext);
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                LogUtils.error(log, "job executor exception. [{}]", jobContext, e);
 | 
			
		||||
            }
 | 
			
		||||
        }).build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void doExecute(final JobContext jobContext) {
 | 
			
		||||
 | 
			
		||||
        // 调度客户端
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,17 @@
 | 
			
		||||
package com.aizuda.easy.retry.server.job.task.scan;
 | 
			
		||||
 | 
			
		||||
import io.netty.util.Timeout;
 | 
			
		||||
import io.netty.util.TimerTask;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author: www.byteblogs.com
 | 
			
		||||
 * @date : 2023-09-25 17:28
 | 
			
		||||
 */
 | 
			
		||||
public class JobTimerTask implements TimerTask  {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void run(final Timeout timeout) throws Exception {
 | 
			
		||||
        // 执行任务调度
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,106 @@
 | 
			
		||||
package com.aizuda.easy.retry.server.job.task.scan;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.util.StrUtil;
 | 
			
		||||
import com.aizuda.easy.retry.common.core.log.LogUtils;
 | 
			
		||||
import com.aizuda.easy.retry.server.common.Lifecycle;
 | 
			
		||||
import com.google.common.cache.Cache;
 | 
			
		||||
import com.google.common.cache.CacheBuilder;
 | 
			
		||||
import io.netty.util.HashedWheelTimer;
 | 
			
		||||
import io.netty.util.Timeout;
 | 
			
		||||
import io.netty.util.TimerTask;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author: www.byteblogs.com
 | 
			
		||||
 * @date : 2023-09-22 17:03
 | 
			
		||||
 */
 | 
			
		||||
@Component
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class JobTimerWheelHandler implements Lifecycle {
 | 
			
		||||
 | 
			
		||||
    private static HashedWheelTimer timer = null;
 | 
			
		||||
 | 
			
		||||
    private static Cache<String, Timeout> cache;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void start() {
 | 
			
		||||
 | 
			
		||||
        // TODO 支持可配置
 | 
			
		||||
        // tickDuration 和 timeUnit 一格的时间长度
 | 
			
		||||
        // ticksPerWheel 一圈有多少格
 | 
			
		||||
        timer = new HashedWheelTimer(
 | 
			
		||||
                new CustomizableThreadFactory("jop-task-timer-wheel-"), 100,
 | 
			
		||||
                TimeUnit.MILLISECONDS, 1024);
 | 
			
		||||
 | 
			
		||||
        timer.start();
 | 
			
		||||
 | 
			
		||||
        cache = CacheBuilder.newBuilder()
 | 
			
		||||
                // 设置并发级别为cpu核心数
 | 
			
		||||
                .concurrencyLevel(Runtime.getRuntime().availableProcessors())
 | 
			
		||||
                .build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void register(String groupName, String uniqueId, TimerTask task, long delay, TimeUnit unit) {
 | 
			
		||||
 | 
			
		||||
        if (delay < 0) {
 | 
			
		||||
            delay = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // TODO 支持可配置
 | 
			
		||||
        if (delay > 60 * 1000) {
 | 
			
		||||
            LogUtils.warn(log, "距离下次执行时间过久, 不满足进入时间轮的条件. groupName:[{}] uniqueId:[{}] delay:[{}ms]",
 | 
			
		||||
                    groupName, uniqueId,  delay);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Timeout timeout = getTimeout(groupName, uniqueId);
 | 
			
		||||
        if (Objects.isNull(timeout)) {
 | 
			
		||||
            try {
 | 
			
		||||
                timeout = timer.newTimeout(task, delay, unit);
 | 
			
		||||
                cache.put(getKey(groupName, uniqueId), timeout);
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                LogUtils.error(log, "加入时间轮失败. groupName:[{}] uniqueId:[{}]",
 | 
			
		||||
                        groupName, uniqueId, e);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static String getKey(String groupName, String uniqueId) {
 | 
			
		||||
        return groupName.concat(StrUtil.UNDERLINE).concat(uniqueId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Timeout getTimeout(String groupName, String uniqueId) {
 | 
			
		||||
        return cache.getIfPresent(getKey(groupName, uniqueId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean isExisted(String groupName, String uniqueId) {
 | 
			
		||||
        return Objects.nonNull(cache.getIfPresent(getKey(groupName, uniqueId)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean cancel(String groupName, String uniqueId) {
 | 
			
		||||
        String key = getKey(groupName, uniqueId);
 | 
			
		||||
        Timeout timeout = cache.getIfPresent(key);
 | 
			
		||||
        if (Objects.isNull(timeout)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        cache.invalidate(key);
 | 
			
		||||
        return timeout.cancel();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void clearCache(String groupName, String uniqueId) {
 | 
			
		||||
        cache.invalidate(getKey(groupName, uniqueId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void close() {
 | 
			
		||||
        timer.stop();
 | 
			
		||||
        cache.invalidateAll();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -7,11 +7,19 @@ import com.aizuda.easy.retry.server.common.akka.ActorGenerator;
 | 
			
		||||
import com.aizuda.easy.retry.server.common.dto.RegisterNodeInfo;
 | 
			
		||||
import com.aizuda.easy.retry.server.common.dto.ScanTask;
 | 
			
		||||
import com.aizuda.easy.retry.server.common.handler.ClientNodeAllocateHandler;
 | 
			
		||||
import com.aizuda.easy.retry.server.job.task.BlockStrategy;
 | 
			
		||||
import com.aizuda.easy.retry.server.job.task.strategy.BlockStrategies;
 | 
			
		||||
import com.aizuda.easy.retry.server.job.task.strategy.BlockStrategies.BlockStrategyContext;
 | 
			
		||||
import com.aizuda.easy.retry.server.job.task.strategy.BlockStrategies.BlockStrategyEnum;
 | 
			
		||||
import com.aizuda.easy.retry.template.datasource.persistence.mapper.JobMapper;
 | 
			
		||||
import com.aizuda.easy.retry.template.datasource.persistence.mapper.JobTaskMapper;
 | 
			
		||||
import com.aizuda.easy.retry.template.datasource.persistence.po.Job;
 | 
			
		||||
import com.aizuda.easy.retry.template.datasource.persistence.po.JobTask;
 | 
			
		||||
import com.aizuda.easy.retry.template.datasource.persistence.po.RetryTask;
 | 
			
		||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 | 
			
		||||
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
 | 
			
		||||
import io.netty.util.Timeout;
 | 
			
		||||
import io.netty.util.TimerTask;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
 | 
			
		||||
@ -23,6 +31,7 @@ import java.time.LocalDateTime;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.concurrent.ConcurrentHashMap;
 | 
			
		||||
import java.util.concurrent.ConcurrentMap;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
import java.util.concurrent.atomic.AtomicLong;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@ -40,6 +49,8 @@ public class ScanJobTaskActor extends AbstractActor {
 | 
			
		||||
    @Autowired
 | 
			
		||||
    private JobMapper jobMapper;
 | 
			
		||||
    @Autowired
 | 
			
		||||
    private JobTaskMapper jobTaskMapper;
 | 
			
		||||
    @Autowired
 | 
			
		||||
    protected ClientNodeAllocateHandler clientNodeAllocateHandler;
 | 
			
		||||
 | 
			
		||||
    private static final AtomicLong lastId = new AtomicLong(0L);
 | 
			
		||||
@ -77,15 +88,39 @@ public class ScanJobTaskActor extends AbstractActor {
 | 
			
		||||
        lastId.set(jobs.get(jobs.size() - 1).getId());
 | 
			
		||||
 | 
			
		||||
        for (Job job : jobs) {
 | 
			
		||||
            JobContext jobContext = new JobContext();
 | 
			
		||||
            // 选择客户端节点
 | 
			
		||||
            RegisterNodeInfo serverNode = clientNodeAllocateHandler.getServerNode(job.getGroupName());
 | 
			
		||||
            // TODO 校验一下客户端是否存活
 | 
			
		||||
            jobContext.setRegisterNodeInfo(clientNodeAllocateHandler.getServerNode(job.getGroupName()));
 | 
			
		||||
 | 
			
		||||
            Long count = jobTaskMapper.selectCount(new LambdaQueryWrapper<JobTask>().eq(JobTask::getTaskStatus, StatusEnum.YES.getStatus()));
 | 
			
		||||
            if (count <= 0) {
 | 
			
		||||
                // 生成可执行任务
 | 
			
		||||
                JobTask jobTask = new JobTask();
 | 
			
		||||
                jobTask.setJobId(job.getId());
 | 
			
		||||
                jobTask.setGroupName(job.getGroupName());
 | 
			
		||||
                jobTaskMapper.insert(jobTask);
 | 
			
		||||
 | 
			
		||||
                // 更新下次触发时间
 | 
			
		||||
                // ToDo 根据CRON表达式计算
 | 
			
		||||
                job.setNextTriggerAt(LocalDateTime.now().plusSeconds(50));
 | 
			
		||||
                jobMapper.updateById(job);
 | 
			
		||||
            } else {
 | 
			
		||||
                BlockStrategyContext blockStrategyContext = new BlockStrategyContext();
 | 
			
		||||
 | 
			
		||||
                BlockStrategy blockStrategy = BlockStrategyEnum.getBlockStrategy(job.getBlockStrategy());
 | 
			
		||||
                blockStrategy.block(blockStrategyContext);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            // 进入时间轮
 | 
			
		||||
            JobTimerWheelHandler.register(job.getGroupName(), job.getId().toString(), new JobTimerTask(), 1, TimeUnit.MILLISECONDS);
 | 
			
		||||
            // 校验是否存在已经在执行的任务了
 | 
			
		||||
//            boolean isExist = true;
 | 
			
		||||
//            if (isExist) {
 | 
			
		||||
//                // 选择丢弃策略
 | 
			
		||||
////                String blockStrategy = job.getBlockStrategy();
 | 
			
		||||
//               String blockStrategy = job.getBlockStrategy();
 | 
			
		||||
//
 | 
			
		||||
//            }
 | 
			
		||||
        }
 | 
			
		||||
@ -93,7 +128,7 @@ public class ScanJobTaskActor extends AbstractActor {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private List<Job> listAvailableJobs(Long lastId) {
 | 
			
		||||
        return jobMapper.selectPage(new PageDTO<Job>(0, 100),
 | 
			
		||||
        return jobMapper.selectPage(new PageDTO<Job>(0, 1000),
 | 
			
		||||
                new LambdaQueryWrapper<Job>()
 | 
			
		||||
                        .eq(Job::getJobStatus, StatusEnum.YES.getStatus())
 | 
			
		||||
                        // TODO 提前10秒把需要执行的任务拉取出来
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,90 @@
 | 
			
		||||
package com.aizuda.easy.retry.server.job.task.strategy;
 | 
			
		||||
 | 
			
		||||
import com.aizuda.easy.retry.common.core.context.SpringContext;
 | 
			
		||||
import com.aizuda.easy.retry.server.job.task.BlockStrategy;
 | 
			
		||||
import com.aizuda.easy.retry.template.datasource.persistence.mapper.JobTaskMapper;
 | 
			
		||||
import com.aizuda.easy.retry.template.datasource.persistence.po.Job;
 | 
			
		||||
import com.aizuda.easy.retry.template.datasource.persistence.po.JobTask;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author: www.byteblogs.com
 | 
			
		||||
 * @date : 2023-09-25 17:52
 | 
			
		||||
 */
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class BlockStrategies {
 | 
			
		||||
 | 
			
		||||
    @AllArgsConstructor
 | 
			
		||||
    @Getter
 | 
			
		||||
    public enum BlockStrategyEnum {
 | 
			
		||||
        DISCARD(1, new DiscardBlockStrategy()),
 | 
			
		||||
        OVERLAY(2, new OverlayBlockStrategy()),
 | 
			
		||||
        CONCURRENCY(3, new ConcurrencyBlockStrategy());
 | 
			
		||||
 | 
			
		||||
        private final int blockStrategy;
 | 
			
		||||
        private final BlockStrategy strategy;
 | 
			
		||||
 | 
			
		||||
        public static BlockStrategy getBlockStrategy(int blockStrategy) {
 | 
			
		||||
            for (final BlockStrategyEnum value : BlockStrategyEnum.values()) {
 | 
			
		||||
                if (value.blockStrategy == blockStrategy) {
 | 
			
		||||
                    return value.getStrategy();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Data
 | 
			
		||||
    public static class BlockStrategyContext {
 | 
			
		||||
 | 
			
		||||
        private Long jobId;
 | 
			
		||||
 | 
			
		||||
        private Job job;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static final class DiscardBlockStrategy implements BlockStrategy {
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public boolean block(final BlockStrategyContext context) {
 | 
			
		||||
            log.warn("阻塞策略为丢弃此次执行. jobId:[{}]", context.getJobId());
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static final class OverlayBlockStrategy implements BlockStrategy {
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public boolean block(final BlockStrategyContext context) {
 | 
			
		||||
            log.warn("阻塞策略为覆盖. jobId:[{}]", context.getJobId());
 | 
			
		||||
            // 向客户端发送中断执行指令
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static final class ConcurrencyBlockStrategy implements BlockStrategy {
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public boolean block(final BlockStrategyContext context) {
 | 
			
		||||
            log.warn("阻塞策略为并行执行. jobId:[{}]", context.getJobId());
 | 
			
		||||
            Job job = context.getJob();
 | 
			
		||||
 | 
			
		||||
            JobTask jobTask = new JobTask();
 | 
			
		||||
            jobTask.setJobId(job.getId());
 | 
			
		||||
            jobTask.setGroupName(job.getGroupName());
 | 
			
		||||
 | 
			
		||||
            JobTaskMapper jobTaskMapper = SpringContext.getBeanByType(JobTaskMapper.class);
 | 
			
		||||
            jobTaskMapper.insert(jobTask);
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user