談?wù)劮植际芥i的設(shè)計(jì)與實(shí)現(xiàn)

定義

正常的單機(jī)狀態(tài)的精盅,共享資源都是在通過(guò)一個(gè)數(shù)據(jù)庫(kù)下的宏胯,可以在單機(jī)中進(jìn)行加鎖,保證共享數(shù)據(jù)的線程安全肢专,分布式環(huán)境下巾乳,因?yàn)椴皇窃谕惶摂M機(jī)進(jìn)程的,全局的某些唯一資源需要進(jìn)行鎖定鸟召,這時(shí)候就需要分布式鎖胆绊。

現(xiàn)如今都是分布式系統(tǒng),需要部署多臺(tái)服務(wù)器欧募,進(jìn)行負(fù)載均衡压状。如圖:


image

上圖可以看到,變量A存在JVM1、JVM2种冬、JVM3三個(gè)JVM內(nèi)存中(這個(gè)變量A主要體現(xiàn)是在一個(gè)類中的一個(gè)成員變量镣丑,是一個(gè)有狀態(tài)的對(duì)象,例如:UserController控制器中的一個(gè)整形類型的成員變量)娱两,如果不加任何控制的話莺匠,變量A同時(shí)都會(huì)在JVM分配一塊內(nèi)存,三個(gè)請(qǐng)求發(fā)過(guò)來(lái)同時(shí)對(duì)這個(gè)變量操作十兢,顯然結(jié)果是不對(duì)的趣竣!即使不是同時(shí)發(fā)過(guò)來(lái),三個(gè)請(qǐng)求分別操作三個(gè)不同JVM內(nèi)存區(qū)域的數(shù)據(jù)旱物,變量A之間不存在共享遥缕,也不具有可見性,處理的結(jié)果也是不對(duì)的宵呛!

如果我們業(yè)務(wù)中確實(shí)存在這個(gè)場(chǎng)景的話单匣,我們就需要一種方法解決這個(gè)問(wèn)題!
為了保證一個(gè)方法或?qū)傩栽诟卟l(fā)情況下的同一時(shí)間只能被同一個(gè)線程執(zhí)行宝穗,在傳統(tǒng)單體應(yīng)用單機(jī)部署的情況下户秤,可以使用Java并發(fā)處理相關(guān)的API(如ReentrantLock或Synchronized)進(jìn)行互斥控制。在單機(jī)環(huán)境中逮矛,Java中提供了很多并發(fā)處理相關(guān)的API虎忌。但是,隨著業(yè)務(wù)發(fā)展的需要橱鹏,原單體單機(jī)部署的系統(tǒng)被演化成分布式集群系統(tǒng)后膜蠢,由于分布式系統(tǒng)多線程、多進(jìn)程并且分布在不同機(jī)器上莉兰,這將使原單機(jī)部署情況下的并發(fā)控制鎖策略失效挑围,單純的Java API并不能提供分布式鎖的能力。

為了解決這個(gè)問(wèn)題就需要一種跨JVM的互斥機(jī)制來(lái)控制共享資源的訪問(wèn)糖荒,這就是分布式鎖要解決的問(wèn)題杉辙!

滿足條件

1、在分布式系統(tǒng)環(huán)境下捶朵,一個(gè)方法在同一時(shí)間只能被一個(gè)機(jī)器的一個(gè)線程執(zhí)行蜘矢;
2、高可用的獲取鎖與釋放鎖综看;
3品腹、高性能的獲取鎖與釋放鎖;
4红碑、具備可重入特性舞吭;
5泡垃、具備鎖失效機(jī)制,防止死鎖羡鸥;
6蔑穴、具備非阻塞鎖特性,即沒有獲取到鎖將直接返回獲取鎖失敗惧浴。

具體例子

  1. 交易訂單鎖定

    需要處理防止重復(fù)下單存和。

    解決業(yè)務(wù)層面的冪等問(wèn)題

  2. MQ消息消費(fèi)的冪等性

    發(fā)送的消息重復(fù)。

    消息消費(fèi)端去重衷旅。

    比如手機(jī)提現(xiàn)捐腿,不能重復(fù)提現(xiàn)。

  3. 在用戶對(duì)商品下單后芜茵,訂單狀態(tài)為待支付叙量,在某一時(shí)刻用戶正在對(duì)該訂單做支付操作倡蝙,商家正在進(jìn)行改價(jià)操作九串??寺鸥? 這時(shí)候猪钮,該狀態(tài)需要做串行處理,避免出現(xiàn)數(shù)據(jù)錯(cuò)亂胆建。

解決方式

1.基于數(shù)據(jù)庫(kù)實(shí)現(xiàn)分布式鎖烤低;

創(chuàng)建一個(gè)表:

DROP TABLE IF EXISTS `method_lock`;
CREATE TABLE `method_lock` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `method_name` varchar(64) NOT NULL COMMENT '鎖定的方法名',
  `desc` varchar(255) NOT NULL COMMENT '備注信息',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uidx_method_name` (`method_name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='鎖定中的方法';

image

(2)想要執(zhí)行某個(gè)方法,就使用這個(gè)方法名向表中插入數(shù)據(jù)

    INSERT INTO method_lock (method_name, desc) VALUES ('methodName', '測(cè)試的methodName');

因?yàn)槲覀儗?duì)method_name做了唯一性約束笆载,這里如果有多個(gè)請(qǐng)求同時(shí)提交到數(shù)據(jù)庫(kù)的話扑馁,數(shù)據(jù)庫(kù)會(huì)保證只有一個(gè)操作可以成功,那么我們就可以認(rèn)為操作成功的那個(gè)線程獲得了該方法的鎖凉驻,可以執(zhí)行方法體內(nèi)容腻要。

(3)成功插入則獲取鎖,執(zhí)行完成后刪除對(duì)應(yīng)的行數(shù)據(jù)釋放鎖:

    delete from method_lock where method_name ='methodName';

注意:這只是使用基于數(shù)據(jù)庫(kù)的一種方法涝登,使用數(shù)據(jù)庫(kù)實(shí)現(xiàn)分布式鎖還有很多其他的玩法雄家!

使用基于數(shù)據(jù)庫(kù)的這種實(shí)現(xiàn)方式很簡(jiǎn)單,但是對(duì)于分布式鎖應(yīng)該具備的條件來(lái)說(shuō)胀滚,它有一些問(wèn)題需要解決及優(yōu)化:

1趟济、因?yàn)槭腔跀?shù)據(jù)庫(kù)實(shí)現(xiàn)的,數(shù)據(jù)庫(kù)的可用性和性能將直接影響分布式鎖的可用性及性能咽笼,所以顷编,數(shù)據(jù)庫(kù)需要雙機(jī)部署、數(shù)據(jù)同步剑刑、主備切換勾效;

2、不具備可重入的特性,因?yàn)橥粋€(gè)線程在釋放鎖之前层宫,行數(shù)據(jù)一直存在杨伙,無(wú)法再次成功插入數(shù)據(jù),所以萌腿,需要在表中新增一列限匣,用于記錄當(dāng)前獲取到鎖的機(jī)器和線程信息,在再次獲取鎖的時(shí)候毁菱,先查詢表中機(jī)器和線程信息是否和當(dāng)前機(jī)器和線程相同米死,若相同則直接獲取鎖;

3贮庞、沒有鎖失效機(jī)制峦筒,因?yàn)橛锌赡艹霈F(xiàn)成功插入數(shù)據(jù)后,服務(wù)器宕機(jī)了窗慎,對(duì)應(yīng)的數(shù)據(jù)沒有被刪除物喷,當(dāng)服務(wù)恢復(fù)后一直獲取不到鎖,所以遮斥,需要在表中新增一列峦失,用于記錄失效時(shí)間,并且需要有定時(shí)任務(wù)清除這些失效的數(shù)據(jù)术吗;

4尉辑、不具備阻塞鎖特性,獲取不到鎖直接返回失敗较屿,所以需要優(yōu)化獲取邏輯隧魄,循環(huán)多次去獲取。

2.基于redis做分布式鎖

為什么隘蝎?

redis本身是單線程购啄,唯一線程串行處理。

實(shí)現(xiàn)方式

Redis Setnx命令末贾,在指定的key不存在時(shí)闸溃,為key設(shè)置指定的值.多個(gè)線程并發(fā)的請(qǐng)求去設(shè)置時(shí),只有一個(gè)可以設(shè)置成功拱撵。其他的會(huì)返回失敗辉川。一般設(shè)置五秒鐘。

//設(shè)置成功拴测,返回1乓旗,設(shè)置失敗,返回 0
Setnx KEY_NAME VALUE Expire Time 

分析存在問(wèn)題:

  1. 單點(diǎn)問(wèn)題

    單機(jī)模式集索,設(shè)置T1 T2兩個(gè)線程.如果T1剛設(shè)置成功屿愚,單機(jī)掛了汇跨,重啟,請(qǐng)求丟了妆距,T2去請(qǐng)求再去拿鎖穷遂,會(huì)獲取不到,這時(shí)候會(huì)獲取不到key娱据。(因?yàn)榉植际芥i一般不考慮做持久化蚪黑,所以這里不考慮持久化。)

    主從模式中剩,主從數(shù)據(jù)異步忌穿,會(huì)存在鎖失效的問(wèn)題,主服務(wù)器還未同步到從服務(wù)器结啼,這時(shí)候主掛了掠剑,從服務(wù)器獲取不到鎖。

  2. 鎖時(shí)間不可以控制郊愧,無(wú)法續(xù)租期

Redis本身建議:使用RedLock算法來(lái)保證朴译,但是問(wèn)題是需要至少三個(gè)Redis主從實(shí)例來(lái)完成,維護(hù)成本很高糕珊。這個(gè)等同于自己簡(jiǎn)單實(shí)現(xiàn)的一致性協(xié)議动分,細(xì)節(jié)繁瑣毅糟,且容易出錯(cuò)红选。

是否能使用

業(yè)務(wù)場(chǎng)景來(lái)規(guī)定,在設(shè)計(jì)交易時(shí)姆另,只能發(fā)一次交易請(qǐng)求喇肋,這時(shí)候不適合。如果是MQ消息消費(fèi)場(chǎng)景迹辐,依次獲取不到蝶防,可以在發(fā)送一次消息保證能被消費(fèi)。

CAP問(wèn)題

分布式鎖明吩,主要選擇滿足C P模型间学,而redis實(shí)現(xiàn)的主要滿足AP模型。不太ok印荔。

代碼實(shí)現(xiàn)

使用命令介紹:

(1)SETNX

    SETNX key val:當(dāng)且僅當(dāng)key不存在時(shí)低葫,set一個(gè)key為val的字符串,返回1仍律;若key存在,則什么都不做,返回0姥宝。

(2)expire

    expire key timeout:為key設(shè)置一個(gè)超時(shí)時(shí)間掏婶,單位為second窒盐,超過(guò)這個(gè)時(shí)間鎖會(huì)自動(dòng)釋放,避免死鎖钢拧。

1
(3)delete

    delete key:刪除key

實(shí)現(xiàn)思想:

(1)獲取鎖的時(shí)候蟹漓,使用setnx加鎖,并使用expire命令為鎖添加一個(gè)超時(shí)時(shí)間源内,超過(guò)該時(shí)間則自動(dòng)釋放鎖牧牢,鎖的value值為一個(gè)隨機(jī)生成的UUID,通過(guò)此在釋放鎖的時(shí)候進(jìn)行判斷姿锭。

(2)獲取鎖的時(shí)候還設(shè)置一個(gè)獲取的超時(shí)時(shí)間塔鳍,若超過(guò)這個(gè)時(shí)間則放棄獲取鎖。

(3)釋放鎖的時(shí)候呻此,通過(guò)UUID判斷是不是該鎖轮纫,若是該鎖,則執(zhí)行delete進(jìn)行鎖釋放焚鲜。

/**
 * 分布式鎖的簡(jiǎn)單實(shí)現(xiàn)代碼
 * Created by liuyang on 2017/4/20.
 */
public class DistributedLock {

    private final JedisPool jedisPool;

    public DistributedLock(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

    /**
     * 加鎖
     * @param lockName       鎖的key
     * @param acquireTimeout 獲取超時(shí)時(shí)間
     * @param timeout        鎖的超時(shí)時(shí)間
     * @return 鎖標(biāo)識(shí)
     */
    public String lockWithTimeout(String lockName, long acquireTimeout, long timeout) {
        Jedis conn = null;
        String retIdentifier = null;
        try {
            // 獲取連接
            conn = jedisPool.getResource();
            // 隨機(jī)生成一個(gè)value
            String identifier = UUID.randomUUID().toString();
            // 鎖名掌唾,即key值
            String lockKey = "lock:" + lockName;
            // 超時(shí)時(shí)間,上鎖后超過(guò)此時(shí)間則自動(dòng)釋放鎖
            int lockExpire = (int) (timeout / 1000);

            // 獲取鎖的超時(shí)時(shí)間忿磅,超過(guò)這個(gè)時(shí)間則放棄獲取鎖
            long end = System.currentTimeMillis() + acquireTimeout;
            while (System.currentTimeMillis() < end) {
                if (conn.setnx(lockKey, identifier) == 1) {
                    conn.expire(lockKey, lockExpire);
                    // 返回value值糯彬,用于釋放鎖時(shí)間確認(rèn)
                    retIdentifier = identifier;
                    return retIdentifier;
                }
                // 返回-1代表key沒有設(shè)置超時(shí)時(shí)間,為key設(shè)置一個(gè)超時(shí)時(shí)間
                if (conn.ttl(lockKey) == -1) {
                    conn.expire(lockKey, lockExpire);
                }

                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        } catch (JedisException e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.close();
            }
        }
        return retIdentifier;
    }

    /**
     * 釋放鎖
     * @param lockName   鎖的key
     * @param identifier 釋放鎖的標(biāo)識(shí)
     * @return
     */
    public boolean releaseLock(String lockName, String identifier) {
        Jedis conn = null;
        String lockKey = "lock:" + lockName;
        boolean retFlag = false;
        try {
            conn = jedisPool.getResource();
            while (true) {
                // 監(jiān)視lock葱她,準(zhǔn)備開始事務(wù)
                conn.watch(lockKey);
                // 通過(guò)前面返回的value值判斷是不是該鎖撩扒,若是該鎖,則刪除吨些,釋放鎖
                if (identifier.equals(conn.get(lockKey))) {
                    Transaction transaction = conn.multi();
                    transaction.del(lockKey);
                    List<Object> results = transaction.exec();
                    if (results == null) {
                        continue;
                    }
                    retFlag = true;
                }
                conn.unwatch();
                break;
            }
        } catch (JedisException e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.close();
            }
        }
        return retFlag;
    }
}

測(cè)試剛才實(shí)現(xiàn)的分布式鎖

例子中使用50個(gè)線程模擬秒殺一個(gè)商品搓谆,使用–運(yùn)算符來(lái)實(shí)現(xiàn)商品減少,從結(jié)果有序性就可以看出是否為加鎖狀態(tài)豪墅。

模擬秒殺服務(wù)泉手,在其中配置了jedis線程池,在初始化的時(shí)候傳給分布式鎖偶器,供其使用.

/**
 * Created by liuyang on 2017/4/20.
 */
public class Service {

    private static JedisPool pool = null;

    private DistributedLock lock = new DistributedLock(pool);

    int n = 500;

    static {
        JedisPoolConfig config = new JedisPoolConfig();
        // 設(shè)置最大連接數(shù)
        config.setMaxTotal(200);
        // 設(shè)置最大空閑數(shù)
        config.setMaxIdle(8);
        // 設(shè)置最大等待時(shí)間
        config.setMaxWaitMillis(1000 * 100);
        // 在borrow一個(gè)jedis實(shí)例時(shí)斩萌,是否需要驗(yàn)證,若為true屏轰,則所有jedis實(shí)例均是可用的
        config.setTestOnBorrow(true);
        pool = new JedisPool(config, "127.0.0.1", 6379, 3000);
    }

    public void seckill() {
        // 返回鎖的value值颊郎,供釋放鎖時(shí)候進(jìn)行判斷
        String identifier = lock.lockWithTimeout("resource", 5000, 1000);
        System.out.println(Thread.currentThread().getName() + "獲得了鎖");
        System.out.println(--n);
        lock.releaseLock("resource", identifier);
    }
}

模擬線程進(jìn)行秒殺服務(wù)

public class ThreadA extends Thread {
    private Service service;

    public ThreadA(Service service) {
        this.service = service;
    }

    @Override
    public void run() {
        service.seckill();
    }
}
//這里推薦使用 countDownLatch
public class Test {
    public static void main(String[] args) {
        Service service = new Service();
        for (int i = 0; i < 50; i++) {
            ThreadA threadA = new ThreadA(service);
            threadA.start();
        }
    }
}

3.基于zookeeper

ZooKeeper是一個(gè)為分布式應(yīng)用提供一致性服務(wù)的開源組件,它內(nèi)部是一個(gè)分層的文件系統(tǒng)目錄樹結(jié)構(gòu)亭枷,規(guī)定同一個(gè)目錄下只能有一個(gè)唯一文件名袭艺。基于ZooKeeper實(shí)現(xiàn)分布式鎖的步驟如下:

(1)創(chuàng)建一個(gè)目錄mylock叨粘;
(2)線程A想獲取鎖就在mylock目錄下創(chuàng)建臨時(shí)順序節(jié)點(diǎn)猾编;
(3)獲取mylock目錄下所有的子節(jié)點(diǎn)瘤睹,然后獲取比自己小的兄弟節(jié)點(diǎn),如果不存在答倡,則說(shuō)明當(dāng)前線程順序號(hào)最小轰传,獲得鎖;
(4)線程B獲取所有節(jié)點(diǎn)瘪撇,判斷自己不是最小節(jié)點(diǎn)获茬,設(shè)置監(jiān)聽比自己次小的節(jié)點(diǎn);
(5)線程A處理完倔既,刪除自己的節(jié)點(diǎn)恕曲,線程B監(jiān)聽到變更事件,判斷自己是不是最小的節(jié)點(diǎn)渤涌,如果是則獲得鎖佩谣。

這里推薦一個(gè)Apache的開源庫(kù)Curator,它是一個(gè)ZooKeeper客戶端实蓬,Curator提供的InterProcessMutex是分布式鎖的實(shí)現(xiàn)茸俭,acquire方法用于獲取鎖,release方法用于釋放鎖安皱。

優(yōu)點(diǎn):具備高可用调鬓、可重入、阻塞鎖特性酌伊,可解決失效死鎖問(wèn)題腾窝。

缺點(diǎn):因?yàn)樾枰l繁的創(chuàng)建和刪除節(jié)點(diǎn),性能上不如Redis方式腺晾。

具體代碼可以看這篇文章:
https://blog.csdn.net/qiangcuo6087/article/details/79067136

自己設(shè)計(jì)一個(gè)分布式鎖

設(shè)計(jì)的目標(biāo)

  • 強(qiáng)一致性
  • 服務(wù)高可用燕锥、系統(tǒng)穩(wěn)健
  • 鎖自動(dòng)續(xù)約及其自動(dòng)釋放
  • 代碼高度抽象業(yè)務(wù)接入極簡(jiǎn)
  • 可視化管理憑他辜贵、監(jiān)控及管理

對(duì)存儲(chǔ)模型進(jìn)行選型


image

N+1 代表部署奇數(shù)個(gè)

由于redis實(shí)現(xiàn)無(wú)法保證一致性悯蝉,zookeeper對(duì)鎖實(shí)現(xiàn)使用創(chuàng)建臨時(shí)節(jié)點(diǎn)和watch機(jī)制,執(zhí)行效率托慨,擴(kuò)展能力鼻由、社區(qū)活躍度等方面低于etcd,所以我們會(huì)選擇基于etcd實(shí)現(xiàn)厚棵。

etcd優(yōu)勢(shì)

  • 簡(jiǎn)單KV(key Value)
  • 強(qiáng)一致性
  • 高可用
    • 無(wú)單點(diǎn)
  • 數(shù)據(jù)可靠性
    • 持久化

整體方案

分布式Client + etcd

Client TTL模式

Server TTL模式

image

拿鎖的時(shí)候蕉世,選擇key,ttl是超時(shí)時(shí)間婆硬,value可以忽略狠轻,uuid為該鎖的唯一憑證,后面對(duì)鎖的操作都是對(duì)uuid做操作彬犯。需要uuid才能做操作向楼。etcd會(huì)保證只有一個(gè)線程能拿到鎖查吊。

使用場(chǎng)景1.申請(qǐng)鎖

image
image

使用場(chǎng)景2.申請(qǐng)鎖,鎖已經(jīng)被占用

image

使用場(chǎng)景3.鎖的清理

image

業(yè)務(wù)接入

JDK7以上湖蜕,建議9.
獲取鎖實(shí)例:

image

釋放鎖示例

image

兼容性考慮

image

ETCD恢復(fù)/版本

image

分布式鎖的特殊場(chǎng)景

特殊場(chǎng)景一:
分布式鎖只是在同一自然時(shí)間的互斥鎖逻卖,本省不解決冪等性問(wèn)題。

接入業(yè)務(wù)需要完善從獲得鎖到釋放鎖中間的數(shù)據(jù)冪等邏輯昭抒。

特殊場(chǎng)景二: 鎖沒有按照日期續(xù)約

心跳續(xù)約沒有成功

馬上啟動(dòng)GC评也,GCs時(shí)間太長(zhǎng)

特殊場(chǎng)景三: etcd內(nèi)部協(xié)調(diào)發(fā)生問(wèn)題

Leader節(jié)點(diǎn)掛了,選主中灭返,

raft日志數(shù)據(jù)同步發(fā)生錯(cuò)誤或者不一致問(wèn)題盗迟。

待續(xù)。熙含。诈乒。

部分摘自:

https://blog.csdn.net/xlgen157387/article/details/79036337

https://blog.csdn.net/qiangcuo6087/article/details/79067136

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市婆芦,隨后出現(xiàn)的幾起案子怕磨,更是在濱河造成了極大的恐慌,老刑警劉巖消约,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件肠鲫,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡或粮,警方通過(guò)查閱死者的電腦和手機(jī)导饲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)氯材,“玉大人渣锦,你說(shuō)我怎么就攤上這事∏庀” “怎么了袋毙?”我有些...
    開封第一講書人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)冗尤。 經(jīng)常有香客問(wèn)我听盖,道長(zhǎng),這世上最難降的妖魔是什么裂七? 我笑而不...
    開封第一講書人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任皆看,我火速辦了婚禮,結(jié)果婚禮上背零,老公的妹妹穿的比我還像新娘腰吟。我一直安慰自己,他們只是感情好徙瓶,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開白布毛雇。 她就那樣靜靜地躺著录语,像睡著了一般。 火紅的嫁衣襯著肌膚如雪禾乘。 梳的紋絲不亂的頭發(fā)上澎埠,一...
    開封第一講書人閱讀 50,050評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音始藕,去河邊找鬼蒲稳。 笑死,一個(gè)胖子當(dāng)著我的面吹牛伍派,可吹牛的內(nèi)容都是我干的江耀。 我是一名探鬼主播,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼诉植,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼祥国!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起晾腔,我...
    開封第一講書人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤舌稀,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后灼擂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體壁查,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年剔应,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了睡腿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡峻贮,死狀恐怖席怪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情纤控,我是刑警寧澤挂捻,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站嚼黔,受9級(jí)特大地震影響细层,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜唬涧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望盛撑。 院中可真熱鬧碎节,春花似錦、人聲如沸抵卫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至殖氏,卻和暖如春晚树,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背雅采。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工爵憎, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人婚瓜。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓宝鼓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親巴刻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子愚铡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

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