Spring Shutdown Hook工作機(jī)制揭秘

前言

上篇文章潮太,我們討論了在Spring環(huán)境中正確關(guān)閉線程池的姿勢(shì)忠藤,拋出了問(wèn)題并給出了解決方案。本篇办铡,將接著討論解決方案背后的原理:Spring Shutdown Hook工作機(jī)制

源碼解析

源碼基于Spring Boot 2.1.0.RELEASE

注冊(cè)Spring Shutdown Hook的時(shí)機(jī)

首先要找到入口在哪双抽,即Spring Shutdown Hook是在哪注冊(cè)的百框,很容易猜想,應(yīng)該是在應(yīng)用啟動(dòng)過(guò)程中注冊(cè)的荠诬,找到如下源碼位置:org.springframework.boot.SpringApplication#refreshContext(Spring Boot)

image-20200722130735903

Spring Boot 在啟動(dòng)過(guò)程中琅翻,刷新Context之后,如果registerShutdownHook開(kāi)啟[默認(rèn)為true]柑贞,則會(huì)注冊(cè)一個(gè)Shutdown Hook

org.springframework.context.support.AbstractApplicationContext#registerShutdownHook (spring-context) 如下:

image-20200722131219294

這里有一點(diǎn)需要注意的是:提供Spring Shutdown Hook能力的是spring-context方椎,即spring framework本身的能力,但是將shutdown hook注冊(cè)進(jìn)JVM shutdown hook的行為钧嘶,卻是Spring Boot提供的棠众。也就是說(shuō),如果在純Spring環(huán)境下有决,需要自己手動(dòng)調(diào)用AbstractApplicationContext#registerShutdownHook注冊(cè)shutdown hook來(lái)支持Spring的優(yōu)雅關(guān)閉

畫(huà)外音:哪有什么歲月靜好闸拿,只不過(guò)有人(Spring Boot) 替你負(fù)重前行

Spring Shutdown Hook的邏輯

接下來(lái)看看Spring Shutdown Hook的具體實(shí)現(xiàn)邏輯,在org.springframework.context.support.AbstractApplicationContext#doClose

protected void doClose() {
    if (this.active.get() && this.closed.compareAndSet(false, true)) {
        // ...(省略)

        // 發(fā)布Spring 應(yīng)用上下文的關(guān)閉事件书幕,讓監(jiān)聽(tīng)器們有機(jī)會(huì)在應(yīng)用關(guān)閉之前做出一些響應(yīng)
        publishEvent(new ContextClosedEvent(this));

        // 執(zhí)行l(wèi)ifecycleProcessor的關(guān)閉方法新荤,讓Lifecycle們有機(jī)會(huì)在應(yīng)用關(guān)閉之前做出一些響應(yīng)
        this.lifecycleProcessor.onClose();
            
        // 銷毀IOC容器里所有單例Bean
        destroyBeans();

        // 關(guān)閉BeanFactory
        closeBeanFactory();

        // 勾子函數(shù),讓子類實(shí)現(xiàn)后做各自的資源清理台汇,比如ServletWebServerApplicationContext會(huì)實(shí)現(xiàn)該勾子函數(shù)關(guān)閉內(nèi)嵌的WebServer(Tomcat)
        onClose();

        this.active.set(false);
    }
}

Spring Shutdown Hook 一共做了5件事:

  1. 發(fā)布Spring應(yīng)用上下文的關(guān)閉事件苛骨,讓監(jiān)聽(tīng)器們有機(jī)會(huì)在應(yīng)用關(guān)閉之前做出一些響應(yīng)
  2. 執(zhí)行l(wèi)ifecycleProcessor的關(guān)閉方法,讓Lifecycle們有機(jī)會(huì)在應(yīng)用關(guān)閉之前做出一些響應(yīng)
  3. 銷毀IOC容器里所有單例Bean
  4. 關(guān)閉BeanFactory
  5. 執(zhí)行勾子函數(shù)苟呐,子類實(shí)現(xiàn)后做各自的資源清理痒芝,比如ServletWebServerApplicationContext會(huì)實(shí)現(xiàn)該勾子函數(shù)關(guān)閉內(nèi)嵌的WebServer(Tomcat)

不得不贊稱,站在上層的角度去理解牵素,該段邏輯非常清晰严衬,這樣的代碼鮮明地為我們展示了編碼原則:一個(gè)方法內(nèi)部,代碼盡量保持在同一抽象層次

其中第1笆呆、第2件事请琳,正是我們?cè)?a href="http://www.reibang.com/p/9be91ae48157" target="_blank">Spring環(huán)境中正確關(guān)閉線程池的姿勢(shì)利用到的解決方案:即在第3件事情開(kāi)始前粱挡,通過(guò)某些機(jī)制通知應(yīng)用程序?qū)κ录龀鲰憫?yīng)

第1件事與第2件事看起來(lái)很像,都是讓?xiě)?yīng)用關(guān)閉之前做出一些響應(yīng)单起,但是有使用場(chǎng)景的區(qū)別:

  • ContextClosedEvent是應(yīng)用級(jí)別的事件抱怔,因此對(duì)之做出的響應(yīng)更適用于全局性的行為
  • Lifecycle一般是Bean級(jí)別的通知劣坊,因此對(duì)之做出的響應(yīng)更適用于單個(gè)Bean的行為

接下來(lái)看第3件事:org.springframework.context.support.AbstractApplicationContext#destroyBeans

image-20200722205332016

這是個(gè)模板方法嘀倒,默認(rèn)情況下會(huì)銷毀IOC容器里的單例Bean,子類可以覆蓋它并添加一些額外的行為局冰,但是迄今為止测蘑,也沒(méi)有子類覆蓋該方法

org.springframework.beans.factory.support.DefaultListableBeanFactory#destroySingletons 方法如下:

image-20200722210024779

destroySingletons是個(gè)重載方法,核心邏輯在父類DefaultSingletonBeanRegistry中康二,調(diào)用完父類方法后就清理一下本類涉及的的一些本地緩存數(shù)據(jù)碳胳。我們接著看父類方法的邏輯:

// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroySingletons

public void destroySingletons() {
    // ...(省略)

    String[] disposableBeanNames;
    // disposableBeans 是個(gè)Map,Key為bean name沫勿,value為disposable bean實(shí)例
    // 即 <beanName, disposableInstance>
    // private final Map<String, Object> disposableBeans = new LinkedHashMap<>();
    synchronized (this.disposableBeans) {
        disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
    }
    // 依次銷毀 disposableInstances
    for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
        destroySingleton(disposableBeanNames[i]);
    }
    // 清除本類使用到的一些本地緩存
    this.containedBeanMap.clear();
    this.dependentBeanMap.clear();
    this.dependenciesForBeanMap.clear();
    // 清除單例緩存
    clearSingletonCache();
}

/**
 * Clear all cached singleton instances in this registry.
 * @since 4.3.15
 */
protected void clearSingletonCache() {
    synchronized (this.singletonObjects) {
        this.singletonObjects.clear();
        this.singletonFactories.clear();
        this.earlySingletonObjects.clear();
        this.registeredSingletons.clear();
        this.singletonsCurrentlyInDestruction = false;
    }
}

這個(gè)段方法的邏輯也很簡(jiǎn)單:

  1. 拿到所有的disposable beans(即實(shí)現(xiàn)了DisposableBean接口的bean)挨约,依次執(zhí)行destroySingleton方法,進(jìn)行資源回收
  2. 清除本類使用到的一些本地緩存
  3. 清除單例緩存

2产雹、3清除緩存的動(dòng)作很簡(jiǎn)單诫惭,就是調(diào)用Map#clear\Set#clear方法,將集合清空

這里有兩個(gè)緩存Map需要注意:dependentBeanMapdependenciesForBeanMap蔓挖,它們的定義如下:

/** Map between dependent bean names: bean name to Set of dependent bean names. */
private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);

/** Map between depending bean names: bean name to Set of bean names for the bean's dependencies. */
private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);
  • dependentBeanMap: Bean名稱和所有依賴于Bean的名稱的映射關(guān)系夕土,即:誰(shuí)依賴我
  • dependenciesForBeanMap: Bean名稱和Bean所依賴的所有名稱的映射關(guān)系,即:我依賴誰(shuí)
image-20200722220042221

命名上很像瘟判,不好理解怨绣。我舉個(gè)例子幫助理解:假設(shè)A依賴B(即A->B),A依賴C(即A->C)拷获,那么篮撑,

  • dependentBeanMap: <B, [A]>與<C, [A]>
  • dependenciesForBeanMap: <A, [B,C]>

此處請(qǐng)先將兩個(gè)Map映射關(guān)系記住,至于具體作用會(huì)在下文解釋

還有一個(gè)緩存Map: containedBeanMap匆瓜,定義如下:

/** Map between containing bean names: bean name to Set of bean names that the bean contains. */
private final Map<String, Set<String>> containedBeanMap = new ConcurrentHashMap<>(16);
  • containedBeanMap: Bean名稱和Bean所包含的所有Bean的名稱的映射關(guān)系赢笨,即:我包含誰(shuí)

這種"我包含誰(shuí)"的關(guān)系在主流的Annotation-Base的場(chǎng)景下已經(jīng)比較少出現(xiàn)了,要構(gòu)造這種映射關(guān)系陕壹,需要是XML-Base质欲,

假設(shè)Foo包含Bar,需要通過(guò)如下Spring的配置文件進(jìn)行配置糠馆,才會(huì)將這種"包含"關(guān)系放入containedBeanMap

public class Foo {

    private Bar bar;

    public Foo(Bar bar) {
        this.bar = bar;
    }
}

public class Bar {
}
// Spring 配置文件

<bean id="foo" class="com.example.demo.Foo">
     <constructor-arg>
         <bean class="com.example.demo.Bar"/>
     </constructor-arg>
 </bean>

從另一個(gè)角度看嘶伟,這也是一種依賴關(guān)系:Foo依賴Bar。由于使用該場(chǎng)景的人越來(lái)越少又碌,因此簡(jiǎn)單了解一下containedBeanMap的含義即可

接下來(lái)看org.springframework.beans.factory.support.DefaultListableBeanFactory#destroySingleton方法九昧,銷毀單個(gè)bean绊袋,注意跟上文提到的方法的區(qū)別,上文是destroySingletons

image-20200722220501030

同樣的铸鹰,destroySingleton也是個(gè)重載方法癌别,核心邏輯也是在父類DefaultSingletonBeanRegistry中,接著看:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroySingleton

image-20200723101422658
image-20200723101835006

接下來(lái)看org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroyBean方法:

// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroyBean

protected void destroyBean(String beanName, @Nullable DisposableBean bean) {
    // 1. 首先回收所有依賴"我"的beans
    Set<String> dependencies;
    synchronized (this.dependentBeanMap) {
        // Within full synchronization in order to guarantee a disconnected Set
        dependencies = this.dependentBeanMap.remove(beanName);
    }
    for (String dependentBeanName : dependencies) {
        // 遞歸調(diào)用DefaultSingletonBeanRegistry#destroySingleton
        destroySingleton(dependentBeanName);
    }

    // 2. 執(zhí)行DisposableBean的destroy方法蹋笼,進(jìn)行資源的回收
    bean.destroy();

    // 3. 回收"我"包含的所有beans
    Set<String> containedBeans;
    synchronized (this.containedBeanMap) {
        // Within full synchronization in order to guarantee a disconnected Set
        containedBeans = this.containedBeanMap.remove(beanName);
    }
    if (containedBeans != null) {
        for (String containedBeanName : containedBeans) {
            destroySingleton(containedBeanName);
        }
    }

    // 4. 解除"我"對(duì)其他Bean的依賴關(guān)系(dependentBeanMap)
    synchronized (this.dependentBeanMap) {
        for (Iterator<Map.Entry<String, Set<String>>> it = this.dependentBeanMap.entrySet().iterator(); it.hasNext();) {
            Map.Entry<String, Set<String>> entry = it.next();
            Set<String> dependenciesToClean = entry.getValue();
            dependenciesToClean.remove(beanName);
            if (dependenciesToClean.isEmpty()) {
                it.remove();
            }
        }
    }

    // 5. 解除"我"對(duì)其他Bean的依賴關(guān)系(dependenciesForBeanMap)
    this.dependenciesForBeanMap.remove(beanName);
}

這個(gè)方法一共做了5件事:

  1. 首先回收所有依賴"我"的beans
  2. 執(zhí)行DisposableBean的destroy方法展姐,進(jìn)行資源的回收
  3. 回收"我"包含的所有beans(containedBeanMap)
  4. 解除"我"對(duì)其他Bean的依賴關(guān)系(dependentBeanMap)
  5. 解除"我"對(duì)其他Bean的依賴關(guān)系(dependenciesForBeanMap)

為了便于理解,我舉個(gè)例子來(lái)分析這整個(gè)過(guò)程:

  • 假設(shè)A\B\C三個(gè)Bean剖毯,A\C都是普通的被Spring管理的Bean圾笨,B實(shí)現(xiàn)了DisposableBean接口,同樣被Spring管理
  • A依賴B逊谋,B依賴C
image-20200723131333874

上圖顯示初始狀態(tài)下的依賴關(guān)系擂达,以及三個(gè)Map各自的數(shù)據(jù)

此時(shí),要銷毀Bean B

  1. 首先回收所有依賴"我"的beans胶滋。通過(guò)dependentBeanMap找到"誰(shuí)依賴我"板鬓,遞歸執(zhí)行destroySingleton將依賴我的對(duì)象先回收掉,由圖可知A依賴了B究恤,因此先回收A俭令。該步驟執(zhí)行完之后,狀態(tài)如下示:

    image-20200723133055164
  2. 執(zhí)行DisposableBean的destroy方法丁溅,進(jìn)行資源的回收唤蔗。此處,要執(zhí)行B的destroy方法窟赏,完成資源的回收妓柜。一旦該方法執(zhí)行完畢,說(shuō)明B就已經(jīng)完成其使命涯穷,可以被回收掉

  3. 回收"我"包含的所有beans(containedBeanMap)棍掐。 由于此處不構(gòu)造containedBeanMap,為空拷况,此步驟跳過(guò)

  4. 解除"我"對(duì)其他Bean的依賴關(guān)系(dependentBeanMap)作煌。B被銷毀之后,已經(jīng)是一個(gè)"無(wú)用"的Bean赚瘦,但是它本身可能還引用著其它的Bean粟誓,這種引用關(guān)系仍然被保存在dependentBeanMap里,因此需要把這種引用關(guān)系斷掉起意,來(lái)保證邏輯語(yǔ)義的正確

  5. 解除"我"對(duì)其他Bean的依賴關(guān)系(dependenciesForBeanMap)鹰服。同第4步,引用關(guān)系仍然可能被保存在dependenciesForBeanMap里,因此需要把這種引用關(guān)系斷掉悲酷,來(lái)保證邏輯語(yǔ)義的正確

第4套菜、第5件事是從不同的Map中斷掉這種引用關(guān)系,因此本質(zhì)上是同一回事设易。經(jīng)過(guò)4逗柴、5之后,如圖示:

image-20200723133241196

注意:本文中提及的解除引用關(guān)系是指在Map上把依賴關(guān)系給刪除顿肺,而不是真正把對(duì)象間的引用給解除戏溺;

同理,銷毀(回收)Bean同樣指的是執(zhí)行destroy方法進(jìn)行了資源的回收挟冠,并不是真的把Bean給銷毀于购、回收

至此,Spring Shutdown Hook整個(gè)執(zhí)行過(guò)程我們已經(jīng)分析完畢知染,為了更好理解,接下來(lái)會(huì)用上篇文章的案例來(lái)分析Spring Shutdown Hook的執(zhí)行過(guò)程

案例解析

為了閱讀的連續(xù)性斑胜,此處再把案例闡述一遍

@Resource
private RedisTemplate<String, Integer> redisTemplate;

// org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor
@Resource
private ThreadPoolTaskExecutor executor;

@GetMapping("/incr")
public void incr() {
    executor.execute(() -> {
        // 依賴Redis進(jìn)行計(jì)數(shù)
        redisTemplate.opsForValue().increment("demo", 1L);
    });
}
  1. 使用Spring的ThreadPoolTaskExecutor控淡,用于異步任務(wù)的執(zhí)行
  2. 高并發(fā)請(qǐng)求/incr接口,每次請(qǐng)求該接口止潘,都會(huì)往線程池中添加一個(gè)任務(wù)掺炭,任務(wù)異步執(zhí)行的過(guò)程中依賴Redis

此時(shí),上游流量被切斷且應(yīng)用程序收到停機(jī)請(qǐng)求凭戴,在應(yīng)用啟動(dòng)之初注冊(cè)的Spring Shutdown Hook被激活

  1. 我們此處并不自定義上篇文章中提到的ContextClosedEvent涧狮,也不實(shí)現(xiàn)Lifecycle接口,因此發(fā)布Spring應(yīng)用上下文的關(guān)閉事件么夫、執(zhí)行l(wèi)ifecycleProcessor的關(guān)閉方法這兩個(gè)過(guò)程略過(guò)(如果有疑問(wèn):這樣做者冤,上篇文章提到的問(wèn)題不就出現(xiàn)了么?不就不能實(shí)現(xiàn)優(yōu)雅關(guān)閉線程池了档痪?別急涉枫,下面會(huì)有答案)
  2. 接著會(huì)銷毀所有實(shí)現(xiàn)了DisposableBean的Bean,很巧的是腐螟,ThreadPoolTaskExecutor與JedisConnectionFactory都實(shí)現(xiàn)了該接口愿汰,因此,依賴關(guān)系如圖所示:
image-20200723190949389

org.springframework.data.redis.connection.jedis.JedisConnectionFactory#destroy

image

org.springframework.scheduling.concurrent.ExecutorConfigurationSupport#destroy

image
image

按照我們上文的分析乐纸,ThreadPoolTaskExecutor衬廷、JedisConnectionFactory的destroy方法都會(huì)被執(zhí)行:

  • ThreadPoolTaskExecutor#destroy: 執(zhí)行線程池的優(yōu)雅關(guān)閉

  • JedisConnectionFactory#destroy: 關(guān)閉Jedis連接池,回收J(rèn)edis連接

那ThreadPoolTaskExecutor與JedisConnectionFactory執(zhí)行destroySingleton方法的先后不同汽绢,會(huì)導(dǎo)致結(jié)果的不同嗎吗跋?

  • 假設(shè)ThreadPoolTaskExecutor先執(zhí)行。此時(shí)XXXController會(huì)先被destroy庶喜,然后執(zhí)行ThreadPoolTaskExecutor#destroy小腊,由于支持優(yōu)雅關(guān)閉救鲤,任務(wù)理論上已經(jīng)執(zhí)行完畢,不再需要使用到RedisTemplate秩冈,因此這種情況OK
  • 假設(shè)JedisConnectionFactory行先執(zhí)行本缠。此時(shí)RedisTemplate會(huì)先要求被destroy,進(jìn)而引發(fā)XXXController與ThreadPoolTaskExecutor先行被destroy入问。此時(shí)就進(jìn)入了第一種情況丹锹,因此這種情況也是OK的

可以發(fā)現(xiàn),無(wú)論ThreadPoolTaskExecutor芬失、JedisConnectionFactory誰(shuí)先執(zhí)行destroySingleton楣黍,結(jié)果都是一樣的,都能使得線程池被優(yōu)雅關(guān)閉棱烂,根本原因就是Spring會(huì)找到引用鏈中的頭節(jié)點(diǎn)先行銷毀租漂,然后依著引用鏈依次銷毀Bean,使得最底層被依賴的對(duì)象最晚被銷毀

那么為什么上篇文章還會(huì)出現(xiàn)Spring環(huán)境下線程池未優(yōu)雅關(guān)閉的問(wèn)題颊糜?

那是因?yàn)榱ㄖ危芏啻a會(huì)直接使用自定義的JDK線程池,未被Spring管理衬鱼,也沒(méi)有找到合適的地方執(zhí)行shutdown(Now) + awaitTermination业筏。Spring Shutdown Hook執(zhí)行的時(shí)候,只能找到它管理的Bean進(jìn)行銷毀鸟赫,而我們使用的自定義的JDK線程池既不被Spring管理蒜胖,也沒(méi)有實(shí)現(xiàn)DisposableBean,Spring必然"看不見(jiàn)"該線程池的存在抛蚤,直接就把JedisConnectionFactory給回收了台谢,導(dǎo)致線程池里的任務(wù)獲取連接失敗

所以你瞧,使用ThreadPoolTaskExecutor還有這種福利霉颠,真是個(gè)意外的驚喜对碌,建議大家在Spring環(huán)境中都使用它代替直接使用JDK線程池類。 當(dāng)然蒿偎,如果有定制線程池的需要朽们,也可以自定義線程池類,然后再實(shí)現(xiàn)DisposableBean接口同時(shí)把相應(yīng)的destroy方法實(shí)現(xiàn)诉位,同時(shí)將實(shí)例交給Spring管理骑脱,效果也是等價(jià)的

那些非DisposableBean beans是如何銷毀的?

需要實(shí)現(xiàn)資源回收的Bean苍糠,需要關(guān)注Bean銷毀事件的Bean才需要實(shí)現(xiàn)DisposableBean接口叁丧。我們一般開(kāi)發(fā)過(guò)程中使用到的無(wú)狀態(tài)的Controller、Service,是不需要實(shí)現(xiàn)DisposableBean接口的--->我們何時(shí)關(guān)心過(guò)它們的銷毀呢拥娄?所以蚊锹,我們不關(guān)心,Spring當(dāng)然也不關(guān)心稚瘾,Spring Shutdown Hook 的第3件事"銷毀IOC容器里所有單例Bean"牡昆,只是執(zhí)行DisposableBean的destroy方法完成資源回收工作,以及清空各種依賴關(guān)系的Map和Singleon Cache摊欠,但對(duì)象本身并沒(méi)有真實(shí)被銷毀丢烘。因此對(duì)于非DisposableBean beans,在接下來(lái)應(yīng)用關(guān)閉之后就自動(dòng)死亡

總結(jié)

本篇文章主要分析了Spring Shutdown Hook的執(zhí)行流程些椒,從源碼層面可以看出作者的代碼功力非常強(qiáng)播瞳,考慮到了多種擴(kuò)展角度(擴(kuò)展點(diǎn)機(jī)制、模板方法免糕、勾子方法)赢乓,代碼從布局上也非常清晰,同一抽象語(yǔ)義的代碼在同一個(gè)方法里说墨,易于理解跟閱讀骏全,這是非常值得我們學(xué)習(xí)的地方(敲重點(diǎn):能從源碼中學(xué)到什么?)尼斧。其次從功能層面可以看到,做為一個(gè)成熟的框架陈醒,Spring考慮的非常全面:哪些Bean需要先銷毀哪些Bean需要后銷毀令漂,哪些Bean需要執(zhí)行資源回收方法身弊,哪些Bean不需要執(zhí)行資源回收方法都是有考量的,資源回收之后還清理各種本地緩存和映射關(guān)系烛恤,確保程序邏輯語(yǔ)義的正確。正是Spring考慮的多余耽,所以我們才可以心安理得考慮的少:哪有什么歲月靜好缚柏,只不過(guò)有人替你負(fù)重前行

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市碟贾,隨后出現(xiàn)的幾起案子币喧,更是在濱河造成了極大的恐慌,老刑警劉巖袱耽,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件杀餐,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡朱巨,警方通過(guò)查閱死者的電腦和手機(jī)史翘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人琼讽,你說(shuō)我怎么就攤上這事必峰。” “怎么了钻蹬?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵吼蚁,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我脉让,道長(zhǎng)桂敛,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任溅潜,我火速辦了婚禮术唬,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘滚澜。我一直安慰自己粗仓,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布设捐。 她就那樣靜靜地躺著借浊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪萝招。 梳的紋絲不亂的頭發(fā)上蚂斤,一...
    開(kāi)封第一講書(shū)人閱讀 51,624評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音槐沼,去河邊找鬼曙蒸。 笑死,一個(gè)胖子當(dāng)著我的面吹牛岗钩,可吹牛的內(nèi)容都是我干的纽窟。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼兼吓,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼臂港!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起视搏,我...
    開(kāi)封第一講書(shū)人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤审孽,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后凶朗,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體瓷胧,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年棚愤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了搓萧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片杂数。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖瘸洛,靈堂內(nèi)的尸體忽然破棺而出揍移,到底是詐尸還是另有隱情,我是刑警寧澤反肋,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布那伐,位于F島的核電站,受9級(jí)特大地震影響石蔗,放射性物質(zhì)發(fā)生泄漏罕邀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一养距、第九天 我趴在偏房一處隱蔽的房頂上張望诉探。 院中可真熱鬧,春花似錦棍厌、人聲如沸肾胯。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)敬肚。三九已至,卻和暖如春束析,著一層夾襖步出監(jiān)牢的瞬間艳馒,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工员寇, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鹰溜,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓丁恭,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親斋日。 傳聞我的和親對(duì)象是個(gè)殘疾皇子牲览,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

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