RabbitMQ笔记二十三 :异步RPC之二(使用Spring AMQP实现RPC异步调用)

使用Spring AMQP实现RPC异步调用

示列

服务器端

应用启动类代码,

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;

import java.util.concurrent.TimeUnit;

@ComponentScan
public class Application {
    public static void main(String[] args) throws Exception{
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
        System.out.println("===server startup======");
        TimeUnit.SECONDS.sleep(120);
        context.close();
    }
}

配置类:
监听了sms队列,这个队列将会是客户端请求消息发送到的队列,配置了适配器,适配器中去调用服务,适配器返回的值就是服务端返回给客户端的RPC调用的结果

import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MQConfig {

    @Bean
    public ConnectionFactory connectionFactory(){
        CachingConnectionFactory factory = new CachingConnectionFactory();
        factory.setUri("amqp://zhihao.miao:123456@192.168.1.131:5672");
        return factory;
    }

    @Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory){
        RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
        return rabbitAdmin;
    }

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        return rabbitTemplate;
    }

    @Bean
    public SimpleMessageListenerContainer messageListenerContainer(ConnectionFactory connectionFactory){
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        container.setQueueNames("sms");
        container.setAcknowledgeMode(AcknowledgeMode.NONE);
        //使用适配器的方式
        container.setMessageListener(new MessageListenerAdapter(new SendSMSHandler()));
        return container;
    }
}

处理器,处理器中调用具体的服务,我们此列子中处理器方法返回的值是boolean类型

import java.util.concurrent.TimeUnit;

public class SendSMSHandler {

    public boolean handleMessage(byte[] body){
        String _body = new String(body);
        System.out.println(_body);
        String[] sms = _body.split(":");
        String phone = sms[0];
        String content = sms[1];

        boolean is = SendSMSTool.sendSMS(phone,content);

        try {
            TimeUnit.SECONDS.sleep(6);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return is;
    }
}

服务接口

public class SendSMSTool {

    public static boolean sendSMS(String phone,String content){
        System.out.println("发送短信内容:【"+content+"】到手机号:"+phone);
        return phone.length() > 6;
    }
}

服务端步骤

  1. 消息处理方法,一定要有返回值,这个返回值就是就是server回复客户端的结果。比如我们SendSMSHandler.handleMessage方法返回的值。

客户端
应用启动类:

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.support.CorrelationData;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

@ComponentScan
public class Application {
    public static void main(String[] args) throws Exception{
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Application.class);

        RabbitTemplate rabbitTemplate = context.getBean(RabbitTemplate.class);

        //设置超时时间,单位是ms
        rabbitTemplate.setReplyTimeout(10000);

        String phone = "15634344321";
        String content ="周年庆,五折优惠";

        MessageProperties messageProperties = new MessageProperties();
        Message message = new Message((phone+":"+content).getBytes(),messageProperties);

        //rabbitTemplate.send("","sms",message);

        Message reply = rabbitTemplate.sendAndReceive("","sms",message,
                new CorrelationData(UUID.randomUUID().toString()));

        System.out.println(reply);
        System.out.println("message,body:"+new String(reply.getBody()));
        System.out.println("message,properties:"+reply.getMessageProperties());

        TimeUnit.SECONDS.sleep(30);
        context.close();
    }
}

配置类代码:

import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MQConfig {

    @Bean
    public ConnectionFactory connectionFactory(){
        CachingConnectionFactory factory = new CachingConnectionFactory();
        factory.setUri("amqp://zhihao.miao:123456@192.168.1.131:5672");
        return factory;
    }

    @Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory){
        RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
        return rabbitAdmin;
    }

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        return rabbitTemplate;
    }
}

如果服务端睡眠6s,则客户端通过sendAndReceive方法接收到的Message对象为空,怎样设置呢?
客户端通过设置rabbitTemplate.setReplyTimeout(10000);就可以了。

客户端步骤

  1. 使用sendAndReceive方法发送消息,该方法返回一个Message对象,该对象就是server返回的结果
  2. sendAndReceive如果超过5s还没有收到结果,则返回null,这个超时时间可以通过rabbitTemplate.setReplyTimeout()来进行设置
  3. server端返回的结果一定要注意,和MessageConverter有关,默认的org.springframework.amqp.support.converter.SimpleMessageConverter会把基本的数据类型转换成Serializable对象,这样的话,client端接收的也是序列化的java对象,所以,需要合理设置MessageConverter

示列代码中服务端返回给客户端的是Boolean类型,

启动服务端客户端代码:
服务器打印控制台打印:

15634344321:周年庆,五折优惠
发送短信内容:【周年庆,五折优惠】到手机号:15634344321

客户端控制台打印:

message,body:����sr��java.lang.Boolean� r�՜�����Z��valuexp�
message,properties:MessageProperties [headers={}, timestamp=null, messageId=null, userId=null, receivedUserId=null, appId=null, clusterId=null, type=null, correlationId=null, correlationIdString=null, replyTo=null, contentType=application/x-java-serialized-object, contentEncoding=null, contentLength=0, deliveryMode=null, receivedDeliveryMode=PERSISTENT, expiration=null, priority=0, redelivered=false, receivedExchange=, receivedRoutingKey=amq.rabbitmq.reply-to.g2dkAB5yYWJiaXRAaVpicDFqY3d4N3NmYjFud3pyZWh5NloAAFu0AAAABwI=.kHL9zxtdQmtcxl0mQF8zrg==, receivedDelay=null, deliveryTag=1, messageCount=null, consumerTag=null, consumerQueue=null]

我们发现客户端接收到的数据乱码,将服务端的处理器的返回值改写成String类型的,

import java.util.concurrent.TimeUnit;

public class SendSMSHandler {

    public String handleMessage(byte[] body){
        String _body = new String(body);
        System.out.println(_body);
        String[] sms = _body.split(":");
        String phone = sms[0];
        String content = sms[1];

        boolean is = SendSMSTool.sendSMS(phone,content);

        try {
            TimeUnit.SECONDS.sleep(6);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return is ? "success":"false";
    }
}

此时发现客户端接收的消息数据没有乱码,原因何在?我们总结一下就是服务器端处理器返回给客户端boolean类型,那么返回的消息数据就乱码,如果返回的是String类型,那么返回的消息数据就不会乱码。

之前我们学习了org.springframework.amqp.support.converter.MessageConverter接口,当客户端向服务端发送消息的时候会进行消息类型转换,调用了fromMessage方法,而当服务器返回给客户端的时候会将服务端的对象转换成Message对象,很明显调用的是toMessage方法。

我们知道org.springframework.amqp.support.converter.MessageConverter接口的默认实现是org.springframework.amqp.support.converter.SimpleMessageConverter,而toMessage方法的实现是在其继承的对象AbstractMessageConverter中,

我们看到其AbstractMessageConverter.toMessage方法的实现逻辑是:

    @Override
    public final Message toMessage(Object object, MessageProperties messageProperties)
            throws MessageConversionException {
        if (messageProperties == null) {
            messageProperties = new MessageProperties();
        }
        //将对象转换成Message对象
        Message message = createMessage(object, messageProperties);
        messageProperties = message.getMessageProperties();
        if (this.createMessageIds && messageProperties.getMessageId() == null) {
            messageProperties.setMessageId(UUID.randomUUID().toString());
        }
        return message;
    }

createMessage方法就是将对象转换成Message对象,

    /**
     * Creates an AMQP Message from the provided Object.
     */
    @Override
    protected Message createMessage(Object object, MessageProperties messageProperties) throws MessageConversionException {
        byte[] bytes = null;
        if (object instanceof byte[]) {
            bytes = (byte[]) object;
            messageProperties.setContentType(MessageProperties.CONTENT_TYPE_BYTES);
        }
        else if (object instanceof String) {
            try {
                bytes = ((String) object).getBytes(this.defaultCharset);
            }
            catch (UnsupportedEncodingException e) {
                throw new MessageConversionException(
                        "failed to convert to Message content", e);
            }
            messageProperties.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN);
            messageProperties.setContentEncoding(this.defaultCharset);
        }
        //因为boolean类型实现Serializable接口,所以会将其序列化
        else if (object instanceof Serializable) {
            try {
                bytes = SerializationUtils.serialize(object);
            }
            catch (IllegalArgumentException e) {
                throw new MessageConversionException(
                        "failed to convert to serialized Message content", e);
            }
            messageProperties.setContentType(MessageProperties.CONTENT_TYPE_SERIALIZED_OBJECT);
        }
        if (bytes != null) {
            messageProperties.setContentLength(bytes.length);
        }
        return new Message(bytes, messageProperties);
    }

我们在程序中将序列化对象直接转换成字符串所以乱码,而返回的是String类型的情形的时候先将字符串转换成相应的字节数组,然后返回new Message(bytes, messageProperties);就不会乱码。

继续探讨,当我们服务端返回的是一个对象的时候,客户端会返回空
返回的对象:

public class SendStatus {

    private String phone;

    private String result;

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getResult() {
        return result;
    }

    public void setResult(String result) {
        this.result = result;
    }
}

将该对象返回:

public class SendSMSHandler {

    public SendStatus handleMessage(byte[] body){
        String _body = new String(body);
        System.out.println(_body);
        String[] sms = _body.split(":");
        String phone = sms[0];
        String content = sms[1];

        boolean is = SendSMSTool.sendSMS(phone,content);
        SendStatus sendStatus = new SendStatus();
        sendStatus.setPhone(phone);
        sendStatus.setResult(is ? "SUCCESS":"FAILURE");
        return sendStatus;
    }
}

服务端控制台:

15634344321:周年庆,五折优惠
发送短信内容:【周年庆,五折优惠】到手机号:15634344321

客户端:

message,body:
message,properties:MessageProperties [headers={}, timestamp=null, messageId=null, userId=null, receivedUserId=null, appId=null, clusterId=null, type=null, correlationId=null, correlationIdString=null, replyTo=null, contentType=application/octet-stream, contentEncoding=null, contentLength=0, deliveryMode=null, receivedDeliveryMode=PERSISTENT, expiration=null, priority=0, redelivered=false, receivedExchange=, receivedRoutingKey=amq.rabbitmq.reply-to.g2dkAB5yYWJiaXRAaVpicDFqY3d4N3NmYjFud3pyZWh5NloAAFxBAAAABwI=.01fOGW/nvS2nz6gKza+cjg==, receivedDelay=null, deliveryTag=1, messageCount=null, consumerTag=null, consumerQueue=null]

原因何在,因为我们定义的SendStatus不走createMessage中的所有if分支,最后返回的是null,怎么解决呢,要么自己去定义一个org.springframework.amqp.support.converter.MessageConverter实现,要么换一个默认的org.springframework.amqp.support.converter.MessageConverter实现。

改造后的示列

使用AMQP自带的消息类型转换器Jackson2JsonMessageConverter
服务端

应用启动类,

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;

import java.util.concurrent.TimeUnit;

@ComponentScan
public class Application {
    public static void main(String[] args) throws Exception{
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
        System.out.println("===server startup======");
        TimeUnit.SECONDS.sleep(120);
        context.close();
    }
}

配置类,添加自定义的消息转换器

import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MQConfig {

    @Bean
    public ConnectionFactory connectionFactory(){
        CachingConnectionFactory factory = new CachingConnectionFactory();
        factory.setUri("amqp://zhihao.miao:123456@192.168.1.131:5672");
        return factory;
    }

    @Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory){
        RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
        return rabbitAdmin;
    }

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        return rabbitTemplate;
    }

    @Bean
    public SimpleMessageListenerContainer messageListenerContainer(ConnectionFactory connectionFactory){
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        container.setQueueNames("sms");
        container.setAcknowledgeMode(AcknowledgeMode.NONE);
        //使用适配器的方式
        container.setMessageListener(new MessageListenerAdapter(new SendSMSHandler(),new Jackson2JsonMessageConverter()));
        return container;
    }
}

处理器handler,返回自定义的SendStatus类型

import java.util.concurrent.TimeUnit;

public class SendSMSHandler {

    public SendStatus handleMessage(byte[] body){
        String _body = new String(body);
        System.out.println(_body);
        String[] sms = _body.split(":");
        String phone = sms[0];
        String content = sms[1];

        boolean is = SendSMSTool.sendSMS(phone,content);
        SendStatus sendStatus = new SendStatus();
        sendStatus.setPhone(phone);
        sendStatus.setResult(is ? "SUCCESS":"FAILURE");

        try {
            TimeUnit.SECONDS.sleep(6);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return sendStatus;
    }
}

接口服务,

public class SendSMSTool {

    public static boolean sendSMS(String phone,String content){
        System.out.println("发送短信内容:【"+content+"】到手机号:"+phone);
        return phone.length() > 6;
    }
}

服务端返回的对象,

public class SendStatus {
    private String phone;

    private String result;

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getResult() {
        return result;
    }

    public void setResult(String result) {
        this.result = result;
    }
}

客户端
应用启动类,

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.support.CorrelationData;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

@ComponentScan
public class Application {
    public static void main(String[] args) throws Exception{
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Application.class);

        RabbitTemplate rabbitTemplate = context.getBean(RabbitTemplate.class);

        //设置超时时间,单位是ms
        rabbitTemplate.setReplyTimeout(10000);

        String phone = "15634344321";
        String content ="周年庆,五折优惠";

        MessageProperties messageProperties = new MessageProperties();
        Message message = new Message((phone+":"+content).getBytes(),messageProperties);

        //rabbitTemplate.send("","sms",message);

        Message reply = rabbitTemplate.sendAndReceive("","sms",message,
                new CorrelationData(UUID.randomUUID().toString()));

        System.out.println(reply);
        System.out.println("message,body:"+new String(reply.getBody()));
        System.out.println("message,properties:"+reply.getMessageProperties());

        TimeUnit.SECONDS.sleep(30);
        context.close();
    }
}

配置类

import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MQConfig {

    @Bean
    public ConnectionFactory connectionFactory(){
        CachingConnectionFactory factory = new CachingConnectionFactory();
        factory.setUri("amqp://zhihao.miao:123456@192.168.1.131:5672");
        return factory;
    }

    @Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory){
        RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
        return rabbitAdmin;
    }

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        return rabbitTemplate;
    }
}

启动服务器客户端,客户端返回

message,body:{"phone":"15634344321","result":"SUCCESS"}
message,properties:MessageProperties [headers={__TypeId__=rpc.server.SendStatus}, timestamp=null, messageId=null, userId=null, receivedUserId=null, appId=null, clusterId=null, type=null, correlationId=null, correlationIdString=null, replyTo=null, contentType=application/json, contentEncoding=UTF-8, contentLength=0, deliveryMode=null, receivedDeliveryMode=PERSISTENT, expiration=null, priority=0, redelivered=false, receivedExchange=, receivedRoutingKey=amq.rabbitmq.reply-to

返回了SendStatus的JSON格式,因为使用了Jackson2JsonMessageConverter消息类型转换器。

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,358评论 6 343
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,503评论 6 13
  • 转眼间回望自己以不或之年,过去,现在还是未来不知觉的突然不怎么喜欢再去想得太多了。生活有点象万花筒般总会有...
    火热如冰阅读 189评论 0 2
  • 对号入座
    0nePIECE阅读 189评论 0 1