MySQL與Redis的數(shù)據(jù)同步方案

之前的文章有講過(guò)MySQL到Elasticsearch的多種數(shù)據(jù)同步方案(多種MySQL與Elasticsearch的數(shù)據(jù)同步解決方案)损俭,今天再來(lái)講下MySQL到Redis的幾種數(shù)據(jù)同步方案。

第一種方案:使用 canal 工具

在之前MySQL同步ES的文章中有簡(jiǎn)單提過(guò)這款工具,但是因?yàn)闆](méi)有用過(guò)所以沒(méi)有詳細(xì)講刚盈,今天就使用這款工具來(lái)實(shí)現(xiàn)MySQL到Redis的數(shù)據(jù)同步(同理肛著,同步到ES甲喝、MySQL等也是一樣的操作)

canal 是阿里巴巴開(kāi)源的一款提供增量數(shù)據(jù)訂閱和消費(fèi)的工具悠咱,應(yīng)用場(chǎng)景有:數(shù)據(jù)庫(kù)鏡像荣瑟、數(shù)據(jù)庫(kù)實(shí)時(shí)備份扫皱、索引構(gòu)建和實(shí)時(shí)維護(hù)足绅、業(yè)務(wù) cache 刷新捷绑、帶業(yè)務(wù)邏輯的增量數(shù)據(jù)處理等。

原理就與MySQL主從復(fù)制相似编检,canal 模擬 MySQL slave 的交互協(xié)議胎食,偽裝自己為 MySQL slave ,向 MySQL master 發(fā)送dump 協(xié)議允懂,從binlog日志中獲取數(shù)據(jù)厕怜。

  1. MySQL配置

    MySQL需要開(kāi)啟 Binlog 寫(xiě)入功能,配置 binlog-format 為 ROW 模式

    [mysqld]
    log-bin=mysql-bin # 開(kāi)啟 binlog
    binlog-format=ROW # 選擇 ROW 模式
    server_id=1 # 配置 MySQL replaction 需要定義蕾总,不要和 canal 的 slaveId 重復(fù)
    

    授權(quán) canal 鏈接 MySQL 賬號(hào)具有作為 MySQL slave 的權(quán)限, 如果已有賬戶可直接 grant

    CREATE USER canal IDENTIFIED BY 'canal';  
    GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';
    FLUSH PRIVILEGES;
    
  2. 下載 canal

    下載地址粥航,選擇自己需要的版本:https://github.com/alibaba/canal/releases

    wget https://github.com/alibaba/canal/releases/download/canal-1.1.5/canal.deployer-1.1.5.tar.gz
    mkdir /usr/local/canal
    tar -zxvf canal.deployer-1.1.5.tar.gz -C /usr/local/canal/
    rm -rf canal.deployer-1.1.5.tar.gz
    cd /usr/local/canal/
    
  3. 修改配置

    vi conf/example/instance.properties

    ## mysql serverId 不能與mysql的server-id一致
    canal.instance.mysql.slaveId = 1234
    #position info,需要改成自己的數(shù)據(jù)庫(kù)信息
    canal.instance.master.address = 127.0.0.1:3306 
    canal.instance.master.journal.name = 
    canal.instance.master.position = 
    canal.instance.master.timestamp = 
    #canal.instance.standby.address = 
    #canal.instance.standby.journal.name =
    #canal.instance.standby.position = 
    #canal.instance.standby.timestamp = 
    #username/password生百,需要改成自己的數(shù)據(jù)庫(kù)信息
    canal.instance.dbUsername = canal  
    canal.instance.dbPassword = canal
    canal.instance.defaultDatabaseName =
    canal.instance.connectionCharset = UTF-8
    #table regex
    canal.instance.filter.regex = .\*\\\\..\*
    
  4. 安裝java的JDK

    # 查看可安裝jdk版本
    yum search java | grep -i --color JDK
    # 選擇某一版本進(jìn)行安裝
    yum install java-1.8.0-openjdk.x86_64
    # 安裝完成后確認(rèn)JDK安裝完畢递雀,如果輸出了版本號(hào),證明安裝正確
    java -version
    

    PS:這里有個(gè)坑蚀浆,要安裝 JDK8缀程,我用的 JDK11的環(huán)境,發(fā)現(xiàn)啟動(dòng)不了canal市俊,報(bào)錯(cuò) Error: Could not create the Java Virtual Machine. 杨凑,切換成 JDK8 就好了

  5. 啟動(dòng)

    sh bin/startup.sh
    
  6. 檢查

    查看 server 日志 和 instance 的日志,有正確的內(nèi)容輸出證明啟動(dòng)成功

    vi logs/canal/canal.log
    vi logs/example/example.log
    

    或者使用 ps -ef | grep canal 查看canal進(jìn)程

  7. 關(guān)閉命令

    sh bin/stop.sh
    
  8. 安裝 canal-php

    canal 提供了多語(yǔ)言的客戶端摆昧,可采用不同語(yǔ)言實(shí)現(xiàn)不同的消費(fèi)邏輯撩满,我用的PHP客戶端,它的詳細(xì)介紹以及其他語(yǔ)言客戶端看文檔:https://github.com/alibaba/canal/wiki#%E5%A4%9A%E8%AF%AD%E8%A8%80

    composer require xingwenge/canal_php
    

    我這通過(guò)創(chuàng)建一個(gè)命令來(lái)測(cè)試

    php artisan make:command canal
    

    內(nèi)容

    <?php
    
    namespace App\Console\Commands;
    
    use App\Services\CanalToRedisService;
    use Illuminate\Console\Command;
    use xingwenge\canal_php\CanalClient;
    use xingwenge\canal_php\CanalConnectorFactory;
    use xingwenge\canal_php\Fmt;
    
    class canal extends Command
    {
        /**
         * The name and signature of the console command.
         *
         * @var string
         */
        protected $signature = 'canal';
    
        /**
         * The console command description.
         *
         * @var string
         */
        protected $description = 'canal客戶端';
    
        /**
         * Create a new command instance.
         *
         * @return void
         */
        public function __construct()
        {
            parent::__construct();
        }
    
        /**
         *
         */
        public function handle()
        {
            try {
                // 創(chuàng)建客戶端绅你,默認(rèn)使用 socket 來(lái)通信
                $client = CanalConnectorFactory::createClient(CanalClient::TYPE_SOCKET_CLUE);
                // 這個(gè)是使用 swoole
                //$client = CanalConnectorFactory::createClient(CanalClient::TYPE_SWOOLE);
    
                $client->connect("127.0.0.1", 11111);
                $client->checkValid();
                // $client->subscribe("1001", "example", ".*\\..*");
                // 設(shè)置過(guò)濾伺帘,指定要同步的表,上邊那種方式是不限制
                $client->subscribe("1001", "example", "lmrs.lmrs_shops");
    
                while (true) {
                    $message = $client->get(100);
                    if ($entries = $message->getEntries()) {
                        foreach ($entries as $entry) {
                            // Fmt::println($entry);
                            // 在這里進(jìn)行具體業(yè)務(wù)的邏輯處理忌锯,比如同步數(shù)據(jù)到 redis伪嫁,es,mysql等
                            CanalToRedisService::println($entry);
                        }
                    }
                    sleep(1);
                }
    
                $client->disConnect();
            } catch (\Exception $e) {
                echo $e->getMessage(), PHP_EOL;
            }
        }
    }
    

    這里我創(chuàng)建了個(gè)service來(lái)處理同步到Redis的邏輯偶垮,內(nèi)容如下

    <?php
    
    namespace App\Services;
    
    
    use Com\Alibaba\Otter\Canal\Protocol\Column;
    use Com\Alibaba\Otter\Canal\Protocol\Entry;
    use Com\Alibaba\Otter\Canal\Protocol\EntryType;
    use Com\Alibaba\Otter\Canal\Protocol\EventType;
    use Com\Alibaba\Otter\Canal\Protocol\RowChange;
    use Com\Alibaba\Otter\Canal\Protocol\RowData;
    
    class CanalToRedisService
    {
        /**
         * @param Entry $entry
         * @throws \Exception
         */
        public static function println($entry)
        {
            switch ($entry->getEntryType()) {
                case EntryType::TRANSACTIONBEGIN:
                case EntryType::TRANSACTIONEND:
                    return;
                    break;
            }
    
            $rowChange = new RowChange();
            $rowChange->mergeFromString($entry->getStoreValue());
            $evenType = $rowChange->getEventType();
            $header = $entry->getHeader();
            $table = $header->getSchemaName().'_'.$header->getTableName();
    
            /** @var RowData $rowData */
            foreach ($rowChange->getRowDatas() as $rowData) {
                switch ($evenType) {
                    case EventType::DELETE: // 刪除
                        self::delete($table, self::ptColumn($rowData->getBeforeColumns()));
                        break;
                    case EventType::INSERT: // 新增
                        self::insert($table, self::ptColumn($rowData->getAfterColumns()));
                        break;
                    default: // 更新
                        self::update($table, self::ptColumn($rowData->getBeforeColumns()), self::ptColumn($rowData->getAfterColumns()));
                        break;
                }
            }
        }
    
        /**
         * 將數(shù)據(jù)表的字段名和值組裝成數(shù)組
         * @param $columns
         * @return array
         */
        private static function ptColumn($columns) {
            $argv = [];
            foreach ($columns as $value) {
                $argv[$value->getName()] = $value->getValue();
            }
         // dump($argv);
            return $argv;
        }
    
        /**
         * 新增操作
         * 可以根據(jù)表名進(jìn)行判斷具體的業(yè)務(wù)操作
         * @param string $table 數(shù)據(jù)表名
         * @param array $data 數(shù)據(jù)
         */
        private static function insert($table, $data)
        {
            app('redis')->set("shop::".$data['id'], serialize($data));
        }
    
        /**
         * 刪除操作
         * 業(yè)務(wù)處理很簡(jiǎn)單礼殊,這里就不寫(xiě)了,自己完善
        *  @param string $table 數(shù)據(jù)表名
         * @param array $data 數(shù)據(jù)
         */
        private static function delete($table, $data)
        {
            //
        }
    
        /**
         * 更新操作
         * 業(yè)務(wù)處理很簡(jiǎn)單针史,這里就不寫(xiě)了晶伦,自己完善
         * @param string $table 數(shù)據(jù)表名
         * @param array $befor_data 更改前的數(shù)據(jù)
         * @param array $after_data 更改后的數(shù)據(jù)
         */
        private static function update($table, $befor_data, $after_data)
        {
         //
        }
    }
    
  9. 測(cè)試

    可以在service里多dump一些參數(shù),運(yùn)行 php artisan canal 查看輸出啄枕,新增MySQL數(shù)據(jù)婚陪,查看Redis是否有變化

  10. canal 還可以結(jié)合消息中間件來(lái)實(shí)現(xiàn)更高效的數(shù)據(jù)同步,比如:Kafka/RocketMQ 频祝。使用文檔:https://github.com/alibaba/canal/wiki/Canal-Kafka-RocketMQ-QuickStart

第二種方案:使用 RabbitMQ 消息隊(duì)列

RabbitMQ 是一個(gè)由erlang語(yǔ)言編寫(xiě)的泌参、開(kāi)源的脆淹、在AMQP基礎(chǔ)上完整的、可復(fù)用的企業(yè)消息系統(tǒng)沽一。支持多種語(yǔ)言盖溺,包括java、Python铣缠、ruby烘嘱、PHP、C/C++等蝗蛙。

RabbitMQ 的核心概念:

  • 生產(chǎn)者(Producer):發(fā)送消息的應(yīng)用
  • 消費(fèi)者(Consumer):接收消息的應(yīng)用
  • 隊(duì)列(Queue):存儲(chǔ)消息的緩存
  • 消息(Message):由生產(chǎn)者通過(guò)RabbitMQ發(fā)送給消費(fèi)者的信息
  • 連接(Connection):連接RabbitMQ和應(yīng)用服務(wù)器的TCP連接
  • 通道(Channel):連接里的一個(gè)虛擬通道蝇庭。當(dāng)你通過(guò)消息隊(duì)列發(fā)送或者接收消息時(shí),這個(gè)操作都是通過(guò)通道進(jìn)行的
  • 交換機(jī)(Exchange):交換機(jī)負(fù)責(zé)從生產(chǎn)者那里接收消息捡硅,并根據(jù)交換類(lèi)型分發(fā)到對(duì)應(yīng)的消息列隊(duì)里哮内。要實(shí)現(xiàn)消息的接收,一個(gè)隊(duì)列必須到綁定一個(gè)交換機(jī)
  • 綁定(Binding):綁定是隊(duì)列和交換機(jī)的一個(gè)關(guān)聯(lián)連接
  • 路由鍵(Routing Key):路由鍵是供交換機(jī)查看并根據(jù)鍵來(lái)決定如何分發(fā)消息到列隊(duì)的一個(gè)鍵壮韭。路由鍵可以說(shuō)是消息的目的地址

RabbitMQ 的工作模式:

  • 簡(jiǎn)單隊(duì)列
  • 工作隊(duì)列
  • 發(fā)布訂閱模式
  • 路由模式
  • 主題模式

理論介紹完畢北发,接下來(lái)進(jìn)入實(shí)操

  1. 安裝 RabbitMQ

    手動(dòng)編譯安裝 RabbitMQ 很麻煩,還得先安裝 erlang 環(huán)境喷屋,所以這里我就直接使用docker安裝了鲫竞。附上erlang和RabbitMQ的下載地址,之后有時(shí)間再去嘗試手動(dòng)安裝

    erlang:https://www.erlang.org/downloads

    RabbitMQ:https://github.com/rabbitmq/rabbitmq-server/releases/

    拉取docker鏡像

    docker pull rabbitmq
    

    構(gòu)建容器

    docker run -d -p 5672:5672 -p 15672:15672 --hostname my-rabbit -v /docker/rabbitmq:/var/lib/rabbitmq --privileged=true --name rabbitmq rabbitmq
    
  2. 進(jìn)入RabbitMQ 容器安裝可視化界面:rabbitmq_management

    docker exec -it rabbitmq bash
    
    rabbitmq-plugins enable rabbitmq_management
    

    在瀏覽器訪問(wèn) ip:15672 打開(kāi)可視化界面逼蒙,賬號(hào)密碼默認(rèn)都是:guest

  3. 安裝擴(kuò)展

    PHP調(diào)用RabbitMQ需要amqp的擴(kuò)展,下載地址:https://pecl.php.net/package/amqp

    wget https://pecl.php.net/get/amqp-1.10.2.tgz
    tar -zxvf amqp-1.10.2.tgz
    cd amqp-1.10.2
    phpize
    ./configure --with-php-config=/usr/local/bin/php-config
    

    到這里報(bào)了一個(gè)錯(cuò)寄疏,configure: error: librabbitmq not found 意思是還缺少個(gè)rabbitmq-c

    接著下載是牢,地址:https://github.com/alanxz/rabbitmq-c/releases

    wget https://github.com/alanxz/rabbitmq-c/archive/refs/tags/v0.11.0.tar.gz
    tar -zxvf v0.11.0.tar.gz
    cd rabbitmq-c-0.11.0/
    yum -y install cmake
    cmake . -DCMAKE_INSTALL_PREFIX=/usr/local/rabbitmq-c
    make && make install
    

    重新編譯amqp

    cd amqp-1.10.2
    ./configure --with-php-config=/usr/local/bin/php-config --with-amqp --with-librabbitmq-dir=/usr/local/rabbitmq-c
    make && make install
    

    在 php.ini 中加入 extension=amqp.so ,然后重啟php

  4. rabbimq在laravel中使用

    安裝組件

    composer require vladimir-yuldashev/laravel-queue-rabbitmq "10.X" --ignore-platform-reqs
    

    在 config/queue.php文件的 connections 中加入配置

    'rabbitmq' => [
    
       'driver' => 'rabbitmq',
       'queue' => env('RABBITMQ_QUEUE', 'default'),
       'connection' => PhpAmqpLib\Connection\AMQPLazyConnection::class,
    
       'hosts' => [
           [
               'host' => env('RABBITMQ_HOST', '127.0.0.1'),
               'port' => env('RABBITMQ_PORT', 5672),
               'user' => env('RABBITMQ_USER', 'guest'),
               'password' => env('RABBITMQ_PASSWORD', 'guest'),
               'vhost' => env('RABBITMQ_VHOST', '/'),
           ],
       ],
    
       'options' => [
           'ssl_options' => [
               'cafile' => env('RABBITMQ_SSL_CAFILE', null),
               'local_cert' => env('RABBITMQ_SSL_LOCALCERT', null),
               'local_key' => env('RABBITMQ_SSL_LOCALKEY', null),
               'verify_peer' => env('RABBITMQ_SSL_VERIFY_PEER', true),
               'passphrase' => env('RABBITMQ_SSL_PASSPHRASE', null),
           ],
           'queue' => [
               'job' => VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob::class,
           ],
       ],
    
       /*
        * Set to "horizon" if you wish to use Laravel Horizon.
        */
       'worker' => env('RABBITMQ_WORKER', 'default'),
        
    ],
    

    在 .env 文件中加入配置

    # 將默認(rèn)的 sync 改為 rabbitmq                            
    QUEUE_CONNECTION=rabbitmq
    # mq的ip地址      
    RABBITMQ_HOST=172.17.0.10
    # mq的端口 
    RABBITMQ_PORT=5672
    # mq的賬號(hào) 
    RABBITMQ_USER=guest
    # mq的密碼
    RABBITMQ_PASSWORD=guest
    # 默認(rèn)的虛擬主機(jī) 
    RABBITMQ_VHOST=my_vhost
    # 默認(rèn)隊(duì)列名稱 
    RABBITMQ_QUEUE=lmrs
    
  5. 創(chuàng)建 service

    <?php
    /**
     * Created by PhpStorm
     * User: Ricky Wong
     * Date: 2021/8/5
     * Time: 0:49
     */
    
    namespace App\Services;
    
    use PhpAmqpLib\Connection\AMQPStreamConnection;
    use PhpAmqpLib\Message\AMQPMessage;
    
    class RabbitmqService
    {
        public static function getConnect()
        {
            //RABBITMQ 配置項(xiàng)
            $config = [
                'host' => env('RABBITMQ_HOST', '127.0.0.1'),
                'port' => env('RABBITMQ_PORT', 5672),
                'user' => env('RABBITMQ_USER', 'guest'),
                'password' => env('RABBITMQ_PASSWORD', 'guest'),
                'vhost' => env('RABBITMQ_VHOST', '/'),
            ];
            return new AMQPStreamConnection($config["host"],$config["port"],$config["user"],$config["password"],$config["vhost"]);
        }
    
        /**
         * 生產(chǎn)者
         * @param $queue
         * @param $messageBody
         * @param string $exchange
         */
        public static function push($queue,$messageBody,$exchange='router')
        {
            //獲取連接
            $connection = self::getConnect();
            //構(gòu)建通道
            $channel = $connection->channel();
            //聲明一個(gè)隊(duì)列
            $channel->queue_declare($queue,false,true,false,false);
            //指定交換機(jī) 以路由模式
            $channel->exchange_declare($exchange,'direct',false,true,false);
            //綁定隊(duì)列和類(lèi)型
            $channel->queue_bind($queue,$exchange);
            $message = new AMQPMessage($messageBody,array('content_type' => 'text/plain',
                'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT));
            //消息推送
            $channel->basic_publish($message,$exchange);
            $channel->close();
            $connection->close();
        }
    
        /**
         * 消費(fèi)者
         * @param $queue
         * @param $callback
         * @param string $exchange
         */
        public static function pop($queue,$callback,$exchange='router')
        {
            $connection = self::getConnect();
            $channel = $connection->channel();
            //從隊(duì)列中取出消息
            $message = $channel->basic_get($queue);
            $res = $callback($message->getBody());
            if ($res){
                //ack 驗(yàn)證
                $channel->basic_ack($message->getDeliveryTag());
            }
            $channel->close();
            $connection->close();
        }
    }
    
  6. 創(chuàng)建異步任務(wù)

    php artisan make:job SyncToRedis
    

    編輯內(nèi)容

    <?php
    
    namespace App\Jobs;
    
    use Illuminate\Bus\Queueable;
    use Illuminate\Contracts\Queue\ShouldQueue;
    use Illuminate\Foundation\Bus\Dispatchable;
    use Illuminate\Queue\InteractsWithQueue;
    use Illuminate\Queue\SerializesModels;
    use App\Services\RabbitmqService;
    
    class SyncToRedis implements ShouldQueue
    {
        use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    
        protected $key;
    
        /**
         * Create a new job instance.
         *
         * @param $data
         */
        public function __construct($data)
        {
            $this->key = 'lmrs::product::info::'.$data->id;
            //寫(xiě)入隊(duì)列
            RabbitmqService::push('update_queue', $data);
        }
    
        /**
         * Execute the job.
         *
         * @return void
         */
        public function handle()
        {
            // 消費(fèi)消息
            RabbitmqService::pop('update_queue', function ($message) {
                $product = app('redis')->set($this->key, serialize($message));
                if (!$product){
                    return;
                }
    
                return true;
            });
        }
    
        /**
         * 異常處理
         * @param \Exception $exception
         */
        public function failed(\Exception $exception)
        {
            print_r($exception->getMessage());
        }
    }
    
  7. 在需要同步的地方觸發(fā)任務(wù)

    dispatch(new SyncToRedis(Product::find($request->input("id"))));
    
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末陕截,一起剝皮案震驚了整個(gè)濱河市驳棱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌农曲,老刑警劉巖社搅,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異乳规,居然都是意外死亡形葬,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)暮的,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)笙以,“玉大人,你說(shuō)我怎么就攤上這事冻辩〔螅” “怎么了拆祈?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)倘感。 經(jīng)常有香客問(wèn)我放坏,道長(zhǎng),這世上最難降的妖魔是什么老玛? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任淤年,我火速辦了婚禮,結(jié)果婚禮上逻炊,老公的妹妹穿的比我還像新娘互亮。我一直安慰自己,他們只是感情好余素,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布豹休。 她就那樣靜靜地躺著,像睡著了一般桨吊。 火紅的嫁衣襯著肌膚如雪威根。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,741評(píng)論 1 289
  • 那天视乐,我揣著相機(jī)與錄音洛搀,去河邊找鬼。 笑死佑淀,一個(gè)胖子當(dāng)著我的面吹牛留美,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播伸刃,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼谎砾,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了捧颅?” 一聲冷哼從身側(cè)響起景图,我...
    開(kāi)封第一講書(shū)人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎碉哑,沒(méi)想到半個(gè)月后挚币,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡扣典,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年妆毕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贮尖。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡设塔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情闰蛔,我是刑警寧澤痕钢,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站序六,受9級(jí)特大地震影響任连,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜例诀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一随抠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧繁涂,春花似錦拱她、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至矿酵,卻和暖如春唬复,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背全肮。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工敞咧, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人辜腺。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓休建,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親评疗。 傳聞我的和親對(duì)象是個(gè)殘疾皇子测砂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

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