xxjob代码执行过程

注解实现xxl-job入参解析 通过注解及AOP实现xxl-job入参解析。 阅读详情

简介

  • 调度中心:
    负责管理调度信息,按照调度配置发出调度请求,自身不承担业务代码。调度系统与任务解耦,提高了系统可用性和稳定性,同时调度系统性能不再受限于任务模块;
    支持可视化、简单且动态的管理调度信息,包括任务新建,更新,删除,GLUE开发和任务报警等,所有上述操作都会实时生效,同时支持监控调度结果以及执行日志,支持执行器Failover,支持创建执行器等功能。

  • 执行模块(执行器):
    负责接收调度请求并执行任务逻辑。任务模块专注于任务的执行等操作,开发和维护更加简单和高效;接收“调度中心”的执行请求、终止请求和日志请求等。

特性
xxl-job的特性有很多,官网上有详细的介绍,这里我会介绍几个重要的特性:

  • 不分片:使用数据库锁在集群模式下同一时刻只有一个调度中心处理任务调度

  • 简单:支持通过Web页面对任务进行CRUD操作,操作简单,一分钟上手;
    动态:支持动态修改任务状态、启动/停止任务,以及终止运行中的任务,都是即时生效的。

  • 调度中心HA(中心式):调度采用中心式设计,“调度中心”自研调度组件并支持集群部署,可保证调度中心HA;

  • 执行器HA(分布式):任务分布式执行,任务”执行器”支持集群部署,可保证任务执行HA;

  • 调度过期策略:调度中心错过调度时间的补偿处理策略:包括:忽略,立即补偿触发一次等;

  • 阻塞处理策略:调度过于密集执行器来不及处理时的处理策略,策略包括:单机串行(默认)、丢弃后续调度、覆盖之前的调用。

  • 任务超时控制:支持自定义任务超时时间,任务运行超时将会主动中断任务;

最大的特点就是不分片,相对其他分布式任务组件,这个组件是最简单解决方式同时又能保证调度任务的一致性

xxl-job定时任务的种类

xxll0job支持java、groovy、脚本(Shell、Python、PHP、NodeJs、PowerShell)的定时任务

xxl-job相关的数据表

xxl-job将任务信息以及日志信息持久化到数据表中,这个就保证了可以动态的添加删除任务。

xxl_job_lock:任务调度锁表,在线程查询任务信息时会调用上锁。
xxl_job_group:执行器信息表,维护任务执行器信息;
xxl_job_info:调度扩展信息表: 用于保存XXL-JOB调度任务的扩展信息,如任务分组、任务名、机器地址、执行器、执行入参和报警邮件等等;
xxl_job_log:调度日志表: 用于保存XXL-JOB任务调度的历史信息,如调度结果、执行结果、调度入参、调度机器和执行器等等;
xxl_job_log_report:调度日志报表:用户存储XXL-JOB任务调度日志的报表,调度中心报表功能页面会用到;
xxl_job_logglue:任务GLUE日志:用于保存GLUE更新历史,用于支持GLUE的版本回溯功能;
xxl_job_registry:执行器注册表,维护在线的执行器和调度中心机器地址信息;
xxl_job_user:系统用户表;

调度服务

  1. xxl-job集群部署时,如何避免多个服务器同时调度任务?
  2. 定时任务是如何实现的?

JobScheduleHelper.start().run任务执行列表获取

JobScheduleHelper.start().run()使用while()循环不断的查询数据库中<5秒的任务列表

public class JobScheduleHelper {
 public static final long PRE_READ_MS = 5000;    // pre read

    private Thread scheduleThread;
    private Thread ringThread;
    private volatile boolean scheduleThreadToStop = false;
    private volatile boolean ringThreadToStop = false;
    //时间轮
    private volatile static Map<Integer, List<Integer>> ringData = new ConcurrentHashMap<>();

    public void start(){
        //扫描任务列表线程
        scheduleThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    TimeUnit.MILLISECONDS.sleep(5000 - System.currentTimeMillis()%1000 );
                } catch (InterruptedException e) {
                    if (!scheduleThreadToStop) {
                        logger.error(e.getMessage(), e);
                    }
                }
                logger.info(">>>>>>>>> init xxl-job admin scheduler success.");

                    //循环扫描任务:重点部分
                while (!scheduleThreadToStop) {
                     try {

                        conn = XxlJobAdminConfig.getAdminConfig().getDataSource().getConnection();
                        connAutoCommit = conn.getAutoCommit();
                        //关闭自动提交
                        conn.setAutoCommit(false);
                         //获取数据库锁
                        preparedStatement = conn.prepareStatement(  "select * from xxl_job_lock where lock_name = 'schedule_lock' for update" );
                        preparedStatement.execute();


                        // 1、pre read
                        long nowTime = System.currentTimeMillis();
                        //关键部分,预读出 当前时间+5s(PRE_READ_MS=5s) 的所有触发任务的时间小于这个时间预读出时间的任务
                        List<XxlJobInfo> scheduleList = XxlJobAdminConfig.getAdminConfig().getXxlJobInfoDao().scheduleJobQuery(nowTime + PRE_READ_MS, preReadCount);
                        if (scheduleList!=null && scheduleList.size()>0) {
                            // 2、push time-ring
                                // time-ring jump
                                //遍历所有任务,过滤出所有过期任务
                                //过期任务的判断方式 当前时间>任务下一次触发时间+空闲间隔周期5s (在这段时间内任务还没触发的话说明任务超时过期了,既在5s的误差内还没出发任务),并按照策略执行
                                if (nowTime > jobInfo.getTriggerNextTime() + PRE_READ_MS) {
                                    // 2.1、trigger-expire > 5s:pass && make next-trigger-time
                                    logger.warn(">>>>>>>>>>> xxl-job, schedule misfire, jobId = " + jobInfo.getId());

                                    //2.1-1、misfire match  查询当前任务的过期策略
                                    MisfireStrategyEnum misfireStrategyEnum = MisfireStrategyEnum.match(jobInfo.getMisfireStrategy(), MisfireStrategyEnum.DO_NOTHING);
                                    if (MisfireStrategyEnum.FIRE_ONCE_NOW == misfireStrategyEnum) {
                                        // FIRE_ONCE_NOW策略则过期立即执行一次
                                        JobTriggerPoolHelper.trigger(jobInfo.getId(), TriggerTypeEnum.MISFIRE, -1, null, null, null);
                                        logger.debug(">>>>>>>>>>> xxl-job, schedule push trigger : jobId = " + jobInfo.getId() );
                                    }

                                    // 2.1-2、fresh 刷新下次执行时间到数据库中
                                    refreshNextValidTime(jobInfo, new Date());
                                //否则不是过期任务  任务下一次触发时间+空闲间隔周期5s>当前时间>下一次任务触发时间  (在允许的误差之内都不算过期任务)
                                } else if (nowTime > jobInfo.getTriggerNextTime()) {
                                    // 2.2、trigger-expire < 5s:direct-trigger && make next-trigger-time
                                    // 2.2-1、trigger
                                    JobTriggerPoolHelper.trigger(jobInfo.getId(), TriggerTypeEnum.CRON, -1, null, null, null);
                                    logger.debug(">>>>>>>>>>> xxl-job, schedule push trigger : jobId = " + jobInfo.getId() );

                                    // 2.1-2、fresh  刷新下次执行时间到数据库中
                                    refreshNextValidTime(jobInfo, new Date());

                                    // 任务状态有效&&执行时间在5秒以内的任务放入时间环中
                                    if (jobInfo.getTriggerStatus()==1 && nowTime + PRE_READ_MS > jobInfo.getTriggerNextTime()) {

                                        // 1.获取剩余秒数
                                        int ringSecond = (int)((jobInfo.getTriggerNextTime()/1000)%60);

                                        // 2、把剩余秒数作为key放入时间轮中
                                        pushTimeRing(ringSecond, jobInfo.getId());

                                        // 3、刷新任务的下次执行时间
                                        refreshNextValidTime(jobInfo, new Date(jobInfo.getTriggerNextTime()));

                                    }

                                } else {
                                    // 2.3 即将促发的任务放入时间轮中5秒钟

                                    // 1、make ring second
                                    int ringSecond = (int)((jobInfo.getTriggerNextTime()/1000)%60);

                                    // 2、push time ring
                                    pushTimeRing(ringSecond, jobInfo.getId());

                                    // 3、fresh next
                                    refreshNextValidTime(jobInfo, new Date(jobInfo.getTriggerNextTime()));

                                }

                            }

                            // 3、update trigger info
                            for (XxlJobInfo jobInfo: scheduleList) {
                                XxlJobAdminConfig.getAdminConfig().getXxlJobInfoDao().scheduleUpdate(jobInfo);
                            }

                        } else {
                            preReadSuc = false;
                        }

                        // tx stop


                    } catch (Exception e) {
                        if (!scheduleThreadToStop) {
                            logger.error(">>>>>>>>>>> xxl-job, JobScheduleHelper#scheduleThread error:{}", e);
                        }
                    } finally {

                        // 最后释放数据库锁,重置自动提交,关闭连接
                        if (conn != null) {
                            try {
                                conn.commit();
                            } catch (SQLException e) {
                                if (!scheduleThreadToStop) {
                                    logger.error(e.getMessage(), e);
                                }
                            }
                            try {
                                conn.setAutoCommit(connAutoCommit);
                            } catch (SQLException e) {
                                if (!scheduleThreadToStop) {
                                    logger.error(e.getMessage(), e);
                                }
                            }
                            try {
                                conn.close();
                            } catch (SQLException e) {
                                if (!scheduleThreadToStop) {
                                    logger.error(e.getMessage(), e);
                                }
                            }
                        }

                        // close PreparedStatement

                    }
                    long cost = System.currentTimeMillis()-start;

                }

                logger.info(">>>>>>>>>>> xxl-job, JobScheduleHelper#scheduleThread stop");
            }
        });
        scheduleThread.setDaemon(true);
        scheduleThread.setName("xxl-job, admin JobScheduleHelper#scheduleThread");
        scheduleThread.start();



        //执行任务线程:从时间轮中获取任务并执行
        ringThread = new Thread(new Runnable() {
            @Override
            public void run() {

                //休息到下一秒=一秒-当前毫秒=剩下的毫秒数:只是启动时执行一次,以确保while是整秒执行
                try {
                    TimeUnit.MILLISECONDS.sleep(1000 - System.currentTimeMillis()%1000 );
                } catch (InterruptedException e) {
                    if (!ringThreadToStop) {
                        logger.error(e.getMessage(), e);
                    }
                }

                //轮询时间轮
                while (!ringThreadToStop) {
                    try {
                        synchronized (ringData){
                            if(ringData.isEmpty()){
                                     ringData.wait();
                            }
                        }

                        // 处理前2秒的数据
                        List<Integer> ringItemData = new ArrayList<>();
                        int nowSecond = Calendar.getInstance().get(Calendar.SECOND); //当前秒数  
                        // 避免处理耗时太长,跨过刻度,向前校验一个刻度=移除前一个刻度的k-v值并jobId放入执行列表
                        for (int i = 0; i < 2; i++) {
                            List<Integer> tmpData = ringData.remove( (nowSecond+60-i)%60 );
                            if (tmpData != null) {
                                ringItemData.addAll(tmpData);
                            }
                        }

                        // 促发执行任务
                        logger.debug(">>>>>>>>>>> xxl-job, time-ring beat : " + nowSecond + " = " + Arrays.asList(ringItemData) );
                        if (ringItemData.size() > 0) {
                            // do trigger
                            for (int jobId: ringItemData) {
                                // 执行任务
                                JobTriggerPoolHelper.trigger(jobId, TriggerTypeEnum.CRON, -1, null, null, null);
                            }
                            // 清空需要执行任务的列表
                            ringItemData.clear();
                        }
                    } catch (Exception e) {
                        if (!ringThreadToStop) {
                            logger.error(">>>>>>>>>>> xxl-job, JobScheduleHelper#ringThread error:{}", e);
                        }
                    }

                    //以上内容执行完可能还没到达下一秒钟整点时间,那就休息到到达下一秒整点时的毫秒数时间
                    try {
                        TimeUnit.MILLISECONDS.sleep(1000 - System.currentTimeMillis()%1000);
                    } catch (InterruptedException e) {
                        if (!ringThreadToStop) {
                            logger.error(e.getMessage(), e);
                        }
                    }
                }
                logger.info(">>>>>>>>>>> xxl-job, JobScheduleHelper#ringThread stop");
            }
        });
        ringThread.setDaemon(true);
        ringThread.setName("xxl-job, admin JobScheduleHelper#ringThread");
        ringThread.start();


    }//start()方法结束

}

任务调度器工作流程:
1.任务线程scheduleThread通过数据库的for update行锁来保证多个调度中心集群在同一时间内只有一个调度中心在调度任务
2.周期性的遍历所有的jobInfo这个表,读取触发时间小于nowtime+5s这个时间之前的所有任务,然后进行引入以下触发机制判断

三种触发任务机制:

1)过了执行时间5S多还没执行的nowtime-TriggerNextTime()>PRE_READ_MS(5s) 既超过有误差5S外的,则查看当前任务的失效调度策略,若为立即重试一次,则立即触发调度任务,且触发类型为misfire
2)过了执行时间5S内还没执行的nowtime-TriggerNextTime()<PRE_READ_MS(5s) 既没有超过有效误差5S内,则立即调度调度任务
3)执行时间即将来5S内(因为他列表查的就是将来5S内的)还没执行的nowtime<TriggerNextTime() 则说明这个任务马上就要触发了,放到一个时间轮上(https://blog.youkuaiyun.com/zalu9810/article/details/113396131),

3.随后将快要触发的任务放到时间轮上(Map<int,List>结构实现固定60个key),时间轮由key(将要触发的时间s),value(在当前触发s的所有任务id集合),然后更新这个任务的下一次触发时间到数据库中

4.第二个线程ringThread对这个时间轮的任务遍历,周期在1s之内周期的扫描这个时间轮,然后执行调度任务

原文链接:https://blog.youkuaiyun.com/s6056826a/article/details/113446126

xxl-job通过mysql悲观锁实现分布式锁,从而避免多个服务器同时调度任务:

  • 通过setAutoCommit(false),关闭自动提交
  • 通过select lock for update语句,其他事务无法获取到锁,显示排她锁。
  • 最后在finally块中commit()提交事务释放for update的排他锁,并且setAutoCommit

执行任务

在上面的调度任务中最终调用的是执行器执行任务方法,具体代码如下

//在调度代码中我们看到:执行任务
JobTriggerPoolHelper.trigger(jobId, TriggerTypeEnum.CRON, -1, null, null, null);

//JobTriggerPoolHelper.trigger方法最终调在线程池中调用的是XxlJobTrigger.trigger
JobTriggerPoolHelper.trigger(){
ThreadPoolExecutor triggerPool_ = fastTriggerPool;
 triggerPool_.execute(new Runnable() {
 //构建参数后执行 XxlJobTrigger.trigger
 XxlJobTrigger.trigger(jobId, triggerType, failRetryCount, executorShardingParam, executorParam, addressList);
 }
}

//XxlJobTrigger.trigger
XxlJobTrigger.trigger(){
 // load data,加载任务信息
 XxlJobInfo jobInfo = XxlJobAdminConfig.getAdminConfig().getXxlJobInfoDao().loadById(jobId);
 if (executorParam != null) {
 jobInfo.setExecutorParam(executorParam);
 }
 processTrigger(group, jobInfo, finalFailRetryCount, triggerType, shardingParam[0], shardingParam[1]);

}

//核心逻辑在processTrigger中
//代码位置: com.xxl.job.admin.core.trigger.XxlJobTrigger#processTrigger
private static void processTrigger(XxlJobGroup group, XxlJobInfo jobInfo, int finalFailRetryCount, TriggerTypeEnum triggerType, int index, int total){

//初始化trigger-param
 TriggerParam triggerParam = new TriggerParam();
 triggerParam.setJobId(jobInfo.getId());
//执行路由策略:index\分片\路由策略\获取任务地址
routeAddressResult = executorRouteStrategyEnum.getRouter().route(triggerParam, group.getRegistryList());
//运行执行器
 triggerResult = runExecutor(triggerParam, address);
 //日志处理,代码省略
}

//代码位置:com.xxl.job.admin.core.trigger.XxlJobTrigger#runExecutor
public static ReturnT<String> runExecutor(TriggerParam triggerParam, String address){
        ReturnT<String> runResult = null;
        try {
            //获取执行器:通过地址从executorBizRepository(map)获取ExecutorBiz
            ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(address);
            //执行任务并返回结果:executorBiz是抽象类,根据具体类型执行本地任务/执行器任务
            runResult = executorBiz.run(triggerParam);
        } catch (Exception e) {
            logger.error(">>>>>>>>>>> xxl-job trigger error, please check if the executor[{}] is running.", address, e);
            runResult = new ReturnT<String>(ReturnT.FAIL_CODE, ThrowableUtil.toString(e));
        }

        //返回执行结果
        StringBuffer runResultSB = new StringBuffer(I18nUtil.getString("jobconf_trigger_run") + ":");
        runResultSB.append("<br>address:").append(address);
        runResultSB.append("<br>code:").append(runResult.getCode());
        runResultSB.append("<br>msg:").append(runResult.getMsg());

        runResult.setMsg(runResultSB.toString());
        return runResult;
}

ExecutorBiz接口有两个实现,分别是ExecutorBizClient(执行器客户端)、ExecutorBizImpl(执行器服务端),

  • ExecutorBizClien类就是客户端操作远程服务的任务

  • ExecutorBizImpl就是服务端操作本地任务

ExecutorBiz接口有beat(心跳检测)、idleBeat(空闲检测)、run(执行任务)、kill(停止任务)、log(打印日志)这些方法。

我们看看ExecutorBizClien的run方法:

//代码位置:com.xxl.job.core.biz.client.ExecutorBizClient#run
public ReturnT<String> run(TriggerParam triggerParam) {
//调用http的POST请求发送触发参数触发服务端的任务执行,然后将结果返回给客户端。请求的地址为addressUrl + "run",当客户端发送请求以后,ExecutorBizImpl的run方法将会接收请求处理,然后将处理的结果返回
        return XxlJobRemotingUtil.postBody(addressUrl + "run", accessToken, timeout, triggerParam, String.class);
}

客户端EmbedServer类的内部类EmbedHttpServerHandler的process方法会调用ExecutorBizImpl类的run方法通过Netty Http调用执行器的EmbedServer的process方法

执行器

根据提供的样例,可以发布为单独的服务,和调度器分开部署。

执行器启动时,会初始化一个EmbedServer类,该类的start方法会启动netty服务器。netty服务器会接收客户端发送过来的http请求,当接收到触发请求(请求路径是/run)会交给EmbedServer类的process方法处理,process方法将会调用ExecutorBizImpl的run方法处理客户端发送的触发请求,process方法接收触发请求的代码如下图所示:

private Object process(HttpMethod httpMethod, String uri, String requestData, String accessTokenReq){

if(){//....
}else if ("/run".equals(uri)) {
                    TriggerParam triggerParam = GsonTool.fromJson(requestData, TriggerParam.class);
                    return executorBiz.run(triggerParam);
                }
 }

ExecutorBizImpl的run方法处理流程大致如下:

  • 加载任务处理器与任务执行线程,校验任务处理器与任务执行线程。
  • 执行阻塞策略
  • 注册任务
  • 保存触发参数到缓存

run方法首先根据任务id从缓存jobThreadRepository(map)中获取任务执行线程jobThread,任务执行线程jobThread保存着任务处理器jobHandler,然后进行校验任务执行线程以及任务处理器。

接下来检验任务执行线程以及任务处理器,就是按照Java、groovy、脚本分别进行校验

xxl-job有三种阻塞策略,分别为SERIAL_EXECUTION(并行)、DISCARD_LATER(丢弃)、COVER_EARLY(覆盖之前的)。当阻塞策略为丢弃,则判断该执行线程是否正在执行,如果是则直接返回结果,不再往下执行任务了。当阻塞策略为覆盖之前的,则判断执行线程是否正在执行,如果是则杀掉原来的执行线程。如果阻塞策略是并行,则不做什么

注册任务线程:如果任务线程等于null,注册任务线程并启动线程。registJobThread方法首先新建一个任务线程,并调用newJobThread的start方法启动任务线程。然后加入jobThreadRepository进行缓存,当旧的oldJobThread不等于null,则停止掉旧的任务线程。

在服务端执行的流程中,将任务交给任务线程池JobThread执行,JobThread的run方法主要做了几件事:

  • 处理器的初始化
  • 任务的执行
  • 销毁清理工作

while()循环从triggerQueue队列中弹出触发参数,如果存在执行超时时间并大于0,则在规定的时间异步调用handler的execute方法执行任务,否则立即调用handler的execute方法执行任务。

while()循环如果任务停止了,需要将队列中所有的触发删除(所有定时任务删除)

执行方法

定时任务执行过程调用处理器的init(初始化方法)、execute(执行方法)、destroy(销毁)方法,这些方法是由IJobHandler抽象类的实现类实现的:

IJobHandler抽象类有三个子类,

  • GlueJobHandler是执行groovy的处理器,MethodJobHandler是执行java的处理器

  • ScriptJobHandler是执行脚本(Pyhotn、PHP、NodeJS、Shell等)的处理器。

  • MethodJobHandler是处理java定时任务的方法,当我们用java开发了定时任务方法,然后用@XxlJob注解修饰方法,就可以调度该定时任务方法了

我们看看MethodJobHandler的execute方法是如何执行定时任务方法的。

//代码位置:com.xxl.job.core.handler.impl.MethodJobHandler#execute
public void execute() throws Exception {
        Class<?>[] paramTypes = method.getParameterTypes();
        if (paramTypes.length > 0) {
            method.invoke(target, new Object[paramTypes.length]);       // method-param can not be primitive-types
        } else {
            method.invoke(target);
        }
}

MethodJobHandler的execute方法利用反射,获取定时任务的method,然后利用invoke执行定时任务方法。

GlueJobHandler是执行groovy的处理器,在admin界面的idea界面上写好groovy保存在数据库,会调用GlueJobHandler类的execute方法执行,groovy是一种基于JVM的开发语言,groovy 代码能够与 Java 代码很好地结合,也能用于扩展现有代码。GlueFactory类的loadNewInstance方法将写好的groovy加载解析为写好的groovy代码,并返回IJobHandler,然后将IJobHandler传进GlueJobHandler构造器中新建GlueJobHandler对象。

GlueJobHandler的execute方法就是调用GlueFactory工厂类创建的IJobHandler的execute方法。

ScriptJobHandler的execute方法有些不重要的代码被省略。主要有几个重要的流程:获取脚本执行命令、保存脚本命令到文件、执行脚本。

ScriptUtil的markScriptFile方法,将脚本定时任务代码保存在名字为jobId_glueUpdatetime_suffix中,jobId为任务id,glueUpdatetime为脚本更新时间、suffix为脚本后缀,如采用python写定时任务时,保存在类似666-123456789.py文件中。

执行脚本ScriptUtil的execToFile方法,execToFile方法是利用Runtime.getRuntime().exec()方法在java程序里运行脚本程序。Runtime.getRuntime().exec()方法会将执行命令发送给操作系统,然后等待操作系统运行程序的结果返回,如Runtime.getRuntime().exec()方法给操作系统发送pyhton hello.py命令,这样就会执行python脚本,并等待python脚本的运行结果的返回

这里直接参考:xxl-job定时任务执行流程分析-任务执行

https://zhuanlan.zhihu.com/p/438464389

参考资料

xxl-job源码(一)服务端客户端简单理解 - QiaoZhi - 博客园

万字长文简单明了的介绍xxl-job以及quartz 万字长文简单明了的介绍xxl-job以及quartz_wx613dbd09b1332的技术博客_51CTO博客

XX-JOB部署(Linux) 例如:以上就是今天要讲的内容,本文仅仅简单介绍了在linux服务器上部署xxjob的整个过程。 阅读详情

相关推荐

从DDH到FastANI:原核生物分类黄金标准的进化史与技术对比

本文系统梳理了原核生物分类标准从DDH到FastANI的四次技术革命,详细解析了95%相似度阈值的科学依据及应用场景。通过对比DDH、16S rRNA、ANI和FastANI的技术特点与性能指标,为微生物研究者提供基因组分类的实战指南,并展望第三代测序时代的分类新挑战。

weixin_29275257的博客 412

xxjob分布式任务调度

前言 在工作中使用到了定时任务,通过查找资料选择了xxjob,以下是xxjob的介绍以及基本的使用. xxjob介绍 XXL-JOB是一个分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。 将调度行为抽象形成“调度中心”公共平台,而平台自身并不承担业务逻辑,“调度中心”负责发起调度请求。 将任务抽象成分散的JobHandler,交由“执行器”统一管理,“执行器”负责接收调度请求并执行对应的JobHandler中业务逻辑。 参考官网:https://github.com/xuxueli/

mcband的博客 1万+

关于深度可分离卷积

关于深度学习中的深度可分离卷积

qq_45670407的博客 1796

快速学习-XXL-JOB快速入门

二、快速入门 2.1 初始化“调度数据库” 请下载项目源码并解压,获取 “调度数据库初始化SQL脚本” 并执行即可。 “调度数据库初始化SQL脚本” 位置为: /xxl-job/doc/db/tables_xxl_job.sql 调度中心支持集群部署,集群情况下各节点务必连接同一个mysql实例; 如果mysql做主从,调度中心集群节点务必强制走主库; 2.2 编译源码 解压源码,按照maven格式将源码导入IDE, 使用maven进行编译即可,源码结构如下: xxl-job-admin:调度中心 xxl

逍遥云恋 1598

Redmi K30 5G刷 LineageOS17.1

前言 买手机前就观望了Xdr论坛,看Redmi K30 5G有LineageOS17.1的Rom并且还挺热,应该还不错,就入手了。晚上就刷机了。买了小米就是为了折腾,为发烧而生。 在开始前还请先安装adb工具,因为我自己是做Android开发的,自带adb,不会安装的可以百度“Android安装adb工具”。还有下载刷机文件:百度云 密码:2a0f 刷机的系统是刚买来的低版本,没有升级过Rom,如果高版本慎刷。 还有这个Rom是5G版本的,4G版本的不要刷。 建议没有刷过机器的最好不要尝试,变砖后果自负。

u010436867的博客 1万+

【脑电分析系列】第15篇:脑电功能连接性与脑网络分析(二):Granger因果性、图论指标与复杂网络构建

摘要: 本文深入探讨了脑电(EEG)分析中的高阶连接性方法——有效连接(Effective Connectivity),重点介绍了Granger因果性(Granger Causality, GC)的原理与应用。GC通过统计预测揭示脑区间信息流动方向,超越了传统功能连接的同步性分析。文章还结合图论指标(如度、路径长度、聚类系数和模块化)量化脑网络的拓扑结构,并通过Python代码(MNE和NetworkX库)演示了从EEG数据构建、分析复杂脑网络的完整流程。该研究为理解大脑动态互动、认知任务信息流及神经系统疾

qq_32516809的博客 1559

springboot集成xxjob

springboot集成xxjob

weixin_45812090的博客 7158

大数据分析案例-基于决策树算法构建世界杯比赛预测模型

本项目旨在分析往届世界杯比赛数据,找出规律,最后使用决策树算法构建世界杯比赛预测模型,给出两个球队即可预测胜率,可以给爱看球的小伙伴提供一个参考。本次实验我们使用了2018年及其之前的世界杯比赛数据,通过建立决策树模型,模型准确率为82%,接着我们使用该模型预测了2022年世界杯的5场半决赛及决赛,模型准确率也是接近80%的,说明模型效果还是很不错的。心得与体会:通过这次Python项目实战,我学到了许多新的知识,这是一个让我把书本上的理论知识运用于实践中的好机会。

m0_64336780的博客 1万+

用Java代码执行一次xxjob

Java代码执行XXJob的实践指南 XXJob是一个分布式任务调度平台,它支持多种任务类型,如定时任务、周期性任务等。本文将通过一个简单的Java代码示例,介绍如何在Java项目中执行一次XXJob任务。 环境准备 在开始之前,请确保你已经安装了以下环境: JDK 1.8或以上版本 Maven或Gradle作为构建工具...

weixin_36336710的博客 1059

sap中途启用物料账

spro->控制->产品成本控制->实际成本核算/物料分类帐->实际成本核算->激活实际成本的在产品。spro->控制->产品成本控制->实际成本核算/物料分类帐->实际成本核算->激活实际成本组件划分。spro->控制->产品成本控制->实际成本核算/物料分类帐->实际成本核算->激活实际成本核算。spro->控制->产品成本控制->实际成本核算/物料分类帐->激活评估范围的物料分类账。2.对于之前存在物料移动,但是期初没有库存的物料,物料分类账里追踪不到相关物料信息。1.需要激活物料分类账。

Yeol827的博客 991

Python之abbrobotegm包语法、参数和实际应用案例

`abbrobotegm` 是用于控制 ABB 机器人的 Python 接口包,主要通过 EGM(Externally Guided Motion,外部引导运动)协议与 ABB 机器人控制器进行通信,实现对机器人的实时位置控制、状态监控和运动引导等功能。该包适用于需要将 ABB 机器人与外部系统(如视觉系统、传感器等)集成的场景。

王国平的博客 1254

XxJob定时任务问题回顾记录

Java web开发的日常工作中,大概率会有定时任务的场景,如果这种需求不多的情况下可以用Spring自带的Scheduled定时任务注解: 如果这种需求较多,写在项目中可能比较混乱,不易管理和迭代,这时可以考虑用XxJob,将任务拆解出来独立成一个服务。XxJob是一个定时任务开源平台,能和Spring程序整合,首先有一个XxJob中心,注册中心登录上去是XxJob可视化面板,可以看到运行报表、任务管理、运行日志,运行日志中可以看到运行的状态,有成功、失败、进行中,服务...

u011602668的博客 4543

redhawk:Low Power Analysis

1.rush current与switch cell 在standby状态下为了控制leakage power我们选择power gating的设计方式,使用power switch cell关闭block/power domain的电源。 power switch的基本介绍可见: 低功耗设计-Power Switch power switch的table中有四种状态,对应block工作、待机和上电,下电。low power analysis主要分析上电的过程。 上电过程可以看做mos电容

m0_61544122的博客 3202

xxjob调度成功任务无响应问题

到这里就基本可以确定我们问题出现的原因,应为我的job任务中会有访问其他网址进行检查的情况,其中某一个网址访问的时候会一直等待,这就导致我的任务会一直阻塞,而job又是一分钟执行一次,就出现了大量的任务往队列中写,但是没有线程去消费掉。这里的结果之前是空的,应为我把服务下线之后,xxJob就找不到服务了,所以就会变成失败,通过日志还是可以看到是没有任何报错信息的。我们可以在控制台中设置超时时间,这样我们就不会有很多一直在等待执行中的任务了,就不会去创建大量的任务,导致我们的线程阻塞了。

weixin_44770756的博客 3801

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

xxjob定时任务

9 创建任务类并在运行方法上添加xxjob注解 注解内属性同任务中jobhandle。5 创建执行器 在当前执行器下创建任务(一个项目一般创建一个执行器)8 创建配置文件并添加配置类中的属性(端口号,调度中心地址等)IDE同时启动多个服务可以在services中进行。6 创建项目导入xxjob相关包 和web包。10 运行启动类 在调度中心开启任务即可。1 下载xxjob压缩包解压导入IDE。7 创建配置类同xxjob项目中一样。2 执行xxjob sql文件。4 运行xxjob启动类。

weixin_51517000的博客 2084

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

对接三方的xxjob

做了一个自动初始化服务的平台,甲方有一个xxjob需要登录进去指定执行定时任务。提示:以下是本篇文章正文内容,下面案例可供参考没总结,最烦总结。

qq_44863964的博客 830

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

快马AI助力xxjob:一键生成分布式任务调度应用

分布式任务调度是现代系统不可或缺的基础设施xxjob的设计非常贴合实际业务场景,功能强大但使用简单AI代码生成工具可以大幅提升开发效率,特别是对于这种有固定模式的场景一键部署功能省去了繁琐的环境配置,让开发者能更专注于业务逻辑如果你也在为定时任务管理发愁,不妨试试InsCode(快马)平台的xxjob解决方案。从代码生成到部署上线一气呵成,这种丝滑的开发体验真的会上瘾!

CrystalwaveStag的博客 1276

XXJOB vs 传统定时任务:开发效率提升300%的实测对比

最近在做一个电商项目时遇到了订单超时自动取消的需求,团队内部对采用XXJOB还是传统Spring Task方案产生了分歧。为了客观评估两者的差异,我做了个详细的对比实验,结果发现XXJOB在开发效率上的优势远超预期。,它内置了XXJOB等常用组件,能快速创建可运行的项目原型,一键部署特别方便,省去了很多环境配置的麻烦。我测试时发现从创建到部署整个过程不到10分钟,对需要快速验证技术方案的场景特别友好。XXJOB方案:核心业务仅需58行。XXJOB:配置+编码共1.5小时。XXJOB支持实时修改立即生效。

AgatePanther34的博客 847

ssm集成xxjob报错问题

在集成到ssm项目中时xxjob官网下载的几个稳定版本我有试过 报错记录 1.最常见的timeout 保证两个项目启动的时候没有错误 保证是执行器管理是否注册上ip 如果注册上ip但是执行定时器有问题则为注册bean出的问题 查看ssm项目扫描包是否注册上 2.ssm项目的xxl.job.admin.addresses=http://127.0.0.1:8888/xxl-job-admin跟调度中心的一致解决 3.此问题是因为ssm项目中没有注入bean、重复注入bean造成 **

这是随心创作的博主 1366

xxJob调用失败

xxjob调度失败

weixin_44754681的博客 1308

1小时验证创意:用XXJOB快速搭建邮件营销系统原型

这个原型后来成功说服了技术合伙人加入创业团队,证明用对工具快速产出可视化成果真的很重要。如果你也在做类似尝试,不妨试试这种"XXJOB+快马平台"的组合拳。提供的开箱即用环境帮我节省了至少2小时。特别是部署功能,写完代码点个按钮就能生成可公开访问的演示链接,不用自己折腾服务器。今天想和大家分享一个快速验证产品创意的实战案例:如何在1小时内用XXJOB搭建邮件营销系统的可演示原型。的一站式开发环境帮了大忙,让我跳过了繁琐的环境配置,直接聚焦核心功能实现。整个开发过程最耗时的其实是各种环境调试,而。

ThunderstormLynx23的博客 966

Java XXjob 定时任务更新数据库sql

XXJOB

qq_32094261的博客 945

电商库存同步实战:XXJOB在每日千万级数据处理中的应用

XXJOB在这个项目中展现了强大的分布式任务处理能力,特别是其分片机制和失败重试功能,让我们能够轻松应对千万级数据处理需求。整个系统上线后运行稳定,库存同步延迟从原来的小时级降低到分钟级,超卖投诉减少了95%。如果你也在处理类似的大规模数据同步问题,不妨试试InsCode(快马)平台。我在上面快速搭建了一个简化版的演示项目,可以一键部署体验。平台内置的Java环境和MySQL支持让项目配置变得特别简单,省去了搭建本地开发环境的麻烦,实测从创建到运行不到5分钟就能搞定,对快速验证方案特别有帮助。

StarfallRaven13的博客 527

xxjob部署(记录篇)

spring.datasource.username:数据库用户名。spring.datasource.password:数据库密码。spring.datasource.url:数据库连接地址。默认用户名密码admin/123456。

孤独的狼 721

XXJOB任务调度系统:AI如何帮你自动生成分布式任务代码

比如要求"生成支持分片处理且失败重试3次的执行器代码",AI会生成包含@XxlJob注解的完整执行器类,自动实现分片参数获取、重试计数器等关键逻辑,甚至包含异常处理的最佳实践。对于需要快速验证业务场景的开发者来说,这种"描述需求-生成代码-立即部署"的闭环体验实在太方便了。生成的代码会自动包含正确的HTTP端点路径、请求/响应体结构,以及XXJOB要求的header信息。作为一个经常需要处理定时任务的开发者,我发现手动编写调度系统代码特别耗时,直到尝试了AI辅助开发,效率直接翻倍。

YellowSun24的博客 732
上一篇: Seata分布式事务AT、TCC、SAGA、XA模式
下一篇: xxl-job分布式框架源码路由/负载均衡策略算法
牧竹子
博客等级 码龄17年 572粉丝 233原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值