五锁保、Quartz中Trigger理解和使用

(一)、Trigger觸發(fā)器

??Job中包含了任務執(zhí)行的邏輯,Scheduler負責掃描需要執(zhí)行的Job任務椰憋,那么Scheduler如何知道何時執(zhí)行這個Job任務呢?接下來就需要觸發(fā)器上場了赔退。

??觸發(fā)器(org.quartz.Trigger)抽象類的幾個主要屬性和JobDetail差不多橙依,這里就不說明了,主要注意的是下面表格中的屬性和misfireInstruction這個屬性硕旗,misfireInstruction這個屬性的是觸發(fā)器錯失執(zhí)行(misfire)后的一個錯失觸發(fā)機制標識窗骑。當線程池中沒有可用的線程執(zhí)行任務時,就會錯過觸發(fā)時間漆枚,Trigger抽象類中默認錯失觸發(fā)機制是常量0(聰明的策略)慧域。派生類有它們自己的錯失觸發(fā)機制。

下面對觸發(fā)器的子類分別進行講述浪读,我們主要看Trigger的4個可用的派生類(注:共5個,UICronTrigger1.5版本后已廢棄)辛藻,分別是:

  • org.quartz.SimpleTrigger
  • org.quartz.CronTrigger (常用)
  • org.quartz.DateIntervalTrigger
  • org.quartz.NthIncludedDayTrigger

以上派生類的幾個主要屬性:

屬性名 詳細介紹
startTime 首次觸發(fā)時間
endTime 截止時間碘橘,就算指定的執(zhí)行次數沒有執(zhí)行完也立即結束
nextFireTime 下一次觸發(fā)時間
previousFireTime 上一次觸發(fā)時間
repeatCount (不提供get/set)重復執(zhí)行次數(不包含首次執(zhí)行)
repeatInterval (不提供get/set) 重復執(zhí)行間隔,單位為毫秒
timesTriggered (不提供get/set) 總觸發(fā)次數

了解了觸發(fā)器的幾個關鍵屬性吱肌,接下來通過代碼來看看幾個觸發(fā)器具體的使用痘拆。
下面這個Job打印了觸發(fā)器的幾種時間,示例代碼中的幾種觸發(fā)器都使用到了這個Job氮墨。


package com.xk.quartz.test;

import java.util.Date;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Trigger;

/**
 * 演示job
 */
public class DemoJob implements Job{

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        // 獲取觸發(fā)器
        Trigger trigger = context.getTrigger();
        
        // 獲取幾種時間
        Date startTime = trigger.getStartTime();
        Date endTime = trigger.getEndTime();
        Date previousFireTime = trigger.getPreviousFireTime();
        Date nextFireTime = trigger.getNextFireTime();
        
        System.out.println("首次執(zhí)行時間:" + startTime.toLocaleString());
        System.out.println("最后截止時間:" + (endTime != null ? endTime.toLocaleString() : null));
        System.out.println("上一次執(zhí)行時間:" + previousFireTime.toLocaleString());
        System.out.println("下一次執(zhí)行時間:" + (nextFireTime != null ? nextFireTime.toLocaleString() : null));
        System.out.println("*******************************");
    }
}

接下來對每個觸發(fā)器進行分別演示纺蛆。

(1)、org.quartz.SimpleTrigger

??SimpleTrigger是一種設置和使用簡單的觸發(fā)器规揪,它是在指定日期/時間且可能需要重復執(zhí)行n次的時機下使用的桥氏。這種觸發(fā)器不適合每天定時執(zhí)行任務這種場景,舉個栗子猛铅,假設指定任務首次觸發(fā)時間是中午12點整字支,間隔時間為1天,那么理論上來講以后的每一天中午12點整都會準時執(zhí)行奸忽,但是堕伪,只要出現了一次misfire,假設錯失執(zhí)行后等待可用線程的時間為1分鐘栗菜,即此任務會在12點01分被恢復觸發(fā)欠雌,那么觸發(fā)器記錄的此次執(zhí)行時間就是12點01分鐘,又因為間隔時間設定的是1天疙筹,所以下一次執(zhí)行時間也會被推算到第二天的12點01分富俄。

為了方便演示禁炒,下面使用的Quartz框架沒有持久化配置,僅使用RAMJobStore(存放在內存中)蛙酪,這樣的好處是可以使用默認配置并且直接main方法啟動齐苛。

package com.xk.quartz.test;

import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;

/**
 * SimpleTrigger演示
 */
public class SimpleTriggerTest {

    public static void main(String[] args) throws SchedulerException {
        // 獲取默認調度器  內部加載順序在調度器章節(jié)提到過
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        // 啟動調度器
        scheduler.start();
        
        // 創(chuàng)建job詳情
        JobDetail jobDetail = new JobDetail("demoJob演示", DemoJob.class);
        
        // 以下是觸發(fā)器的不同構造
        // 立即執(zhí)行
        SimpleTrigger simpleTrigger = new SimpleTrigger("simpleTrigger演示");
        
        // 立即執(zhí)行
        // SimpleTrigger simpleTrigger = new SimpleTrigger("simpleTrigger演示", new Date());
        
        // 當前立即執(zhí)行,指定重復次數3次桂塞,間隔500毫秒
        // 總執(zhí)行次數為4次(首次執(zhí)行+3次重復執(zhí)行)
        // SimpleTrigger simpleTrigger = new SimpleTrigger("simpleTrigger演示", 3, 500);
        
        // 當前時間2秒后執(zhí)行凹蜂,當前時間5秒后結束,指定重復次數20次阁危,間隔1000毫秒
        // 這里雖然指定執(zhí)行次數為20次玛痊,但是實際只能執(zhí)行3次(首次執(zhí)行+2次重復執(zhí)行),因為有"當前時間5秒后結束"這個因素在
        // SimpleTrigger simpleTrigger = new SimpleTrigger("simpleTrigger演示", new Date(System.currentTimeMillis() + 2000), new Date(System.currentTimeMillis() + 5000), 20, 1000);
        
        // TODO
        // 只要明白上面幾種創(chuàng)建方式   其他沒有寫出的夠著就很容易明白
        
        // 注冊job和觸發(fā)器
        scheduler.scheduleJob(jobDetail, simpleTrigger);
    }
}

(2)狂打、org.quartz.CronTrigger

??CronTrigger觸發(fā)器比SimpleTrigger觸發(fā)器強大很多擂煞,上面的代碼可以看出,使用SimpleTrigger觸發(fā)器時趴乡,需要設置不同的屬性來支撐对省,代碼編寫相對較多,而且不是很靈活晾捏。而接下來CronTrigger觸發(fā)器蒿涎,可以設定復雜的觸發(fā)時間表,設置方式也很簡單惦辛,唯一的難點就是需要理解時間表達式如何編寫劳秋。
??為了更深刻的了解CronTrigger,我們需要了解一下Cron到底是什么胖齐?
??Cron概念是來自UNIX(Linux也是基于UNIX的)操作系統的玻淑,它就是一個執(zhí)行計劃任務的服務器或程序,你只需要將你的計劃和執(zhí)行時間交給它呀伙,它就會根據指定時間來調度工作任務补履。這里不要混淆UNIX的Cron和Quartz的Cron,因為他們之間有一些很明顯的差別剿另,UNIX cron 計劃僅支持至分鐘級干像,而Quartz支持到秒級的計劃。說這些就是讓大家了解下Quartz Cron的歷史驰弄。

下面先看一下幾個示例麻汰,會感覺也沒多難,很好理解:

package com.xk.quartz.test;

import java.text.ParseException;

import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;

/**
 * CronTrigger演示
 */
public class CronTriggerTest {

    public static void main(String[] args) throws SchedulerException {
        // 獲取默認調度器  內部加載順序在調度器章節(jié)提到過
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        // 啟動調度器
        scheduler.start();
        
        // 創(chuàng)建job詳情
        JobDetail jobDetail = new JobDetail("demoJob演示", DemoJob.class);
        
        // 創(chuàng)建觸發(fā)器  下面只演示一種觸發(fā)器構造   其他的構造也很好理解
        CronTrigger cronTrigger = null;
        try {
            // 每秒執(zhí)行一次兩種寫法
            cronTrigger = new CronTrigger("cronTrigger演示", "DEFAULT", "* * * * * ?");
            // cronTrigger = new CronTrigger("cronTrigger演示", "DEFAULT", "0/1 * * * * ?");
            
            // 每分鐘的秒鐘等于30時執(zhí)行
            // cronTrigger = new CronTrigger("cronTrigger演示", "DEFAULT", "30 * * * * ?");
            
            // 每5分鐘的整分時執(zhí)行
            // cronTrigger = new CronTrigger("cronTrigger演示", "DEFAULT", "0 0/5 * * * ?");
            
            // 每天的9點和18點整執(zhí)行  (通知下班啦?)
            // cronTrigger = new CronTrigger("cronTrigger演示", "DEFAULT", "0 0 9,18 * * ?");
            
            // 每個月最后一天的23點59分59秒執(zhí)行  (做月統計時使用)
            // cronTrigger = new CronTrigger("cronTrigger演示", "DEFAULT", "59 59 23 L * ?");
            
        } catch (ParseException e) {
            e.printStackTrace();
        }
        
        // 注冊job和觸發(fā)器
        scheduler.scheduleJob(jobDetail, cronTrigger);
    }
}

下面列出Quartz Cron表達式格式的7個域:

名稱 是否必須 允許值 特殊字符
0-59 , - * /
0-59 , - * /
0-23 , - * /
1-31 , - * ? / L W C
1-12或 JAN-DEC , - * /
1-7 或 SUN-SAT , - * ? / L C #
空 或 1970-2099 , - * /

域之間通過空格進行分隔戚篙,年份不是必須項五鲫,月份和星期的名稱不區(qū)分大小寫,比如SUN和sun是一樣的岔擂。

  • 星號( * )表示該域上所有合法的值都會被觸發(fā)位喂,如果使用在秒域上浪耘,就表示 每秒鐘
  • 問號( ? )表示不為該域指定值。?號只能用在日和周兩個域上塑崖,但不能兩個域同時使用七冲,只要記住一點,在這兩個域的其中一個指定了值规婆,那另外一個值就放一個?問號澜躺,這樣不會出現混亂
  • 逗號( , )表示該域上指定一個值列表,例如秒的域上使用值0,20,40 表示秒鐘等于0抒蚜、20或40時觸發(fā)
  • 斜杠( / )表示時間表遞增掘鄙,上面逗號舉例20秒觸發(fā)一次,也可以在秒域上寫成 0/20
  • 中劃線( - )表示取值范圍嗡髓,例如時域上指定9-18操漠,那么表示上午9點到下午6點每小時觸發(fā)一次
  • 字母( L )表示合法范圍的最后一個值,只能在日和周兩個域上使用饿这,用在日上時表示指定月份的最后一天浊伙;用在周上時表示周的最后一天,注意是數字7或者星期六(國外周日是星期的第一天)长捧,用在周上時還可以用一個數字與 L 連起來表示月份的最后一個星期 X吧黄。例如,表達式 0 0 0 ? * 1L表示在每個月的最后一個星期日觸發(fā)
  • 字母( W )表示指定日的最近的一個工作日(周一到周五)唆姐,并且只能用在日域上。假如日字段指定的是15W廓八,那么就表示該月15號最相近的工作日執(zhí)行奉芦。如果15號是周六,那么就是14號周五觸發(fā)剧蹂;如果15號是周日声功,那么就會在16號周一觸發(fā);如果15號是周三宠叼,那就當天觸發(fā)
  • 井號( # )表示指定月份中第幾周的第幾天先巴,只能用在周域上。例如值為1#2冒冬,表示某月第二周的星期日觸發(fā)伸蚯,2表示第二周,1表示星期日简烤。如果把2改為大于4的數字剂邮,比如5或6,那么就不會被觸發(fā)横侦,因為一個月中不會超過4周
  • 字母( C )表示計算需要依賴日歷(org.quartz.impl.calendar.CronCalendar)類的計算結果挥萌,只能用在日和周兩個域上绰姻。

下面是一些例子,可以照葫蘆畫瓢進行延伸擴展:

表達式 含義
0 0 12 * * ? 每天中午12點整觸發(fā)
0 0 12 ? * * 每天中午12點整觸發(fā)
0 0 12 * * ? 2018 2018年的每天中午12點整觸發(fā)
0 0/5 12 * * ? 每天12點開始到12點55分結束每5分鐘觸發(fā)
0 0-5 12 * * ? 每天12:00到12:05每分鐘觸發(fā)
0 0 12,13,14 * * ? 每天12點引瀑、13點狂芋、14點整點時各觸發(fā)
0 0 12 ? 6 WED 6月份的每個周三中午12點整觸發(fā)
0 0 12 L * ? 每個月最后一天12點整觸發(fā)
0 0 12 15W * ? 每個月15號最近的工作日(周一到周五)觸發(fā)一次
0 0 12 ? * 2#3 每個月第三周的星期一觸發(fā)
(3)、org.quartz.DateIntervalTrigger

??這是quartz1.7版本以后加入的觸發(fā)器憨栽,該觸發(fā)器適用于每小時帜矾,每幾周,每幾月重復執(zhí)行的任務徒像。
??舉個例子黍特,假如任務需求是需要每5個月間隔時觸發(fā)一次,如果用Cron來實現锯蛀,那表達式就是“0 0 12 * 0/5 ?“灭衷,啟動時會報一個解析異常ParseException: Month values must be between 1 and 12。雖然我們也可以使用SimpleTrigger觸發(fā)器旁涤,但是上面提到翔曲,SimpleTrigger當出現misfire時,下一次執(zhí)行的時間也會向后延遲劈愚。而DateIntervalTrigger就不會有這些問題瞳遍。使用DateIntervalTrigger時只要注意一個問題,如果使用的Unit單位為月菌羽,開始執(zhí)行時間是1月31日掠械,那么第二次執(zhí)行將會是2月28日(可能29日),第三次執(zhí)行會是3月28日注祖,以后都是28日猾蒂,而不會出現31日了,這是需要注意點是晨。如果想要實現每個月最后一天觸發(fā)肚菠,那就使用CronTrigger觸發(fā)器去實現(示例代碼中有提到)。

上代碼:


package com.xk.quartz.test;

import org.quartz.DateIntervalTrigger;
import org.quartz.DateIntervalTrigger.IntervalUnit;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;

/**
 * DateIntervalTrigger演示
 */
public class DateIntervalTriggerTest {

    public static void main(String[] args) throws SchedulerException {
        // 獲取默認調度器  內部加載順序在調度器章節(jié)提到過
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        // 啟動調度器
        scheduler.start();
        
        // 創(chuàng)建job詳情
        JobDetail jobDetail = new JobDetail("demoJob演示", DemoJob.class);
        
        // 創(chuàng)建觸發(fā)器   只演示一種構造方法  其他構造屬性和以上觸發(fā)器屬性類似
        // 每一個月觸發(fā)一次   IntervalUnit.MONTH是個枚舉常量  1表示重復間隔
        DateIntervalTrigger dateIntervalTrigger = new DateIntervalTrigger("dateIntervalTrigger演示", IntervalUnit.MONTH, 1);
        
        // TODO
        // 下面就不一一列舉了 IntervalUnit的其他常量很容易理解罩缴,每個常量對應了一個日期域

        // 注冊job和觸發(fā)器
        scheduler.scheduleJob(jobDetail, dateIntervalTrigger);
    }
}

(4)蚊逢、org.quartz.NthIncludedDayTrigger

NthIncludedDayTrigger適用于在每一個間隔類型(月或年等)的第N天觸發(fā)。直接上代碼:

package com.xk.quartz.test;

import org.quartz.Calendar;
import org.quartz.JobDetail;
import org.quartz.NthIncludedDayTrigger;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;

/**
 * NthIncludedDayTrigger演示
 */
public class NthIncludedDayTriggerTest {

    public static void main(String[] args) throws SchedulerException {
        // 獲取默認調度器  內部加載順序在調度器章節(jié)提到過
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        // 啟動調度器
        scheduler.start();
        
        // 創(chuàng)建job詳情
        JobDetail jobDetail = new JobDetail("demoJob演示", DemoJob.class);
        
        // 創(chuàng)建觸發(fā)器  
        // 下面觸發(fā)器表示每個月的第10天中午12點整觸發(fā)
        NthIncludedDayTrigger nthIncludedDayTrigger = new NthIncludedDayTrigger("nthIncludedDayTrigger演示");
        nthIncludedDayTrigger.setN(10);
        nthIncludedDayTrigger.setIntervalType(NthIncludedDayTrigger.INTERVAL_TYPE_MONTHLY);
        nthIncludedDayTrigger.setFireAtTime("12:00:00");
        
        // 注冊job和觸發(fā)器
        scheduler.scheduleJob(jobDetail, nthIncludedDayTrigger);
    }
}

??四種觸發(fā)器的創(chuàng)建方式都演示了箫章,大家平時需要考慮的就是哪些情況下使用哪種觸發(fā)器烙荷。這需要大家在開發(fā)的過程中自己進行研究和調試。大家使用最多的是CronTrigger檬寂,通過Cron表達式幾乎可以完成工作業(yè)務中的絕大多數任務奢讨。
(二)、使用TriggerUtils工具類創(chuàng)建觸發(fā)器

??下面介紹一個常用的觸發(fā)器工具類(org.quartz.TriggerUtils)∧弥睿可以很方便創(chuàng)建一些簡單的觸發(fā)器扒袖。上代碼:

package com.xk.quartz.test;

import org.quartz.Trigger;
import org.quartz.TriggerUtils;

/**
 * TriggerUtils演示
 */
public class TriggerUtilsTest {

    public static void main(String[] args) {
        
        // 構造函數有很多  下面只列舉常用的幾種
        
        // 創(chuàng)建一個每2秒執(zhí)行一次 重復執(zhí)行5次的觸發(fā)器
        Trigger trigger = TriggerUtils.makeSecondlyTrigger("trigger", 2, 5);
        
        // 創(chuàng)建一個每5分鐘執(zhí)行一次的觸發(fā)器
        Trigger trigger2 = TriggerUtils.makeMinutelyTrigger(5);
        trigger2.setName("trigger2");
        
        // 創(chuàng)建一個每2小時執(zhí)行一次 重復執(zhí)行5次的觸發(fā)器
        Trigger trigger3 = TriggerUtils.makeHourlyTrigger(2, 5);
        trigger3.setName("trigger3");
        
        // 創(chuàng)建一個每天中午12點10分執(zhí)行的觸發(fā)器
        Trigger trigger4 = TriggerUtils.makeDailyTrigger("trigger4", 12, 10);
        
        // 創(chuàng)建一個每個月5號中午12點10執(zhí)行的觸發(fā)器
        Trigger trigger5 = TriggerUtils.makeMonthlyTrigger("trigger5", 5, 12, 10);
        
        // 創(chuàng)建一個每周三中午12點整執(zhí)行的觸發(fā)器
        Trigger trigger6 = TriggerUtils.makeWeeklyTrigger("trigger6", 4, 12, 0);
                
        // 創(chuàng)建一個立即觸發(fā)的觸發(fā)器
        Trigger trigger7 = TriggerUtils.makeImmediateTrigger("trigger7", 0, 0);
    }
}

下面是工具類中的其他方法,了解一下含義:

  • computeFireTimes(Trigger trigg, org.quartz.Calendar cal, int numTimes)
    返回下次執(zhí)行的日期列表
  • computeFireTimesBetween(Trigger trigg, org.quartz.Calendar cal, Date from, Date to)
    返回指定時間段內下次執(zhí)行的日期列表
  • computeEndTimeToAllowParticularNumberOfFirings(Trigger trigg, org.quartz.Calendar cal, int numTimes)
    返回第n次觸發(fā)后的第1秒時間
  • getDateOf(int second, int minute, int hour)
    返回指定時分秒的一個今天的日期對象亩码,此方法有包含日季率,月,年參數重載方法
  • getEvenHourDate(Date date)
    返回一個舍入到下一個小時以上的日期描沟。比如:入參 09:35:12 返回10:00:00
    此方法也有多個重載飒泻。
到此,Quartz中Trigger理解和使用說完了吏廉。如果大家有什么問題泞遗,請在下方留言,謝謝席覆!
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末史辙,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子佩伤,更是在濱河造成了極大的恐慌聊倔,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件生巡,死亡現場離奇詭異耙蔑,居然都是意外死亡,警方通過查閱死者的電腦和手機孤荣,發(fā)現死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門甸陌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人盐股,你說我怎么就攤上這事钱豁。” “怎么了遂庄?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長劲赠。 經常有香客問我涛目,道長,這世上最難降的妖魔是什么凛澎? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任霹肝,我火速辦了婚禮,結果婚禮上塑煎,老公的妹妹穿的比我還像新娘沫换。我一直安慰自己,他們只是感情好最铁,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布讯赏。 她就那樣靜靜地躺著垮兑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪漱挎。 梳的紋絲不亂的頭發(fā)上系枪,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機與錄音磕谅,去河邊找鬼私爷。 笑死,一個胖子當著我的面吹牛膊夹,可吹牛的內容都是我干的衬浑。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼放刨,長吁一口氣:“原來是場噩夢啊……” “哼工秩!你這毒婦竟也來了?” 一聲冷哼從身側響起宏榕,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤拓诸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后麻昼,有當地人在樹林里發(fā)現了一具尸體奠支,經...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年抚芦,在試婚紗的時候發(fā)現自己被綠了倍谜。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡叉抡,死狀恐怖尔崔,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情褥民,我是刑警寧澤季春,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站消返,受9級特大地震影響载弄,放射性物質發(fā)生泄漏。R本人自食惡果不足惜撵颊,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一宇攻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧倡勇,春花似錦逞刷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽仑最。三九已至,卻和暖如春题篷,著一層夾襖步出監(jiān)牢的瞬間词身,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工番枚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留法严,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓葫笼,卻偏偏與公主長得像深啤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子路星,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內容