精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

XXL-JOB增強組件,支持注解驅動的自動注冊

開發 前端
XXL-JOB Plus增強組件成功解決了老項目定時任務遷移的痛點,通過注解驅動和自動注冊機制,實現了零人工干預的任務遷移。該組件不僅大幅降低了運維成本,提升了開發效率,還為后續的任務擴展提供了良好的技術基礎。

背景介紹

在數字化轉型的大背景下,企業級應用的復雜度日益增加,特別是數據處理類服務往往承載著大量的定時任務。以我們團隊的yqqb-data服務為例,該服務負責處理數據接入、數據處理等核心業務功能,其中包含了68個定時任務,涵蓋了數據清洗、報表生成、數據同步等多個業務場景。

這些定時任務最初采用Spring Boot的@Scheduled注解實現,在項目初期能夠滿足基本需求。但隨著業務規模的擴大和系統復雜度的提升,原有的調度方案逐漸暴露出諸多問題:配置管理混亂、開發效率低下、運維管理困難、擴展性不足等。特別是在多實例部署和分布式環境下,原有的單機調度方案已經無法滿足業務需求。

為了應對這些挑戰,我們開始探索更先進的分布式調度解決方案。經過技術調研和方案對比,我們選擇了XXL-JOB作為基礎調度框架,但發現對于老項目的遷移,特別是68個定時任務的配置遷移,工作量巨大且容易出錯。為了解決這個問題,我們基于XXL-JOB開發了一套增強組件XXL-JOB Plus,通過注解驅動和自動注冊機制,實現了零人工干預的任務遷移,大幅提升了開發效率和運維便利性。

一、核心挑戰與解決方案

1.1 面臨的挑戰

在yqqb-data服務中,我們面臨著68個定時任務的管理難題。原有的Spring Boot @Scheduled方案存在以下核心問題:

配置管理混亂: 每個任務都需要在yml文件中單獨配置開關、執行頻率等參數,配置分散在多個文件中,維護成本極高。

開發效率低下: 每次新增任務都需要手動配置yml文件,68個任務需要逐個配置,工作量巨大且容易出錯。

運維管理困難: 任務狀態難以監控,缺乏統一的運維管理平臺,只能通過日志查看任務執行情況。

擴展性不足: 無法支持分布式調度,多實例部署時所有實例都會執行任務,無法實現任務分發和負載均衡。

1.2 解決方案概述

我們基于XXL-JOB開發了XXL-JOB Plus增強組件,通過注解驅動的方式實現了定時任務的自動注冊和管理。該組件具有以下核心特性:

注解驅動: 通過@XxlRegister注解,將原本分散在多個配置文件中的復雜配置簡化為代碼級別的聲明式配置。

自動注冊: 利用Spring Boot的生命周期事件,在應用啟動時自動發現和注冊所有任務,實現零人工干預。

統一管理: 通過XXL-JOB管理平臺統一管理所有任務,支持任務的啟用/禁用、修改配置、執行監控等功能。

分布式支持: 支持多實例部署時的任務分發,實現真正的分布式調度和負載均衡。

二、問題分析與思考

2.1 核心設計思路

我們的解決方案基于"增強而非改變"的設計理念,在現有XXL-JOB基礎上構建增強組件,通過注解驅動實現自動注冊機制。整個方案的核心是讓開發人員只需要在代碼中添加注解,系統就能自動完成任務的注冊和配置管理。

2.2 原有方案分析

2.2.1 原有Spring Boot @Scheduled的執行邏輯

Spring Boot定時任務執行流程圖:

具體代碼執行邏輯:

// 1. 應用啟動時,Spring Boot掃描@EnableScheduling注解
@SpringBootApplication
@EnableScheduling  // 啟用定時任務
public class YqqbDataApplication {
   public static void main(String[] args) {
      SpringApplication.run(YqqbDataApplication.class, args);
   }
}

// 2. 掃描@Scheduled注解,創建定時任務
@Component
publicclassDataProcessTask {

   @Value("${task.dataProcess.enabled:false}")
   privateboolean enabled;

   @Scheduled(cron = "${task.dataProcess.cron:0 0 2 * * ?}")
   publicvoiddataProcessTask() {
      // 執行邏輯
      if (!enabled) {
         log.info("任務已禁用");
         return;
      }

      try {
         log.info("開始執行數據處理任務");
         processData();
         log.info("數據處理任務執行完成");
      } catch (Exception e) {
         log.error("數據處理任務執行失敗", e);
      }
   }
}

# 3. 從yml配置文件讀取配置
task:
   dataProcess:
      enabled: true
      cron: "0 0 2 * * ?"
      retryCount: 3
      timeout: 30000

基于原有方案,分析出以下幾個問題

1.配置管理問題

問題表現:

? 每個任務都需要在yml中配置開關

? 配置項重復,維護成本高

? 缺乏配置驗證機制

影響范圍:

? 68個任務需要340個配置項

? 每次新增任務都需要手動配置

? 配置分散在多個文件中

2 監控管理問題

問題表現:

? 只能通過日志監控任務狀態

? 缺乏任務執行統計

? 無法統一管理任務生命周期

影響范圍:

? 運維人員需要手動查看日志

? 缺乏任務執行統計和報表

? 告警機制不完善

3 擴展性問題

問題表現:

? 無法支持分布式調度

? 多實例部署時所有實例都會執行

? 缺乏任務依賴關系管理

影響范圍:

? 無法實現任務分發和負載均衡

? 資源浪費,性能下降

? 限制了系統的擴展性

三、技術方案實現

3.1 技術方案設計

3.1.1 核心思路

基于XXL-JOB構建增強組件,實現注解驅動的自動注冊機制

技術架構圖:

3.1.2 組件架構設計

模塊結構:

com.xxxxxx.xxljob
├── annotation    // 注解定義
│   └── XxlRegister.java
├── config        // 配置管理
│   ├── XxlJobExecutorConfig.java
│   └── XxlJobPlusConfig.java
├── constant      // 常量定義
│   ├── XxlJobConstants.java
│   └── ScheduleType.java
├── core          // 核心實現
│   ├── MyXxlJobSpringExecutor.java
│   └── XxlJobAutoRegister.java
├── entity        // 實體類
│   ├── XxlJobGroup.java
│   └── XxlJobInfo.java
├── exception     // 異常處理
│   └── XxlJobException.java
└── service       // 服務接口
    ├── JobGroupService.java
    ├── JobInfoService.java
    ├── JobLoginService.java
    └── impl/
        ├── JobGroupServiceImpl.java
        ├── JobInfoServiceImpl.java
        └── JobLoginServiceImpl.java

3.2 核心組件實現

3.2.1 注解定義

XxlRegister注解:

package com.xxxxxx.xxljob.annotation;

import com.xxxxxx.xxljob.constant.ScheduleType;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * xxl 注冊
 *
 * @date 2025-05-09
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public@interface XxlRegister {
   /**
    * 類型 CRON 定時任務,FIX_RATE 固定間隔執行任務
    */
   String type()default ScheduleType.CRON;

   /**
    * 支持,cron表達式  固定間隔執行任務
    */
   String conf()default"";

   /**
    * 備注
    */
   String jobDesc()default"default jobDesc";

   /**
    * 作者
    */
   String author()default"default xxxxx";

   /**
    * 默認為 ROUND 輪詢方式
    * 可選: FIRST 第一個
    */
   String executorRouteStrategy()default"ROUND";

   /**
    * 默認狀態
    */
   inttriggerStatus()default0;
}

ScheduleType常量:

package com.xxxxxx.xxljob.constant;

/**
 * 任務類型
 *
 * @sin 2025-05-28
 */
publicinterfaceScheduleType {
   /**
    * CRON 定時任務,FIX_RATE 固定間隔執行任務
    */
   StringCRON="CRON";
   /**
    * CRON 定時任務,FIX_RATE 固定間隔執行任務
    */
   StringFIX_RATE="FIX_RATE";
}

3.2.2 自動注冊器

XxlJobAutoRegister核心實現:

package com.xxxxxx.xxljob.core;

/**
 * xxl-job 自動注冊
 * 負責在應用啟動時自動注冊執行器和任務
 * 支持自定義實現,使用者可以繼承此類并重寫相關方法來實現自定義邏輯
 *
 * @date 2024-05-09
 * @see XxlJob
 * @see XxlRegister
 * @since 1.0.0
 */
@Slf4j
@Component
@RequiredArgsConstructor
publicclassXxlJobAutoRegisterimplementsApplicationListener<ApplicationReadyEvent>,
        ApplicationContextAware {

   /**
    * 應用上下文
    */
   private ApplicationContext applicationContext;

   /**
    * 任務組服務
    */
   privatefinal JobGroupService jobGroupService;

   /**
    * 任務信息服務
    */
   privatefinal JobInfoService jobInfoService;

   /**
    * 默認執行器組索引
    * 用于在多個執行器組中選擇第一個作為默認組
    */
   protectedstaticfinalintDEFAULT_GROUP_INDEX=0;

   /**
    * 默認超時時間(毫秒)
    * 0表示不超時
    */
   protectedstaticfinalintDEFAULT_TIMEOUT=0;

   /**
    * 默認重試次數
    * 0表示不重試
    */
   protectedstaticfinalintDEFAULT_RETRY_COUNT=0;

   /**
    * 設置應用上下文
    * 實現 ApplicationContextAware 接口的方法,用于注入 Spring 應用上下文
    *
    * @param applicationContext Spring 應用上下文
    * @throws BeansException 當設置上下文失敗時拋出
    */
   @Override
   publicvoidsetApplicationContext(ApplicationContext applicationContext)throws BeansException {
      this.applicationContext = applicationContext;
   }

   /**
    * 處理應用就緒事件
    * 在應用啟動完成后,自動注冊執行器和任務
    *
    * @param event 應用就緒事件
    */
   @Override
   publicvoidonApplicationEvent(ApplicationReadyEvent event) {
      try {
         beforeRegister();
         registerJobGroup();
         registerJobInfo();
         afterRegister();
      } catch (Exception e) {
         log.error("自動注冊 xxl-job 失敗!", e);
         handleRegisterError(e);
      }
   }

   /**
    * 注冊前的處理,子類可以重寫此方法實現自定義邏輯
    */
   protectedvoidbeforeRegister() {
      // 默認空實現
   }

   /**
    * 注冊后的處理,子類可以重寫此方法實現自定義邏輯
    */
   protectedvoidafterRegister() {
      // 默認空實現
   }

   /**
    * 處理注冊錯誤,子類可以重寫此方法實現自定義錯誤處理
    *
    * @param e 異常
    */
   protectedvoidhandleRegisterError(Exception e) {
      if (e instanceof XxlJobException) {
         throw (XxlJobException) e;
      }
      thrownewXxlJobException(XxlJobConstants.ErrorCode.SYSTEM_ERROR, "自動注冊 xxl-job 失敗: " + e.getMessage(), e);
   }

   /**
    * 注冊執行器組
    */
   protectedvoidregisterJobGroup() {
      addJobGroup();
   }

   /**
    * 添加執行器組
    * 檢查執行器組是否存在,如果不存在則自動注冊
    * 如果執行器組已存在,則跳過注冊
    */
   privatevoidaddJobGroup() {
      booleanexists=this.jobGroupService.preciselyCheck();
      log.info("檢查執行器組是否存在: {}", exists);

      if (!exists) {
         this.jobGroupService.autoRegisterGroup();
         log.info("嘗試注冊執行器組");
      } else {
         log.info("執行器組已存在,無需注冊");
      }
   }

   /**
    * 注冊任務信息
    */
   protectedvoidregisterJobInfo() {
      List<XxlJobGroup> jobGroups = jobGroupService.getJobGroup();
      if (jobGroups.isEmpty()) {
         thrownewXxlJobException(XxlJobConstants.ErrorCode.BUSINESS_ERROR, "未找到可用的執行器組");
      }

      XxlJobGroupxxlJobGroup= selectJobGroup(jobGroups);
      if (xxlJobGroup == null) {
         thrownewXxlJobException(XxlJobConstants.ErrorCode.BUSINESS_ERROR, "未選擇到有效的執行器組");
      }

      String[] beanNames = applicationContext.getBeanNamesForType(Object.class, false, true);
      if (beanNames.length == 0) {
         log.warn("未找到任何Bean,跳過任務注冊");
         return;
      }

      for (String beanName : beanNames) {
         processBean(beanName, xxlJobGroup);
      }
   }

   /**
    * 選擇執行器組,子類可以重寫此方法實現自定義選擇邏輯
    *
    * @param jobGroups 執行器組列表
    * @return 選中的執行器組
    */
   protected XxlJobGroup selectJobGroup(List<XxlJobGroup> jobGroups) {
      if (jobGroups == null || jobGroups.isEmpty()) {
         returnnull;
      }
      return jobGroups.get(DEFAULT_GROUP_INDEX);
   }

   /**
    * 處理單個Bean的任務注冊
    * 遍歷Bean中所有帶有XxlJob注解的方法,并進行任務注冊
    *
    * @param beanName    Bean名稱
    * @param xxlJobGroup 執行器組
    * @throws BeansException 當獲取Bean實例失敗時拋出
    */
   protectedvoidprocessBean(String beanName, XxlJobGroup xxlJobGroup) {
      if (!StringUtils.hasText(beanName) || xxlJobGroup == null) {
         return;
      }

      try {
         Objectbean= applicationContext.getBean(beanName);
         Map<Method, XxlJob> annotatedMethods = findAnnotatedMethods(bean.getClass());

         for (Map.Entry<Method, XxlJob> entry : annotatedMethods.entrySet()) {
            processMethod(entry.getKey(), entry.getValue(), xxlJobGroup);
         }
      } catch (Exception e) {
         log.error("處理Bean[{}]時發生錯誤", beanName, e);
      }
   }

   /**
    * 查找帶有XxlJob注解的方法
    *
    * @param beanClass Bean類
    * @return 注解方法映射
    */
   protected Map<Method, XxlJob> findAnnotatedMethods(Class<?> beanClass) {
      return MethodIntrospector.selectMethods(beanClass,
              (MethodIntrospector.MetadataLookup<XxlJob>) method ->
                      AnnotatedElementUtils.findMergedAnnotation(method, XxlJob.class));
   }

   /**
    * 處理單個方法的任務注冊
    *
    * @param method      方法
    * @param xxlJob      XxlJob注解
    * @param xxlJobGroup 執行器組
    */
   protectedvoidprocessMethod(Method method, XxlJob xxlJob, XxlJobGroup xxlJobGroup) {
      if (method == null || xxlJob == null || xxlJobGroup == null) {
         return;
      }

      try {
         if (!method.isAnnotationPresent(XxlRegister.class)) {
            return;
         }

         XxlRegisterxxlRegister= method.getAnnotation(XxlRegister.class);
         if (isJobExists(xxlJobGroup.getId(), xxlJob.value())) {
            log.info("任務[{}]已存在,跳過注冊", xxlJob.value());
            return;
         }

         XxlJobInfoxxlJobInfo= createXxlJobInfo(xxlJobGroup, xxlJob, xxlRegister);
         IntegerjobInfoId= jobInfoService.addJobInfo(xxlJobInfo);
         log.info("任務[{}]注冊成功,任務ID: {}", xxlJob.value(), jobInfoId);
      } catch (Exception e) {
         log.error("處理任務[{}]時發生錯誤", xxlJob.value(), e);
      }
   }

   /**
    * 檢查任務是否已存在
    * 根據執行器組ID和執行器處理器名稱檢查任務是否已注冊
    *
    * @param jobGroupId      執行器組ID
    * @param executorHandler 執行器處理器名稱
    * @return 如果任務已存在返回true,否則返回false
    */
   protected boolean isJobExists(Integer jobGroupId, String executorHandler) {
      if (jobGroupId == null || !StringUtils.hasText(executorHandler)) {
         returnfalse;
      }

      try {
         List<XxlJobInfo> jobInfo = jobInfoService.getJobInfo(jobGroupId, executorHandler);
         if (jobInfo.isEmpty()) {
            returnfalse;
         }

         return jobInfo.stream()
                 .anyMatch(info -> executorHandler.equals(info.getExecutorHandler()));
      } catch (Exception e) {
         log.error("檢查任務[{}]是否存在時發生錯誤", executorHandler, e);
         returnfalse;
      }
   }

   /**
    * 創建任務信息
    * 根據執行器組、XxlJob注解和XxlRegister注解創建任務信息對象
    *
    * @param xxlJobGroup 執行器組,包含執行器組ID等信息
    * @param xxlJob      XxlJob注解,包含任務處理器名稱
    * @param xxlRegister XxlRegister注解,包含任務調度配置信息
    * @return 任務信息對象
    * @throws XxlJobException 當參數不完整時拋出
    */
   protected XxlJobInfo createXxlJobInfo(XxlJobGroup xxlJobGroup, XxlJob xxlJob, XxlRegister xxlRegister) {
      if (xxlJobGroup == null || xxlJob == null || xxlRegister == null) {
         thrownewXxlJobException(XxlJobConstants.ErrorCode.PARAM_ERROR, "創建任務信息參數不完整");
      }

      XxlJobInfoxxlJobInfo=newXxlJobInfo();
      xxlJobInfo.setJobGroup(xxlJobGroup.getId());
      xxlJobInfo.setJobDesc(xxlRegister.jobDesc());
      xxlJobInfo.setAuthor(xxlRegister.author());
      xxlJobInfo.setScheduleType(xxlRegister.type());
      xxlJobInfo.setScheduleConf(xxlRegister.conf());
      xxlJobInfo.setGlueType("BEAN");
      xxlJobInfo.setExecutorHandler(xxlJob.value());
      xxlJobInfo.setExecutorRouteStrategy(xxlRegister.executorRouteStrategy());
      xxlJobInfo.setMisfireStrategy("DO_NOTHING");
      xxlJobInfo.setExecutorBlockStrategy("SERIAL_EXECUTION");
      xxlJobInfo.setExecutorTimeout(DEFAULT_TIMEOUT);
      xxlJobInfo.setExecutorFailRetryCount(DEFAULT_RETRY_COUNT);
      xxlJobInfo.setGlueRemark("初始化");
      xxlJobInfo.setTriggerStatus(xxlRegister.triggerStatus());
      return xxlJobInfo;
   }
}

3.2.3 服務接口實現

JobGroupService接口:

package com.xxxxxx.xxljob.service;

/**
 * xxl-job 任務組服務接口
 * @date 2025-05-09
 */
publicinterfaceJobGroupService {
   /**
    * 獲取job 組
    *
    * @return 獲取job 組
    */
   List<XxlJobGroup> getJobGroup();

   /**
    * 是否自動注冊
    *
    * @return boolean
    */
   booleanautoRegisterGroup();

   /**
    * 校驗任務組信息
    *
    * @return boolean
    */
   booleanpreciselyCheck();
}

JobGroupServiceImpl實現:

package com.xxxxxx.xxljob.service.impl;

/**
 * xxl-job 任務組服務接口
 *
 * @date 2025-05-09
 */
@RequiredArgsConstructor
@Service
publicclassJobGroupServiceImplimplementsJobGroupService {
   /**
    * xxl-job-plus配置
    */
   privatefinal XxlJobPlusConfig xxlJobPlusConfig;
   /**
    * xxl-job執行器配置
    */
   privatefinal XxlJobExecutorConfig xxlJobExecutorConfig;
   /**
    * 登錄服務
    */
   privatefinal JobLoginService jobLoginService;

   @Override
   public List<XxlJobGroup> getJobGroup() {
      Stringurl= xxlJobPlusConfig.getAdminAddresses() + XxlJobConstants.Api.JOB_GROUP_LIST_API;
      HttpResponseresponse= HttpRequest.post(url)
              .form(XxlJobConstants.Param.APP_NAME, xxlJobExecutorConfig.getAppName())
              .form(XxlJobConstants.Param.TITLE, xxlJobExecutorConfig.getTitle())
              .cookie(jobLoginService.getCookie())
              .timeout(XxlJobConstants.Http.CONNECT_TIMEOUT_MS)
              .execute();
      Stringbody= response.body();
      JSONArrayarray= JSONUtil.parse(body).getByPath(XxlJobConstants.Response.DATA_FIELD, JSONArray.class);
      return array.stream()
              .map(o -> JSONUtil.toBean((JSONObject) o, XxlJobGroup.class))
              .collect(Collectors.toList());
   }

   @Override
   publicbooleanautoRegisterGroup() {
      Stringurl= xxlJobPlusConfig.getAdminAddresses() + XxlJobConstants.Api.JOB_GROUP_SAVE_API;
      HttpRequesthttpRequest= HttpRequest.post(url)
              .form(XxlJobConstants.Param.APP_NAME, xxlJobExecutorConfig.getAppName())
              .form(XxlJobConstants.Param.TITLE, xxlJobExecutorConfig.getTitle())
              .timeout(XxlJobConstants.Http.CONNECT_TIMEOUT_MS);

      httpRequest.form(XxlJobConstants.Param.ADDRESS_TYPE, xxlJobExecutorConfig.getAddressType());
      if (Objects.equals(xxlJobExecutorConfig.getAddressType(), XxlJobConstants.Business.ADDRESS_TYPE_MANUAL)) {
         if (Strings.isBlank(xxlJobExecutorConfig.getAddressList())) {
            thrownewXxlJobException(XxlJobConstants.ErrorCode.PARAM_ERROR,
                    XxlJobConstants.ErrorMessage.MANUAL_ADDRESS_LIST_EMPTY);
         }
         httpRequest.form(XxlJobConstants.Param.ADDRESS_LIST, xxlJobExecutorConfig.getAddressList());
      }

      HttpResponseresponse= httpRequest.cookie(jobLoginService.getCookie())
              .execute();
      Objectcode= JSONUtil.parse(response.body()).getByPath(XxlJobConstants.Response.CODE_FIELD);
      return code.equals(XxlJobConstants.Response.XXL_JOB_SUCCESS_CODE);
   }

   @Override
   publicbooleanpreciselyCheck() {
      List<XxlJobGroup> jobGroup = getJobGroup();
      Optional<XxlJobGroup> has = jobGroup.stream()
              .filter(xxlJobGroup -> xxlJobGroup.getAppname().equals(xxlJobExecutorConfig.getAppName())
                      && xxlJobGroup.getTitle().equals(xxlJobExecutorConfig.getTitle()))
              .findAny();
      return has.isPresent();
   }
}

3.3 使用示例對比

3.3.1 原有代碼

// 原有代碼示例
@Component
publicclassDataProcessTask {

   @Value("${task.dataProcess.enabled:false}")
   privateboolean enabled;

   @Scheduled(cron = "${task.dataProcess.cron:0 0 2 * * ?}")
   publicvoiddataProcessTask() {
      if (!enabled) {
         log.info("數據處理任務已禁用");
         return;
      }

      try {
         log.info("開始執行數據處理任務");
         processData();
         log.info("數據處理任務執行完成");
      } catch (Exception e) {
         log.error("數據處理任務執行失敗", e);
      }
   }

   privatevoidprocessData() {
      // 具體的數據處理邏輯
   }
}

3.3.2 改造后代碼

// 改造后代碼示例
@Component
publicclassDataProcessTask {

   @XxlJob("dataProcessTask")
   @XxlRegister(
           jobDesc = "數據處理任務",
           author = "xydong18",
           type = ScheduleType.CRON,
           conf = "0 0 2 * * ?",
           executorRouteStrategy = "ROUND",
           triggerStatus = 1
   )
   publicvoiddataProcessTask() {
      try {
         log.info("開始執行數據處理任務");
         processData();
         log.info("數據處理任務執行完成");
      } catch (Exception e) {
         log.error("數據處理任務執行失敗", e);
         throw e; // 讓XXL-JOB處理異常
      }
   }

   privatevoidprocessData() {
      // 具體的數據處理邏輯
   }
}

3.4 配置管理對比

3.4.1 原有配置

# 原有配置 - 需要為每個任務單獨配置
task:
   dataProcess:
      enabled:true
      cron:"0 0 2 * * ?"
      retryCount:3
      timeout:30000
   reportGenerate:
      enabled:true
      cron:"0 0 3 * * ?"
      retryCount:2
      timeout:60000
   dataClean:
      enabled:true
      cron:"0 0 4 * * ?"
      retryCount:1
      timeout:45000
   # ... 68個任務的配置

3.4.2 改造后配置

# 改造后配置 - 只需要配置XXL-JOB相關參數
xxl:
   job:
      admin:
         addresses:http://your-xxl-job-admin:8080/xxl-job-admin
         username:admin
         password:123456
         accessToken:your-access-token
         timeout:2000
      executor:
         appName:yqqb-data
         title:yqqb-data執行器
         addressType:0# 0=自動注冊、1=手動錄入
         address:# 執行器地址
         ip:# 執行器IP
         port:9999# 執行器端口
         logPath:./# 日志路徑
         logRetentionDays:30  # 日志保留天數

3.5 執行流程

3.5.1 基本流程圖

3.5.2 時序圖

3.6 擴展機制

3.6.1 自定義注冊器示例

@Component
publicclassCustomXxlJobAutoRegisterextendsXxlJobAutoRegister {

   @Override
   protectedvoidbeforeRegister() {
      // 自定義注冊前的處理
      log.info("開始自定義注冊流程");
   }

   @Override
   protected XxlJobGroup selectJobGroup(List<XxlJobGroup> jobGroups) {
      // 自定義執行器組選擇邏輯
      return jobGroups.stream()
              .filter(group -> "custom-group".equals(group.getAppName()))
              .findFirst()
              .orElse(jobGroups.get(0));
   }

   @Override
   protectedvoidafterRegister() {
      // 自定義注冊后的處理
      log.info("自定義注冊流程完成");
   }
}

四、實踐總結

4.1 驗收測試

4.1.1 功能驗收用例

1. 自動注冊測試:

? 應用啟動后,檢查XXL-JOB管理平臺是否自動創建執行器組

? 驗證68個任務是否全部自動注冊成功

? 確認任務配置信息是否正確

2. 任務執行測試:

? 驗證任務是否能正常執行

? 檢查任務執行日志是否正確記錄

? 確認任務執行狀態是否正常更新

3. 配置管理測試:

? 驗證新增任務時是否只需要添加注解

? 確認修改任務配置時是否只需要修改注解

? 檢查配置變更是否立即生效

4. 監控告警測試:

? 驗證任務執行失敗時是否有告警

? 確認任務執行超時時是否有處理

? 檢查任務執行統計是否正確

4.1.2 驗收Checklist


驗收項目

驗收標準

驗收結果

備注

任務自動注冊

所有68個任務自動注冊成功

通過

檢查XXL-JOB管理平臺任務列表

執行器組創建

任務執行器組創建成功

通過

驗證執行器組配置信息

任務配置信息

任務配置信息正確

通過

核對任務描述、作者、調度配置等

任務執行狀態

任務執行狀態正常

通過

檢查任務執行歷史和狀態

任務日志記錄

任務日志記錄完整

通過

驗證日志輸出和錯誤處理

監控告警功能

監控告警功能正常

通過

測試任務失敗告警機制

配置管理便捷性

配置管理便捷性驗證通過

通過

驗證注解配置的便利性

性能影響評估

性能影響評估通過

通過

對比改造前后的性能指標

4.2 效果評估

4.2.1 技術指標達成

任務注冊成功率: 100%(68/68)

? 所有68個任務全部自動注冊成功

? 執行器組創建成功

? 任務配置信息完整準確

配置簡化程度: 從68個yml配置項減少到1個注解配置

? 原有方案:每個任務需要5個配置項,總計340個配置項

? 新方案:每個任務只需要1個注解,總計68個注解

? 配置復雜度降低85%

人工配置成本: 從68個任務×5分鐘=340分鐘減少到0分鐘

? 原有方案:每個任務需要手動配置5分鐘

? 新方案:零人工干預,自動注冊

? 人力成本節省100%

運維便利性: 從分散管理提升到統一平臺管理

? 原有方案:通過日志分散監控

? 新方案:XXL-JOB統一管理平臺

? 運維效率提升90%

4.2.2 業務價值實現

簡單來說,XXL-JOB Plus組件最大的價值就是讓定時任務的管理變得簡單高效。以前我們管理68個定時任務,需要在多個配置文件中寫一大堆配置,開發人員要記住每個任務的開關、執行時間等參數,運維人員要手動監控每個任務的運行狀態,工作量很大還容易出錯。現在有了這個組件,開發人員只需要在代碼上加個注解就能搞定,系統會自動注冊到XXL-JOB管理平臺,運維人員可以在一個平臺上統一管理所有任務,還能實時看到任務執行狀態,大大降低了工作量和出錯概率。

從技術角度看,這個組件為我們的系統架構升級提供了很好的基礎。以前我們的定時任務只能在單機上運行,現在可以支持多臺機器分布式執行,系統可以根據負載情況自動分配任務,還能根據業務需求動態調整資源。這種能力對于現代企業應用來說非常重要,因為業務量是波動的,我們需要系統能夠靈活應對。

4.2.3 經驗總結

這次技術升級成功的關鍵在于我們采用了 "漸進式改造" 的策略。在大型系統中做技術升級風險很大,特別是涉及到核心業務時。我們的做法是在現有系統基礎上做增強,而不是推倒重來。通過注解的方式,我們巧妙地把復雜的配置簡化了,既保持了與現有系統的兼容性,又為后續升級留下了空間。這種"約定優于配置"的思想,讓代碼更清晰,維護更容易。

從運維角度看,自動注冊機制的設計很實用。在分布式環境中,手動配置不僅容易出錯,而且無法應對環境變化。通過利用Spring Boot的啟動事件,系統能在啟動時自動發現和注冊所有任務,這樣既降低了運維成本,又提高了系統的適應性。同時,統一的管理平臺為運維團隊提供了可視化的操作界面,讓日常運維工作變得簡單,問題排查也更方便,這正好符合現代DevOps的理念。

4.3 技術啟發

4.3.1 架構設計啟發

1. 增強而非改變:

// 在現有技術棧基礎上做增強,降低遷移風險
@XxlJob("taskName")  // 保持原有XXL-JOB注解
@XxlRegister(...)    // 新增注冊配置注解
public void taskMethod() {
   // 任務邏輯保持不變
}

2. 注解驅動:

// 通過注解實現配置的聲明式管理
@XxlRegister(
        jobDesc = "任務描述",
        author = "作者",
        type = ScheduleType.CRON,
        conf = "0 0 2 * * ?"
)

3. 自動注冊:

// 利用Spring Boot的生命周期事件實現自動化
@Component
public class XxlJobAutoRegister implements ApplicationListener<ApplicationReadyEvent> {
   @Override
   public void onApplicationEvent(ApplicationReadyEvent event) {
      // 應用啟動完成后自動注冊
   }
}

4. 分層設計:

com.iflytek.xxljob
├── annotation    // 注解層
├── core          // 核心層
├── service       // 服務層
└── entity        // 實體層

4.3.2 開發規范啟發

1. 統一命名:

// 任務名稱、描述、作者等信息統一管理
@XxlRegister(
        jobDesc = "數據處理任務",  // 統一的任務描述格式
        author = "xxx",     // 統一的作者命名
        type = ScheduleType.CRON  // 統一的類型定義
)

2. 配置簡化:

// 將復雜配置簡化為注解配置
// 原有:yml配置文件 + 代碼配置
// 新方案:注解配置
@XxlRegister(
        conf = "0 0 2 * * ?",           // cron表達式
        executorRouteStrategy = "ROUND",  // 路由策略
        triggerStatus = 1                // 觸發狀態
)

3. 錯誤處理:

// 完善的異常處理和日志記錄
public void taskMethod() {
   try {
      // 任務邏輯
      processTask();
   } catch (Exception e) {
      log.error("任務執行失敗", e);
      throw e; // 讓XXL-JOB處理異常
   }
}

4. 擴展性設計:

// 提供自定義接口支持業務擴展
publicclassCustomXxlJobAutoRegisterextendsXxlJobAutoRegister {
   @Override
   protectedvoidbeforeRegister() {
      // 自定義注冊前處理
   }

   @Override
   protected XxlJobGroup selectJobGroup(List<XxlJobGroup> jobGroups) {
      // 自定義執行器組選擇邏輯
   }
}

4.3.3 運維管理啟發

1. 統一監控:

? 通過XXL-JOB管理平臺統一監控所有任務

? 提供任務執行狀態的可視化展示

? 支持任務執行歷史的查詢

2. 自動化運維:

? 減少人工干預,提升運維效率

? 自動化的任務注冊和配置管理

? 智能的任務執行監控和告警

3. 可視化管理:

? 提供Web界面進行任務管理

? 支持任務的啟用/禁用、修改配置等操作

? 提供任務執行統計和報表

4. 告警機制:

? 完善的告警和通知機制

? 支持任務執行失敗、超時等異常情況的告警

? 提供多種告警方式(郵件、短信、i訊飛等)

4.4 最佳實踐建議

4.4.1 開發階段

1. 任務設計原則:

// 單一職責原則:每個任務只負責一個功能
@XxlJob("dataProcessTask")
@XxlRegister(jobDesc = "數據處理任務")
public void dataProcessTask() {
   // 只處理數據相關的邏輯
}

@XxlJob("reportGenerateTask")
@XxlRegister(jobDesc = "報表生成任務")
public void reportGenerateTask() {
   // 只處理報表相關的邏輯
}

2. 異常處理策略:

@XxlJob("robustTask")
@XxlRegister(jobDesc = "健壯的任務")
publicvoidrobustTask() {
   try {
      // 任務邏輯
      processTask();
   } catch (BusinessException e) {
      // 業務異常,記錄日志但不拋出
      log.warn("業務異常,任務繼續執行", e);
   } catch (Exception e) {
      // 系統異常,記錄日志并拋出
      log.error("系統異常,任務執行失敗", e);
      throw e;
   }
}

3. 配置管理策略:

// 使用常量管理配置
publicclassTaskConstants {
   publicstaticfinalStringCRON_DAILY="0 0 2 * * ?";
   publicstaticfinalStringCRON_HOURLY="0 0 * * * ?";
   publicstaticfinalStringAUTHOR="xydong18";
}

@XxlJob("scheduledTask")
@XxlRegister(
        jobDesc = "定時任務",
        author = TaskConstants.AUTHOR,
        conf = TaskConstants.CRON_DAILY
)
publicvoidscheduledTask() {
   // 任務邏輯
}

4.4.2 運維階段

1. 監控策略:

? 定期檢查任務執行狀態

? 監控任務執行時間和成功率

? 設置合理的告警閾值

2. 日志管理:

@XxlJob("logTask")
@XxlRegister(jobDesc = "日志管理任務")
public void logTask() {
   log.info("任務開始執行,時間:{}", LocalDateTime.now());
   try {
      // 任務邏輯
      processTask();
      log.info("任務執行成功,時間:{}", LocalDateTime.now());
   } catch (Exception e) {
      log.error("任務執行失敗,時間:{},錯誤:{}", LocalDateTime.now(), e.getMessage(), e);
      throw e;
   }
}

3. 性能優化:

@XxlJob("performanceTask")
@XxlRegister(jobDesc = "性能優化任務")
publicvoidperformanceTask() {
   longstartTime= System.currentTimeMillis();
   try {
      // 任務邏輯
      processTask();
      longendTime= System.currentTimeMillis();
      log.info("任務執行完成,耗時:{}ms", endTime - startTime);
   } catch (Exception e) {
      longendTime= System.currentTimeMillis();
      log.error("任務執行失敗,耗時:{}ms", endTime - startTime, e);
      throw e;
   }
}

4.5 未來規劃

在現有XXL-JOB Plus組件的基礎上,我們制定了全面的未來發展規劃,旨在進一步提升組件的功能完整性、性能表現和運維便利性。未來規劃主要圍繞功能增強、性能優化和運維支持三個維度展開,確保組件能夠適應更復雜的業務場景和更高的性能要求。

功能增強方面, 我們將重點擴展對更多調度框架的支持,包括Quartz、Elastic-Job、SchedulerX等主流調度框架,以滿足不同技術棧的集成需求。同時,我們將構建更完善的任務監控體系,涵蓋任務執行時間、成功率、依賴關系等關鍵指標的實時監控,并增強告警功能,支持多種告警方式、靈活的告警規則配置和分級告警管理,確保運維人員能夠及時發現和處理異常情況。

性能優化方面, 我們將對注冊流程進行全面優化,引入批量注冊、異步注冊和失敗重試機制,大幅提升任務注冊的效率和可靠性。在任務執行層面,我們將重點優化任務執行邏輯、資源使用策略和并發控制機制,確保在高并發場景下仍能保持穩定的性能表現。同時,我們將持續優化內存使用、CPU利用率和網絡請求效率,減少系統資源消耗,提升整體運行效率。

運維支持方面, 我們將開發一套完整的運維工具集,包括任務管理工具、配置管理工具和監控分析工具,為運維人員提供便捷的操作界面和強大的分析能力。我們將構建實時監控面板,支持歷史數據分析和性能趨勢分析,幫助運維人員更好地了解系統運行狀態和性能變化趨勢。此外,我們將完善文檔支持體系,提供詳細的使用文檔、最佳實踐指南和故障排查手冊,確保用戶能夠快速上手并有效解決使用過程中遇到的問題。

4.6 總結

XXL-JOB Plus增強組件成功解決了老項目定時任務遷移的痛點,通過注解驅動和自動注冊機制,實現了零人工干預的任務遷移。該組件不僅大幅降低了運維成本,提升了開發效率,還為后續的任務擴展提供了良好的技術基礎。

核心價值:

1. 零人工干預: 68個任務自動注冊,節省340分鐘人工配置時間

2. 配置簡化: 從340個配置項減少到68個注解,配置復雜度降低85%

3. 統一管理: 通過XXL-JOB平臺統一管理所有任務

4. 擴展性強: 支持分布式調度和動態擴縮容

技術亮點:

1. 注解驅動: 通過注解實現配置的聲明式管理

2. 自動注冊: 利用Spring Boot生命周期事件實現自動化

3. 增強設計: 在現有技術棧基礎上做增強,降低遷移風險

4. 分層架構: 清晰的分層設計,便于維護和擴展

這個解決方案為類似的老項目改造提供了可復制的技術方案,具有很高的實用價值和推廣意義。

責任編輯:武曉燕 來源: JAVA日知錄
相關推薦

2024-12-04 10:47:26

2020-07-17 09:33:39

CPU內存調度

2025-09-18 09:31:01

2022-03-26 17:13:22

ElasticJobxxl-job分布式

2022-12-29 08:32:50

xxl-job緩存Schedule

2025-05-26 09:31:23

2022-09-23 13:57:11

xxl-job任務調度中間件

2025-02-18 14:08:14

2023-01-04 09:23:58

2025-06-27 09:31:25

2024-09-09 08:11:12

2023-11-30 22:06:43

2024-08-27 09:34:24

2021-12-26 19:07:51

MySQL存儲容器

2024-07-31 08:18:40

2023-06-27 07:44:53

xxl-job分布式任務調度平臺

2021-12-26 00:03:27

響應式編程異步

2022-01-27 08:44:58

調度系統開源

2023-11-07 07:56:40

2024-11-06 18:01:15

分布式任務調度組件
點贊
收藏

51CTO技術棧公眾號

69视频免费看| 麻豆传媒在线看| 懂色av中文在线| 麻豆一区二区三区| 久久伊人免费视频| 三级黄色片网站| 成人在线视频www| 欧美日韩在线观看视频| 在线视频不卡一区二区| 熟妇人妻中文av无码| 久久精品久久精品| 1769国内精品视频在线播放| 阿v天堂2014| 国产精品色呦| 911精品产国品一二三产区| 国产黄页在线观看| www.欧美日本韩国| 国产三级久久久| 国产一区二区三区高清| 一级黄色免费看| 久久久国产精品一区二区中文| 久久夜色精品国产欧美乱| 亚洲做受高潮无遮挡| 亚洲天堂av资源在线观看| 欧美日韩一区国产| 116极品美女午夜一级| 欧美高清另类hdvideosexjaⅴ| 欧美国产综合一区二区| 久久久久se| 国模无码一区二区三区| 久久99国产精品久久99| 国产精品久久久久国产a级| 国产成人亚洲欧洲在线| 国产一区清纯| 久久视频免费观看| 成人欧美一区二区三区黑人一| 一区二区三区视频免费观看| 亚洲国产精彩中文乱码av| 中文字幕无人区二| 精品一区二区三区视频在线播放| 欧美偷拍一区二区| 最近免费中文字幕中文高清百度| 欧美裸体视频| 欧美日韩美女在线观看| 亚洲人精品午夜射精日韩 | 亚洲欧美一区二区三区久久| 国产艳妇疯狂做爰视频| 亚洲小说春色综合另类电影| 欧美一区二视频| 在线视频日韩欧美| 精品国产鲁一鲁****| 91精品在线免费| 日本美女久久久| 麻豆国产一区| 日韩欧美在线影院| xfplay5566色资源网站| 国产精品国产| 日韩av在线免费观看| av直播在线观看| 亚洲美女久久| 中文字幕日韩精品在线| 91大神福利视频| 午夜av一区| 美女性感视频久久久| 欧美毛片在线观看| 99精品视频免费| 日本欧美在线视频| 欧美成人精品网站| 国产真实乱偷精品视频免| 91亚洲精品丁香在线观看| 亚洲毛片欧洲毛片国产一品色| 懂色av一区二区三区免费看| 国产亚洲精品美女久久久m| 天堂a√在线| 国产欧美一区二区精品仙草咪| 亚洲精品无人区| 含羞草www国产在线视频| 一区二区三区在线免费| 精品少妇在线视频| 免费观看成人性生生活片| 欧美日韩一区二区三区高清| 国产不卡的av| 网红女主播少妇精品视频| 一区二区国产精品视频| 中文字幕五月天| 国产日韩1区| 国产精品自拍网| 免费观看黄一级视频| 久久一区二区三区四区| 一区二区视频在线播放| www欧美xxxx| 欧洲中文字幕精品| 极品白嫩少妇无套内谢| 国产一区二区三区日韩精品 | 免费的成人av| 国产成人免费电影| 成人精品一区二区三区校园激情| 一区二区三区中文字幕精品精品| 日本精品一区二区三区四区| 日本一区二区中文字幕| 亚洲电影免费观看高清| 娇妻被老王脔到高潮失禁视频| 一区二区三区在线电影| 欧美在线免费视频| 精品国产乱码久久久久久蜜臀网站| 99久久久久免费精品国产| 一本一本a久久| 欧美三级网站| 欧美成人a∨高清免费观看| 男人的天堂官网| 国产欧美91| 97netav| 在线视频91p| 一本色道a无线码一区v| 无码人妻一区二区三区免费n鬼沢 久久久无码人妻精品无码 | 91日本在线观看| 免费在线观看污视频| 亚洲一卡二卡三卡四卡五卡| 午夜剧场高清版免费观看 | 一区二区免费在线观看| 自拍视频在线看| 精品国产区一区| av激情在线观看| 久久er精品视频| 日本午夜精品一区二区| 夜鲁夜鲁夜鲁视频在线播放| 欧美大片一区二区三区| 夫妻性生活毛片| 美腿丝袜在线亚洲一区| 日本福利一区二区三区| 日韩伦理在线一区| 亚洲成人亚洲激情| 亚洲国产精一区二区三区性色| 国产一区二区三区不卡在线观看| 亚洲人久久久| 成人一级视频| 国产亚洲成精品久久| 日本黄色一级视频| 久久久91精品国产一区二区三区| 99视频在线免费播放| 国产一区二区三区亚洲| 欧美激情综合亚洲一二区| jlzzjlzz亚洲女人18| 一区二区在线电影| 久久国产免费视频| 伊人久久亚洲热| 国产伦精品一区二区三区四区视频 | 国产精品视频在线看| 欧美视频第三页| 精品国产91| 国产精品免费一区豆花| av在线中文| 欧美高清www午色夜在线视频| 东京热无码av男人的天堂| 免播放器亚洲一区| 欧美 日韩 国产 在线观看| 综合久久av| 欧美贵妇videos办公室| 国产 日韩 欧美 综合| 亚洲成a人v欧美综合天堂下载| 国产视频精品视频| 亚洲欧美日韩精品一区二区| 欧美在线3区| 国产精品无码久久久久| 美乳少妇欧美精品| 蜜桃视频久久一区免费观看入口| 亚洲mv在线观看| 欧美色图亚洲激情| 蜜桃91丨九色丨蝌蚪91桃色| 中文字幕一区二区三区精彩视频| 亚洲国产高清在线观看| 91av福利视频| av午夜在线| 欧美一区二区三区在线电影| 国产精品1234区| 久久精品在线免费观看| 91在线第一页| 亚洲精品孕妇| 亚洲日本理论电影| 亚洲精品黑牛一区二区三区| 欧美一级高清免费| 日本中文字幕在线2020| 精品国精品自拍自在线| 手机在线看片1024| 亚洲欧美偷拍另类a∨色屁股| japanese在线观看| 免费看日韩精品| 国产资源在线免费观看| 欧美手机在线| 国产传媒一区二区| 性欧美freehd18| 欧美激情精品久久久| 国产免费a∨片在线观看不卡| 91精品在线一区二区| 区一区二在线观看| 亚洲综合无码一区二区| youjizz亚洲女人| 国产**成人网毛片九色| 三级在线视频观看| 91久久亚洲| 老司机av福利| 久久av导航| 国产伦精品一区二区三区视频孕妇| www.国产精品| 88xx成人精品| 日韩精品亚洲人成在线观看| 一区二区三区视频免费| 欧美一级特黄aaaaaa| 欧美精选一区二区| 色老头一区二区| 午夜精品福利在线| 黄色一级片在线| 国产精品素人一区二区| 国产亚洲无码精品| 成人一区二区三区| 手机av在线网站| 热久久一区二区| 国产1区2区在线| 99国产精品私拍| 人妻av无码专区| 欧美伊人久久| 伊人色综合影院| 日韩大片在线观看| 欧美自拍资源在线| 亚洲欧洲av| 久久人人97超碰人人澡爱香蕉| 91精品啪在线观看国产手机| 国产原创欧美精品| 国产电影一区二区三区爱妃记| 2024亚洲男人天堂| 性爽视频在线| 97视频在线免费观看| gogo高清在线播放免费| 欧美激情中文字幕乱码免费| 日本动漫同人动漫在线观看| 久久韩剧网电视剧| 香蕉视频在线免费看| 最近2019免费中文字幕视频三 | 精品午夜电影| 国产欧美一区二区三区另类精品| 日韩视频在线直播| 91成人理论电影| 亚洲精品国产九九九| av一区二区三区四区电影| 精品国产亚洲一区二区三区大结局| 国产精品一区二区三区久久| 国产精品伊人| 成人中心免费视频| 欧美影院在线| http;//www.99re视频| 超碰地址久久| 精品伦理一区二区三区| 亚洲第一二三区| 日韩欧美亚洲区| 日韩在线精品| 喜爱夜蒲2在线| 在线看片成人| 日韩a在线播放| 美日韩一区二区| 亚洲免费在线播放视频| 高清不卡在线观看| 国产毛片毛片毛片毛片毛片毛片| 91网站在线播放| 精品人妻无码一区| 亚洲色图欧洲色图婷婷| 精品无码久久久久久久久| 婷婷综合另类小说色区| 国产又粗又猛又黄视频| 欧美日本韩国一区二区三区视频| 国产一区二区在线视频观看| 日韩精品一区二区三区视频播放 | 欧州一区二区三区| 国产精品久久亚洲7777| 中文有码一区| 精品国产无码在线| 亚洲人成久久| wwwwww.色| 国产1区2区3区精品美女| www.久久国产| 国产精品高潮呻吟久久| 久久精品女人毛片国产| 色婷婷激情一区二区三区| 一区二区三区精彩视频| 亚洲风情亚aⅴ在线发布| 国产在线91| 久操成人在线视频| 午夜日韩成人影院| 99国产在线| 欧美亚洲在线日韩| 中国丰满熟妇xxxx性| 日韩和欧美一区二区| 波多野结衣三级视频| 国产日韩一级二级三级| 久久精品一级片| 欧美视频一区在线观看| 人妻精品无码一区二区| 中文字幕亚洲欧美在线| 狼人综合视频| 成人激情视频在线观看| 国产精品亚洲片在线播放| 成人高清dvd| 蜜桃视频在线观看一区二区| 大尺度做爰床戏呻吟舒畅| 国产精品嫩草影院com| 日本一区二区三区精品| 日韩精品资源二区在线| 思思99re6国产在线播放| 88xx成人精品| xxxx日韩| 男人天堂网站在线| 美女看a上一区| 97超碰在线免费观看| 亚洲国产成人av网| 国产99999| 久久亚洲春色中文字幕| 成人精品国产亚洲| 品久久久久久久久久96高清| 精品动漫3d一区二区三区免费版 | 国产v在线观看| 色悠悠久久久久| 欧美极品免费| 久久久久se| 国产视频一区在线观看一区免费| 性色av浪潮av| 亚洲欧美电影一区二区| 中文字幕1区2区3区| 国产午夜精品全部视频播放| 中国字幕a在线看韩国电影| 国产精品有限公司| 欧美视频日韩| 国产chinesehd精品露脸| 亚洲欧美激情插 | 激情图片qvod| 精品无码三级在线观看视频| 极品久久久久久久| 欧美色图一区二区三区| 成人av毛片| 国产精品美腿一区在线看| 精品日韩一区| 亚洲国产精品三区| 中文字幕欧美激情| 一级黄色短视频| 久久久精品在线观看| 婷婷成人av| 日本黄xxxxxxxxx100| 国产一区二区网址| 青青草激情视频| 日韩免费视频线观看| 暧暧视频在线免费观看| 国产中文一区二区| 国产日韩高清一区二区三区在线| 亚洲国产精品自拍视频| 色网综合在线观看| 91xxx在线观看| 亚洲精品日产aⅴ| 精品1区2区3区4区| 在线免费观看麻豆| 欧美日韩中文字幕一区| 国产精品剧情一区二区在线观看 | 亚洲第一天堂影院| 91精品国产91久久久久久最新| 亚洲免费专区| 在线黄色免费看| 一区二区三区免费在线观看| 国产自产一区二区| 国产va免费精品高清在线观看| 欧美亚洲国产激情| 国产在线a视频| 欧美日韩另类在线| 日本中文字幕电影在线免费观看| 亚洲一区二区三区视频| 91久久综合| 日本污视频网站| 欧美成人猛片aaaaaaa| 中老年在线免费视频| 亚洲一区尤物| 成人免费毛片高清视频| 免费精品一区二区| 伦理中文字幕亚洲| 偷拍精品福利视频导航| 国产成人在线综合| 亚洲国产精品久久一线不卡| 国产一级片在线| 97伦理在线四区| 日日欢夜夜爽一区| 国产精品九九九九九九| 亚洲欧洲第一视频| 精品午夜av| 99热手机在线| 亚洲国产精品人人做人人爽| h视频网站在线观看| 国产欧美欧洲| 老司机午夜精品99久久| 日韩精品人妻中文字幕| 日韩中文字幕免费视频| 牛牛精品成人免费视频| 亚洲精品永久视频| 一本色道久久综合亚洲精品按摩| 曰本三级在线| 亚洲精品在线免费|