一寸光阴一寸金,寸金难买寸光阴!

    

初识RabbitMQ

前两天我们讲到了RabbitMQ&docker与软件安装 今天呢,我们就来讲讲RabbitMQ

RabbitMQ是一个开源的消息代理和队列服务器,用来通过普通协议
在完全不同的应用之间共享数据,RabbitMQ是使用Erlang语言来编写
的,并且RabbitMQ是基于AMQP协议的。

RabbitMQ整体架构模型是什么样子的?
RabbitMQ消息是如何流转的?

互联网大厂为什么选择RabbitMQ?

免费,性能高。

哪些大厂再用RabbitMQ,为什么?

滴滴,头条,美团,去哪儿

explain开源、性能优秀,稳定性保障
提供可靠性消息投递模式(confirm) 、返回模式( return )
与SpringAMQP完美的整合、API丰富
Spring对 RabbitMQ提供了原生的API进行了一次再封装,把他的这个功能变的更简单,更易用,扩展性更强,比如池化。
集群模式丰富,表达式配置,HA模式,镜像队列模型
保证数据不丢失的前提做到高可靠性、可用性

RabbitMQ高性能的原因:核心就是采用了Erlang语言。

RabbitMQ高性能的原因?Erlang语言最初在于交换机领域的架构模式,这样使得
RabbitMQ在Broker之间进行数据交互的性能是非常优秀的
Erlang的优点: Erlang有着和原生Socket- 样的延迟

explain其实,在选择MQ的时候,主要有一个非常关键的考量目标,就是它消息入到我们MQ的节点上之后它的延迟以及相应,这个是至关重要的,任何一个架构的选型者或是设计者都会考虑到这一点,你这个MQ到底延迟怎么样,吞吐量怎么样,性能怎么样。

什么是AMQP高级消息队列协议?

AMQP全称: Advanced Message Queuing Protocol

翻译:高级消息队列协议
AMQP相当于 JDBC 就是一个协议
RabbitMQ 相当于 实现 。

        ECMAScript
JavaScript      ActionScript => flash

AMQP定义:是具有现代特征的二进制协议。是一个提供统一消息服务的应用层
标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。

AMQP协议模型

explainServer即RabbitMQ的一个节点。
Virtual host 是一个逻辑概念

AMQP核心概念

Channel:网络信道,几乎所有的操作都在Channel中进行,
Channel是进行消息读写的通道。客户端可建立多个Channel,每个Channel代表一个会话任务。

Message:消息,服务器和应用程序之间传送的数据,由Properties和Body组成。
Properties可以对消息进行修饰,比如消息的优先级、延迟等高级特性; Body则就是消息体内容。

  1. AMQP定义的消息协议由两部分组成:Properties+Body

Virtual host:虚拟地址,用于进行逻辑隔离,最上层的消息路由。
一个Virtual Host里面可以有若千个Exchange和Queue,同-个Virtual
Host里面不能有相同名称的Exchange或Queue

Exchange交换机

Exchange:交换机,接收消息,根据路由键转发消息到绑定的队列

交换机属性Name:交换机名称
Type:交换机类型direct. topic、 fanout. headers
Durability:是否需要持久化,true为持久化

Binding: Exchange和Queue之间的虚拟连接, binding中可以包含routing key

Routing key: 一个路由规则,虚拟机可用它来确定如何路由一一个特定消息

Queue:也称为Message Queue,消息队列,保存消息并将它们转发给消费者

消息队列原理:

explain生产者将消息投送到了 server端的 exchange,exchange将消息往下传递到对列message queue中,
我们的生产者只需要关注把消息投递到指定的exchange即可,我们的消费者也只需要监听指定的对列即可,
生产者不需要关注我要把消息投递到哪个对列,消费者也不需要关注消息是从哪个exchange上来的。

RabbitMQ消息是如何流转的

explain生产者生产一个消息message,投递到 Exchange上,一个Exchange可以绑定多个message queue,
为什么有三个对列中只有一个对列收到了message?
因为exchange有一个路由策略Routingkey,也就是说我们发消息的时候要指定两个非常重要的关键点—属性,

  1. 你这个消息要发到哪个exchange上
  2. 你发消息的时候要带上路由的Routingkey,然后通过exchange和对列建立一个绑定关系,通过路由key把消息路由到一个指定的对列上,然后我们的消费端直接监听对列即可—就能消费了。
  3. 我们交换器去和队列建立关系,用来决定交换器把数据到底扔到哪个队列中去,
  4. 所以,这时不同类型的交换器,它往队列里扔数据的方式不同,我们会演示三种交换器。

1、direct交换器
2、广播交换器, 也叫订阅发布
3、主题交换器

案例

explainRoutigKey本身就是一个匹配规则,直连,就是对列的名字,fanout,不需要,
topic 主题: 靠RoutingKey来确定到底转发到哪个队列。

基本案例 直连

package com.bytedance.rabbitmq;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @author 小优
 * @version 1.0
 * @description
 */
public class Producter {
    private static final String QUEUE_NAME = "rabbit:mq01:queue";
    public static void main(String[] args) throws IOException, TimeoutException {
        //1,创建一个 ConnectionFactory
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("xxx.xxx.xx.200");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");

        //2,通过连接工厂创建真实的连接
        Connection connection = connectionFactory.newConnection();

        //3,通过Connection创建一个 Channel
        Channel channel = connection.createChannel();

        //4,通过Channel发送数据,我们生产者发消息时,只要指定exchange和routingkey
        String msg = "hello";

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

        //不用建立生产者角色,直接通过Channel发送消息
        channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());

        //5,关闭相关连接(重要) 由小到大
        channel.close();
        connection.close();
    }
}

重要参数说明:props和body 上面说了,RabbitMQ它的Message消息组成部分就是props和body。props用于修饰消息的附加属性,body就是我们实际的消息实体。

package com.bytedance.rabbitmq;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @author 小优
 * @version 1.0
 * @description
 */
public class Comsumer {
    private static final String QUEUE_NAME = "rabbit:mq01:queue";
    public static void main(String[] args) throws IOException, TimeoutException {
        //1,创建一个 ConnectionFactory
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("xxx.xxx.xx.200");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");

        //2,通过连接工厂创建真实的连接
        Connection connection = connectionFactory.newConnection();

        //3,通过Connection创建一个 Channel
        Channel channel = connection.createChannel();

        //4,要有一个队列存储消息,客户端才能监听队列,接收消息
        // 声明一个队列
        //durable:true 持久化表示即使服务器重启队列中也不会消失。
        //autoDelete:队列与exchange没有绑定关系,脱离了exchange,会自动删除
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);

        //5, 创建消费者 ,rabbitMQ不需要创建生产者角色,而是直接通过Channel发送消息
        Consumer 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(" [x] Received '" + message + "'");
            }
        };

        //6,设置Channel
        //autoAck 是否自动签收,如果设为false,手动签收
        channel.basicConsume(QUEUE_NAME, true, consumer);
     }
}

explain如果你的对列脱离了exchange ,那么会自动的删除。
arguments:扩展参数。

Direct Exchange :路由键和匹配键完全一样处理路由键。需要将一个队列绑定到交换机上,要求该消息与一个特定的路由键完全匹配。这是一个完整的匹配。
如果一个队列绑定到该交换机上要求路由键 “dog”,则只有被标记为“dog”的消息才被转发,不会转发dog.puppy,
也不会转发dog.guard,只会转发dog。 

Fanout Exchange 不需要路由键(RoutingKey)。你只需要简单的将队列绑定到交换机上。一个发送到交换机的消息都会被转发到与该交换机绑定的所有队列上。很像子网广播,每台子网内的主机都获得了一份复制的消息。Fanout交换机转发消息是最快的。 

explainP代表生产者,就是发消息的一方。 X代表的是路由器交换机,它负责接收发送者发送的消息并将消息转发到订阅它的所有队列上。
红色部分是队列。它如果对某个交换机感兴趣的话,那么就可以把自己绑定到这个交换机上,专业术语叫绑定。

explain交换机X在收到任何消息后,都会直接将消息分发给订阅它的队列。这相当于发布订阅模式。
这是一种最直接的广播形式,直接不BB,路由器接收到什么消息,就转发什么消息到所有订阅它的队列中。
在RabbitMQ中,Fanout类型的交换机(路由器)将会直接将数据转发到绑定它的队列中。
发送到Fanout exchange的所有消息会被转发到与exchange绑定的所有queue,不需要处理路由

代码:

  1. 共三个类,第一个类是发送者,第二个和第三个类是两个接收者。

代码大致思路:我们创建了一个Fanout类型的交换机。再创建了两个队列,这两个队列都绑定了这个Fanout类型的交换机。
然后我们给这个交换机发送消息,查看绑定这个交换机的队列们是否可以接收到数据。

public class Producer {
    private static final Logger LOGGER = LoggerFactory.getLogger(Producer.class);
    private static final String EXCHANGE_NAME = "local::mq02:exchange:e01";
 
    public static void main(String[] args) {
        try {
            //连接管理器:我们的应用程序与RabbitMQ建立连接的管理器。
            ConnectionFactory factory = new ConnectionFactory();
            //设置RabbitMQ服务器地址
            factory.setHost("127.0.0.1");
            //设置帐号密码,默认为guest/guest,所以下面两行可以省略
            factory.setUsername("guest");
            factory.setPassword("guest");
 
            //创建一个连接
            Connection connection = factory.newConnection();
            //创建一个信道
            Channel channel = connection.createChannel();
            //通过信道创建一个交换机
            /**
             * 第一个参数是交换机的名称
             * 第二个参数是交换机的类型 Fanout:
             * fanout:直接把队列绑定到路由器。路由器在收到消息后,直接把消息投递到队列中,不需要路由键。
             */
            channel.exchangeDeclare(EXCHANGE_NAME, ExchangeTypes.FANOUT);
 
            //消息用二进制的方式传输
            final String message = "中国联通股票价格又跌了,不开心";
            final byte[] msg = message.getBytes("UTF-8");
 
            //直接把消息发送到路由器。路由在收到消息后,直接投递到订阅这个路由器的队列
            channel.basicPublish(EXCHANGE_NAME, "", null, msg);
 
            LOGGER.info("消息发送成功:{}", message);
            channel.close();
            connection.close();
        } catch (Exception e) {
            LOGGER.error("an exception was occurred , caused by :{}", e.getMessage());
        }
    }
}

public class Consume01 {
    private static final Logger LOGGER = LoggerFactory.getLogger(Producer.class);
    private static final String EXCHANGE_NAME = "local::mq02:exchange:e01";
    private static final String QUEUE_NAME_01 = "local::mq02:queue:q01";
    private static final String QUEUE_NAME_02 = "local::mq02:queue:q02";
 
    public static void main(String[] args) {
        try {
            //设置RabbitMQ服务器信息
            ConnectionFactory factory = new ConnectionFactory();
            factory.setHost("127.0.0.1");
            Connection connection = factory.newConnection();
            Channel channel = connection.createChannel();
 
            //申明一个队列
            /**
             * 第一个参数是queue:要创建的队列名
             * 第二个参数是durable:是否持久化。如果为true,可以在RabbitMQ崩溃后恢复消息
             * 第三个参数是exclusive:true表示一个队列只能被一个消费者占有并消费
             * 第四个参数是autoDelete:true表示服务器不在使用这个队列是会自动删除它
             * 第五个参数是arguments:其它参数
             */
            channel.queueDeclare(QUEUE_NAME_01, true, false, false, null);
            //声明交换机
            channel.exchangeDeclare(EXCHANGE_NAME, ExchangeTypes.FANOUT);
 
            //在fanout类型的路由器中,路由键无效,所以设计为空字符串
            final String routeKey = "";
            //将这个队列订阅到这个路由器上,表示这个队列对这个路由器感兴趣
            channel.queueBind(QUEUE_NAME_01, EXCHANGE_NAME, routeKey);
 
            Consumer 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");
                    LOGGER.info("收件人一号收到消息:{}", message);
                }
            };
            //队列一确认收到消息
            channel.basicConsume(QUEUE_NAME_01, true, consumer);
 
        } catch (Exception e) {
            LOGGER.error("an exception was occurred , caused by :{}", e.getMessage());
        }
 
    }
}

public class Consume02 {
    private static final Logger LOGGER = LoggerFactory.getLogger(Producer.class);
    private static final String EXCHANGE_NAME = "local::mq02:exchange:e01";
    private static final String QUEUE_NAME_01 = "local::mq02:queue:q01";
    private static final String QUEUE_NAME_02 = "local::mq02:queue:q02";
 
    public static void main(String[] args) {
        try {
            //设置RabbitMQ服务器信息
            ConnectionFactory factory = new ConnectionFactory();
            factory.setHost("127.0.0.1");
            Connection connection = factory.newConnection();
            Channel channel = connection.createChannel();
 
            //申明一个队列
            /**
             * 第一个参数是queue:要创建的队列名
             * 第二个参数是durable:是否持久化。如果为true,可以在RabbitMQ崩溃后恢复消息
             * 第三个参数是exclusive:true表示一个队列只能被一个消费者占有并消费
             * 第四个参数是autoDelete:true表示服务器不在使用这个队列是会自动删除它
             * 第五个参数是arguments:其它参数
             */
            channel.queueDeclare(QUEUE_NAME_02, true, false, false, null);
            //声明交换机
            channel.exchangeDeclare(EXCHANGE_NAME, ExchangeTypes.FANOUT);
 
            //在fanout类型的路由器中,路由键无效,所以设计为空字符串
            final String routeKey = "";
            //将这个队列订阅到这个路由器上,表示这个队列对这个路由器感兴趣
            channel.queueBind(QUEUE_NAME_02, EXCHANGE_NAME, routeKey);
 
            Consumer 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");
                    LOGGER.info("收件人二号收到消息:{}", message);
                }
            };
            //队列一确认收到消息
            channel.basicConsume(QUEUE_NAME_02, true, consumer);
 
        } catch (Exception e) {
            LOGGER.error("an exception was occurred , caused by :{}", e.getMessage());
        }
 
    }
}

Topic Exchange :路右键和绑定键模糊匹配将路由键和某模式进行匹配。此时队列需要绑定要一个模式上。
符号“#”匹配0个或多个词,
符号“*”匹配一个词。因此“audit.#”能够匹配到“audit.irs.corporate”,但是“audit.*” 只会匹配到“audit.irs”。我在RedHat的朋友做了一张不错的图,来表明topic交换机是如何工作的: 

explainP是生产者,是发消息的人。X是topic类型的交换机。它会把消息的路由键取出来,与绑定它的队列做路由键匹配。如果队列关心的路由键能匹配上消息的路由键,则将这个消息投递到这个队列中。
Q1是一个队列,它是关心orange颜色的队列。
O2是一个队列,它是关心rabbit与lazy的队列。
C1是消费者,从Q1队列中获取消息。
C2是消费者,从Q2队列中获取消息。

explain* 可以替代一个单词。
# 可以替换0个或多个单词
上图的基本模型希望表达的意思是,如果生产者发送了一个消息,其路由键是 Routing是 a.orange.a。路由器在收到这个消息后,会把这个消息的路由键与Q1和Q2关注的主题进行模糊匹配。因为*代表一个单词的意思,所以可以匹配到Q1队列。
如果消息的路由键是 lazy.a.b.c ,C2队列关心的主题是 lazy.#,#代表多个单词,所以可以匹配上这个消息的路由键。这个消息会被路由器转发到C2队列中。

explain我们创建了一个Topic类型的交换机,然后在创建了两个队列。
    第一个队列关心的主题是  *.orange.* ,* 代表一个单词的意思。如果消息的路由键能匹配的上这个主题,例如路由键是like.orange.color,那么交换机就会把这条消息转发到第一个队列中。
    第二个队列关心的主题是 lazy.# ,#代表多个的意思。如果消息的路由键能匹配的上这个主题,例如路由键是lazy.body.girl,那么交换机就会把这条消息转发到第二个队列中。

代码

public class Producer {
    private static final Logger LOGGER = LoggerFactory.getLogger(Producer.class);
    private static final String EXCHANGE_NAME = "local::mq04:exchange:e01";
 
    public static void main(String[] args) {
        try {
            ConnectionFactory factory = new ConnectionFactory();
            factory.setHost("127.0.0.1");
            Connection connection = factory.newConnection();
            Channel channel = connection.createChannel();
 
            String message = "topic交换机很有用";
            //声明一个TOPIC类型的交换机
            channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
 
            //channel.basicPublish(EXCHANGE_NAME, "like.orange.color", null, message.getBytes("UTF-8"));
            channel.basicPublish(EXCHANGE_NAME, "lazy.boy.girl", null, message.getBytes("UTF-8"));
 
            LOGGER.info("消息发送成功:{}", message);
            channel.close();
            connection.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class Consumer01 {
    private static final Logger LOGGER = LoggerFactory.getLogger(Producer.class);
    private static final String EXCHANGE_NAME = "local::mq04:exchange:e01";
    private static final String QUEUE_NAME_01 = "local::mq04:queue:q01";
    private static final String QUEUE_NAME_02 = "local::mq04:queue:q02";
 
    public static void main(String[] args) {
        try {
            ConnectionFactory factory = new ConnectionFactory();
            factory.setHost("127.0.0.1");
            Connection connection = factory.newConnection();
            Channel channel = connection.createChannel();
 
            //声明一个TOPIC类型的交换机
            channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
            //申明两个个队列
            /**
             * 第一个参数是queue:要创建的队列名
             * 第二个参数是durable:是否持久化。如果为true,可以在RabbitMQ崩溃后恢复消息
             * 第三个参数是exclusive:true表示一个队列只能被一个消费者占有并消费
             * 第四个参数是autoDelete:true表示服务器不在使用这个队列是会自动删除它
             * 第五个参数是arguments:其它参数
             */
            channel.queueDeclare(QUEUE_NAME_01, true, false, false, null);
            channel.queueDeclare(QUEUE_NAME_02, true, false, false, null);
 
            final String ROUTING_KEY_ORANGE = "*.orange.*";
            final String ROUTING_KEY_LAZY = "lazy.#";
            //队列一对ORANGE感兴趣,匹配  XXX.orange.XXX 的消息
            channel.queueBind(QUEUE_NAME_01, EXCHANGE_NAME, ROUTING_KEY_ORANGE);
            //队列二对LAZY感兴趣,匹配  lazy.XXX.XXX.XXX
            channel.queueBind(QUEUE_NAME_02, EXCHANGE_NAME, ROUTING_KEY_LAZY);
 
            Consumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    final String message = new String(body, "UTF-8");
                    LOGGER.info("队列一收到消息:{}", message);
                }
            };
 
            //队列一确认消息
            channel.basicConsume(QUEUE_NAME_01, true, consumer);
 
        } catch (Exception e) {
            LOGGER.error("an exception was occurred , caused by :{}", e.getMessage());
        }
    }
}

public class Consumer02 {
    private static final Logger LOGGER = LoggerFactory.getLogger(Producer.class);
    private static final String EXCHANGE_NAME = "local::mq04:exchange:e01";
    private static final String QUEUE_NAME_01 = "local::mq04:queue:q01";
    private static final String QUEUE_NAME_02 = "local::mq04:queue:q02";
 
    public static void main(String[] args) {
        try {
            ConnectionFactory factory = new ConnectionFactory();
            factory.setHost("127.0.0.1");
            Connection connection = factory.newConnection();
            Channel channel = connection.createChannel();
 
            //声明一个TOPIC类型的交换机
            channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
            //申明两个个队列
            /**
             * 第一个参数是queue:要创建的队列名
             * 第二个参数是durable:是否持久化。如果为true,可以在RabbitMQ崩溃后恢复消息
             * 第三个参数是exclusive:true表示一个队列只能被一个消费者占有并消费
             * 第四个参数是autoDelete:true表示服务器不在使用这个队列是会自动删除它
             * 第五个参数是arguments:其它参数
             */
            channel.queueDeclare(QUEUE_NAME_01, true, false, false, null);
            channel.queueDeclare(QUEUE_NAME_02, true, false, false, null);
 
            final String ROUTING_KEY_ORANGE = "*.orange.*";
            final String ROUTING_KEY_LAZY = "lazy.#";
            //队列一对ORANGE感兴趣,匹配  XXX.orange.XXX 的消息
            channel.queueBind(QUEUE_NAME_01, EXCHANGE_NAME, ROUTING_KEY_ORANGE);
            //队列二对LAZY感兴趣,匹配  lazy.XXX.XXX.XXX
            channel.queueBind(QUEUE_NAME_02, EXCHANGE_NAME, ROUTING_KEY_LAZY);
 
            Consumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    final String message = new String(body, "UTF-8");
                    LOGGER.info("队列二收到消息:{}", message);
                }
            };
 
            //队列一确认消息
            channel.basicConsume(QUEUE_NAME_02, true, consumer);
 
        } catch (Exception e) {
            LOGGER.error("an exception was occurred , caused by :{}", e.getMessage());
        }
    }
}

总结

Topic类型的交换机 任何发送到Topic Exchange的消息都会被转发到所有关心RouteKey中指定话题的Queue上。使用的是一种正则匹配规则。生产者会发送一个带路由键的消息。Exchange会将消息转发到所有关注主题能与RouteKey模糊匹配的队列。

Fanout类型的交换机        发送到exchange的所有消息会被转发到与exchange绑定的所有queue,不需要处理路由 

Direct类型的交换机需要处理路由键。该交换机收到消息后会把消息发送到接收指定routing-key的queue中。

所有原创文章采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。
您可以自由的转载和修改,但请务必注明文章来源并且不可用于商业目的。
本站部分内容收集于互联网,如果有侵权内容、不妥之处,请联系我们删除。敬请谅解!

支持一下呗!Y(・∀・)Y
  • QQ
  • AliPay
  • WeChat

评论已关闭

  Timeline:成长

成长就是要多学、多问、多看、多记

updated on :

  关于博主

blog名-小优,平时喜欢跑步,喜欢听一些温柔,轻松的音乐,喜欢接触新事物,对自己的能力有一个很好的认知,人生在于折腾,一寸光阴一寸金,寸金难买寸光阴!我就是我,颜色不一样的烟火!

  近期评论

有种脾气叫,不放弃。

梦想是注定孤独的旅行,路上少不了质疑和嘲笑,但那又怎样,哪怕遍体鳞伤也要活的漂亮。

不管现在有多么艰辛,我们也要做个生活的舞者。

命运从来不会同情弱者。

不怕万人阻挡在前方,只怕自己先行投降。