RabbitMq七种工作模式,结合简单的java实例使用,别再说你不会

一、Maven依赖添加

com.rabbitmq

amqp-client

3.0.4

二、七种工作模式的java实例

1、简单模式

最简单的一个消费者和一个生产者模式,生产者生成消息,消费者监听消息,若是消费者监听到它所需要的消息,就会消费该消息,这种消息是次性的,被消费了就没有了。

1.1.1、EasyRecv.java

import com.rabbitmq.client.Channel;

import com.rabbitmq.client.Connection;

import com.rabbitmq.client.ConnectionFactory;

import com.rabbitmq.client.QueueingConsumer;

public class EasyRecv {

//队列名称

private final static String QUEUE_NAME ="hello world";

public static void main(String[] argv) throws java.io.IOException,java.lang.InterruptedException {

//打开连接和创建频道,与发送端一样

ConnectionFactory factory = new ConnectionFactory();

//设置RabbitMQ所在主机ip或者主机名

factory.setHost("127.0.0.1");

Connection connection = factory.newConnection();

Channel channel = connection.createChannel();

//声明队列,主要为了防止消息接收者先运行此程序,队列还不存在时创建队列。

/**

* 队列名

* 是否持久化

*  是否排外  即只允许该channel访问该队列   一般等于true的话用于一个队列只能有一个消费者来消费的场景

*  是否自动删除  消费完删除

*  其他属性

*

*/

channel.queueDeclare(QUEUE_NAME,false,false,false, null);

System.out.println("Waiting for messages. To exit press CTRL+C");

//创建队列消费者

QueueingConsumer consumer = new QueueingConsumer(channel);

//指定消费队列

/**

* 队列名

* 其他属性  路由

* 消息body

*/

channel.basicConsume(QUEUE_NAME,true, consumer);

while(true)

{

//nextDelivery是一个阻塞方法(内部实现其实是阻塞队列的take方法)

QueueingConsumer.Delivery delivery = consumer.nextDelivery();

String message = new String(delivery.getBody());

System.out.println("Received '"+ message +"'");

}

}

}

1.1.2、EasySend.java

import com.rabbitmq.client.Channel;

import com.rabbitmq.client.Connection;

import com.rabbitmq.client.ConnectionFactory;

import java.util.Scanner;

public class EasySend {

//队列名称

private final static String QUEUE_NAME ="hello world";

public static void main(String[] argv) throws java.io.IOException

{

/**

* 创建连接连接到MabbitMQ

*/

ConnectionFactory factory = new ConnectionFactory();

//设置MabbitMQ所在主机ip或者主机名

factory.setHost("127.0.0.1");

while(true){

//创建一个连接

Connection connection = factory.newConnection();

//创建一个频道

Channel channel = connection.createChannel();

//指定一个队列

channel.queueDeclare(QUEUE_NAME,false,false,false, null);

//发送的消息

Scanner scanner = new Scanner(System.in);

String ms = scanner.nextLine();

//String message ="hello world!";

//往队列中发出一条消息

channel.basicPublish("", QUEUE_NAME, null, ms.getBytes());

System.out.println("Sent '"+ ms +"'");

//关闭频道和连接

channel.close();

connection.close();

}

}

以上两个已经可以进行通信了,下面同样是简单的实例,但是我们可以看到在代码层面上,连接的代码都是一样的,所以我们可以创建一个连接的工具类。

1.2.1、RabbitmqConnectionUtil .java

import com.rabbitmq.client.Connection;

import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;

public  class RabbitmqConnectionUtil {

public static Connection getConnection() throws IOException {

//连接工厂

ConnectionFactory factory = new ConnectionFactory();

factory.setHost("localhost");

//连接5672端口  注意15672为工具界面端口  25672为集群端口

factory.setPort(5672);

//factory.setVirtualHost("/xxxxx");

// factory.setUsername("xxxxxx");

// factory.setPassword("123456");

//获取连接

Connection connection = factory.newConnection();

returnconnection;

}

}

1.2.2、UtilSend.java

import com.rabbitmq.client.Channel;

import com.rabbitmq.client.Connection;

import top.san.RabbitMq.util.RabbitmqConnectionUtil;

import java.io.IOException;

public class UtilSend {

private final static String QUEUE_NAME ="UtilConn";

public static void main(String[] args) throws IOException {

Connection connection = RabbitmqConnectionUtil.getConnection();        //创建通道

Channel channel = connection.createChannel();

//声明队列

channel.queueDeclare(QUEUE_NAME,false,false,false, null);

//消息内容

String message ="这里是lbw广场";

channel.basicPublish("", QUEUE_NAME,null,message.getBytes());

System.out.println("[x]Sent '"+message +"'");

//最后关闭通关和连接

channel.close();

connection.close();

}

}

1.2.3、UtilRecv.java

import com.rabbitmq.client.Channel;

import com.rabbitmq.client.Connection;

import com.rabbitmq.client.QueueingConsumer;

import top.san.RabbitMq.util.RabbitmqConnectionUtil;

import java.io.IOException;

public class UtilRecv {

private final static String QUEUE_NAME ="UtilConn";

public static void main(String[] args) throws IOException, InterruptedException {

Connection connection = null;

connection = RabbitmqConnectionUtil.getConnection();        //创建通道

Channel channel = connection.createChannel();

//声明队列

channel.queueDeclare(QUEUE_NAME,false,false,false, null);

QueueingConsumer queueingConsumer = new QueueingConsumer(channel);

channel.basicConsume(QUEUE_NAME,true,queueingConsumer);

while(true){

//该方法会阻塞

QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();

String message = new String(delivery.getBody());

System.out.println("[x] Received '"+message+"'");

}

}

}

2、工作队列

工作队列也就是简单模式的强化版,一个队列是可以多个生产者,也可以有多个消费者来竞争消费消息,但是我们仍需保证队列的幂等性,队列存在就不能再创建同名队列。

下面的每个进程都控制其主线程休眠,让我们可以更好的看到结果。

2.1.1、Sender1.java

import com.rabbitmq.client.Channel;

import com.rabbitmq.client.Connection;

import top.san.RabbitMq.util.RabbitmqConnectionUtil;

import java.io.IOException;

public class Sender1 {

private final  static String QUEUE_NAME ="queue_work";

public static void main(String[] args) throws IOException, InterruptedException {

Connection connection = RabbitmqConnectionUtil.getConnection();

Channel channel = connection.createChannel();

channel.queueDeclare(QUEUE_NAME,false,false,false, null);

for(int i = 0; i < 100; i++){

String message ="lbw"+ i;

channel.basicPublish("", QUEUE_NAME, null, message.getBytes());

System.out.println("[x] Sent '"+message +"'");

Thread.sleep(i*10);

}

channel.close();

connection.close();

}

}

2.1.2、Sender2.java

import com.rabbitmq.client.Channel;

import com.rabbitmq.client.Connection;

import top.san.RabbitMq.util.RabbitmqConnectionUtil;

import java.io.IOException;

public class Sender2 {

private final  static String QUEUE_NAME ="queue_work";

public static void main(String[] args) throws IOException, InterruptedException {

Connection connection = RabbitmqConnectionUtil.getConnection();

Channel channel = connection.createChannel();

channel.queueDeclare(QUEUE_NAME,false,false,false, null);

for(int i = 0; i < 100; i++){

String message ="nb"+ i;

channel.basicPublish("", QUEUE_NAME, null, message.getBytes());

System.out.println("[x] Sent '"+message +"'");

Thread.sleep(i*10);

}

channel.close();

connection.close();

}

}

2.1.3、Receiver1.java

import com.rabbitmq.client.Channel;

import com.rabbitmq.client.Connection;

import com.rabbitmq.client.QueueingConsumer;

import top.san.RabbitMq.util.RabbitmqConnectionUtil;

import java.io.IOException;

/**

* Created by san

*/

public class Receiver1 {

private final static  String QUEUE_NAME ="queue_work";

public static void main(String[] args) throws IOException, InterruptedException {

Connection connection = RabbitmqConnectionUtil.getConnection();        Channel channel = connection.createChannel();        channel.queueDeclare(QUEUE_NAME,false,false,false,null);

//同一时刻服务器只会发送一条消息给消费者

channel.basicQos(1);

QueueingConsumer consumer = new QueueingConsumer(channel);

//关于手工确认 待之后有时间研究下

channel.basicConsume(QUEUE_NAME,false, consumer);

while(true){

QueueingConsumer.Delivery delivery = consumer.nextDelivery();

String message = new String(delivery.getBody());

System.out.println("[x] Received1 '"+message+"'");

Thread.sleep(10);

//返回确认状态

channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);

}

}

}

2.1.4、Receiver2.java

import com.rabbitmq.client.Channel;

import com.rabbitmq.client.Connection;

import com.rabbitmq.client.QueueingConsumer;

import top.san.RabbitMq.util.RabbitmqConnectionUtil;

import java.io.IOException;

/**

* Created by san

*/

public class Receiver2 {

private final static  String QUEUE_NAME ="queue_work";

public static void main(String[] args) throws IOException, InterruptedException {

Connection connection = RabbitmqConnectionUtil.getConnection();        Channel channel = connection.createChannel();        channel.queueDeclare(QUEUE_NAME,false,false,false,null);

//同一时刻服务器只会发送一条消息给消费者

channel.basicQos(1);

QueueingConsumer consumer = new QueueingConsumer(channel);

channel.basicConsume(QUEUE_NAME,false, consumer);

while(true){

QueueingConsumer.Delivery delivery = consumer.nextDelivery();

String message = new String(delivery.getBody());

System.out.println("[x] Received2 '"+message+"'");

Thread.sleep(1000);

//返回确认状态

channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);

}

}

}

2.1.5、结果

上面的四个程序都运行起来,结果可以看到如下,依据结果分析,可知,同一个消息队列,是可以有多个生产者和消费者的。

3、发布/订阅(fanout)

3.1.1、Sender.java

import com.rabbitmq.client.Channel;

import com.rabbitmq.client.Connection;

import com.rabbitmq.client.ConnectionFactory;

import top.san.RabbitMq.util.RabbitmqConnectionUtil;

public class Sender {

private final static String EXCHANGE_NAME ="test_exchange_fanout";

public static void main(String[] args)

{

try

{            //获取连接

Connection connection = RabbitmqConnectionUtil.getConnection();

//从连接中获取一个通道

Channel channel = connection.createChannel();

//声明交换机(分发:发布/订阅模式)

channel.exchangeDeclare(EXCHANGE_NAME,"fanout");

//发送消息

for(int i = 0; i < 5; i++)

{

String message ="卢本伟广场"+ i;

System.out.println("[send]:"+ message);

//发送消息

channel.basicPublish(EXCHANGE_NAME,"", null, message.getBytes("utf-8"));

Thread.sleep(5 * i);

}

channel.close();

connection.close();

}

catch (Exception e)

{

e.printStackTrace();

}

}

}

3.1.2、Receiver1.java

import com.rabbitmq.client.*;

import top.san.RabbitMq.util.RabbitmqConnectionUtil;

import java.io.IOException;

public class Receiver1 {

//交换机名称

private final static String EXCHANGE_NAME ="test_exchange_fanout";

//队列名称

private static final String QUEUE_NAME    ="test_queue_email";

public static void main(String[] args)

{

try

{

//获取连接

Connection connection = RabbitmqConnectionUtil.getConnection();

//从连接中获取一个通道

final Channel channel = connection.createChannel();

//声明交换机(分发:发布/订阅模式)

channel.exchangeDeclare(EXCHANGE_NAME,"fanout");

//声明队列

channel.queueDeclare(QUEUE_NAME,false,false,false, null);

//将队列绑定到交换机

channel.queueBind(QUEUE_NAME, EXCHANGE_NAME,"");

//保证一次只分发一个

int prefetchCount = 1;

channel.basicQos(prefetchCount);

//定义消费者

DefaultConsumer consumer = new DefaultConsumer(channel)

{

//当消息到达时执行回调方法

@Override

public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,

byte[] body) throws IOException

{

String message = new String(body,"utf-8");

System.out.println("[email] Receive message:"+ message);

try

{

//消费者休息2s处理业务

Thread.sleep(1000);

}

catch (InterruptedException e)

{

e.printStackTrace();

}

finally

{

System.out.println("[1] done");

//手动应答

channel.basicAck(envelope.getDeliveryTag(),false);

}

}

};

//设置手动应答

boolean autoAck =false;

//监听队列

channel.basicConsume(QUEUE_NAME, autoAck, consumer);

}

catch (IOException e)

{

e.printStackTrace();

}

}

}

3.1.3、Receiver2.java

import com.rabbitmq.client.*;

import top.san.RabbitMq.util.RabbitmqConnectionUtil;

import java.io.IOException;

public class Receiver2 {

//交换机名称

private final static String EXCHANGE_NAME ="test_exchange_fanout";

//队列名称

private static final String QUEUE_NAME    ="test_queue_phone";

public static void main(String[] args)

{

try

{

//获取连接

Connection connection = RabbitmqConnectionUtil.getConnection();

//从连接中获取一个通道

final Channel channel = connection.createChannel();

//声明交换机(分发:发布/订阅模式)

channel.exchangeDeclare(EXCHANGE_NAME,"fanout");

//声明队列

channel.queueDeclare(QUEUE_NAME,false,false,false, null);

//将队列绑定到交换机

channel.queueBind(QUEUE_NAME, EXCHANGE_NAME,"");

//保证一次只分发一个

int prefetchCount = 1;

channel.basicQos(prefetchCount);

//定义消费者

DefaultConsumer consumer = new DefaultConsumer(channel)

{

//当消息到达时执行回调方法

@Override

public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,

byte[] body) throws IOException

{

String message = new String(body,"utf-8");

System.out.println("[phone] Receive message:"+ message);

try

{

//消费者休息1s处理业务

Thread.sleep(1000);

}

catch (InterruptedException e)

{

e.printStackTrace();

}

finally

{

System.out.println("[2] done");

//手动应答

channel.basicAck(envelope.getDeliveryTag(),false);

}

}

};

//设置手动应答

boolean autoAck =false;

//监听队列

channel.basicConsume(QUEUE_NAME, autoAck, consumer);

}

catch (IOException e)

{

e.printStackTrace();

}

}

}

3.1.4、结果

从程序运行结果和RabbitMq的后台看出,这样的消息属于广播型,两个不同名的队列的都能收到该消息,只需它们都将自己绑定到同一个交换机,而且,该消息是持久的,只要交换机还在,消费者啥时候上线都能消费它所绑定的交换机,而且只会一个消费者只会消费一次。

4、路由(direct)

1、在前面的示例中,我们已经在创建绑定。您可能会想起类似的代码:

channel.queueBind(queueName,EXCHANGE_NAME,“”);

绑定是交换和队列之间的关系。可以简单地理解为:队列对来自此交换的消息感兴趣。

2、绑定可以采用额外的routingKey参数。为了避免与basic_publish参数混淆,我们将其称为 绑定键。这是我们可以创建带有键的绑定的方法:

channel.queueBind(queueName,EXCHANGE_NAME,“ black”);

直接绑定(密钥直接绑定到单个队列)

多重绑定(相同的绑定密钥绑定多个队列)

不同密钥绑定不同的队列,可以发挥出不同日志级别发送到不同的队列的效果。

4.1.1、Sender

import com.rabbitmq.client.Channel;

import com.rabbitmq.client.Connection;

import top.san.RabbitMq.util.RabbitmqConnectionUtil;

import java.io.IOException;

public class Sender {

private final static String EXCHANGE_NAME ="exchange_direct";

private final static String EXCHANGE_TYPE ="direct";

public static void main(String[] args) throws IOException {

Connection connection = RabbitmqConnectionUtil.getConnection();

Channel channel = connection.createChannel();

channel.exchangeDeclare(EXCHANGE_NAME,EXCHANGE_TYPE);

String message ="那一定是蓝色";

channel.basicPublish(EXCHANGE_NAME,"key2", null, message.getBytes());

System.out.println("[x] Sent '"+message+"'");

channel.close();

connection.close();

}

}

4.1.2、Receiver1.java

import com.rabbitmq.client.Channel;

import com.rabbitmq.client.Connection;

import com.rabbitmq.client.QueueingConsumer;

import top.san.RabbitMq.util.RabbitmqConnectionUtil;

import java.io.IOException;

/**

* Created by san

*/

public class Receiver1 {

private final  static  String QUEUE_NAME ="queue_routing";

private final static String EXCHANGE_NAME ="exchange_direct";

public static void main(String[] args) throws IOException, InterruptedException {

// 获取到连接以及mq通道

Connection connection = RabbitmqConnectionUtil.getConnection();

Channel channel = connection.createChannel();

channel.queueDeclare(QUEUE_NAME,false,false,false,null);

channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"key");

channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"key2");

channel.basicQos(1);

QueueingConsumer consumer = new QueueingConsumer(channel);

channel.basicConsume(QUEUE_NAME,false, consumer);

while(true){

QueueingConsumer.Delivery delivery = consumer.nextDelivery();

String message = new String(delivery.getBody());

System.out.println("[x] Received1 "+message);

Thread.sleep(10);

channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);

}

}

}

4.1.3、Receiver2.java

import com.rabbitmq.client.Channel;

import com.rabbitmq.client.Connection;

import com.rabbitmq.client.QueueingConsumer;

import top.san.RabbitMq.util.RabbitmqConnectionUtil;

import java.io.IOException;

/**

* Created by san

*/

public class Receiver2 {

private final  static  String QUEUE_NAME ="queue_routing2";

private final static String EXCHANGE_NAME ="exchange_direct";

public static void main(String[] args) throws IOException, InterruptedException {

// 获取到连接以及mq通道

Connection connection = RabbitmqConnectionUtil.getConnection();

Channel channel = connection.createChannel();

channel.queueDeclare(QUEUE_NAME,false,false,false,null);

channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"key2");

channel.basicQos(1);

QueueingConsumer consumer = new QueueingConsumer(channel);

channel.basicConsume(QUEUE_NAME,false, consumer);

while(true){

QueueingConsumer.Delivery delivery = consumer.nextDelivery();

String message = new String(delivery.getBody());

System.out.println("[x] Received2 "+message);

Thread.sleep(10);

channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);

}

}

}

4.1.4、结果-总结

有一点要注意是:在direct下,必须是Exchange(交换机)已经存在,消费端的队列才能绑定到Exchange,否则会报错。也就说上面的程序第一次运行时,需先启Sender,才能成功启动Reciver。

5、话题(topic)

话题也是一个持久的消息,只要交换机还在,每个上线的消费者都可以消费一次自己感兴趣的topic。

*(星号)可以代替一个单词。

#(哈希)可以替代零个或多个单词。

5.1.1、Sender.java

import com.rabbitmq.client.Channel;

import com.rabbitmq.client.Connection;

import top.san.RabbitMq.util.RabbitmqConnectionUtil;

import java.io.IOException;

public class Sender {

private final static String EXCHANGE_NAME ="exchange_topic";

private final static String EXCHANGE_TYPE ="topic";

public static void main(String[] args) throws IOException {

Connection connection = RabbitmqConnectionUtil.getConnection();        Channel channel = connection.createChannel();        channel.exchangeDeclare(EXCHANGE_NAME, EXCHANGE_TYPE);        //消息内容

String message ="这里是卢本伟广场";

//第二个参数是topic匹配值

channel.basicPublish(EXCHANGE_NAME,"lbw.nb",null,message.getBytes());

System.out.println("[x] Sent '"+message+"'");

//关通道 关连接

channel.close();

connection.close();

}

}

5.1.2、Receiver1.java

import com.rabbitmq.client.Channel;

import com.rabbitmq.client.Connection;

import com.rabbitmq.client.QueueingConsumer;

import top.san.RabbitMq.util.RabbitmqConnectionUtil;

import java.io.IOException;

public class Receiver1 {

private final static String QUEUE_NAME ="queue_topic";

private final static String EXCHANGE_NAME ="exchange_topic";

private final static String EXCHANGE_TYPE ="topic";

public static void main(String[] args) throws IOException, InterruptedException {

Connection connection = RabbitmqConnectionUtil.getConnection();

Channel channel = connection.createChannel();

channel.queueDeclare(QUEUE_NAME,false,false,false, null);

//第二参数就是去匹配我兴趣的topic

channel.queueBind(QUEUE_NAME, EXCHANGE_NAME,"lbw.nb.*");

channel.basicQos(1);

QueueingConsumer consumer = new QueueingConsumer(channel);

channel.basicConsume(QUEUE_NAME,false, consumer);

while(true){

QueueingConsumer.Delivery delivery = consumer.nextDelivery();

String message = new String(delivery.getBody());

System.out.println("[x] Received1 '"+message +"'");

Thread.sleep(10);

channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);

}

}

}

5.1.3、Receiver2.java

import com.rabbitmq.client.Channel;

import com.rabbitmq.client.Connection;

import com.rabbitmq.client.QueueingConsumer;

import top.san.RabbitMq.util.RabbitmqConnectionUtil;

import java.io.IOException;

public class Receiver2 {

private final static String QUEUE_NAME ="queue_topic2";

private final static String EXCHANGE_NAME ="exchange_topic";

private final static String EXCHANGE_TYPE ="topic";

public static void main(String[] args) throws IOException, InterruptedException {

Connection connection = RabbitmqConnectionUtil.getConnection();

Channel channel = connection.createChannel();

channel.queueDeclare(QUEUE_NAME,false,false,false, null);

//第二参数就是去匹配我兴趣的topic

channel.queueBind(QUEUE_NAME, EXCHANGE_NAME,"lbw.#");

channel.basicQos(1);

QueueingConsumer consumer = new QueueingConsumer(channel);

channel.basicConsume(QUEUE_NAME,false, consumer);

while(true){

QueueingConsumer.Delivery delivery = consumer.nextDelivery();

String message = new String(delivery.getBody());

System.out.println("[x] Received2 '"+message +"'");

Thread.sleep(10);

channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);

}

}

}

5.1.4、结果-分析

话题的特色就是队列可以获取自己感兴趣的话题消息,可以通过通配符*或#来表示匹配所有的感兴趣的字符串。

6、RPC(远程过程调用)

给张图自己体会吧(官网没给示例代码,我也就不写了),就是通过两个交换机实现一个可回调的过程吧。

三、RabbitMq的交换机

RabbitMq是有一个交换机的概念的, 消息(Message)由Client发送,RabbitMQ接收到消息之后通过交换机转发到对应的队列上面。Worker会从队列中获取未被读取的数据处理。这样就可以实现消息的发送者无需知道消息使用者的存在,反之亦然。Direct exchange:直连(路由)交换机,转发消息到routigKey指定的队列

Fanout exchange:扇形交换机,转发消息到所有绑定队列(相当于广播)

Topic exchange:主题交换机,按规则转发消息(很灵活)

Headers exchange:首部交换机

前面的简单类型我们都是忽略了交换机的参数的,如该方法:channel.basicPublish("", QUEUE_NAME, null, message.getBytes());就是这个方法的第一个参数,置空说明使用了默认的交换机。 有几种交换类型可用:direct,topic,headers 和fanout。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容