RabbitMQ可靠消息和死信隊(duì)列

一吱抚、消息確認(rèn)機(jī)制

1.1 概念

保證消息不丟失震缭,可靠抵達(dá)缚陷,可以使用的方式如下

  • 使用事務(wù)消息:通過事務(wù)保證消息不丟失嵌莉,但是性能下降250倍
  • 確認(rèn)機(jī)制:publisher: confirmCallback 確認(rèn)模式publisher: returnCallback 為投遞到queue退回模式consumer: ack機(jī)制
    image.png


1.2. 具體實(shí)現(xiàn)

2.1 從publisher到exchange

原理:消息只要被broker接受就會(huì)執(zhí)行confirmCallback蚁孔,如果是cluster模式袱讹,需要所有的broker接受到才會(huì)調(diào)用confirmCallback矩屁。

yml配置

spring.rabbitmq.publisher-confirm-type=correlated

收信后回調(diào)操作:

    /**
     * 設(shè)置消息接受的回調(diào)方法
     */
    @PostConstruct
    public void initRabbitTemplate() {

        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             * 設(shè)置消息抵達(dá)exchange的回調(diào)方法
             * @param correlationData 當(dāng)前消息的唯一關(guān)聯(lián)數(shù)據(jù)(消息的唯一id)
             * @param ack 消息是否成功收到
             * @param cause 失敗的原因
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                System.out.println("confirm...correlationData[" + correlationData + "]==>ack[" + ack + "]");
            }
        });

    }

CorrelationData:用來(lái)表示當(dāng)前消息的唯一性纪隙,可以在發(fā)送消息時(shí)進(jìn)行設(shè)置 rabbitTemplate.convertAndSend("test-exchange", "test.java", car, new CorrelationData(UUID.randomUUID().toString()));

2.2 從exchange到queue

原理:我們要保證exchange中的消息要投遞到目標(biāo)queue中,需要開啟return退回模式碗硬。

yml配置

spring.rabbitmq.publisher-returns=true
spring.rabbitmq.template.mandatory=true

exchange發(fā)送到queue失敗后回調(diào)操作:

    /**
     * 設(shè)置消息接受的回調(diào)方法
     */
    @PostConstruct
    public void initRabbitTemplate() {

        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
             * 只要消息沒有投遞給指定的隊(duì)列瓤湘,就觸發(fā)這個(gè)失敗回調(diào)
             * @param message 投遞失敗的消息
             * @param replyCode 回復(fù)的狀態(tài)碼
             * @param replyText 回復(fù)的文本內(nèi)容
             * @param exchange 這個(gè)消息發(fā)送給那個(gè)交換機(jī)
             * @param routingKey 消息使用的是哪個(gè)路由鍵
             */
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                System.out.println("Fail Message[" + message + "]-->replyCode[" + replyCode + "]-->replyText[" + replyText + "]-->exchange[" + exchange + "]-->routingKey[" + routingKey + "]");
            }
        });

    }
  • 可以在回調(diào)方法中將發(fā)送失敗的信息存儲(chǔ)到MySQL中

2.3 從queue到consumer

原理:
消費(fèi)者獲取到消息,成功處理恩尾,可以回復(fù)Ack給Broker

  • basic.ack用于肯定回復(fù)弛说;broker將移除此消息
  • basic.nack用于否定回復(fù);可以指定broker是否丟棄消息翰意,可以批量
  • basic.reject用于否定回復(fù)木人;同上,但不能批量

默認(rèn)情況下Broker的消息發(fā)送給queue后會(huì)自動(dòng)Ack冀偶,刪除該消息醒第。但是如果無(wú)法確定此消息是否被處理完成或成功處理。我們可以手動(dòng)開啟ack模式:

  • 消息處理成功进鸠,ack()稠曼,接受下一個(gè)消息,broker刪除該消息
  • 消息處理失敗客年,nack()/reject()霞幅,重新發(fā)送給其他人進(jìn)行處理漠吻,或者容錯(cuò)后ack
  • 消息一直沒有調(diào)用ack/nack方法,broker認(rèn)為此消息被其他consumer處理司恳,不會(huì)投遞給別人途乃,此時(shí)consumer斷開,消息不會(huì)被broker刪除扔傅,會(huì)投遞給別人耍共。

配置

spring.rabbitmq.listener.simple.acknowledge-mode=manual


二、延時(shí)隊(duì)列和死信隊(duì)列

2.1 延時(shí)隊(duì)列

消息的TTL(Time To Live)
消息的TTL就是消息的存活時(shí)間铅鲤。RabbitMQ可以對(duì)隊(duì)列和消息分別設(shè)置TTL划提。

  • 對(duì)隊(duì)列設(shè)置TTL:隊(duì)列沒有消費(fèi)者連接的過期時(shí)間枫弟。
  • 對(duì)消息設(shè)置TTL:超時(shí)后該消息就是死信邢享。

如果隊(duì)列和消息都設(shè)置了TTL,那么會(huì)取最小的淡诗。通過設(shè)置消息的expiration字段或者x-message-ttl屬性來(lái)設(shè)置時(shí)間骇塘,兩者效果相同。


2.2 死信隊(duì)列

死信情況包括如下三種:

  • 消息被consumer拒收(unack或reject)且requeue是false韩容;
  • 消息TTL超時(shí)未消費(fèi)款违;
  • 隊(duì)列長(zhǎng)度滿了,排在前面的消息會(huì)被丟棄或發(fā)送到死信exchange中群凶。
  • 超過最大重試次數(shù)

DLE(Dead Letter Exchange)
死信exchange是一種普通的exchange插爹,只是所有的死信都會(huì)自動(dòng)從隊(duì)列中發(fā)送到該exchange中。


2.3 應(yīng)用場(chǎng)景

場(chǎng)景:比如未支付訂單请梢,超時(shí)一定時(shí)間后赠尾,系統(tǒng)自動(dòng)取消訂單并釋放占有的物品。

解決方案:

  • Spring的schedule定時(shí)或xxl中間件定時(shí)任務(wù):消耗系統(tǒng)內(nèi)存毅弧、增加數(shù)據(jù)庫(kù)壓力气嫁、存在較大的時(shí)間誤差;
  • RabbitMQ的消息TTL和死信Exchange結(jié)合

如果訂單超時(shí)時(shí)間為1小時(shí)够坐,即為消息設(shè)置1小時(shí)的TTL時(shí)間且為隊(duì)列設(shè)置DLE和死信路由鍵寸宵,消息超時(shí)過期后發(fā)送到DLE,根據(jù)死信路由鍵路由到死信隊(duì)列中元咙,從死信隊(duì)列中獲得的消息就是超時(shí)訂單梯影。

實(shí)現(xiàn)一:
給隊(duì)列中的所有消息設(shè)定TTL時(shí)間,到達(dá)隊(duì)列的時(shí)間時(shí)開始計(jì)時(shí)庶香。

image.png

        // 創(chuàng)建信息過期時(shí)間為1m的隊(duì)列
        HashMap<String, Object> arguments = new HashMap<>();
        arguments.put("x-dead-letter-exchange", "order-event-exchange"); // 指定死信交換機(jī)的名字
        arguments.put("x-dead-letter-routing-key", "order.release.order"); // 指定死信的路由鍵
        arguments.put("x-message-ttl", 10000); // TTL時(shí)間設(shè)為1m
        Queue delayQueue = new Queue("order.delay.queue", true, false, false, arguments);
        amqpAdmin.declareQueue(delayQueue);

實(shí)現(xiàn)二:
給發(fā)布者發(fā)送的每條消息設(shè)施TTL時(shí)間甲棍,缺點(diǎn)是前一個(gè)過期處理之后才會(huì)處理后續(xù)的過期數(shù)據(jù),存在過期數(shù)據(jù)處理不及時(shí)的情況脉课。

image.png

    @Test
    public void sendOrderCreate() {
        MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                message.getMessageProperties().setExpiration("60000");
                message.getMessageProperties().setContentEncoding("UTF-8");
                return message;
            }
        };
        Order book = new Order(1, "book");
        rabbitTemplate.convertAndSend("order-event-exchange", "order.create.order", book, messagePostProcessor, new CorrelationData(UUID.randomUUID().toString()));
    }

注意點(diǎn):如果創(chuàng)建隊(duì)列成功后救军,代碼中修改隊(duì)列的屬性财异,是不會(huì)覆蓋原有屬性的,需要?jiǎng)h除后再次創(chuàng)建隊(duì)列唱遭。

2.4 實(shí)現(xiàn)

2.4.1 流程

①定義交換機(jī)(普通信息和死信共用)戳寸、延時(shí)隊(duì)列(參數(shù)指定死信交換器、死信路由和延時(shí)時(shí)間)和死信隊(duì)列拷泽。
②綁定交換器和延時(shí)隊(duì)列與死信隊(duì)列疫鹊。
③發(fā)送消息到交換機(jī),路由到延時(shí)隊(duì)列司致。
④不消費(fèi)延時(shí)隊(duì)列信息拆吆,信息過期后發(fā)送到死信交換器枣耀,交換器通過死信路由發(fā)送到死信隊(duì)列。
⑤消費(fèi)死信隊(duì)列獲得死信信息捞奕。

2.4.2 代碼

依賴

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

配置類

@Configuration
public class RabbitMqConfig {

    @Resource
    private AmqpAdmin amqpAdmin;

    @Resource
    private RabbitTemplate rabbitTemplate;

    /**
     * 注入并使用指定的消息轉(zhuǎn)換器(將序列化傳輸轉(zhuǎn)變?yōu)閖son傳輸)
     *
     * @return
     */
//    @Bean
//    public Jackson2JsonMessageConverter converter(){
//        return new Jackson2JsonMessageConverter();
//    }

    /**
     * 設(shè)置消息接受的回調(diào)方法
     */
    @PostConstruct
    public void initRabbitTemplate() {
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             * 設(shè)置消息抵達(dá)exchange的回調(diào)方法
             * @param correlationData 當(dāng)前消息的唯一關(guān)聯(lián)數(shù)據(jù)(消息的唯一id)
             * @param ack 消息是否成功收到
             * @param cause 失敗的原因
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                System.out.println("confirm...correlationData[" + correlationData + "]==>ack[" + ack + "]");
            }
        });

        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
             * 只要消息沒有投遞給指定的隊(duì)列拄轻,就觸發(fā)這個(gè)失敗回調(diào)
             * @param message 投遞失敗的消息
             * @param replyCode 回復(fù)的狀態(tài)碼
             * @param replyText 回復(fù)的文本內(nèi)容
             * @param exchange 這個(gè)消息發(fā)送給那個(gè)交換機(jī)
             * @param routingKey 消息使用的是哪個(gè)路由鍵
             */
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                System.out.println("Fail Message[" + message + "]-->replyCode[" + replyCode + "]-->replyText[" + replyText + "]-->exchange[" + exchange + "]-->routingKey[" + routingKey + "]");
            }
        });
    }


    /**
     * 定義交換機(jī),參數(shù)說(shuō)明
     * 1. name 交換機(jī)名稱
     * 2. durable 是否持久化恨搓,如果持久化院促,mq重啟后交換機(jī)還在
     * 3. autoDelete 自動(dòng)刪除,交換機(jī)沒有綁定隊(duì)列則刪除斧抱,如果將此參數(shù)和exclusive參數(shù)設(shè)置為true就可以實(shí)現(xiàn)臨時(shí)隊(duì)列(隊(duì)列不用了就自動(dòng)刪除)
     * 4. arguments 參數(shù)常拓,可以設(shè)置一個(gè)隊(duì)列的擴(kuò)展參數(shù),比如設(shè)置存活時(shí)間
     */
    @PostConstruct
    public void createExchange() {
        // 創(chuàng)建交換機(jī)(同時(shí)作為訂單信息交換機(jī)和死信交換機(jī))
        DirectExchange directExchange = new DirectExchange("order-event-exchange", true, false);
        amqpAdmin.declareExchange(directExchange);
    }

    /**
     * 定義隊(duì)列夺姑,參數(shù)說(shuō)明
     * 1. queue 隊(duì)列名稱
     * 2. durable 是否持久化墩邀,如果持久化,mq重啟后隊(duì)列還在
     * 3. exclusive 是否獨(dú)占連接盏浙,隊(duì)列只允許在該連接中訪問眉睹,如果連接關(guān)閉隊(duì)列自動(dòng)刪除(如果將此參數(shù)設(shè)置true可用于臨時(shí)隊(duì)列的創(chuàng)建)
     * 4. autoDelete 自動(dòng)刪除,隊(duì)列不再使用時(shí)是否自動(dòng)刪除此隊(duì)列废膘,如果將此參數(shù)和exclusive參數(shù)設(shè)置為true就可以實(shí)現(xiàn)臨時(shí)隊(duì)列(隊(duì)列不用了就自動(dòng)刪除)
     * 5. arguments 參數(shù)竹海,可以設(shè)置一個(gè)隊(duì)列的擴(kuò)展參數(shù),比如設(shè)置存活時(shí)間
     */
    @PostConstruct
    public void createQueue() {
        // 創(chuàng)建信息過期時(shí)間為1m的隊(duì)列
        HashMap<String, Object> arguments = new HashMap<>();
        arguments.put("x-dead-letter-exchange", "order-event-exchange"); // 指定死信交換機(jī)的名字
        arguments.put("x-dead-letter-routing-key", "order.release.order"); // 指定死信的路由鍵
        arguments.put("x-message-ttl", 10000); // TTL時(shí)間設(shè)為1m
        Queue delayQueue = new Queue("order.delay.queue", true, false, false, arguments);
        amqpAdmin.declareQueue(delayQueue);

        // 創(chuàng)建死信隊(duì)列
        Queue releaseQueue = new Queue("order.release.order.queue", true, false, false);
        amqpAdmin.declareQueue(releaseQueue);
    }

    /**
     * 定義隊(duì)列丐黄,參數(shù)說(shuō)明
     * 1. destination 綁定的隊(duì)列或者交換機(jī)的名字
     * 2. destinationType 需要綁定的類型
     * 3. exchange 交換機(jī)的名字
     * 4. routingKey 路由鍵
     * 5. arguments 參數(shù)斋配,可以設(shè)置一個(gè)隊(duì)列的擴(kuò)展參數(shù)
     */
    @PostConstruct
    public void createBinding() {
        // 交換機(jī)order-event-exchange與訂單創(chuàng)建隊(duì)列order.delay.queue綁定,路由鍵為order.create.order
        Binding createBinding = new Binding("order.delay.queue", Binding.DestinationType.QUEUE, "order-event-exchange", "order.create.order", null);
        amqpAdmin.declareBinding(createBinding);

        // 交換機(jī)order-event-exchange與死信隊(duì)列order.release.order.queue綁定,路由鍵為order.release.order
        Binding releaseBinding = new Binding("order.release.order.queue", Binding.DestinationType.QUEUE, "order-event-exchange", "order.release.order", null);
        amqpAdmin.declareBinding(releaseBinding);
    }

}

監(jiān)聽死信隊(duì)列

@Service
public class RabbitListenerService {

    @RabbitListener(queues = "order.release.order.queue")
    public void consumer1(Message message, Order order, Channel channel) throws IOException {
        System.out.println(order);

        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        channel.basicAck(deliveryTag,false);
    }

}

發(fā)送信息

    @Test
    public void sendOrderCreate() {
        Order book = new Order(1, "book");
        rabbitTemplate.convertAndSend("order-event-exchange", "order.create.order",book, new CorrelationData(UUID.randomUUID().toString()));
    }


三艰争、 硬件

image.png

3.1 內(nèi)存

默認(rèn)如果rabbitmq使用超過物理內(nèi)存的40%坏瞄,會(huì)報(bào)警并阻塞所有隊(duì)列。
可以通過配置文件或輸入命令來(lái)改變默認(rèn)的配置
命令方式:

  1. 如果使用內(nèi)存超過90MB則報(bào)警:rabbitmqctl set_vm_memory_high_watermark absolute 90MB
  2. 如果使用內(nèi)存超過物理內(nèi)存的40%則報(bào)警:rabbitmqctl set_vm_memory_high_watermark 0.4

3.2 磁盤

磁盤剩余空間低于閾值時(shí)甩卓,同樣會(huì)阻塞生產(chǎn)者鸠匀,避免因非持久化的消息持續(xù)換頁(yè)導(dǎo)致服務(wù)器磁盤耗盡而崩潰。

  1. 如果磁盤空間剩余小于100GB則報(bào)警:rabbitmqctl set_disk_free_limit 100GB
  2. 如果磁盤空間剩余小于內(nèi)存的1.5倍逾柿,則報(bào)警:rabbitmqctl set_disk_free_limit memory_limit 1.5

3.3 內(nèi)存換頁(yè)

在某個(gè)broker節(jié)點(diǎn)及內(nèi)存阻塞生產(chǎn)者之前缀棍,它會(huì)嘗試將隊(duì)列中的消息換頁(yè)到磁盤以釋放內(nèi)存空間,持久化和非持久化的消息都會(huì)寫入到磁盤中机错,其中持久化的消息本身就在磁盤胡中有一個(gè)副本爬范,所以在轉(zhuǎn)移過程中持久化的消息會(huì)先從內(nèi)存中清除掉。

默認(rèn)情況下弱匪,內(nèi)存到達(dá)的閾值是50%時(shí)進(jìn)行換頁(yè)青瀑,即在默認(rèn)情況下物理內(nèi)存使用超過0.4*0.5=0.2時(shí)狱窘,進(jìn)行換頁(yè)。
可以通過設(shè)置vm_memory_high_watermark_paging_ratio進(jìn)行調(diào)整躬络。

官網(wǎng)配置


四穷当、附

常用配置

spring:
  rabbitmq:
    host: 192.168.32.207
    port: 5672
    # addresses: 192.168.32.207:5672,... # 配置集群的地址
    username: guest
    password: guest
    virtual-host: /transaction_demo
    publisher-confirm-type: correlated
    publisher-returns: true
    template:
      mandatory: true
    listener:
      simple:
        acknowledge-mode: manual
        #retry:
          #enabled: true # 開啟重試
          #max-attempts: 10 # 最大重試次數(shù)
          #initial-interval: 2000m # 重試間隔時(shí)間

消費(fèi)者retry重試消費(fèi)消息后要放入死信隊(duì)列茴扁,就不能將acknowledge-mode設(shè)為manual峭火,否則消息一直是unack狀態(tài)智嚷。

完整配置

spring:
  rabbitmq:
    host: 192.168.32.207
    # addresses: # 配置集群的地址
    port: 5672
    username: guest
    password: guest
    virtual-host: /transaction_demo
    publisher-confirm-type: correlated
    publisher-returns: true
    requested-heartbeat: # 指定心態(tài)超時(shí)稍浆,單位秒,0為不指定:默認(rèn)60s
    connection-timeout: # 連接超時(shí)嫁艇,單位毫秒裳仆,0表示無(wú)窮大歧斟,不超時(shí)
    cache:
      channel:
        size: # 緩存中保存的channel數(shù)量
        checkout-timeout: # 當(dāng)緩存數(shù)量被設(shè)置時(shí)静袖,從緩存中獲取一個(gè)channel的超時(shí)時(shí)間队橙,單位毫秒捐康;如果為0解总,則總是創(chuàng)新一個(gè)新channel
      connection:
        size: # 緩存的鏈接數(shù)花枫,只有是CONNECTION模式時(shí)生效
        mode: # 連接工廠緩存模式:CHANNEL和CONNECTION
    template:
      mandatory: true
      receive-timeout: # receive()操作的超時(shí)時(shí)間
      reply-timeout: # sendAndReceive()操作的超時(shí)時(shí)間
      retry:
        enabled: # 發(fā)送重試是否可用
        max-attempts: #最大重試次數(shù)
        initial-interval: # 第一次和第二次嘗試發(fā)布或傳遞消息之間的間隔
        multiplier: # 應(yīng)用于上一重試間隔的乘數(shù)
        max-interval: # 最大重試時(shí)間間隔
    listener:
      simple:
        acknowledge-mode: manual
        retry:
          enabled: true # 開啟重試
          max-attempts: 10 # 最大重試次數(shù)
          initial-interval: 2000ms # 重試間隔時(shí)間
          multiplier: # 應(yīng)用于上一重試間隔的乘數(shù)
          max-interval: # 最大重試時(shí)間間隔
          stateless: # 重試是有狀態(tài)or無(wú)狀態(tài)
        auto-startup: # 是否啟動(dòng)時(shí)自動(dòng)啟動(dòng)容器
        concurrency: # 最小的消費(fèi)者數(shù)量
        max-concurrency: # 最大的消費(fèi)者數(shù)量
        prefetch: # 指定一個(gè)請(qǐng)求能處理多少個(gè)消息劳翰,如果有事務(wù)的話佳簸,必須大于等于transaction數(shù)量
        transaction-size: # 指定一個(gè)事務(wù)處理的消息數(shù)量生均,最好小于等于prefetch的數(shù)量
        default-requeue-rejected: # 決定被拒絕的消息是否重新入隊(duì)悼做,默認(rèn)是true(與參數(shù)acknowledge-mode有關(guān)系)
        idle-event-interval: # 多少長(zhǎng)時(shí)間發(fā)布空閑容器時(shí)間肛走,單位毫秒


面試題

RabbitMQ為什么需要信道,為什么不是TCP直接通信:

  1. TCP的創(chuàng)建和銷毀開銷大组题,創(chuàng)建要三次握手抱冷,銷毀要四次分手旺遮。每個(gè)線程都開一個(gè)TCP連接,造成底層操作系統(tǒng)處理繁忙边翼;
  2. 信道的原理是一條線程一個(gè)信道组底,多條線程多條信道同用一條TCP連接债鸡,一條TCP連接可以容納無(wú)限的信道铛纬,即使每秒成千上萬(wàn)的請(qǐng)求也不會(huì)成為性能瓶頸饺鹃。


五悔详、RabbitMQ集群搭建

......待續(xù)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
禁止轉(zhuǎn)載茄螃,如需轉(zhuǎn)載請(qǐng)通過簡(jiǎn)信或評(píng)論聯(lián)系作者归苍。
  • 序言:七十年代末拼弃,一起剝皮案震驚了整個(gè)濱河市吻氧,隨后出現(xiàn)的幾起案子盯孙,更是在濱河造成了極大的恐慌振惰,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,919評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件痛垛,死亡現(xiàn)場(chǎng)離奇詭異榜晦,居然都是意外死亡乾胶,警方通過查閱死者的電腦和手機(jī)朽寞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門喻频,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)肘迎,“玉大人妓布,你說(shuō)我怎么就攤上這事≌玻” “怎么了释涛?”我有些...
    開封第一講書人閱讀 163,316評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵唇撬,是天一觀的道長(zhǎng)豫柬。 經(jīng)常有香客問我,道長(zhǎng)暂雹,這世上最難降的妖魔是什么杭跪? 我笑而不...
    開封第一講書人閱讀 58,294評(píng)論 1 292
  • 正文 為了忘掉前任涧尿,我火速辦了婚禮姑廉,結(jié)果婚禮上桥言,老公的妹妹穿的比我還像新娘号阿。我一直安慰自己扔涧,他們只是感情好枯夜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,318評(píng)論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著程剥,像睡著了一般织鲸。 火紅的嫁衣襯著肌膚如雪搂擦。 梳的紋絲不亂的頭發(fā)上瀑踢,一...
    開封第一講書人閱讀 51,245評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音氨距,去河邊找鬼俏让。 笑死,一個(gè)胖子當(dāng)著我的面吹牛勒奇,可吹牛的內(nèi)容都是我干的巧骚。 我是一名探鬼主播劈彪,決...
    沈念sama閱讀 40,120評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼草添!你這毒婦竟也來(lái)了扼仲?” 一聲冷哼從身側(cè)響起远寸,我...
    開封第一講書人閱讀 38,964評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎屠凶,沒想到半個(gè)月后驰后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,376評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡矗愧,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,592評(píng)論 2 333
  • 正文 我和宋清朗相戀三年灶芝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片唉韭。...
    茶點(diǎn)故事閱讀 39,764評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡夜涕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情驾胆,我是刑警寧澤,帶...
    沈念sama閱讀 35,460評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜志于,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,070評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦歌殃、人聲如沸贮懈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春衙猪,著一層夾襖步出監(jiān)牢的瞬間吊输,已是汗流浹背扭屁。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工高每, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人乔煞。 一個(gè)月前我還...
    沈念sama閱讀 47,819評(píng)論 2 370
  • 正文 我出身青樓锦溪,卻偏偏與公主長(zhǎng)得像则涯,于是被迫代替她去往敵國(guó)和親档礁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,665評(píng)論 2 354

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