亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > .NET > 正文

詳解免費開源的DotNet任務調度組件Quartz.NET(.NET組件介紹之五)

2024-07-10 13:31:45
字體:
來源:轉載
供稿:網友

很多的軟件項目中都會使用到定時任務、定時輪詢數據庫同步,定時郵件通知等功能。.NET Framework具有“內置”定時器功能,通過System.Timers.Timer類。在使用Timer類需要面對的問題:計時器沒有持久化機制;計時器具有不靈活的計劃(僅能設置開始時間和重復間隔,沒有基于日期,時間等);計時器不使用線程池(每個定時器一個線程);計時器沒有真正的管理方案 - 你必須編寫自己的機制,以便能夠記住,組織和檢索任務的名稱等。

如果需要在.NET實現定時器的功能,可以嘗試使用以下這款開源免費的組件Quartz.Net組件。目前Quartz.NET版本為3.0,修改了原來的一些問題:修復由于線程本地存儲而不能與AdoJobStore協同工作的調度器信令;線程局部狀態完全刪除;quartz.serializer.type是必需的,即使非序列化RAMJobStore正在使用;JSON序列化錯誤地稱為序列化回調。

一.Quart.NET概述: 

Quartz是一個作業調度系統,可以與任何其他軟件系統集成或一起使用。作業調度程序是一個系統,負責在執行預處理程序時執行(或通知)其他軟件組件 - 確定(調度)時間到達。Quartz是非常靈活的,并且包含多個使用范例,可以單獨使用或一起使用,以實現您所需的行為,并使您能夠以您的項目看起來最“自然”的方式編寫代碼。組件的使用非常輕便,并且需要非常少的設置/配置 - 如果您的需求相對基礎,它實際上可以使用“開箱即用”。Quartz是容錯的,并且可以在系統重新啟動之間保留(記住)您的預定作業。盡管Quartz對于在給定的時間表上簡單地運行某些系統進程非常有用,但當您學習如何使用Quartz來驅動應用程序的業務流程時,Quartz的全部潛能可以實現。

 Quartz是作為一個小的動態鏈接庫(.dll文件)分發的,它包含所有的核心Quartz功能。 此功能的主要接口(API)是調度程序接口。 它提供簡單的操作,如調度/非調度作業,啟動/停止/暫停調度程序。如果你想安排你自己的軟件組件執行,他們必須實現簡單的Job接口,它包含方法execute()。 如果希望在計劃的觸發時間到達時通知組件,則組件應實現TriggerListener或JobListener接口。主要的Quartz'進程'可以在您自己的應用程序或獨立應用程序(使用遠程接口)中啟動和運行。

二.Quartz.NET主體類和方法解析:

1.StdSchedulerFactory類:創建QuartzScheduler實例。

  /// <summary>    /// 返回此工廠生成的調度程序的句柄。    /// </summary>    /// <remarks>    ///如果<see cref =“Initialize()”/>方法之一沒有先前調用,然后是默認(no-arg)<see cref =“Initialize()”/>方法將被這個方法調用。    /// </remarks>    public virtual IScheduler GetScheduler()    {      if (cfg == null)      {        Initialize();      }      SchedulerRepository schedRep = SchedulerRepository.Instance;      IScheduler sched = schedRep.Lookup(SchedulerName);      if (sched != null)      {        if (sched.IsShutdown)        {          schedRep.Remove(SchedulerName);        }        else        {          return sched;        }      }      sched = Instantiate();      return sched;    }
public interface ISchedulerFactory  {    /// <summary>    /// Returns handles to all known Schedulers (made by any SchedulerFactory    /// within this app domain.).    /// </summary>    ICollection<IScheduler> AllSchedulers { get; }    /// <summary>    /// Returns a client-usable handle to a <see cref="IScheduler" />.    /// </summary>    IScheduler GetScheduler();    /// <summary>    /// Returns a handle to the Scheduler with the given name, if it exists.    /// </summary>    IScheduler GetScheduler(string schedName);  }

2.JobDetailImpl:傳遞給定作業實例的詳細信息屬性。

/// <summary>    /// 獲取或設置與<see cref =“IJob”/>相關聯的<see cref =“JobDataMap”/>。    /// </summary>    public virtual JobDataMap JobDataMap    {      get      {        if (jobDataMap == null)        {          jobDataMap = new JobDataMap();        }        return jobDataMap;      }      set { jobDataMap = value; }    }

3.JobKey:鍵由名稱和組組成,名稱必須是唯一的,在組內。 如果只指定一個組,則默認組將使用名稱。

  [Serializable]  public sealed class JobKey : Key<JobKey>  {    public JobKey(string name) : base(name, null)    {    }    public JobKey(string name, string group) : base(name, group)    {    }    public static JobKey Create(string name)    {      return new JobKey(name, null);    }    public static JobKey Create(string name, string group)    {      return new JobKey(name, group);    }  }

4.StdSchedulerFactory.Initialize():

/// <summary>     /// 使用初始化<see cref =“ISchedulerFactory”/>     ///給定鍵值集合對象的內容。    /// </summary>    public virtual void Initialize(NameValueCollection props)    {      cfg = new PropertiesParser(props);      ValidateConfiguration();    }    protected virtual void ValidateConfiguration()    {      if (!cfg.GetBooleanProperty(PropertyCheckConfiguration, true))      {        // should not validate        return;      }      // determine currently supported configuration keys via reflection      List<string> supportedKeys = new List<string>();      List<FieldInfo> fields = new List<FieldInfo>(GetType().GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy));      // choose constant string fields      fields = fields.FindAll(field => field.FieldType == typeof (string));      // read value from each field      foreach (FieldInfo field in fields)      {        string value = (string) field.GetValue(null);        if (value != null && value.StartsWith(ConfigurationKeyPrefix) && value != ConfigurationKeyPrefix)        {          supportedKeys.Add(value);        }      }      // now check against allowed      foreach (string configurationKey in cfg.UnderlyingProperties.AllKeys)      {        if (!configurationKey.StartsWith(ConfigurationKeyPrefix) || configurationKey.StartsWith(ConfigurationKeyPrefixServer))        {          // don't bother if truly unknown property          continue;        }        bool isMatch = false;        foreach (string supportedKey in supportedKeys)        {          if (configurationKey.StartsWith(supportedKey, StringComparison.InvariantCulture))          {            isMatch = true;            break;          }        }        if (!isMatch)        {          throw new SchedulerConfigException("Unknown configuration property '" + configurationKey + "'");        }      }    }

三.Quartz.NET的基本應用:

 下面提供一些較為通用的任務處理代碼:

1.任務處理幫助類:

 /// <summary>  /// 任務處理幫助類  /// </summary>  public class QuartzHelper  {    public QuartzHelper() { }    public QuartzHelper(string quartzServer, string quartzPort)    {      Server = quartzServer;      Port = quartzPort;    }    /// <summary>    /// 鎖對象    /// </summary>    private static readonly object Obj = new object();    /// <summary>    /// 方案    /// </summary>    private const string Scheme = "tcp";    /// <summary>    /// 服務器的地址    /// </summary>    public static string Server { get; set; }    /// <summary>    /// 服務器的端口    /// </summary>    public static string Port { get; set; }    /// <summary>    /// 緩存任務所在程序集信息    /// </summary>    private static readonly Dictionary<string, Assembly> AssemblyDict = new Dictionary<string, Assembly>();    /// <summary>    /// 程序調度    /// </summary>    private static IScheduler _scheduler;    /// <summary>    /// 初始化任務調度對象    /// </summary>    public static void InitScheduler()    {      try      {        lock (Obj)        {          if (_scheduler != null) return;          //配置文件的方式,配置quartz實例          ISchedulerFactory schedulerFactory = new StdSchedulerFactory();          _scheduler = schedulerFactory.GetScheduler();        }      }      catch (Exception ex)      {        throw new Exception(ex.Message);      }    }    /// <summary>    /// 啟用任務調度    /// 啟動調度時會把任務表中狀態為“執行中”的任務加入到任務調度隊列中    /// </summary>    public static void StartScheduler()    {      try      {        if (_scheduler.IsStarted) return;        //添加全局監聽        _scheduler.ListenerManager.AddTriggerListener(new CustomTriggerListener(), GroupMatcher<TriggerKey>.AnyGroup());        _scheduler.Start();        //獲取所有執行中的任務        List<TaskModel> listTask = TaskHelper.GetAllTaskList().ToList();        if (listTask.Count > 0)        {          foreach (TaskModel taskUtil in listTask)          {            try            {              ScheduleJob(taskUtil);            }            catch (Exception e)            {             throw new Exception(taskUtil.TaskName,e);            }          }        }             }      catch (Exception ex)      {        throw new Exception(ex.Message);      }    }    /// <summary>    /// 啟用任務    /// <param name="task">任務信息</param>    /// <param name="isDeleteOldTask">是否刪除原有任務</param>    /// <returns>返回任務trigger</returns>    /// </summary>    public static void ScheduleJob(TaskModel task, bool isDeleteOldTask = false)    {      if (isDeleteOldTask)      {        //先刪除現有已存在任務        DeleteJob(task.TaskID.ToString());      }      //驗證是否正確的Cron表達式      if (ValidExpression(task.CronExpressionString))      {        IJobDetail job = new JobDetailImpl(task.TaskID.ToString(), GetClassInfo(task.AssemblyName, task.ClassName));        //添加任務執行參數        job.JobDataMap.Add("TaskParam", task.TaskParam);        CronTriggerImpl trigger = new CronTriggerImpl        {          CronExpressionString = task.CronExpressionString,          Name = task.TaskID.ToString(),          Description = task.TaskName        };        _scheduler.ScheduleJob(job, trigger);        if (task.Status == TaskStatus.STOP)        {          JobKey jk = new JobKey(task.TaskID.ToString());          _scheduler.PauseJob(jk);        }        else        {          List<DateTime> list = GetNextFireTime(task.CronExpressionString, 5);          foreach (var time in list)          {            LogHelper.WriteLog(time.ToString(CultureInfo.InvariantCulture));          }        }      }      else      {        throw new Exception(task.CronExpressionString + "不是正確的Cron表達式,無法啟動該任務!");      }    }    /// <summary>    /// 初始化 遠程Quartz服務器中的,各個Scheduler實例。    /// 提供給遠程管理端的后臺,用戶獲取Scheduler實例的信息。    /// </summary>    public static void InitRemoteScheduler()    {      try      {        NameValueCollection properties = new NameValueCollection        {          ["quartz.scheduler.instanceName"] = "ExampleQuartzScheduler",          ["quartz.scheduler.proxy"] = "true",          ["quartz.scheduler.proxy.address"] =string.Format("{0}://{1}:{2}/QuartzScheduler", Scheme, Server, Port)        };        ISchedulerFactory sf = new StdSchedulerFactory(properties);        _scheduler = sf.GetScheduler();      }      catch (Exception ex)      {        throw new Exception(ex.StackTrace);      }    }    /// <summary>    /// 刪除現有任務    /// </summary>    /// <param name="jobKey"></param>    public static void DeleteJob(string jobKey)    {      try      {        JobKey jk = new JobKey(jobKey);        if (_scheduler.CheckExists(jk))        {          //任務已經存在則刪除          _scheduler.DeleteJob(jk);                  }      }      catch (Exception ex)      {        throw new Exception(ex.Message);      }    }       /// <summary>    /// 暫停任務    /// </summary>    /// <param name="jobKey"></param>    public static void PauseJob(string jobKey)    {      try      {        JobKey jk = new JobKey(jobKey);        if (_scheduler.CheckExists(jk))        {          //任務已經存在則暫停任務          _scheduler.PauseJob(jk);        }      }      catch (Exception ex)      {        throw new Exception(ex.Message);      }    }    /// <summary>    /// 恢復運行暫停的任務    /// </summary>    /// <param name="jobKey">任務key</param>    public static void ResumeJob(string jobKey)    {      try      {        JobKey jk = new JobKey(jobKey);        if (_scheduler.CheckExists(jk))        {          //任務已經存在則暫停任務          _scheduler.ResumeJob(jk);        }      }      catch (Exception ex)      {       throw new Exception(ex.Message);      }    }    /// <summary>     /// 獲取類的屬性、方法     /// </summary>     /// <param name="assemblyName">程序集</param>     /// <param name="className">類名</param>     private static Type GetClassInfo(string assemblyName, string className)    {      try      {        assemblyName = FileHelper.GetAbsolutePath(assemblyName + ".dll");        Assembly assembly = null;        if (!AssemblyDict.TryGetValue(assemblyName, out assembly))        {          assembly = Assembly.LoadFrom(assemblyName);          AssemblyDict[assemblyName] = assembly;        }        Type type = assembly.GetType(className, true, true);        return type;      }      catch (Exception ex)      {        throw new Exception(ex.Message);      }    }    /// <summary>    /// 停止任務調度    /// </summary>    public static void StopSchedule()    {      try      {        //判斷調度是否已經關閉        if (!_scheduler.IsShutdown)        {          //等待任務運行完成          _scheduler.Shutdown(true);        }      }      catch (Exception ex)      {        throw new Exception(ex.Message);      }    }    /// <summary>    /// 校驗字符串是否為正確的Cron表達式    /// </summary>    /// <param name="cronExpression">帶校驗表達式</param>    /// <returns></returns>    public static bool ValidExpression(string cronExpression)    {      return CronExpression.IsValidExpression(cronExpression);    }    /// <summary>    /// 獲取任務在未來周期內哪些時間會運行    /// </summary>    /// <param name="CronExpressionString">Cron表達式</param>    /// <param name="numTimes">運行次數</param>    /// <returns>運行時間段</returns>    public static List<DateTime> GetNextFireTime(string CronExpressionString, int numTimes)    {      if (numTimes < 0)      {        throw new Exception("參數numTimes值大于等于0");      }      //時間表達式      ITrigger trigger = TriggerBuilder.Create().WithCronSchedule(CronExpressionString).Build();      IList<DateTimeOffset> dates = TriggerUtils.ComputeFireTimes(trigger as IOperableTrigger, null, numTimes);      List<DateTime> list = new List<DateTime>();      foreach (DateTimeOffset dtf in dates)      {        list.Add(TimeZoneInfo.ConvertTimeFromUtc(dtf.DateTime, TimeZoneInfo.Local));      }      return list;    }    public static object CurrentTaskList()    {      throw new NotImplementedException();    }    /// <summary>    /// 獲取當前執行的Task 對象    /// </summary>    /// <param name="context"></param>    /// <returns></returns>    public static TaskModel GetTaskDetail(IJobExecutionContext context)    {      TaskModel task = new TaskModel();      if (context != null)      {        task.TaskID = Guid.Parse(context.Trigger.Key.Name);        task.TaskName = context.Trigger.Description;        task.RecentRunTime = DateTime.Now;        task.TaskParam = context.JobDetail.JobDataMap.Get("TaskParam") != null ? context.JobDetail.JobDataMap.Get("TaskParam").ToString() : "";      }      return task;    }  }

2.設置執行中的任務:

public class TaskBll  {    private readonly TaskDAL _dal = new TaskDAL();    /// <summary>    /// 獲取任務列表    /// </summary>    /// <param name="pageIndex"></param>    /// <param name="pageSize"></param>    /// <returns></returns>    public PageOf<TaskModel> GetTaskList(int pageIndex, int pageSize)    {      return _dal.GetTaskList(pageIndex, pageSize);    }    /// <summary>    /// 讀取數據庫中全部的任務    /// </summary>    /// <returns></returns>    public List<TaskModel> GetAllTaskList()    {      return _dal.GetAllTaskList();    }    /// <summary>    ///     /// </summary>    /// <param name="taskId"></param>    /// <returns></returns>    public TaskModel GetById(string taskId)    {      throw new NotImplementedException();    }    /// <summary>    /// 刪除任務    /// </summary>    /// <param name="taskId"></param>    /// <returns></returns>    public bool DeleteById(string taskId)    {      return _dal.UpdateTaskStatus(taskId, -1);    }    /// <summary>    /// 修改任務    /// </summary>    /// <param name="taskId"></param>    /// <param name="status"></param>    /// <returns></returns>    public bool UpdateTaskStatus(string taskId, int status)    {      return _dal.UpdateTaskStatus(taskId, status);    }    /// <summary>    /// 修改任務的下次啟動時間    /// </summary>    /// <param name="taskId"></param>    /// <param name="nextFireTime"></param>    /// <returns></returns>    public bool UpdateNextFireTime(string taskId, DateTime nextFireTime)    {      return _dal.UpdateNextFireTime(taskId, nextFireTime);    }    /// <summary>    /// 根據任務Id 修改 上次運行時間    /// </summary>    /// <param name="taskId"></param>    /// <param name="recentRunTime"></param>    /// <returns></returns>    public bool UpdateRecentRunTime(string taskId, DateTime recentRunTime)    {      return _dal.UpdateRecentRunTime(taskId, recentRunTime);    }    /// <summary>    /// 根據任務Id 獲取任務    /// </summary>    /// <param name="taskId"></param>    /// <returns></returns>    public TaskModel GetTaskById(string taskId)    {      return _dal.GetTaskById(taskId);    }    /// <summary>    /// 添加任務    /// </summary>    /// <param name="task"></param>    /// <returns></returns>    public bool Add(TaskModel task)    {      return _dal.Add(task);    }    /// <summary>    /// 修改任務    /// </summary>    /// <param name="task"></param>    /// <returns></returns>    public bool Edit(TaskModel task)    {      return _dal.Edit(task);    }  }

3.任務實體:

/// <summary>  /// 任務實體  /// </summary>  public class TaskModel  {    /// <summary>    /// 任務ID    /// </summary>    public Guid TaskID { get; set; }    /// <summary>    /// 任務名稱    /// </summary>    public string TaskName { get; set; }    /// <summary>    /// 任務執行參數    /// </summary>    public string TaskParam { get; set; }    /// <summary>    /// 運行頻率設置    /// </summary>    public string CronExpressionString { get; set; }    /// <summary>    /// 任務運頻率中文說明    /// </summary>    public string CronRemark { get; set; }    /// <summary>    /// 任務所在DLL對應的程序集名稱    /// </summary>    public string AssemblyName { get; set; }    /// <summary>    /// 任務所在類    /// </summary>    public string ClassName { get; set; }    public TaskStatus Status { get; set; }    /// <summary>    /// 任務創建時間    /// </summary>    public DateTime? CreatedTime { get; set; }    /// <summary>    /// 任務修改時間    /// </summary>    public DateTime? ModifyTime { get; set; }    /// <summary>    /// 任務最近運行時間    /// </summary>    public DateTime? RecentRunTime { get; set; }    /// <summary>    /// 任務下次運行時間    /// </summary>    public DateTime? NextFireTime { get; set; }    /// <summary>    /// 任務備注    /// </summary>    public string Remark { get; set; }    /// <summary>    /// 是否刪除    /// </summary>    public int IsDelete { get; set; }  }

 4.配置文件:

 # You can configure your scheduler in either <quartz> configuration section# or in quartz properties file# Configuration section has precedencequartz.scheduler.instanceName = ExampleQuartzScheduler# configure thread pool infoquartz.threadPool.type = Quartz.Simpl.SimpleThreadPool, Quartzquartz.threadPool.threadCount = 10quartz.threadPool.threadPriority = Normal# job initialization plugin handles our xml reading, without it defaults are used# quartz.plugin.xml.type = Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz# quartz.plugin.xml.fileNames = ~/quartz_jobs.xml# export this server to remoting contextquartz.scheduler.exporter.type = Quartz.Simpl.RemotingSchedulerExporter, Quartzquartz.scheduler.exporter.port = 555quartz.scheduler.exporter.bindName = QuartzSchedulerquartz.scheduler.exporter.channelType = tcpquartz.scheduler.exporter.channelName = httpQuartz

四.總結:

 在項目中比較多的使用到定時任務的功能,今天的介紹的組件可以很好的完成一些定時任務的要求。這篇文章主要是作為引子,簡單的介紹了組件的背景和組件的使用方式,如果項目中需要使用,可以進行更加深入的了解。

 

注:相關教程知識閱讀請移步到ASP.NET教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美大全免费观看电视剧大泉洋| 国产精品久久不能| 亚洲欧美激情在线视频| 久久精品免费播放| 欧美日韩国产成人高清视频| 欧美黄色免费网站| 97精品久久久中文字幕免费| 国产精品精品一区二区三区午夜版| 国产精品黄视频| 久久精品视频播放| 成人黄色av网| 91色视频在线观看| 国产精品久在线观看| 日韩欧美一区二区三区久久| 亚洲精品国产精品久久清纯直播| 国产精品久久久久久一区二区| 国产精品欧美在线| 亚洲精品理论电影| www.国产精品一二区| 欧美激情videoshd| 亚洲欧美日韩另类| 美日韩丰满少妇在线观看| 欧美成人精品三级在线观看| 欧美亚洲另类激情另类| 在线播放国产一区二区三区| 亚洲网在线观看| 91中文字幕在线| 亚洲精品动漫100p| 国产一区二区丝袜高跟鞋图片| 国产成人av在线播放| 92国产精品视频| 俺去啦;欧美日韩| 国产成人福利视频| 久久久久中文字幕| 久久精品91久久久久久再现| 国产成人亚洲精品| 亚洲女人被黑人巨大进入| 成人在线播放av| 国产视频精品在线| 国产999精品视频| 亚洲中国色老太| 亚洲天天在线日亚洲洲精| 国产精品爽爽爽爽爽爽在线观看| 欧美成人激情图片网| 国产精品久久久久久久久久三级| 久久色在线播放| 久久不射电影网| 色多多国产成人永久免费网站| 日韩电影中文字幕av| 亚洲精品一区二三区不卡| 欧美电影电视剧在线观看| 精品综合久久久久久97| 亚洲爱爱爱爱爱| 国产日韩在线亚洲字幕中文| 裸体女人亚洲精品一区| 精品中文字幕在线| 一区二区欧美亚洲| 国产一区香蕉久久| 国产精品aaa| 久久精品电影一区二区| 久久久免费av| 91视频免费网站| 日韩在线激情视频| 日韩精品在线影院| 亚洲天堂av在线免费| 久久夜色撩人精品| 国产在线观看精品一区二区三区| 国产精品444| 91啪国产在线| 亚洲高清一二三区| 欧美一级大片在线免费观看| 国产精品情侣自拍| 久久久亚洲精品视频| 国产精品自拍偷拍| 成人疯狂猛交xxx| 成人黄色av免费在线观看| 九九热r在线视频精品| 久久久久久久国产精品| 色妞一区二区三区| 日韩精品在线观看一区| 色综合91久久精品中文字幕| 日韩av大片在线| 久久久999成人| 国产精品久久一| 亚洲区bt下载| 国产精品揄拍一区二区| 97视频免费观看| 91福利视频在线观看| 国产精品a久久久久久| 91精品久久久久久久久久久久久久| 91久久在线观看| 久久久欧美一区二区| 精品国产1区2区| 亚洲一区国产精品| 国产成人精彩在线视频九色| 欧美激情精品久久久久久变态| 国产91|九色| 欧美午夜片欧美片在线观看| 亚洲人成在线观| 久久久久久久91| 中文字幕精品在线视频| 91精品视频免费看| 亚洲成人a级网| 国产主播喷水一区二区| 欧美日本高清一区| 亚洲精品aⅴ中文字幕乱码| 亚洲自拍偷拍福利| 欧美疯狂做受xxxx高潮| 欧美黄色成人网| 久久国产精品久久久| 亚洲欧美日韩精品| 欧美视频中文字幕在线| 91精品国产综合久久香蕉的用户体验| 在线中文字幕日韩| 精品视频久久久久久| 91久久精品在线| 欧美午夜视频一区二区| 91精品免费久久久久久久久| 亚洲精品福利在线观看| 亚洲国产成人久久| 日韩精品视频免费专区在线播放| 亚洲欧美日韩高清| 日韩成人在线电影网| 日韩成人网免费视频| 欧美国产亚洲精品久久久8v| 国产精品普通话| 亚洲国产精品小视频| 国产成人精品综合久久久| 亚洲精品一区久久久久久| 成人免费视频网址| 亚洲级视频在线观看免费1级| 国产亚洲成av人片在线观看桃| 国产精品91免费在线| 亚洲大胆人体视频| 亚洲精品国产福利| 欧美电影《睫毛膏》| 国产精品欧美日韩久久| 久久在线视频在线| 美女国内精品自产拍在线播放| 成人综合网网址| 在线亚洲午夜片av大片| 粉嫩老牛aⅴ一区二区三区| 日韩精品久久久久久久玫瑰园| 精品国产依人香蕉在线精品| 这里只有精品在线播放| 亚洲午夜精品视频| 富二代精品短视频| 日韩大片在线观看视频| 欧美黑人性生活视频| 8x拔播拔播x8国产精品| 国产午夜精品视频| 精品久久久久久久大神国产| 一本一本久久a久久精品综合小说| 久久久精品美女| 亚洲美女久久久| 久久久www成人免费精品张筱雨| 日韩视频免费大全中文字幕| 91精品视频一区| 日韩欧美a级成人黄色| 岛国av在线不卡| 日韩av一区在线观看| 91极品视频在线| 狠狠躁夜夜躁人人爽超碰91| 欧美片一区二区三区|