32. 從零開(kāi)始學(xué)springboot-一篇搞定Schedule定時(shí)任務(wù)

前言

實(shí)際開(kāi)發(fā)中,經(jīng)常會(huì)碰到“定期定時(shí)去做一些重復(fù)操作”的需求,這個(gè)時(shí)候,定時(shí)任務(wù)顯得是那么的方便.本章,我們來(lái)講講SpringBoot的定時(shí)任務(wù)如何使用.

分類

使用SpringBoot創(chuàng)建定時(shí)任務(wù)非常簡(jiǎn)單胶哲,目前主要有以下三種創(chuàng)建方式

  • 基于注解(@Scheduled)
  • 基于接口(SchedulingConfigurer)
  • 基于注解多線程定時(shí)任務(wù)

基于注解(單線程)

基于注解@Scheduled默認(rèn)為單線程宪拥,開(kāi)啟多個(gè)任務(wù)時(shí)梭依,任務(wù)的執(zhí)行時(shí)機(jī)會(huì)受上一個(gè)任務(wù)執(zhí)行時(shí)間的影響蒜危。
什么意思呢?
意思就是假設(shè)基于注解注冊(cè)了兩個(gè)定時(shí)任務(wù),我們叫做task1和task2
假設(shè)task1每5s執(zhí)行一次并延時(shí)10s在執(zhí)行下一次.
假設(shè)task2每5s執(zhí)行一次.

@Scheduled默認(rèn)為單線程的執(zhí)行機(jī)制會(huì)導(dǎo)致如下情況:
task2在task1執(zhí)行后才執(zhí)行的(也就是task2實(shí)際上10s后才開(kāi)始執(zhí)行)

@Scheduled支持多種注解(參數(shù)),我們接下來(lái)一一演示

占位符

占位符概念

這邊補(bǔ)上一個(gè)占位符的概念,因?yàn)楹竺鏁?huì)用到.

假設(shè)你將定時(shí)任務(wù)的時(shí)間周期寫(xiě)在了配置文件里,那么此時(shí),你編寫(xiě)定時(shí)任務(wù)時(shí)如何自動(dòng)識(shí)別呢?
這就使用到了占位符.

占位符的使用

yml配置文件中有配置:

time:
  cron: */5 * * * * *
  interval: 5

此時(shí)使用注解定時(shí)任務(wù)應(yīng)該使用占位符

    @Scheduled(cron="${time.cron}")
    void testCron1() {
        System.out.println("占位符演示1" + System.currentTimeMillis());
    }

    @Scheduled(cron="*/${time.interval} * * * * *")
    void testCron2() {
        System.out.println("占位符演示2" + System.currentTimeMillis());
    }

八大參數(shù)-cron表達(dá)式(支持占位符)

語(yǔ)法

cron表達(dá)式語(yǔ)法
[秒] [分] [小時(shí)] [日] [月] [周] [年]
注:[年]不是必須的域,可以省略[年]身冀,則一共6個(gè)域

cron.png

通配符

關(guān)于各個(gè)通配符的意義如下:

  • “*”表示所有值钝尸。
例如:在分的字段上設(shè)置 *,表示每一分鐘都會(huì)觸發(fā)。
  • “?”表示不指定值搂根。
使用的場(chǎng)景為不需要關(guān)心當(dāng)前設(shè)置這個(gè)字段的值珍促。例如:要在每月
的10號(hào)觸發(fā)一個(gè)操作,但不關(guān)心是周幾剩愧,所以需要周位置的那個(gè)
字段設(shè)置為”?” 具體設(shè)置為 0 0 0 10 * ?
  • “-”表示區(qū)間猪叙。
例如 在小時(shí)上設(shè)置 “10-12”,表示 10,11,12點(diǎn)都會(huì)觸發(fā)。
  • “,” 表示指定多個(gè)值.
例如在周字段上設(shè)置 “MON,WED,FRI” 表示周一仁卷,周三和周五觸
發(fā)
  • “/” 用于遞增觸發(fā)穴翩。
如在秒上面設(shè)置”5/15” 表示從5秒開(kāi)始,每增15秒觸(5,20,35,50)五督。 
在月字段上設(shè)置’1/3’所示每月1號(hào)開(kāi)始,每隔三天觸發(fā)一次瓶殃。
  • “L” 表示最后的意思充包。
在日字段設(shè)置上,表示當(dāng)月的最后一天(依據(jù)當(dāng)前月份遥椿,如果是二
月還會(huì)依據(jù)是否是潤(rùn)年[leap]), 在周字段上表示星期六基矮,相當(dāng)
于”7”或”SAT”。如果在”L”前加上數(shù)字冠场,則表示該數(shù)據(jù)的最后一個(gè)家浇。
例如在周字段上設(shè)置”6L”這樣的格式,則表示“本月最后一個(gè)星期五”
  • “W” 表示離指定日期的最近那個(gè)工作日(周一至周五).
例如在日字段上置”15W”,表示離每月15號(hào)最近的那個(gè)工作日觸
發(fā)碴裙。如果15號(hào)正好是周六钢悲,則找最近的周五(14號(hào))觸發(fā), 如果15號(hào)
是周未,則找最近的下周一(16號(hào))觸發(fā).如果15號(hào)正好在工作日(周
一至周五)舔株,則就在該天觸發(fā)莺琳。如果指定格式為 “1W”,它則表示每
月1號(hào)往后最近的工作日觸發(fā)。如果1號(hào)正是周六载慈,則將在3號(hào)下周
一觸發(fā)惭等。(注,”W”前只能設(shè)置具體的數(shù)字,不允許區(qū)間”-“)办铡。
  • “#”序號(hào)(表示每月的第幾個(gè)周幾).
例如在周字段上設(shè)置”6#3”表示在每月的第三個(gè)周六.注意如果指
定”#5”,正好第五周沒(méi)有周六辞做,則不會(huì)觸發(fā)該配置(用在母親節(jié)和父
親節(jié)再合適不過(guò)了) 琳要;小提示:’L’和 ‘W’可以一組合使用。如果在
日字段上設(shè)置”LW”,則表示在本月的最后一個(gè)工作日觸發(fā)秤茅;周字段
的設(shè)置稚补,若使用英文字母是不區(qū)分大小寫(xiě)的,即MON與mon相
同嫂伞。

通配符實(shí)例

為了幫助大家理解,我們?cè)谂e幾個(gè)例子:

  • 每隔5秒執(zhí)行一次:*/5 * * * * ?
  • 每隔1分鐘執(zhí)行一次:0 */1 * * * ?
  • 每天23點(diǎn)執(zhí)行一次:0 0 23 * * ?
  • 每天凌晨1點(diǎn)執(zhí)行一次:0 0 1 * * ?
  • 每月1號(hào)凌晨1點(diǎn)執(zhí)行一次:0 0 1 1 * ?
  • 每月最后一天23點(diǎn)執(zhí)行一次:0 0 23 L * ?
  • 每周星期天凌晨1點(diǎn)實(shí)行一次:0 0 1 ? * L
  • 在26分孔厉、29分、33分執(zhí)行一次:0 26,29,33 * * * ?
  • 每天的0點(diǎn)帖努、13點(diǎn)撰豺、18點(diǎn)、21點(diǎn)都執(zhí)行一次:0 0 0,13,18,21 * * ?

cron定時(shí)任務(wù)實(shí)例

package com.mrcoder.sbschedule.job;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;

@Component
@Configuration      //1.主要用于標(biāo)記配置類拼余,兼?zhèn)銫omponent的效果污桦。
@EnableScheduling   //2.開(kāi)啟定時(shí)任務(wù)
public class StaticSchedule {
    //3.添加定時(shí)任務(wù)
    //基于注解@Scheduled默認(rèn)為單線程
    //開(kāi)啟多個(gè)任務(wù)時(shí),任務(wù)的執(zhí)行時(shí)機(jī)會(huì)受上一個(gè)任務(wù)執(zhí)行時(shí)間的影響匙监。
    //這邊定時(shí)任務(wù)1延時(shí)10s來(lái)演示這個(gè)單線程的問(wèn)題
    @Scheduled(cron = "0/5 * * * * ?")
    private void configureTasks() {
        System.err.println("執(zhí)行靜態(tài)定時(shí)任務(wù)(單線程)1: " + LocalDateTime.now().toLocalTime());

        try {
            Thread.sleep(1000 * 10);
        } catch (InterruptedException ex) {
            Thread.currentThread().interrupt();
            System.err.println(ex);
        }
    }

    @Scheduled(cron = "0/5 * * * * ?")
    private void configureTasks2() {
        System.err.println("執(zhí)行靜態(tài)定時(shí)任務(wù)(單線程)2: " + LocalDateTime.now().toLocalTime());
    }
}

八大參數(shù)-ZONE

時(shí)區(qū)凡橱,接收一個(gè)java.util.TimeZone#ID。cron表達(dá)式會(huì)基于該時(shí)區(qū)解析亭姥。默認(rèn)是一個(gè)空字符串稼钩,即取服務(wù)器所在地的時(shí)區(qū)。比如我們一般使用的時(shí)區(qū)Asia/Shanghai达罗。該字段我們一般留空坝撑。

使用實(shí)例

    @Scheduled(cron = "0/5 * * * * ?", zone = "")
    private void configureTasks() {
        System.err.println("執(zhí)行靜態(tài)定時(shí)任務(wù)(單線程)1: " + LocalDateTime.now().toLocalTime());
    }

八大參數(shù)-fixedDelay

fixedDelay用于上一次執(zhí)行完畢時(shí)間點(diǎn)之后多長(zhǎng)時(shí)間再執(zhí)行

使用實(shí)例

    //上一次執(zhí)行完畢時(shí)間點(diǎn)之后5秒再執(zhí)行
    @Scheduled(fixedDelay = 5000) 
    private void configureTasks() {
        System.err.println("執(zhí)行靜態(tài)定時(shí)任務(wù)(單線程)1: " + LocalDateTime.now().toLocalTime());
    }

八大參數(shù)-fixedDelayString(支持占位符)

與fixedDelay相同,只是使用字符串的形式粮揉。唯一不同的是支持占位符巡李。

使用實(shí)例

    //上一次執(zhí)行完畢時(shí)間點(diǎn)之后5秒再執(zhí)行
    @Scheduled(fixedDelayString = "5000")
    private void configureTasks() {
        System.err.println("執(zhí)行靜態(tài)定時(shí)任務(wù)(單線程)1: " + LocalDateTime.now().toLocalTime());
    }

八大參數(shù)-fixedRate

上一次開(kāi)始執(zhí)行時(shí)間點(diǎn)之后多長(zhǎng)時(shí)間再執(zhí)行

使用實(shí)例

    //上一次開(kāi)始執(zhí)行時(shí)間點(diǎn)之后5秒再執(zhí)行
    @Scheduled(fixedRate = 5000) 
    private void configureTasks() {
        System.err.println("執(zhí)行靜態(tài)定時(shí)任務(wù)(單線程)1: " + LocalDateTime.now().toLocalTime());
    }

八大參數(shù)-fixedRateString(支持占位符)

與fixedRate相同,只是使用字符串的形式。唯一不同的是支持占位符扶认。

使用實(shí)例

    //上一次開(kāi)始執(zhí)行時(shí)間點(diǎn)之后5秒再執(zhí)行
    @Scheduled(fixedRateString = "5000") 
    private void configureTasks() {
        System.err.println("執(zhí)行靜態(tài)定時(shí)任務(wù)(單線程)1: " + LocalDateTime.now().toLocalTime());
    }

八大參數(shù)-initialDelay

第一次延遲多長(zhǎng)時(shí)間后再執(zhí)行

使用實(shí)例

    //第一次延遲1秒后執(zhí)行侨拦,之后按fixedRate的規(guī)則每5秒執(zhí)行一次
    @Scheduled(initialDelay = 1000, fixedRate = 5000) 
    private void configureTasks() {
        System.err.println("執(zhí)行靜態(tài)定時(shí)任務(wù)(單線程)1: " + LocalDateTime.now().toLocalTime());
    }

八大參數(shù)-initialDelayString(支持占位符)

與initialDelay相同,只是使用字符串的形式。唯一不同的是支持占位符辐宾。

使用實(shí)例

    //第一次延遲1秒后執(zhí)行狱从,之后按fixedRate的規(guī)則每5秒執(zhí)行一次
    @Scheduled(initialDelayString = "1000", fixedRate = 5000) 
    private void configureTasks() {
        System.err.println("執(zhí)行靜態(tài)定時(shí)任務(wù)(單線程)1: " + LocalDateTime.now().toLocalTime());
    }

基于注解(多線程)

這邊主要使用注解@EnableAsync開(kāi)啟多線程支持
使用注解@Async來(lái)表明定義一個(gè)異步的線程任務(wù)

案例

package com.mrcoder.sbschedule.job;
import org.springframework.scheduling.annotation.*;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;

@Component
@EnableScheduling //1.開(kāi)啟定時(shí)任務(wù)
@EnableAsync      //2.開(kāi)啟多線程,開(kāi)啟后多個(gè)定時(shí)任務(wù)不會(huì)互相影響

public class ThreadSchedule {
    @Async
    @Scheduled(fixedDelay = 1000) //間隔1秒
    public void first() {
        System.err.println("執(zhí)行定時(shí)任務(wù)(多線程)1 : " + LocalDateTime.now().toLocalTime() + "線程 : " + Thread.currentThread().getName());
        try {
            Thread.sleep(1000 * 10);
        } catch (InterruptedException ex) {

            Thread.currentThread().interrupt();
            System.err.println(ex);
        }
    }

    @Async
    @Scheduled(fixedDelay = 2000)
    public void second() {
        System.err.println("執(zhí)行定時(shí)任務(wù)(多線程)2 : " + LocalDateTime.now().toLocalTime() + "線程 : " + Thread.currentThread().getName());
    }
}

基于接口SchedulingConfigurer(單/多線程)

上一小結(jié)介紹了基于注解@Scheduled的定時(shí)任務(wù),可以看到,很方便.但是有個(gè)致命的缺點(diǎn),
就是當(dāng)需要臨時(shí)調(diào)整定時(shí)任務(wù)執(zhí)行周期時(shí),必須要實(shí)時(shí)的重啟應(yīng)用才能生效.
那么能不能不重啟應(yīng)用就能“動(dòng)態(tài)”調(diào)整定時(shí)任務(wù)的執(zhí)行周期呢?

下面我們介紹基于接口SchedulingConfigurer的定時(shí)任務(wù)實(shí)現(xiàn)方式.

需要說(shuō)明的是,SchedulingConfigurer默認(rèn)使用的也是單線程的方式叠纹,如果需要配置多線程矫夯,則需要指定 PoolSize,加入如下代碼即可:

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        //10個(gè)線程
        taskScheduler.setPoolSize(10);
        taskScheduler.initialize();
        taskRegistrar.setTaskScheduler(taskScheduler);
    }

案例

定時(shí)任務(wù)周期庫(kù)

創(chuàng)建一張表用來(lái)存儲(chǔ)定時(shí)任務(wù)的執(zhí)行周期,后續(xù)調(diào)整周期時(shí)直接修改對(duì)應(yīng)的記錄即可

DROP DATABASE
IF
    EXISTS `demo`;
CREATE DATABASE `demo`;
USE `demo`;
DROP TABLE
IF
    EXISTS `crontab`;
CREATE TABLE `crontab` ( `cron_id` VARCHAR ( 30 ) NOT NULL PRIMARY KEY, `cron` VARCHAR ( 30 ) NOT NULL );
INSERT INTO `crontab`
VALUES
    ( '1', '0/10 * * * * ?' );

配置yml

spring:
  datasource:
    url: jdbc:mysql://127.0.01:3306/demo?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

實(shí)現(xiàn)SchedulingConfigurer接口

package com.mrcoder.sbschedule.job;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.config.TriggerTask;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.Date;


@Component
//1.主要用于標(biāo)記配置類吊洼,兼?zhèn)銫omponent的效果训貌。
@Configuration
//2.開(kāi)啟定時(shí)任務(wù)
@EnableScheduling
public class DynamicSchedule implements SchedulingConfigurer {
    @Mapper
    public interface CronMapper {
        @Select("select cron from crontab limit 1")
        public String getCron();
    }

    //注入mapper
    @Autowired
    @SuppressWarnings("all")
    CronMapper cronMapper;

    /**
     * 執(zhí)行定時(shí)任務(wù).
     */
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {

        //多線程支持,線程池創(chuàng)建10個(gè)線程
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(10);
        taskScheduler.initialize();
        taskRegistrar.setTaskScheduler(taskScheduler);
        

        //定時(shí)任務(wù)1: 每1s執(zhí)行一次(每執(zhí)行一次睡眠10s)
        taskRegistrar.addFixedRateTask(
                new Runnable() {
                    //添加任務(wù)內(nèi)容
                    @Override
                    public void run() {
                        System.err.println("執(zhí)行動(dòng)態(tài)定時(shí)任務(wù)1: " + LocalDateTime.now().toLocalTime());
                        //為了驗(yàn)證多線程不阻塞,這邊睡眠 10s
                        try {
                            Thread.sleep(1000 * 10);
                        } catch (InterruptedException ex) {
                            Thread.currentThread().interrupt();
                            System.err.println(ex);
                        }
                    }
                }
                , 1000);

        //定時(shí)任務(wù)2: 讀取mysql中的執(zhí)行cron公式
        TriggerTask triggrtTask = new TriggerTask(
                //1.添加任務(wù)內(nèi)容(Runnable),這邊使用"拉姆達(dá)表達(dá)式"
                () -> System.err.println("執(zhí)行動(dòng)態(tài)定時(shí)任務(wù)2: " + LocalDateTime.now().toLocalTime()),
                //2.設(shè)置執(zhí)行周期(Trigger)
                triggerContext -> {
                    //2.1 從數(shù)據(jù)庫(kù)獲取執(zhí)行周期
                    String cron = cronMapper.getCron();
                    //2.2 合法性校驗(yàn).
                    if (StringUtils.isEmpty(cron)) {
                        // Omitted Code ..
                    }
                    //2.3 返回執(zhí)行周期(Date)
                    return new CronTrigger(cron).nextExecutionTime(triggerContext);
                }
        );
        taskRegistrar.addTriggerTask(triggrtTask);
    }
}

案例代碼

https://github.com/MrCoderStack/SpringBootDemo/tree/master/sb-schedule

請(qǐng)關(guān)注我的訂閱號(hào)

訂閱號(hào).png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末递沪,一起剝皮案震驚了整個(gè)濱河市豺鼻,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌款慨,老刑警劉巖儒飒,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異檩奠,居然都是意外死亡桩了,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門埠戳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)井誉,“玉大人,你說(shuō)我怎么就攤上這事整胃】攀ィ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵屁使,是天一觀的道長(zhǎng)在岂。 經(jīng)常有香客問(wèn)我,道長(zhǎng)蛮寂,這世上最難降的妖魔是什么蔽午? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮酬蹋,結(jié)果婚禮上及老,老公的妹妹穿的比我還像新娘。我一直安慰自己除嘹,他們只是感情好写半,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布岸蜗。 她就那樣靜靜地躺著尉咕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪璃岳。 梳的紋絲不亂的頭發(fā)上年缎,一...
    開(kāi)封第一講書(shū)人閱讀 49,821評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音铃慷,去河邊找鬼单芜。 笑死,一個(gè)胖子當(dāng)著我的面吹牛犁柜,可吹牛的內(nèi)容都是我干的洲鸠。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼扒腕!你這毒婦竟也來(lái)了绢淀?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤瘾腰,失蹤者是張志新(化名)和其女友劉穎皆的,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蹋盆,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡费薄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了栖雾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片楞抡。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖岩灭,靈堂內(nèi)的尸體忽然破棺而出拌倍,到底是詐尸還是另有隱情,我是刑警寧澤噪径,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布柱恤,位于F島的核電站,受9級(jí)特大地震影響找爱,放射性物質(zhì)發(fā)生泄漏梗顺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一车摄、第九天 我趴在偏房一處隱蔽的房頂上張望寺谤。 院中可真熱鬧,春花似錦吮播、人聲如沸变屁。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)粟关。三九已至,卻和暖如春环戈,著一層夾襖步出監(jiān)牢的瞬間闷板,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工院塞, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留遮晚,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓拦止,卻偏偏與公主長(zhǎng)得像县遣,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349