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

首頁 > 開發 > Java > 正文

Quartz集群原理以及配置應用的方法詳解

2024-07-14 08:40:40
字體:
來源:轉載
供稿:網友

1、Quartz任務調度的基本實現原理

  Quartz是OpenSymphony開源組織在任務調度領域的一個開源項目,完全基于Java實現。作為一個優秀的開源調度框架,Quartz具有以下特點:

   ?。?)強大的調度功能,例如支持豐富多樣的調度方法,可以滿足各種常規及特殊需求;

    (2)靈活的應用方式,例如支持任務和調度的多種組合方式,支持調度數據的多種存儲方式;

    (3)分布式和集群能力,Terracotta收購后在原來功能基礎上作了進一步提升。本文將對該部分相加闡述。

1.1 Quartz 核心元素

  Quartz任務調度的核心元素為:Scheduler——任務調度器、Trigger——觸發器、Job——任務。其中trigger和job是任務調度的元數據,scheduler是實際執行調度的控制器。

  Trigger是用于定義調度時間的元素,即按照什么時間規則去執行任務。Quartz中主要提供了四種類型的trigger:SimpleTrigger,CronTirgger,DateIntervalTrigger,和NthIncludedDayTrigger。這四種trigger可以滿足企業應用中的絕大部分需求。

  Job用于表示被調度的任務。主要有兩種類型的job:無狀態的(stateless)和有狀態的(stateful)。對于同一個trigger來說,有狀態的job不能被并行執行,只有上一次觸發的任務被執行完之后,才能觸發下一次執行。Job主要有兩種屬性:volatility和durability,其中volatility表示任務是否被持久化到數據庫存儲,而durability表示在沒有trigger關聯的時候任務是否被保留。兩者都是在值為true的時候任務被持久化或保留。一個job可以被多個trigger關聯,但是一個trigger只能關聯一個job。

  Scheduler由scheduler工廠創建:DirectSchedulerFactory或者StdSchedulerFactory。第二種工廠StdSchedulerFactory使用較多,因為DirectSchedulerFactory使用起來不夠方便,需要作許多詳細的手工編碼設置。Scheduler主要有三種:RemoteMBeanScheduler,RemoteScheduler和StdScheduler。

  Quartz核心元素之間的關系如圖1.1所示:

quartz,集群原理,集群實現原理,quartz集群配置

圖1.1 核心元素關系圖

1.2 Quartz 線程視圖

  在Quartz中,有兩類線程,Scheduler調度線程和任務執行線程,其中任務執行線程通常使用一個線程池維護一組線程。

quartz,集群原理,集群實現原理,quartz集群配置

圖1.2 Quartz線程視圖

  Scheduler調度線程主要有兩個:執行常規調度的線程,和執行misfiredtrigger的線程。常規調度線程輪詢存儲的所有trigger,如果有需要觸發的trigger,即到達了下一次觸發的時間,則從任務執行線程池獲取一個空閑線程,執行與該trigger關聯的任務。Misfire線程是掃描所有的trigger,查看是否有misfiredtrigger,如果有的話根據misfire的策略分別處理(fire now OR wait for the next fire)。

1.3 Quartz Job數據存儲

  Quartz中的trigger和job需要存儲下來才能被使用。Quartz中有兩種存儲方式:RAMJobStore,JobStoreSupport,其中RAMJobStore是將trigger和job存儲在內存中,而JobStoreSupport是基于jdbc將trigger和job存儲到數據庫中。RAMJobStore的存取速度非???,但是由于其在系統被停止后所有的數據都會丟失,所以在集群應用中,必須使用JobStoreSupport。

2、Quartz集群原理2.1 Quartz 集群架構

  一個Quartz集群中的每個節點是一個獨立的Quartz應用,它又管理著其他的節點。這就意味著你必須對每個節點分別啟動或停止。Quartz集群中,獨立的Quartz節點并不與另一其的節點或是管理節點通信,而是通過相同的數據庫表來感知到另一Quartz應用的,如圖2.1所示。

quartz,集群原理,集群實現原理,quartz集群配置

圖2.1 Quartz集群架構

2.2 Quartz集群相關數據庫表

  因為Quartz集群依賴于數據庫,所以必須首先創建Quartz數據庫表,Quartz發布包中包括了所有被支持的數據庫平臺的SQL腳本。這些SQL腳本存放于<quartz_home>/docs/dbTables 目錄下。這里采用的Quartz 1.8.4版本,總共12張表,不同版本,表個數可能不同。數據庫為mysql,用tables_mysql.sql創建數據庫表。全部表如圖2.2所示,對這些表的簡要介紹如圖2.3所示。

quartz,集群原理,集群實現原理,quartz集群配置

圖2.2 Quartz 1.8.4在mysql數據庫中生成的表

quartz,集群原理,集群實現原理,quartz集群配置

圖2.3 Quartz數據表簡介

2.2.1 調度器狀態表(QRTZ_SCHEDULER_STATE)

  說明:集群中節點實例信息,Quartz定時讀取該表的信息判斷集群中每個實例的當前狀態。

  instance_name:配置文件中org.quartz.scheduler.instanceId配置的名字,如果設置為AUTO,quartz會根據物理機名和當前時間產生一個名字。

  last_checkin_time:上次檢入時間

  checkin_interval:檢入間隔時間

2.2.2 觸發器與任務關聯表(qrtz_fired_triggers)

  存儲與已觸發的Trigger相關的狀態信息,以及相聯Job的執行信息。

2.2.3 觸發器信息表(qrtz_triggers)

  trigger_name:trigger的名字,該名字用戶自己可以隨意定制,無強行要求

  trigger_group:trigger所屬組的名字,該名字用戶自己隨意定制,無強行要求

  job_name:qrtz_job_details表job_name的外鍵

  job_group:qrtz_job_details表job_group的外鍵

  trigger_state:當前trigger狀態設置為ACQUIRED,如果設為WAITING,則job不會觸發

  trigger_cron:觸發器類型,使用cron表達式

2.2.4 任務詳細信息表(qrtz_job_details)

  說明:保存job詳細信息,該表需要用戶根據實際情況初始化

  job_name:集群中job的名字,該名字用戶自己可以隨意定制,無強行要求。

  job_group:集群中job的所屬組的名字,該名字用戶自己隨意定制,無強行要求。

  job_class_name:集群中job實現類的完全包名,quartz就是根據這個路徑到classpath找到該job類的。

  is_durable:是否持久化,把該屬性設置為1,quartz會把job持久化到數據庫中

  job_data:一個blob字段,存放持久化job對象。

2.2.5權限信息表(qrtz_locks)

  說明:tables_oracle.sql里有相應的dml初始化,如圖2.4所示。

quartz,集群原理,集群實現原理,quartz集群配置

圖2.4 Quartz權限信息表中的初始化信息

2.3 Quartz Scheduler在集群中的啟動流程

  Quartz Scheduler自身是察覺不到被集群的,只有配置給Scheduler的JDBC JobStore才知道。當Quartz Scheduler啟動時,它調用JobStore的schedulerStarted()方法,它告訴JobStore Scheduler已經啟動了。schedulerStarted() 方法是在JobStoreSupport類中實現的。JobStoreSupport類會根據quartz.properties文件中的設置來確定Scheduler實例是否參與到集群中。假如配置了集群,一個新的ClusterManager類的實例就被創建、初始化并啟動。ClusterManager是在JobStoreSupport類中的一個內嵌類,繼承了java.lang.Thread,它會定期運行,并對Scheduler實例執行檢入的功能。Scheduler也要查看是否有任何一個別的集群節點失敗了。檢入操作執行周期在quartz.properties中配置。

2.4 偵測失敗的Scheduler節點

  當一個Scheduler實例執行檢入時,它會查看是否有其他的Scheduler實例在到達他們所預期的時間還未檢入。這是通過檢查SCHEDULER_STATE表中Scheduler記錄在LAST_CHEDK_TIME列的值是否早于org.quartz.jobStore.clusterCheckinInterval來確定的。如果一個或多個節點到了預定時間還沒有檢入,那么運行中的Scheduler就假定它(們) 失敗了。

2.5 從故障實例中恢復Job

  當一個Sheduler實例在執行某個Job時失敗了,有可能由另一正常工作的Scheduler實例接過這個Job重新運行。要實現這種行為,配置給JobDetail對象的Job可恢復屬性必須設置為true(job.setRequestsRecovery(true))。如果可恢復屬性被設置為false(默認為false),當某個Scheduler在運行該job失敗時,它將不會重新運行;而是由另一個Scheduler實例在下一次觸發時間觸發。Scheduler實例出現故障后多快能被偵測到取決于每個Scheduler的檢入間隔(即2.3中提到的org.quartz.jobStore.clusterCheckinInterval)。

3、Quartz集群實例(Quartz+Spring)

3.1 Spring不兼容Quartz問題

  Spring從2.0.2開始便不再支持Quartz。具體表現在Quartz+Spring把Quartz的Task實例化進入數據庫時,會產生:Serializable的錯誤:

<bean id="jobtask" class="org.springframework.scheduling.quartz. MethodInvokingJobDetailFactoryBean ">  <property name="targetObject">    <ref bean="quartzJob"/>  </property>  <property name="targetMethod">    <value>execute</value>  </property></bean>

  這個MethodInvokingJobDetailFactoryBean類中的methodInvoking方法,是不支持序列化的,因此在把QUARTZ的TASK序列化進入數據庫時就會拋錯。

  首先解決MethodInvokingJobDetailFactoryBean的問題,在不修改Spring源碼的情況下,可以避免使用這個類,直接調用JobDetail。但是使用JobDetail實現,需要自己實現MothodInvoking的邏輯,可以使用JobDetail的jobClass和JobDataAsMap屬性來自定義一個Factory(Manager)來實現同樣的目的。例如,本示例中新建了一個MyDetailQuartzJobBean來實現這個功能。

3.2 MyDetailQuartzJobBean.java文件

package org.lxh.mvc.jobbean;import java.lang.reflect.Method;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.quartz.JobExecutionContext;import org.quartz.JobExecutionException;import org.springframework.context.ApplicationContext;import org.springframework.scheduling.quartz.QuartzJobBean;public class MyDetailQuartzJobBean extends QuartzJobBean { protected final Log logger = LogFactory.getLog(getClass()); private String targetObject; private String targetMethod; private ApplicationContext ctx; protected void executeInternal(JobExecutionContext context) throws JobExecutionException {  try {   logger.info("execute [" + targetObject + "] at once>>>>>>");   Object otargetObject = ctx.getBean(targetObject);   Method m = null;   try {    m = otargetObject.getClass().getMethod(targetMethod, new Class[] {});    m.invoke(otargetObject, new Object[] {});   } catch (SecurityException e) {    logger.error(e);   } catch (NoSuchMethodException e) {    logger.error(e);   }  } catch (Exception e) {   throw new JobExecutionException(e);  } } public void setApplicationContext(ApplicationContext applicationContext){  this.ctx=applicationContext; } public void setTargetObject(String targetObject) {  this.targetObject = targetObject; } public void setTargetMethod(String targetMethod) {  this.targetMethod = targetMethod; }}

3.3真正的Job實現類

  在Test類中,只是簡單實現了打印系統當前時間的功能。

package org.lxh.mvc.job;import java.io.Serializable;import java.util.Date;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;public class Test implements Serializable{ private Log logger = LogFactory.getLog(Test.class); private static final long serialVersionUID = -2073310586499744415L;  public void execute () {  Date date=new Date();   System.out.println(date.toLocaleString());  }}

3.4 配置quartz.xml文件

<bean id="Test" class="org.lxh.mvc.job.Test" scope="prototype"> </bean> <bean id="TestjobTask" class="org.springframework.scheduling.quartz.JobDetailBean">  <property name="jobClass"><value>org.lxh.mvc.jobbean.MyDetailQuartzJobBean</value>  </property>  <property name="jobDataAsMap">   <map>    <entry key="targetObject" value="Test" />    <entry key="targetMethod" value="execute" />    </map>   </property>   </bean>  <bean name="TestTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">   <property name="jobDetail" ref="TestjobTask" />  <property name="cronExpression" value="0/1 * * * * ?" /> </bean> <bean id="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">  <property name="configLocation" value="classpath:quartz.properties"/>  <property name="triggers">   <list>    <ref bean="TestTrigger" />   </list>  </property>  <property name="applicationContextSchedulerContextKey" value="applicationContext" /> </bean>

3.5 測試

  ServerA、ServerB的代碼、配置完全一樣,先啟動ServerA,后啟動ServerB,當Server關斷之后,ServerB會監測到其關閉,并將ServerA上正在執行的Job接管,繼續執行。

4、Quartz集群實例(單獨Quartz)

  盡管我們已經實現了Spring+Quartz的集群配置,但是因為Spring與Quartz之間的兼容問題還是不建議使用該方式。在本小節中,我們實現了單獨用Quartz配置的集群,相對Spring+Quartz的方式來說,簡單、穩定。

4.1 工程結構

  我們采用單獨使用Quartz來實現其集群功能,代碼結構及所需的第三方jar包如圖3.1所示。其中,Mysql版本:5.1.52,Mysql驅動版本:mysql-connector-java-5.1.5-bin.jar(針對于5.1.52,建議采用該版本驅動,因為Quartz存在BUG使得其與某些Mysql驅動結合時不能正常運行)。

quartz,集群原理,集群實現原理,quartz集群配置

圖4.1 Quartz集群工程結構及所需第三方jar包

  其中quartz.properties為Quartz配置文件,放在src目錄下,若無該文件,Quartz將自動加載jar包中的quartz.properties文件;SimpleRecoveryJob.java、SimpleRecoveryStatefulJob.java為兩個Job;ClusterExample.java中編寫了調度信息、觸發機制及相應的測試main函數。

4.2 配置文件quartz.properties

  默認文件名稱quartz.properties,通過設置"org.quartz.jobStore.isClustered"屬性為"true"來激活集群特性。在集群中的每一個實例都必須有一個唯一的"instance id" ("org.quartz.scheduler.instanceId" 屬性), 但是應該有相同的"scheduler instance name" ("org.quartz.scheduler.instanceName"),也就是說集群中的每一個實例都必須使用相同的quartz.properties 配置文件。除了以下幾種例外,配置文件的內容其他都必須相同:

  a.線程池大小。

  b.不同的"org.quartz.scheduler.instanceId"屬性值(通過設定為"AUTO"即可)。

#============================================================== #Configure Main Scheduler Properties #============================================================== org.quartz.scheduler.instanceName = quartzSchedulerorg.quartz.scheduler.instanceId = AUTO#============================================================== #Configure JobStore #============================================================== org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTXorg.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegateorg.quartz.jobStore.tablePrefix = QRTZ_org.quartz.jobStore.isClustered = trueorg.quartz.jobStore.clusterCheckinInterval = 10000 org.quartz.jobStore.dataSource = myDS #============================================================== #Configure DataSource #============================================================== org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driverorg.quartz.dataSource.myDS.URL = jdbc:mysql://192.168.31.18:3306/test?useUnicode=true&characterEncoding=UTF-8org.quartz.dataSource.myDS.user = rootorg.quartz.dataSource.myDS.password = 123456org.quartz.dataSource.myDS.maxConnections = 30#============================================================== #Configure ThreadPool #============================================================== org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPoolorg.quartz.threadPool.threadCount = 5org.quartz.threadPool.threadPriority = 5org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

4.3 ClusterExample.java文件

package cluster;import java.util.Date;import org.quartz.JobDetail;import org.quartz.Scheduler;import org.quartz.SchedulerFactory;import org.quartz.SimpleTrigger;import org.quartz.impl.StdSchedulerFactory;public class ClusterExample {  public void cleanUp(Scheduler inScheduler) throws Exception {  System.out.println("***** Deleting existing jobs/triggers *****");  // unschedule jobs  String[] groups = inScheduler.getTriggerGroupNames();  for (int i = 0; i < groups.length; i++) {   String[] names = inScheduler.getTriggerNames(groups[i]);   for (int j = 0; j < names.length; j++) {    inScheduler.unscheduleJob(names[j], groups[i]);   }  }  // delete jobs  groups = inScheduler.getJobGroupNames();  for (int i = 0; i < groups.length; i++) {   String[] names = inScheduler.getJobNames(groups[i]);   for (int j = 0; j < names.length; j++) {    inScheduler.deleteJob(names[j], groups[i]);   }  } }   public void run(boolean inClearJobs, boolean inScheduleJobs)   throws Exception {  // First we must get a reference to a scheduler  SchedulerFactory sf = new StdSchedulerFactory();  Scheduler sched = sf.getScheduler();    if (inClearJobs) {   cleanUp(sched);  }  System.out.println("------- Initialization Complete -----------");  if (inScheduleJobs) {   System.out.println("------- Scheduling Jobs ------------------");   String schedId = sched.getSchedulerInstanceId();   int count = 1;   JobDetail job = new JobDetail("job_" + count, schedId, SimpleRecoveryJob.class);   // ask scheduler to re-execute this job if it was in progress when   // the scheduler went down...   job.setRequestsRecovery(true);   SimpleTrigger trigger =     new SimpleTrigger("triger_" + count, schedId, 200, 1000L);   trigger.setStartTime(new Date(System.currentTimeMillis() + 1000L));   System.out.println(job.getFullName() +     " will run at: " + trigger.getNextFireTime() +      " and repeat: " + trigger.getRepeatCount() +      " times, every " + trigger.getRepeatInterval() / 1000 + " seconds");   sched.scheduleJob(job, trigger);   count++;   job = new JobDetail("job_" + count, schedId,      SimpleRecoveryStatefulJob.class);   // ask scheduler to re-execute this job if it was in progress when   // the scheduler went down...   job.setRequestsRecovery(false);   trigger = new SimpleTrigger("trig_" + count, schedId, 100, 2000L);   trigger.setStartTime(new Date(System.currentTimeMillis() + 2000L));   System.out.println(job.getFullName() +     " will run at: " + trigger.getNextFireTime() +      " and repeat: " + trigger.getRepeatCount() +      " times, every " + trigger.getRepeatInterval() / 1000 + " seconds");   sched.scheduleJob(job, trigger);  }  // jobs don't start firing until start() has been called...  System.out.println("------- Starting Scheduler ---------------");  sched.start();  System.out.println("------- Started Scheduler ----------------");  System.out.println("------- Waiting for one hour... ----------");  try {   Thread.sleep(3600L * 1000L);  } catch (Exception e) {  }  System.out.println("------- Shutting Down --------------------");  sched.shutdown();  System.out.println("------- Shutdown Complete ----------------");  }  public static void main(String[] args) throws Exception {  boolean clearJobs = true;  boolean scheduleJobs = true;  for (int i = 0; i < args.length; i++) {   if (args[i].equalsIgnoreCase("clearJobs")) {    clearJobs = true;       } else if (args[i].equalsIgnoreCase("dontScheduleJobs")) {    scheduleJobs = false;   }  }  ClusterExample example = new ClusterExample();  example.run(clearJobs, scheduleJobs); }}

4.4 SimpleRecoveryJob.java

package cluster;import java.io.Serializable;import java.util.Date;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.quartz.Job;import org.quartz.JobExecutionContext;import org.quartz.JobExecutionException;//如果有想反復執行的動作,作業,任務就把相關的代碼寫在execute這個方法里,前提:實現Job這個接口//至于SimpleJob這個類什么時候實例化,execute這個方法何時被調用,我們不用關注,交給Quartzpublic class SimpleRecoveryJob implements Job, Serializable { private static Log _log = LogFactory.getLog(SimpleRecoveryJob.class); public SimpleRecoveryJob() { } public void execute(JobExecutionContext context)  throws JobExecutionException {  //這個作業只是簡單的打印出作業名字和此作業運行的時間  String jobName = context.getJobDetail().getFullName();  System.out.println("JOB 1111111111111111111 SimpleRecoveryJob says: " + jobName + " executing at " + new Date()); }}

4.5 運行結果

  Server A與Server B中的配置和代碼完全一樣。運行方法:運行任意主機上的ClusterExample.java,將任務加入調度,觀察運行結果:

  運行ServerA,結果如圖4.2所示。

quartz,集群原理,集群實現原理,quartz集群配置

圖4.2 ServerA運行結果1  

  開啟ServerB后,ServerA與ServerB的輸出如圖4.3、4.4所示。

quartz,集群原理,集群實現原理,quartz集群配置

圖4.3 ServerA運行結果2

quartz,集群原理,集群實現原理,quartz集群配置

圖4.4 ServerB運行結果1

  從圖4.3、4.4可以看出,ServerB開啟后,系統自動實現了負責均衡,ServerB接手Job1。關斷ServerA后,ServerB的運行結果如圖4.5所示。

quartz,集群原理,集群實現原理,quartz集群配置

圖4.5 ServerB運行結果2

  從圖4.5中可以看出,ServerB可以檢測出ServerA丟失,將其負責的任務Job2接手,并將ServerA丟失到Server檢測出這段異常時間中需要執行的Job2重新執行了。

5、注意事項

5.1 時間同步問題

  Quartz實際并不關心你是在相同還是不同的機器上運行節點。當集群放置在不同的機器上時,稱之為水平集群。節點跑在同一臺機器上時,稱之為垂直集群。對于垂直集群,存在著單點故障的問題。這對高可用性的應用來說是無法接受的,因為一旦機器崩潰了,所有的節點也就被終止了。對于水平集群,存在著時間同步問題。

  節點用時間戳來通知其他實例它自己的最后檢入時間。假如節點的時鐘被設置為將來的時間,那么運行中的Scheduler將再也意識不到那個結點已經宕掉了。另一方面,如果某個節點的時鐘被設置為過去的時間,也許另一節點就會認定那個節點已宕掉并試圖接過它的Job重運行。最簡單的同步計算機時鐘的方式是使用某一個Internet時間服務器(Internet Time Server ITS)。

5.2 節點爭搶Job問題

  因為Quartz使用了一個隨機的負載均衡算法, Job以隨機的方式由不同的實例執行。Quartz官網上提到當前,還不存在一個方法來指派(釘住) 一個 Job 到集群中特定的節點。

5.3 從集群獲取Job列表問題

  當前,如果不直接進到數據庫查詢的話,還沒有一個簡單的方式來得到集群中所有正在執行的Job列表。請求一個Scheduler實例,將只能得到在那個實例上正運行Job的列表。Quartz官網建議可以通過寫一些訪問數據庫JDBC代碼來從相應的表中獲取全部的Job信息。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VeVb武林網的支持。


注:相關教程知識閱讀請移步到JAVA教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
精品亚洲夜色av98在线观看| 欧美激情综合亚洲一二区| 日韩精品免费在线| 亚洲第一精品久久忘忧草社区| 国产一区二区三区直播精品电影| 亚洲免费影视第一页| 久久精品久久久久久国产 免费| 久久九九精品99国产精品| 日韩成人在线视频| 日韩中文字幕在线视频| 国产精品女主播| 亚洲成人激情在线观看| 国产亚洲人成网站在线观看| 欧美在线视频免费观看| 国产精品久久99久久| 欧美电影免费观看电视剧大全| 亚洲欧美中文另类| 欧美日韩久久久久| 日韩av影片在线观看| 中文字幕日韩av电影| 午夜精品一区二区三区在线视频| 亚洲欧美日韩一区在线| 欧美高清视频一区二区| 精品视频久久久久久久| 午夜精品一区二区三区在线| 国产日韩欧美视频| 精品人伦一区二区三区蜜桃免费| 久久综合色88| 97香蕉超级碰碰久久免费的优势| 亚洲欧洲一区二区三区久久| 午夜精品久久久久久久99热| 久久久久久久一区二区三区| 欧美性猛交xxxx黑人| 久久99热这里只有精品国产| 欧美福利视频网站| 日韩在线观看视频免费| 韩国国内大量揄拍精品视频| 亚洲最大av网| 亚洲日本aⅴ片在线观看香蕉| 曰本色欧美视频在线| 最近2019中文字幕第三页视频| 国产精品日日摸夜夜添夜夜av| 日韩av在线免费看| 成人欧美在线观看| 成人黄色在线免费| 深夜福利一区二区| 欧美一级电影在线| 久久免费视频在线| 欧美另类高清videos| 国产欧美日韩亚洲精品| 超碰91人人草人人干| 91精品在线一区| xxxx欧美18另类的高清| 成人午夜高潮视频| 欧美国产乱视频| 国产成人aa精品一区在线播放| 欧美在线日韩在线| 久久综合久久八八| 51久久精品夜色国产麻豆| 热久久视久久精品18亚洲精品| 欧美肥婆姓交大片| 午夜精品国产精品大乳美女| 欧美激情视频一区二区三区不卡| 国产精品成人va在线观看| 欧美性生交大片免费| 欧美日韩中文字幕| 国产精品igao视频| 国产精品旅馆在线| 国产精品丝袜久久久久久不卡| 国产精品视频网址| 成人黄色免费在线观看| 久久久久久久久久国产精品| 欧美激情欧美激情| 中文字幕在线看视频国产欧美| 亚洲第一天堂av| 久久亚洲综合国产精品99麻豆精品福利| 91九色视频在线| 日本成人免费在线| 国产99久久精品一区二区永久免费| 欧美视频在线观看 亚洲欧| 国产91精品久久久久| 国产午夜一区二区| 青草青草久热精品视频在线观看| 欧美视频免费在线| 色午夜这里只有精品| 亚洲专区中文字幕| 日本成人在线视频网址| 色偷偷噜噜噜亚洲男人的天堂| 亚洲美女动态图120秒| 亚洲精品色婷婷福利天堂| 欧美成人精品在线观看| 91亚洲精品久久久久久久久久久久| 久久精品电影网站| 91在线|亚洲| 国产日韩欧美夫妻视频在线观看| 精品亚洲国产视频| 中文字幕欧美日韩在线| 国产福利视频一区| 久久精品国产成人| 成人日韩在线电影| 久久精品小视频| 高清欧美电影在线| 亚洲一区二区日本| 久久久视频在线| 国产一区二区三区在线观看视频| 精品精品国产国产自在线| 日韩有码在线电影| 亚洲精品一区二区在线| 欧美日韩中文在线观看| 丝袜美腿亚洲一区二区| 日韩精品一二三四区| 夜夜狂射影院欧美极品| 亚洲午夜精品视频| 性日韩欧美在线视频| 日韩亚洲欧美中文在线| 最近2019年好看中文字幕视频| 欧美日韩国产中文字幕| 欧美成人精品在线观看| 亚洲精品一区av在线播放| 亚洲精品美女在线| 欧美在线性视频| 国产综合色香蕉精品| 亚洲片国产一区一级在线观看| 国产亚洲欧洲黄色| 日韩欧美国产视频| 精品成人久久av| 91天堂在线视频| 九九热在线精品视频| 国产精品丝袜白浆摸在线| xvideos成人免费中文版| 久久久久久综合网天天| 成人网页在线免费观看| 亚洲精品成人免费| 日韩av在线资源| 久久久国产成人精品| 欧美精品一本久久男人的天堂| www.日韩免费| 亚洲欧美国产一本综合首页| 欧美在线视频观看免费网站| 午夜欧美不卡精品aaaaa| 自拍亚洲一区欧美另类| 91久久久国产精品| 国产一区二区三区高清在线观看| 狠狠色香婷婷久久亚洲精品| 亚洲美女久久久| 国产日韩精品综合网站| 成人美女免费网站视频| 亚洲精品久久久久久久久| 欧美在线激情网| 欧美日韩综合视频网址| 国产精品视频播放| 久久精品国产视频| 日韩一二三在线视频播| 日韩欧美福利视频| 国产精品96久久久久久又黄又硬| 亚洲夜晚福利在线观看| 韩国19禁主播vip福利视频| 亚洲国产精品va在线观看黑人| 日韩精品免费在线| 在线视频亚洲欧美| 欧美亚洲激情视频| 国产精品自拍偷拍视频| 日韩av网站导航| 亚洲精品综合精品自拍|