Lazy loaded image
🗒️Xxl-Job-路由
Words 1193Read Time 3 min
2025-9-12
2025-9-14
type
status
date
slug
summary
tags
category
icon
password
原文
路由策略本质是 admin 需要挑一个执行器的地址去执行。
路由策略使用策略模式,根据执行器的配置自动选择对应的配置。 abstract ExecutorRouter
  • ├── ExecutorRouteBusyover.java 忙碌路由 ├── ExecutorRouteConsistentHash.java 一致性 hash ├── ExecutorRouteFailover.java 故障转移 ├── ExecutorRouteFirst.java 第一个 ├── ExecutorRouteLast.java 最后一个 ├── ExecutorRouteLFU.java 最不经常使用 ├── ExecutorRouteLRU.java 最近最久未使用 ├── ExecutorRouteRandom.java 随机 └── ExecutorRouteRound.java 轮询

执行器地址

执行器的对应 MySQL的xxl_job_group,地址为 address_list 的字段。
在添加执行器会有注册方式的选项,自动注册、手动输入两种方式。
  • 自动注册 application 端会在启动时自动获取本机 ip向 admin 端注册。
  • 手动输入 页面的操作直接会更新数据库中 xxl_job_group 的address_list 数据。
    • 🚀
      手动输入的执行器地址不会自动更新。
      1. application 端向 admin 发起注册请求时,admin 端会向 xxl_job_registry表中写入一条数据。
      1. 之后的application 端发起的注册请求只会更新该条数据,主要是更新 updateTime。
       
notion image
 

application 注册 OR 删除

注册

  1. application 启动时会读取配置文件中 xxl-job 相关的配置如admin的地址,以及执行器的地址。如果未填写执行器的地址,则会自动获取本机地址。
📖
自动获取本机地址:IpUtil工具类获取本机ip地址。多网卡环境和docker部署时注意获取地址的问题。
  1. 启动 registryThread 线程发起远程调用(admin端EmbedServer中的/registry接口)。
📖
配置文件
admin 端支持集群配置,application 可以向多个 admin端 注册。
  1. admin 接收到请求后交由registryOrRemoveThreadPool线程池去添加或更新 xxl_job_registry表。

删除

application 如果使用 kill 15 或 application shutdown 时,会向admin端发送/registryRemove 请求,admin 端接收到请求后删除xxl_job_registry表中数据。

自动更新地址

  1. 自动更新地址指的是执行器地址选择的是自动。
  1. admin 端启动时会启动一个registryMonitorThread线程,该线程会更新执行器地址(必须注册方式是自动)。
  1. registryMonitorThread线程:
    1. 获取注册方式为自动的执行器(xxl_job_group)列表。
    2. 删除过期的xxl_job_registry数据。
      1. 过期的数据:xxl_job_registry 的updateTime 超过 90 秒变动的视为过期数据。
    3. 获取所有的未过期的 registry 数据,构建 kv 结构。
      1. 🚀
        KV结构:HashMap<String, List<String>> key为执行器名称
        key为appname(执行器名称),value:执行器的地址。
    4. 遍历执行器列表(a 步骤获取的所有 xxl_job_group 列表),根据 appname 获取执行器地址列表(registryList = appAddressMap.get(appname)),然后直接更新 xxl_job_group 表。
    5. page icon
      对于执行器以注册方式为自动时,application 与 admin 交互,以更新xxl_job_registry表中数据。执行器需要最新的地址,以便job调度时能够顺利执行。
      xxl_job_group 存了执行器的地址,admin的后台线程则会从 xxl_job_registry 中获取最新的执行器的地址,更新每个执行器的最新调度地址。
       

路由策略

 

忙碌转移

  1. admin端会循环向所有的调用地址端发送一个 idleBeat 请求。
  1. application 端接受到请求后会检测本地的 jobThread 有无当前的 jobId 任务正在运行。如果没有当前 jobId 的任务正在执行,响应的handleCode为 500,无当前 jobId 运行则为 200.
  1. admin 接收到响应,如果请求的响应的 handleCode为200,则直接使用当前地址。

故障转移

  • 向所有调用地址发送beat请求,如果第一个地址的请求返回的 handleCode =200,则使用该地址。
  • beat 请求只是简单的一个心跳请求。

LRU

最近最少使用
LRU算法使用linkedHashMap 结构实现。
jobLRUMap
jobLRUMap的缓存为 24 小时。
key为 jobId,value: LinkedHashMap key、value 都为调用地址。
LinkedHashMap:
  • 每次访问该元素都会将该元素移动到链表尾部
  • 存储都是该 job 可以调用的地址,通过 传入的 addressList,会将LinkedHashMap中的地址更新。
    • ruItem.entrySet().iterator().next().getKey(); // 获取 LRU链表中的第
      一个元素(即最久未访问的元素)

LFU

基于最少使用频率即选择使用频率较低的执行器来平衡负载
jobLfuMap
缓存时间为 24小时
key 为 jobId ,value为 HashMap<String,Integer>
通过key可以获取该 job 的所有调用地址的调用次数。
取出hashmap根据value(调用次数)升序排序,这样首位元素即调用地址。

一致性 Hash

  • 构建 hash环
    • 根据所有 addressList ,为每个 address 构建 100 个虚拟地址。
      TreeMap<Long,String> 作为 hash 环的数据结构。
      key为 hash值,value 为调用地址。
  • jobHash = hash(jobId)
    • 根据 jobId 的 hash 值 ,在 hash环获取调用地址,如果未能获取后,则从hash环返回环中最小的节点。
      -----A1------A2-------A3------ hash 环 -----------J1------------------ hash(job)
      addressRing.tailMap(jobHash); 获取环上所有hash ≥ 任务 hash 值的节点。

轮询

轮询分配调用地址
  • 计数器
    • private static ConcurrentMap<Integer, AtomicInteger> routeCountEachJob
      key 为 job,value 为计数器
  • 获取地址
     
    上一篇
    Xxl-Job
    下一篇
    Seata TCC示例

    Comments
    Loading...