La spécification JMS ActiveMQ

Qu'est-ce que JMS?

Java JMS message à savoir service (Java Message Service) Interface de programme d'application, API fait référence à la communication asynchrone est effectuée entre les deux applications, ce qui fournit une interface commune aux protocoles standards et des services de messagerie, y compris créer, envoyer, lecture messages, etc., le soutien au développement d'applications Java. En JavaEE, lorsque deux utilisations d'application JMS de communiquer entre eux ne sont pas directement reliés, mais associé par un composant de service de messagerie commune pour obtenir le découplage / écrêtage asynchrone.
Insérer ici l'image Description

Structure de composition JMS

  • JMS Provider: implémenter des interfaces standardisées JSM et middleware de messagerie, est ce que nous appelons le serveur MQ
  • JMS Producteur: producteurs de nouvelles, créer et envoyer des messages application client JSM
  • JMS consommateurs: nouvelles des consommateurs, recevoir et messages processus application client JSM
  • JSM Message: message transmis par le JSM
    • En-tête
    • corps Nouvelles
    • Propriétés du message

En-tête

JMS tête de message propriétés communes:

  • JMSDestination: destination du message est envoyé, principalement la file d'attente et sujet
  • JMSDeliveryMode: Message mode persistant
  • JMSExpiration: expiration du message temps
  • JMSPriority: priorité du message
  • Identifiant unique pour l'ID de message, le message: JMSMessageID

Lors de l' envoi d' un producteur de messages peut définir ces propriétés, les consommateurs consomment les messages peuvent obtenir ces propriétés;
Exemple de code:

TextMessage textMessage = session.createTextMessage("hello_activeMQ_Server" + i);
// 单独设置消息的目的地,可以是queue或topic
textMessage.setJMSDestination(queue);

/**
* 设置消息持久化模式
* 持久模式:DeliveryMode.PERSISTENT
* 非持久化模式:DeliveryMode.NON_PERSISTENT
* 消息默认是持久化的
*
* 一条持久化的消息:应该被传送“一次仅仅一次”,这就意味着如果JMS提供者出现故障,该消息并不会丢失,它会在服务器恢复之后再次传递。
* 一条非持久的消息:最多会传递一次,这意味着服务器出现故障,该消息将会永远丢失。
*/
textMessage.setJMSDeliveryMode(DeliveryMode.NON_PERSISTENT);

/**
* 设置消息在一定时间后过期,默认是永不过期。
* 消息过期时间,等于Destination的send方法中的timeToLive值加上发送时刻的GMT时间值。
* 如果timeToLive值等于0,则JMSExpiration被设为0,表示该消息永不过期。
*  如果发送后,在消息过期时间之后还没有被发送到目的地,则该消息会被清除。
*/
textMessage.setJMSExpiration(1000);

/**
* 设置消息优先级
* 从0-9十个级别,0-4是普通消息,5-9是加急消息
* JMS不要求MQ严格按照这十个优先级发送消息但必须保证加急消息要先于普通消息到达。默认是4级。
*/
textMessage.setJMSPriority(9);

/**
* 消息唯一标识符,MQ会给我们默认生成一个,也可以自己指定,需全局唯一
*/
textMessage.setJMSMessageID("123");

messageProducer.send(textMessage);

corps Nouvelles

JMS peut créer cinq types de message, lorsque le type de message du message reçu doit être envoyé une correspondance;
Insérer ici l'image Description

  • TextMessage: string message contenant une chaîne;
  • MapMessage: types Carte des messages, des paires clé-valeur, le type String key, la valeur des types Java;
  • BytesMessage: message de matrice binaire comprend un byte [];
  • StreamMessage: Java flux de données de message, le flux de données en utilisant une procédure standard de remplissage et de la lecture
  • ObjectMessage: objets de type de message, comprenant une séquence de la cible

Généralement utilisé TextMessage et MapMessage, Montrons que l'utilisation de deux types de messages:

// 发送
TextMessage textMessage = session.createTextMessage("hello_activeMQ_Server" + i);
messageProducer.send(textMessage);

MapMessage mapMessage = session.createMapMessage();
mapMessage.setString("k1","mapMessage");
mapMessage.setInt("k2",1);

messageProducer.send(mapMessage);

// 接收
messageConsumer.setMessageListener((message) -> {
    if (message != null) {
        if (message instanceof TextMessage) {
            TextMessage textMessage = (TextMessage) message;
            try {
                System.out.println("消费掉的消息 -> " + textMessage.getText());
            } catch (JMSException e) {
                e.printStackTrace();
            }
        } else if (message instanceof MapMessage) {
            MapMessage mapMessage = (MapMessage) message;
            try {
                System.out.println("消费掉的Map消息 -> " + mapMessage.getString("k1"));
                System.out.println("消费掉的Map消息 -> " + mapMessage.getInt("k2"));
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }

    }
});

Propriétés du message

Si les informations véhiculées dans l'en-tête du message ne suffit pas pour répondre à vos besoins, vous pouvez également effectuer plus d'informations par les propriétés du message, souvent utilisé pour identifier / désaccentuation / étiquetage de mise au point et d'autres opérations;

Ils sont sous la forme de noms d'attributs et les valeurs des pays développés. La propriété peut être obtenue pour l'en-tête de message étendu, attributs de message pour spécifier les informations d'en-tête supplémentaires ne sont pas inclus, comme un sélecteur de message peut être spécifié dans la propriété. propriétés de message supplémentaires du message comme un message peuvent être attribués à la même tête. Ils permettent aux développeurs d'ajouter des informations supplémentaires sur l'opaque du message. Ils sont également pour exposer le message de données pour une utilisation dans un filtre de message sélectionné.

Selon les types de données transporte des attributs de message, un procédé est fourni pour sélectionner le approprié
Insérer ici l'image Description

// 发送
TextMessage textMessage = session.createTextMessage("hello_activeMQ_Server" + i);
textMessage.setStringProperty("user","zhangsan");
textMessage.setBooleanProperty("isLogin", true);
messageProducer.send(textMessage);

// 获取
TextMessage textMessage = (TextMessage) message;
try {
    System.out.println("消费掉的消息 -> " + textMessage.getText());
    System.out.println("消费掉的消息属性 -> " + textMessage.getStringProperty("user"));
    System.out.println("消费掉的消息属性 -> " + textMessage.getBooleanProperty("isLogin"));
} catch (JMSException e) {
    e.printStackTrace();
}

Nouvelles endurance spécifique

Quand un message est envoyé à une destination persistante dans le message, le serveur de messages enregistre sa persistance, si les consommateurs n'ont pas eu consommation de temps en cas de défaillance du serveur, la machine macro, et lorsque le service rétabli à nouveau, ce message ne sera pas perdu existeront toujours, et de veiller à ce que (sujet à enregistrer avant aussi, la file d'attente ne) pas consommée avant que le consommateur de nouvelles nouvelles des consommateurs peut réussir dans cette façon d'assurer la fiabilité du message;

File d' attente: file d' attente non persistante, lorsque le serveur tombe en panne, le message n'existe pas (le message est perdu). Même les non-persistante, les consommateurs ne sont pas en ligne, le message ne sera pas perdu, en attendant que les consommateurs en ligne, ou de recevoir des messages.
file d' attente persistante, lorsque le serveur est en panne, le message reste. La file d' attente de message par défaut est persistant.
Les messages persistants, afin de veiller à ce que ces messages sont transmis qu'une seule fois et utilisé avec succès une fois. Pour ces messages, la fiabilité est une considération primordiale.
Un autre aspect important est d'assurer la fiabilité de la diffusion des messages persistants à la destination, le service de messagerie avant de les transmettre au consommateur ne perd pas les messages.

Message Queue persistance producteurs seulement besoin de définir, comme suit:

// 根据session创建队列
Queue queue = session.createQueue(QUEUE_NAME);

// 创建消息的生产者并指定队列
messageProducer = session.createProducer(queue);

// 持久化:当服务器出现故障,服务恢复后消息依然存在
// messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT);
// 非持久化:当服务器出现故障,服务恢复后消息不存在
// messageProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);

: Sujet est la valeur par défaut non persistante, car la production des producteurs de nouvelles, les consommateurs aussi en ligne, afin que les consommateurs peuvent consommer le message. messages sujet persistent, tant que les consommateurs avaient enregistré avec le serveur MQ, tous les producteurs ont annoncé avec succès, le consommateur peut recevoir, si le serveur MQ est en panne ou le consommateur n'est pas en ligne.

Sujet persistance message ,, les producteurs et les consommateurs ont besoin de mettre en place, comme suit:
Fabricant:

// 根据session创建主题
Topic topic = session.createTopic(TOPIC_NAME);

// 创建消息的生产者并指定队列
messageProducer = session.createProducer(topic);

// 设置持久化topic 
 messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT);
 // 设置持久化topic之后再启动连接
 connection.start();

Les consommateurs:

// 通过连接工厂,获得connection,持久化的topic必须在消费者创建并创建好订阅者完成后调用start
connection = activeMQConnection.createConnection();
// 设置客户端id,向MQ服务器注册自己
connection.setClientID("Consumer_Persistence");
/**
 * 创建session,得到会话
 * 两个参数transacted=事务,acknowledgeMode=确认模式(签收)
 */
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 根据session创建主题
Topic topic = session.createTopic(TOPIC_NAME);
// 创建一个topic订阅者对象,参数以:topic;参数二:订阅者名称
session.createDurableSubscriber(topic,"订阅者");

//  打开连接
connection.start();

Lorsque les consommateurs souscrivent à un thème persistant, vous pouvez activeMQ console Subscriberspage pour voir:
Insérer ici l'image Description
A ce stade , nous avons arrêté les consommateurs, lancer le producteur d'envoyer un message:
Insérer ici l'image Description
Insérer ici l'image Description
Dans le tableau vous pouvez voir qu'il est un consommateur, l'équipe de nouvelles numéro trois, le nombre de messages est 0, qui est, pas de nouvelles dépenses de consommation, les abonnés de nouvelles est hors - ligne;

À ce stade , nous devons commencer l' abonné de messagerie, voir si vous pouvez recevoir le message:
Insérer ici l'image Description
Insérer ici l'image Description
vous pouvez voir que les consommateurs peuvent recevoir un message envoyé par le producteur, le nombre de messages de 0 à 3

choses messages

En parlant des choses, nous ne pouvons pas être au courant, dans le développement de notre projet par jour, l'insertion de données, modification, impliquera les choses de la même façon, activeMQ soutenir aussi des choses;

Le producteur après l'ouverture de la transaction, une méthode commit, ces messages sont vraiment soumis. Méthode Commit n'est pas exécutée, ces messages ne sont pas soumis. Le procédé de restauration, avant que le message est annulée. Les producteurs de mécanisme de transaction, plus élevé que le mécanisme de réception, lorsque le producteur transaction tourné, mécanisme de réception n'est plus importante.

Après l'ouverture de la consommation, une méthode commettras, ces messages sont vraiment des consommateurs. Méthode Commit n'est pas exécutée, ces messages ne sont pas marqués a consommé, la prochaine sera consommée. La méthode de restauration, la restauration n'est pas effectuée avant la logique métier, mais avant la possibilité de revenir le message, le message est annulée, la prochaine sera consommée. Les consommateurs utilisent COMMIT et méthodes d'annulation, même en violation d'un consommateur ne peut consommer un principe de message.

Les producteurs et les consommateurs de choses indépendamment, peuvent être activés de façon indépendante sur, il n'y a pas d'association

Fabricant Exemple de code:

package com.chaytech.activemq.transaction;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

/**
 * 消息生产者(开启事物)
 *
 * @author Chency
 * @email [email protected]
 * @Date 2020/03/21 22:20
 */
public class JmsProducer {

    private static final String BROKER_URL = "tcp://192.168.0.166:61616"; // activeMQ服务地址
    private static final String QUEUE_NAME = "test_queue_1"; // 消息队列名称

    public static void main(String[] args) {
        /**
         * 根据我们指定activeMQ服务地址来创建activeMQ工厂
         * 如果我们自己不指定activeMQ服务地址则默认为本机
         */
        ActiveMQConnectionFactory activeMQConnection = new ActiveMQConnectionFactory(BROKER_URL);
        Connection connection = null;
        Session session = null;
        MessageProducer messageProducer = null;
        try {
            // 通过连接工厂,获得connection
            connection = activeMQConnection.createConnection();
            //  打开连接
            connection.start();
            /**
             * 创建session,得到会话
             * 两个参数transacted=事务,true开启,acknowledgeMode=确认模式(签收)
             */
            session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
            // 根据session创建队列
            Queue queue = session.createQueue(QUEUE_NAME);

            // 创建消息的生产者并指定队列
            messageProducer = session.createProducer(queue);
            // 通过使用消息生产者,生产消息,发送到MQ的队列里面
            for (int i = 1; i <= 3; i++) {
                TextMessage textMessage = session.createTextMessage("hello_activeMQ_Server" + i);
                messageProducer.send(textMessage);
            }

            // 开启事务后,使用commit提交事务,这样这批消息才能真正的被提交。
            session.commit();
            System.out.println("**********msg send sucess**********");
        } catch (JMSException e) {
            e.printStackTrace();
            // 当出现异常了,我们可以在catch代码块中回滚。这样这批发送的消息就能回滚。
            try {
                session.rollback();
            } catch (JMSException e1) {
            }
        } finally {
            try {
                messageProducer.close();
                session.close();
                connection.close();
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }
    }
}

échantillon Code de la consommation:

package com.chaytech.activemq.transaction;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

/**
 * 消息消费者(开启事物)
 *
 * @author Chency
 * @email [email protected]
 * @Date 2020/03/22 10:45
 */
public class JmsConsumer {
    private static final String BROKER_URL = "tcp://192.168.0.166:61616"; // activeMQ服务地址
    private static final String QUEUE_NAME = "test_queue_1"; // 消息队列名称

    public static void main(String[] args) {
        /**
         * 根据我们指定activeMQ服务地址来创建activeMQ工厂
         * 如果我们自己不指定activeMQ服务地址则默认为本机
         */
        ActiveMQConnectionFactory activeMQConnection = new ActiveMQConnectionFactory(BROKER_URL);
        Connection connection = null;
        Session session = null;
        MessageConsumer messageConsumer = null;
        try {
            // 通过连接工厂,获得connection
            connection = activeMQConnection.createConnection();
            //  打开连接
            connection.start();
            /**
             * 创建session,得到会话
             * 两个参数transacted=事务,true 开启,acknowledgeMode=确认模式(签收)
             * 消费者开启了事务就必须手动提交,不然会出现消息重复消费
             */
            session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
            // 根据session创建队列
            Queue queue = session.createQueue(QUEUE_NAME);
            // 创建消息消费者并指定消费的队列
            messageConsumer = session.createConsumer(queue);

            /**
             * 通过监听的方式来消费消息,是以异步非阻塞的方式来消费消息
             * 通过messageConsumer 的setMessageListener 注册一个监听器,当有消息发送来时,系统自动调用MessageListener 的 onMessage 方法处理消息
             */
            messageConsumer.setMessageListener((message) -> {
                if (message != null) {
                    if (message instanceof TextMessage) {
                        TextMessage textMessage = (TextMessage) message;
                        try {
                            System.out.println("消费掉的消息 -> " + textMessage.getText());
                            System.out.println("消费掉的消息属性 -> " + textMessage.getStringProperty("user"));
                            System.out.println("消费掉的消息属性 -> " + textMessage.getBooleanProperty("isLogin"));
                        } catch (JMSException e) {
                            e.printStackTrace();
                        }
                    } else if (message instanceof MapMessage) {
                        MapMessage mapMessage = (MapMessage) message;
                        try {
                            System.out.println("消费掉的Map消息 -> " + mapMessage.getString("k1"));
                            System.out.println("消费掉的Map消息 -> " + mapMessage.getInt("k2"));
                        } catch (JMSException e) {
                            e.printStackTrace();
                        }
                    }

                }
            });

            // 开启事务后,使用commit提交事务,这样这批消息才会真正的被消费掉。
            session.commit();
            /**
             * 此处是为了不让主线程结束,因为一旦主线程结束了,其他的线程(如此处的监听消息的线程)也都会被迫结束。
             * 实际开发中不需要
             */
            System.in.read();
        } catch (Exception e) {
            e.printStackTrace();
            try {
                // // 当出现异常了,我们可以在catch代码块中回滚。这样就可以再次消费此消息
                session.rollback();
            } catch (JMSException e1) {
            }
        } finally {
            try {
                messageConsumer.close();
                session.close();
                connection.close();
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }
    }
}

Message signe

Nous parlons devant des producteurs et des consommateurs dans la création de la session, vous devez passer deux paramètres, la première est de savoir si les choses ouvertes, la seconde consiste à signer un message, le message de ce signe pour notre mécanisme futur dans le chat activeMQ de;

manière signe du message, a été divisé dans les catégories suivantes:

  1. Session.AUTO_ACKNOWLEDGE (connexion automatique): Ce mode est la valeur par défaut. On n'a pas besoin des opérations du programme, il nous aidera à signer automatiquement les messages reçus.
  2. Session.CLIENT_ACKNOWLEDGE (signe de la main): De cette façon , nous devons appeler manuellement la Message.acknowledge()méthode pour signer le message. Si vous ne signez pas le message, le message est que nous avons à plusieurs reprises la consommation, jusqu'à la réception.
  3. Session.DUPS_OK_ACKNOWLEDGE (laisser des messages en double): les consommateurs multithread ou plusieurs consommation simultanément à un message parce que le fil est dangereux, la consommation peut être répétée. Les façons rarement utilisées.
  4. Session.SESSION_TRANSACTED (sous le signe des choses): le cas de début d'une transaction, vous pouvez utiliser cette façon. Les façons rarement utilisées.

Message aux choses des signes et des relations:

  • Dans la session transactionnelle est signe automatiquement lorsqu'une transaction est un message transmis avec succès. Si la transaction est annulée, le message sera transmis à nouveau. Services de préférence à signer, après le début de la transaction, le mécanisme de réception ne servirait à rien.
  • session non transactionnel, signer le message quand il a été créé en fonction du mode de réponse de la session.
  • Les producteurs des affaires ouvertes, de commettre seulement après que tous les messages sont devenus consommateurs
  • Les services font souvent les producteurs, les consommateurs ont tendance à signer. C'est-à-dire les producteurs qui utilisent un meilleur point de transaction, les mécanismes de signalisation des consommateurs meilleur point.

Laissez-nous vous montrer, le consommateur à signer manuellement le message dans une non-chose:

package com.chaytech.activemq.queue;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

/**
 * 消息消费者
 *
 * @author Chency
 * @email [email protected]
 * @Date 2020/03/22 10:45
 */
public class JmsConsumer {
    private static final String BROKER_URL = "tcp://192.168.0.166:61616"; // activeMQ服务地址
    private static final String QUEUE_NAME = "test_queue_1"; // 消息队列名称

    public static void main(String[] args) {
        /**
         * 根据我们指定activeMQ服务地址来创建activeMQ工厂
         * 如果我们自己不指定activeMQ服务地址则默认为本机
         */
        ActiveMQConnectionFactory activeMQConnection = new ActiveMQConnectionFactory(BROKER_URL);
        Connection connection = null;
        Session session = null;
        MessageConsumer messageConsumer = null;
        try {
            // 通过连接工厂,获得connection
            connection = activeMQConnection.createConnection();
            //  打开连接
            connection.start();
            /**
             * 创建session,得到会话
             * 两个参数transacted=事务,acknowledgeMode=确认模式(签收)
             */
            session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
            // 根据session创建队列
            Queue queue = session.createQueue(QUEUE_NAME);
            // 创建消息消费者并指定消费的队列
            messageConsumer = session.createConsumer(queue);
            /**
             * 通过监听的方式来消费消息,是以异步非阻塞的方式来消费消息
             * 通过messageConsumer 的setMessageListener 注册一个监听器,当有消息发送来时,系统自动调用MessageListener 的 onMessage 方法处理消息
             */
            messageConsumer.setMessageListener((message) -> {
                if (message != null) {
                    if (message instanceof TextMessage) {
                        TextMessage textMessage = (TextMessage) message;
                        try {
                            System.out.println("消费掉的消息 -> " + textMessage.getText());
                            System.out.println("消费掉的消息属性 -> " + textMessage.getStringProperty("user"));
                            System.out.println("消费掉的消息属性 -> " + textMessage.getBooleanProperty("isLogin"));

                            /**
                             * 设置为Session.CLIENT_ACKNOWLEDGE后,要调用该方法,标志着该消息已被签收(消费)。
                             * 如果不调用该方法,该消息的标志还是未消费,下次启动消费者或其他消费者还会收到此消息。
                             */
                            textMessage.acknowledge();
                        } catch (JMSException e) {
                            e.printStackTrace();
                        }
                    } else if (message instanceof MapMessage) {
                        MapMessage mapMessage = (MapMessage) message;
                        try {
                            System.out.println("消费掉的Map消息 -> " + mapMessage.getString("k1"));
                            System.out.println("消费掉的Map消息 -> " + mapMessage.getInt("k2"));
                        } catch (JMSException e) {
                            e.printStackTrace();
                        }
                    }

                }
            });

            /**
             * 此处是为了不让主线程结束,因为一旦主线程结束了,其他的线程(如此处的监听消息的线程)也都会被迫结束。
             * 实际开发中不需要
             */
            System.in.read();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                messageConsumer.close();
                session.close();
                connection.close();
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }
    }
}

résumé

Point (file d'attente):

  • modèle de pairs est basée sur la file d'attente, le producteur de messages de file d'attente, la consommation de nouvelles à la consommation à partir de la file d'attente, la file d'attente afin qu'il y ait une transmission de message asynchrone possible. Et nous envoyons habituellement un message texte à un ami similaire;
  • une partie du message a été reçu mais pas encore signer (Reçue) si la session est fermée, lorsque le consommateur que la connexion suivante à la même file d'attente, les messages seront reçus à nouveau;
  • file d'attente de messages peut contenir pendant longtemps jusqu'à ce que les consommateurs reçoivent le message. Les consommateurs ne doivent pas inquiéter parce que le message sera perdu et le temps et les files d'attente restent le statut de connexion active, incarne pleinement les avantages de l'ATM;

Publier et abonnez-vous (sujet):

  • JMS Pub / Sub modèle définit comment et abonnez-vous des messages éditent à un noeud contenu, ces noeuds sont appelés sujet;
  • sujet peut être considéré comme une transmission de l'agence de nouvelles, l'éditeur (éditeur) publier des messages à un sujet, les abonnés (s'abonner) abonner au thème;
  • Thème et message afin que les abonnés de message pour rester éditeurs indépendants ne doivent pas lever les uns des autres pour assurer la livraison des messages
  • Abonnement:
    • abonnements persistants:
      • Le client détient une identité de son propre numéro d'identification pour identifier le MQ, lorsque le client est hors-ligne, les producteurs économiseront tous les messages envoyés au thème de cet ID, lorsqu'un client se connecte à MQ une fois de plus, seront basés sur les consommateurs ID obtenir tous envoyé à un sujet quand ils sont dans un message hors-ligne
      • Lorsque l'état d'abonnement durable ne peut pas être restauré ou non un signe du message délivré re;
      • abonnement durable pour récupérer ou non resigner pour une livraison du message;
    • Souscriptions non persistants:
      • abonnement client de messagerie non persistant est actif, qui est, MQ et rester connecté à l'émetteur-récepteur à un sujet que si;
      • Si les consommateurs sont hors ligne, le message envoyé par le sujet producteur seront perdues vide, le consommateur ne recevra jamais;
      • Inscrivez-vous pour recevoir le premier enregistrement des seuls poster des messages aux abonnés
  • Comment choisir: Lorsque tous les messages ont été reçus, avec abonnement persistant. Lorsque le message est perdu peut être toléré, l'abonnement non durable
Publié 107 articles originaux · a gagné les éloges 19 · vues 20000 +

Je suppose que tu aimes

Origine blog.csdn.net/chen_changying/article/details/105040438
conseillé
Classement