定時(shí)任務(wù)框架介紹

來自公眾號(hào):JavaGuide扰法,作者Guide哥

最近有朋友問到定時(shí)任務(wù)相關(guān)的問題店展。

于是萎羔,我簡(jiǎn)單寫了一篇文章總結(jié)一下定時(shí)任務(wù)的一些概念以及一些常見的定時(shí)任務(wù)技術(shù)選型抵恋。希望能對(duì)小伙伴們有幫助纲菌!

個(gè)人能力有限挠日。如果文章有任何需要補(bǔ)充/完善/修改的地方,歡迎在評(píng)論區(qū)指出驰后,共同進(jìn)步肆资!

為什么需要定時(shí)任務(wù)?

我們來看一下幾個(gè)非常常見的業(yè)務(wù)場(chǎng)景:

  1. 某系統(tǒng)凌晨要進(jìn)行數(shù)據(jù)備份灶芝。
  2. 某電商平臺(tái)郑原,用戶下單半個(gè)小時(shí)未支付的情況下需要自動(dòng)取消訂單。
  3. 某媒體聚合平臺(tái)夜涕,每 10 分鐘動(dòng)態(tài)抓取某某網(wǎng)站的數(shù)據(jù)為自己所用犯犁。
  4. 某博客平臺(tái),支持定時(shí)發(fā)送文章女器。
  5. 某基金平臺(tái)酸役,每晚定時(shí)計(jì)算用戶當(dāng)日收益情況并推送給用戶最新的數(shù)據(jù)。
  6. ......

這些場(chǎng)景往往都要求我們?cè)谀硞€(gè)特定的時(shí)間去做某個(gè)事情驾胆。

單機(jī)定時(shí)任務(wù)技術(shù)選型

Timer

java.util.Timer是 JDK 1.3 開始就已經(jīng)支持的一種定時(shí)任務(wù)的實(shí)現(xiàn)方式涣澡。

Timer 內(nèi)部使用一個(gè)叫做 TaskQueue 的類存放定時(shí)任務(wù),它是一個(gè)基于最小堆實(shí)現(xiàn)的優(yōu)先級(jí)隊(duì)列丧诺。TaskQueue 會(huì)按照任務(wù)距離下一次執(zhí)行時(shí)間的大小將任務(wù)排序入桂,保證在堆頂?shù)娜蝿?wù)最先執(zhí)行。這樣在需要執(zhí)行任務(wù)時(shí)驳阎,每次只需要取出堆頂?shù)娜蝿?wù)運(yùn)行即可抗愁!

Timer 使用起來比較簡(jiǎn)單,通過下面的方式我們就能創(chuàng)建一個(gè) 1s 之后執(zhí)行的定時(shí)任務(wù)呵晚。

// 示例代碼:
TimerTask task = new TimerTask() {
    public void run() {
        System.out.println("當(dāng)前時(shí)間: " + new Date() + "n" +
                "線程名稱: " + Thread.currentThread().getName());
    }
};
System.out.println("當(dāng)前時(shí)間: " + new Date() + "n" +
        "線程名稱: " + Thread.currentThread().getName());
Timer timer = new Timer("Timer");
long delay = 1000L;
timer.schedule(task, delay);

//輸出:
當(dāng)前時(shí)間: Fri May 28 15:18:47 CST 2021n線程名稱: main
當(dāng)前時(shí)間: Fri May 28 15:18:48 CST 2021n線程名稱: Timer` </pre>

不過其缺陷較多蜘腌,比如一個(gè) Timer 一個(gè)線程,這就導(dǎo)致 Timer 的任務(wù)的執(zhí)行只能串行執(zhí)行饵隙,一個(gè)任務(wù)執(zhí)行時(shí)間過長(zhǎng)的話會(huì)影響其他任務(wù)(性能非常差)撮珠,再比如發(fā)生異常時(shí)任務(wù)直接停止(Timer 只捕獲了 InterruptedException )。

Timer 類上的有一段注釋是這樣寫的:

* This class does not offer real-time guarantees: it schedules
 * tasks using the Object.wait(long) method.
 *Java 5.0 introduced the {@code java.util.concurrent} package and
 * one of the concurrency utilities therein is the {@link
 * java.util.concurrent.ScheduledThreadPoolExecutor
 * ScheduledThreadPoolExecutor} which is a thread pool for repeatedly
 * executing tasks at a given rate or delay.  It is effectively a more
 * versatile replacement for the {@code Timer}/{@code TimerTask}
 * combination, as it allows multiple service threads, accepts various
 * time units, and doesn't require subclassing {@code TimerTask} (just
 * implement {@code Runnable}).  Configuring {@code
 * ScheduledThreadPoolExecutor} with one thread makes it equivalent to
 * {@code Timer}.

大概的意思就是:ScheduledThreadPoolExecutor 支持多線程執(zhí)行定時(shí)任務(wù)并且功能更強(qiáng)大金矛,是 Timer 的替代品劫瞳。

ScheduledExecutorService

ScheduledExecutorService 是一個(gè)接口,有多個(gè)實(shí)現(xiàn)類绷柒,比較常用的是 ScheduledThreadPoolExecutor 志于。

image.png

ScheduledThreadPoolExecutor 本身就是一個(gè)線程池,支持任務(wù)并發(fā)執(zhí)行废睦。并且伺绽,其內(nèi)部使用 DelayQueue 作為任務(wù)隊(duì)列。

// 示例代碼:
TimerTask repeatedTask = new TimerTask() {
    @SneakyThrows
    public void run() {
        System.out.println("當(dāng)前時(shí)間: " + new Date() + "n" +
                "線程名稱: " + Thread.currentThread().getName());
    }
};
System.out.println("當(dāng)前時(shí)間: " + new Date() + "n" +
        "線程名稱: " + Thread.currentThread().getName());
ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
long delay  = 1000L;
long period = 1000L;
executor.scheduleAtFixedRate(repeatedTask, delay, period, TimeUnit.MILLISECONDS);
Thread.sleep(delay + period * 5);
executor.shutdown();
//輸出:
當(dāng)前時(shí)間: Fri May 28 15:40:46 CST 2021n線程名稱: main
當(dāng)前時(shí)間: Fri May 28 15:40:47 CST 2021n線程名稱: pool-1-thread-1
當(dāng)前時(shí)間: Fri May 28 15:40:48 CST 2021n線程名稱: pool-1-thread-1
當(dāng)前時(shí)間: Fri May 28 15:40:49 CST 2021n線程名稱: pool-1-thread-2
當(dāng)前時(shí)間: Fri May 28 15:40:50 CST 2021n線程名稱: pool-1-thread-2
當(dāng)前時(shí)間: Fri May 28 15:40:51 CST 2021n線程名稱: pool-1-thread-2
當(dāng)前時(shí)間: Fri May 28 15:40:52 CST 2021n線程名稱: pool-1-thread-2

不論是使用 Timer 還是 ScheduledExecutorService 都無法使用 Cron 表達(dá)式指定任務(wù)執(zhí)行的具體時(shí)間。

Spring Task

image.png

我們直接通過 Spring 提供的 @Scheduled 注解即可定義定時(shí)任務(wù)奈应,非常方便澜掩!

/**
 * cron:使用Cron表達(dá)式。 每分鐘的1杖挣,2秒運(yùn)行
 */
@Scheduled(cron = "1-2 * * * * ? ")
public void reportCurrentTimeWithCronExpression() {
  log.info("Cron Expression: The time is now {}", dateFormat.format(new Date()));
}

我在大學(xué)那會(huì)做的一個(gè) SSM 的企業(yè)級(jí)項(xiàng)目肩榕,就是用的 Spring Task 來做的定時(shí)任務(wù)。

并且惩妇,Spring Task 還是支持 Cron 表達(dá)式 的株汉。Cron 表達(dá)式主要用于定時(shí)作業(yè)(定時(shí)任務(wù))系統(tǒng)定義執(zhí)行時(shí)間或執(zhí)行頻率的表達(dá)式,非常厲害歌殃,你可以通過 Cron 表達(dá)式進(jìn)行設(shè)置定時(shí)任務(wù)每天或者每個(gè)月什么時(shí)候執(zhí)行等等操作乔妈。咱們要學(xué)習(xí)定時(shí)任務(wù)的話,Cron 表達(dá)式是一定是要重點(diǎn)關(guān)注的氓皱。推薦一個(gè)在線 Cron 表達(dá)式生成器:http://cron.qqe2.com/ 路召。

但是,Spring 自帶的定時(shí)調(diào)度只支持單機(jī)波材,并且提供的功能比較單一股淡。之前寫過一篇文章:《5 分鐘搞懂如何在 Spring Boot 中 Schedule Tasks》 ,不了解的小伙伴可以參考一下廷区。

Spring Task 底層是基于 JDK 的 ScheduledThreadPoolExecutor 線程池來實(shí)現(xiàn)的揣非。

優(yōu)缺點(diǎn)總結(jié):

  • 優(yōu)點(diǎn):簡(jiǎn)單,輕量躲因,支持 Cron 表達(dá)式
  • 缺點(diǎn) :功能單一

時(shí)間輪

Kafka、Dubbo忌傻、ZooKeeper大脉、Netty 、Caffeine 水孩、Akka 中都有對(duì)時(shí)間輪的實(shí)現(xiàn)镰矿。

時(shí)間輪簡(jiǎn)單來說就是一個(gè)環(huán)形的隊(duì)列(底層一般基于數(shù)組實(shí)現(xiàn)),隊(duì)列中的每一個(gè)元素(時(shí)間格)都可以存放一個(gè)定時(shí)任務(wù)列表俘种。

時(shí)間輪中的每個(gè)時(shí)間格代表了時(shí)間輪的基本時(shí)間跨度或者說時(shí)間精度秤标,加入時(shí)間一秒走一個(gè)時(shí)間格的話,那么這個(gè)時(shí)間輪的最高精度就是 1 秒(也就是說 3 s 和 3.9s 會(huì)在同一個(gè)時(shí)間格中)宙刘。

下圖是一個(gè)有 12 個(gè)時(shí)間格的時(shí)間輪苍姜,轉(zhuǎn)完一圈需要 12 s。當(dāng)我們需要新建一個(gè) 3s 后執(zhí)行的定時(shí)任務(wù)悬包,只需要將定時(shí)任務(wù)放在下標(biāo)為 3 的時(shí)間格中即可衙猪。當(dāng)我們需要新建一個(gè) 9s 后執(zhí)行的定時(shí)任務(wù),只需要將定時(shí)任務(wù)放在下標(biāo)為 9 的時(shí)間格中即可。

image.png

那當(dāng)我們需要?jiǎng)?chuàng)建一個(gè) 13s 后執(zhí)行的定時(shí)任務(wù)怎么辦呢垫释?這個(gè)時(shí)候可以引入一叫做 圈數(shù)/輪數(shù) 的概念丝格,也就是說這個(gè)任務(wù)還是放在下標(biāo)為 3 的時(shí)間格中, 不過它的圈數(shù)為 2 棵譬。

除了增加圈數(shù)這種方法之外显蝌,還有一種 多層次時(shí)間輪 (類似手表),Kafka 采用的就是這種方案订咸。

針對(duì)下圖的時(shí)間輪曼尊,我來舉一個(gè)例子便于大家理解。

圖片

上圖的時(shí)間輪算谈,第 1 層的時(shí)間精度為 1 涩禀,第 2 層的時(shí)間精度為 20 ,第 3 層的時(shí)間精度為 400然眼。假如我們需要添加一個(gè) 350s 后執(zhí)行的任務(wù) A 的話(當(dāng)前時(shí)間是 0s)艾船,這個(gè)任務(wù)會(huì)被放在第 2 層(因?yàn)榈诙拥臅r(shí)間跨度為 20*20=400>350)的第 350/20=17 個(gè)時(shí)間格子。

當(dāng)?shù)谝粚愚D(zhuǎn)了 17 圈之后高每,時(shí)間過去了 340s 屿岂,第 2 層的指針此時(shí)來到第 17 個(gè)時(shí)間格子。此時(shí)鲸匿,第 2 層第 17 個(gè)格子的任務(wù)會(huì)被移動(dòng)到第 1 層爷怀。

任務(wù) A 當(dāng)前是 10s 之后執(zhí)行,因此它會(huì)被移動(dòng)到第 1 層的第 10 個(gè)時(shí)間格子带欢。

這里在層與層之間的移動(dòng)也叫做時(shí)間輪的升降級(jí)运授。參考手表來理解就好!

圖片

時(shí)間輪比較適合任務(wù)數(shù)量比較多的定時(shí)任務(wù)場(chǎng)景乔煞,它的任務(wù)寫入和執(zhí)行的時(shí)間復(fù)雜度都是 0(1)吁朦。

分布式定時(shí)任務(wù)技術(shù)選型

上面提到的一些定時(shí)任務(wù)的解決方案都是在單機(jī)下執(zhí)行的,適用于比較簡(jiǎn)單的定時(shí)任務(wù)場(chǎng)景比如每天凌晨備份一次數(shù)據(jù)渡贾。

如果我們需要一些高級(jí)特性比如支持任務(wù)在分布式場(chǎng)景下的分片和高可用的話逗宜,我們就需要用到分布式任務(wù)調(diào)度框架了。

通常情況下空骚,一個(gè)定時(shí)任務(wù)的執(zhí)行往往涉及到下面這些角色:

  • 任務(wù) :首先肯定是要執(zhí)行的任務(wù)纺讲,這個(gè)任務(wù)就是具體的業(yè)務(wù)邏輯比如定時(shí)發(fā)送文章。
  • 調(diào)度器 :其次是調(diào)度中心囤屹,調(diào)度中心主要負(fù)責(zé)任務(wù)管理熬甚,會(huì)分配任務(wù)給執(zhí)行器。
  • 執(zhí)行器 :最后就是執(zhí)行器肋坚,執(zhí)行器接收調(diào)度器分派的任務(wù)并執(zhí)行则涯。

Quartz

圖片

一個(gè)很火的開源任務(wù)調(diào)度框架复局,完全由Java寫成。Quartz 可以說是 Java 定時(shí)任務(wù)領(lǐng)域的老大哥或者說參考標(biāo)準(zhǔn)粟判,其他的任務(wù)調(diào)度框架基本都是基于 Quartz 開發(fā)的亿昏,比如當(dāng)當(dāng)網(wǎng)的elastic-job就是基于quartz二次開發(fā)之后的分布式調(diào)度解決方案。

使用 Quartz 可以很方便地與 Spring 集成档礁,并且支持動(dòng)態(tài)添加任務(wù)和集群角钩。但是,Quartz 使用起來也比較麻煩呻澜,API 繁瑣递礼。

并且,Quzrtz 并沒有內(nèi)置 UI 管理控制臺(tái)羹幸,不過你可以使用 quartzui 這個(gè)開源項(xiàng)目來解決這個(gè)問題脊髓。

另外,Quartz 雖然也支持分布式任務(wù)栅受。但是将硝,它是在數(shù)據(jù)庫層面,通過數(shù)據(jù)庫的鎖機(jī)制做的屏镊,有非常多的弊端比如系統(tǒng)侵入性嚴(yán)重依疼、節(jié)點(diǎn)負(fù)載不均衡。有點(diǎn)偽分布式的味道而芥。

優(yōu)缺點(diǎn)總結(jié):

  • 優(yōu)點(diǎn):可以與 Spring 集成律罢,并且支持動(dòng)態(tài)添加任務(wù)和集群。
  • 缺點(diǎn) :分布式支持不友好棍丐,沒有內(nèi)置 UI 管理控制臺(tái)误辑、使用麻煩(相比于其他同類型框架來說)

Elastic-Job

image.png

Elastic-Job 是當(dāng)當(dāng)網(wǎng)開源的一個(gè)基于QuartzZooKeeper的分布式調(diào)度解決方案,由兩個(gè)相互獨(dú)立的子項(xiàng)目 Elastic-Job-LiteElastic-Job-Cloud 組成歌逢,一般我們只要使用 Elastic-Job-Lite 就好巾钉。

ElasticJob 支持任務(wù)在分布式場(chǎng)景下的分片和高可用、任務(wù)可視化管理等功能趋翻。

image.png

ElasticJob-Lite 的架構(gòu)設(shè)計(jì)如下圖所示:

image.png

從上圖可以看出,Elastic-Job 沒有調(diào)度中心這一概念盒蟆,而是使用 ZooKeeper 作為注冊(cè)中心踏烙,注冊(cè)中心負(fù)責(zé)協(xié)調(diào)分配任務(wù)到不同的節(jié)點(diǎn)上。

Elastic-Job 中的定時(shí)調(diào)度都是由執(zhí)行器自行觸發(fā)历等,這種設(shè)計(jì)也被稱為去中心化設(shè)計(jì)(調(diào)度和處理都是執(zhí)行器單獨(dú)完成)讨惩。

@Component
@ElasticJobConf(name = "dayJob", cron = "0/10 * * * * ?", shardingTotalCount = 2,
        shardingItemParameters = "0=AAAA,1=BBBB", description = "簡(jiǎn)單任務(wù)", failover = true)
public class TestJob implements SimpleJob {
    @Override
    public void execute(ShardingContext shardingContext) {
        log.info("TestJob任務(wù)名:【{}】, 片數(shù):【{}】, param=【{}】", shardingContext.getJobName(), shardingContext.getShardingTotalCount(),
                shardingContext.getShardingParameter());
    }
}

相關(guān)地址:

優(yōu)缺點(diǎn)總結(jié):

  • 優(yōu)點(diǎn) :可以與 Spring 集成荐捻、支持分布式黍少、支持集群、性能不錯(cuò)
  • 缺點(diǎn) :依賴了額外的中間件比如 Zookeeper(復(fù)雜度增加处面,可靠性降低厂置、維護(hù)成本變高)

XXL-JOB

image.png

XXL-JOB 于 2015 年開源,是一款優(yōu)秀的輕量級(jí)分布式任務(wù)調(diào)度框架魂角,支持任務(wù)可視化管理昵济、彈性擴(kuò)容縮容、任務(wù)失敗重試和告警野揪、任務(wù)分片等功能访忿,

image.png

根據(jù) XXL-JOB 官網(wǎng)介紹,其解決了很多 Quartz 的不足斯稳。

image.png

XXL-JOB 的架構(gòu)設(shè)計(jì)如下圖所示:

image.png

從上圖可以看出海铆,XXL-JOB調(diào)度中心執(zhí)行器 兩大部分組成。調(diào)度中心主要負(fù)責(zé)任務(wù)管理挣惰、執(zhí)行器管理以及日志管理卧斟。執(zhí)行器主要是接收調(diào)度信號(hào)并處理。另外通熄,調(diào)度中心進(jìn)行任務(wù)調(diào)度時(shí)唆涝,是通過自研 RPC 來實(shí)現(xiàn)的。

不同于 Elastic-Job 的去中心化設(shè)計(jì)唇辨, XXL-JOB 的這種設(shè)計(jì)也被稱為中心化設(shè)計(jì)(調(diào)度中心調(diào)度多個(gè)執(zhí)行器執(zhí)行任務(wù))廊酣。

Quzrtz 類似 XXL-JOB 也是基于數(shù)據(jù)庫鎖調(diào)度任務(wù),存在性能瓶頸赏枚。不過亡驰,一般在任務(wù)量不是特別大的情況下,沒有什么影響的饿幅,可以滿足絕大部分公司的要求凡辱。

不要被 XXL-JOB 的架構(gòu)圖給嚇著了,實(shí)際上栗恩,我們要用 XXL-JOB 的話透乾,只需要重寫 IJobHandler 自定義任務(wù)執(zhí)行邏輯就可以了,非常易用磕秤!

@JobHandler(value="myApiJobHandler")
@Component
public class MyApiJobHandler extends IJobHandler {

    @Override
    public ReturnT<String> execute(String param) throws Exception {
        //......
        return ReturnT.SUCCESS;
    }
}

還可以直接基于注解定義任務(wù)乳乌。

@XxlJob("myAnnotationJobHandler")
public ReturnT<String> myAnnotationJobHandler(String param) throws Exception {
  //......
  return ReturnT.SUCCESS;
}
image.png

相關(guān)地址:

優(yōu)缺點(diǎn)總結(jié):

  • 優(yōu)點(diǎn):開箱即用(學(xué)習(xí)成本比較低)汉操、與 Spring 集成、支持分布式蒙兰、支持集群磷瘤、內(nèi)置了 UI 管理控制臺(tái)芒篷。
  • 缺點(diǎn):不支持動(dòng)態(tài)添加任務(wù)(如果一定想要?jiǎng)討B(tài)創(chuàng)建任務(wù)也是支持的,參見:xxl-job issue277)采缚。

PowerJob

image.png

非常值得關(guān)注的一個(gè)分布式任務(wù)調(diào)度框架针炉,分布式任務(wù)調(diào)度領(lǐng)域的新星。目前仰担,已經(jīng)有很多公司接入比如 OPPO糊识、京東、中通摔蓝、思科赂苗。

這個(gè)框架的誕生也挺有意思的,PowerJob 的作者當(dāng)時(shí)在阿里巴巴實(shí)習(xí)過贮尉,阿里巴巴那會(huì)使用的是內(nèi)部自研的 SchedulerX(阿里云付費(fèi)產(chǎn)品)拌滋。實(shí)習(xí)期滿之后,PowerJob 的作者離開了阿里巴巴猜谚。想著說自研一個(gè) SchedulerX败砂,防止哪天 SchedulerX 滿足不了需求,于是 PowerJob 就誕生了魏铅。

更多關(guān)于 PowerJob 的故事昌犹,小伙伴們可以去看看 PowerJob 作者的視頻 《我和我的任務(wù)調(diào)度中間件》。簡(jiǎn)單點(diǎn)概括就是:“游戲沒啥意思了览芳,我要扛起了新一代分布式任務(wù)調(diào)度與計(jì)算框架的大旗斜姥!”。

由于 SchedulerX 屬于人民幣產(chǎn)品沧竟,我這里就不過多介紹铸敏。PowerJob 官方也對(duì)比過其和 QuartZ、XXL-JOB 以及 SchedulerX悟泵。

image.png

總結(jié)

這篇文章中杈笔,我主要介紹了:

  • 定時(shí)任務(wù)的相關(guān)概念 :為什么需要定時(shí)任務(wù)、定時(shí)任務(wù)中的核心角色糕非、分布式定時(shí)任務(wù)蒙具。
  • 定時(shí)任務(wù)的技術(shù)選型 :XXL-JOB 2015 年推出,已經(jīng)經(jīng)過了很多年的考驗(yàn)朽肥。XXL-JOB 輕量級(jí)禁筏,并且使用起來非常簡(jiǎn)單。雖然存在性能瓶頸鞠呈,但是融师,在絕大多數(shù)情況下右钾,對(duì)于企業(yè)的基本需求來說是沒有影響的蚁吝。PowerJob 屬于分布式任務(wù)調(diào)度領(lǐng)域里的新星旱爆,其穩(wěn)定性還有待繼續(xù)考察。ElasticJob 由于在架構(gòu)設(shè)計(jì)上是基于 Zookeeper 窘茁,而 XXL-JOB 是基于數(shù)據(jù)庫怀伦,性能方面的話,ElasticJob 略勝一籌山林。

這篇文章并沒有介紹到實(shí)際使用房待,但是,并不代表實(shí)際使用不重要驼抹。我在寫這篇文章之前桑孩,已經(jīng)動(dòng)手寫過相應(yīng)的 Demo。像 Quartz框冀,我在大學(xué)那會(huì)就用過流椒。不過,當(dāng)時(shí)用的是 Spring 明也。為了能夠更好地體驗(yàn)宣虾,我自己又在 Spring Boot 上實(shí)際體驗(yàn)了一下。如果你并沒有實(shí)際使用某個(gè)框架温数,就直接說它并不好用的話绣硝,是站不住腳的。

最后撑刺,這篇文章要感謝艿艿的幫助鹉胖,寫這篇文章的時(shí)候向艿艿詢問過一些問題。推薦一篇艿艿寫的偏實(shí)戰(zhàn)類型的硬核文章:《Spring Job猜煮?Quartz次员?XXL-Job?年輕人才做選擇王带,艿艿全莽~》 淑蔚。

--- EOF ---

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市愕撰,隨后出現(xiàn)的幾起案子刹衫,更是在濱河造成了極大的恐慌,老刑警劉巖搞挣,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件带迟,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡囱桨,警方通過查閱死者的電腦和手機(jī)仓犬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來舍肠,“玉大人搀继,你說我怎么就攤上這事窘面。” “怎么了叽躯?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵财边,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我点骑,道長(zhǎng)酣难,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任黑滴,我火速辦了婚禮憨募,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘袁辈。我一直安慰自己馋嗜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布吵瞻。 她就那樣靜靜地躺著葛菇,像睡著了一般。 火紅的嫁衣襯著肌膚如雪橡羞。 梳的紋絲不亂的頭發(fā)上眯停,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音卿泽,去河邊找鬼莺债。 笑死,一個(gè)胖子當(dāng)著我的面吹牛签夭,可吹牛的內(nèi)容都是我干的齐邦。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼第租,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼措拇!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起慎宾,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤丐吓,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后趟据,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體券犁,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年汹碱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了粘衬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖稚新,靈堂內(nèi)的尸體忽然破棺而出泼舱,到底是詐尸還是另有隱情,我是刑警寧澤枷莉,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站尺迂,受9級(jí)特大地震影響笤妙,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜噪裕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一蹲盘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧膳音,春花似錦召衔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至兵志,卻和暖如春醇蝴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背想罕。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來泰國打工悠栓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人按价。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓惭适,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親楼镐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子癞志,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容