Maison > Java > javaDidacticiel > Java NIO : modèle d'E/S

Java NIO : modèle d'E/S

大家讲道理
Libérer: 2017-05-28 11:32:33
original
1299 Les gens l'ont consulté

Peut-être que de nombreux amis trouvent cela un peu difficile d'apprendre NIO, et de nombreux concepts ne sont pas si clairs. Avant d'entrer dans Java NIOProgrammation, discutons aujourd'hui de quelques connaissances de base : I/OModèle. L'article suivant commence par les concepts de synchronisation et d'asynchrone, puis explique la différence entre les E/S bloquantes et non bloquantes, puis introduit la différence entre les E/S bloquantes et les E/S non bloquantes, puis introduit la différence entre les E/S synchrones et les E/S asynchrones, puis introduit 5 modèles IO, et présente enfin deux modèles de conception (Reactou et Proactor) liés à la conception IO hautes performances.

Voici le sommaire de la table des matières de cet article :

1. Qu'est-ce que la synchronisation ? Qu'est-ce que l'asynchrone ?

2. Qu'est-ce que le blocage ? Qu'est-ce que le non bloquant ?

3. Qu'est-ce qui bloque les IO ? Qu’est-ce que les E/S non bloquantes ?

4. Qu'est-ce que les E/S synchrones ? Qu’est-ce que les E/S asynchrones ?

5. Cinq modèles IO

6. Deux modèles de conception IO hautes performances

S'il y a quelque chose qui ne va pas, veuillez me pardonner et accueillir les critiques et les corrections.

1. Qu'est-ce que la synchronisation ? Qu'est-ce que l'asynchrone ?

Les concepts de synchronisation et d'asynchronisme existent depuis longtemps, et il existe de nombreuses opinions sur la synchronisation et l'asynchronisme sur Internet. Voici ma compréhension personnelle :

La synchronisation signifie : s'il y a plusieurs tâches ou événements à se produire, ces tâches ou événements doivent être effectués un par un. L'exécution d'un événement ou. la tâche entraînera l'ensemble du processus en attente temporaire et ces événements ne peuvent pas être exécutés simultanément

Asynchrone signifie : si plusieurs tâches ou événements se produisent, ces événements peuvent être exécutés simultanément et l'exécution d'un événement ou d'une tâche ne fera pas échouer l’ensemble du processus. Attendez maintenant.

C'est synchrone et asynchrone. Pour donner un exemple simple, s'il existe une tâche qui comprend deux sous-tâches A et B. Pour la synchronisation, lorsque A est en cours d'exécution, B ne peut qu'attendre que A soit terminé, puis B peut s'exécuter de manière asynchrone, A et B peuvent être exécutés ; exécuté simultanément, et B n'a pas à attendre que A termine son exécution, de sorte que l'exécution de A ne fera pas attendre temporairement la tâche entière.

Si vous ne comprenez toujours pas, vous pouvez d'abord lire les deux morceaux de code suivants :




1

2

3

4

5

6

7

8

9

10

11

12

13

14


void fun1() {

      

  }

  

  void fun2() {

      

  }

  

  void function(){

      fun1();

      fun2()

      .....

      .....

  }


Ce code est une synchronisation typique. Dans la fonction de méthode, l'exécution de fun1 empêchera le fun2 suivant de s'exécuter avant que l'exécution de fun1 ne soit terminée. il peut être exécuté.

Regardez ensuite le code suivant :




1

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24


void fun1() {

     

}

 

void fun2() {

     

}

 

void function(){

    <a href="//m.sbmmt.com/wiki/165.html" target="_blank">new</a> Thread(){

        public void run() {

            fun1();

        }

    }.start();

     

    new Thread(){

        public void run() {

            fun2();

        }

    }.start();

 

    .....

    .....

}

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

void fun1() {

}<p class="numéro de ligne4 index3 alt1"> </p> <p class="numéro de ligne5 index4 alt2"><code class="java password">void fun2() {

}

void function(){

 <code class="java password"><a href="//m.sbmmt.com/wiki/165.html" target="_blank">nouveau</a> Thread()

                                                 >public void run() {

code><code class="java plain">fun1();

}

}.start( );

nouveau Thread(){<p class="line number17 index16 alt2"><code class="espaces java"> public void run() {

code><code class="java plain">fun2 ();

}

}.start( );

......<p class="numéro de ligne23 index22 alt2"><code class="espaces java"> .....

}


Ce code est un code asynchrone typique. L'exécution de fun1 n'affectera pas l'exécution de fun2, et l'exécution de fun1 et fun2 ne rendra pas le processus d'exécution ultérieur temporaire. attendez.

En fait, la synchronisation et l'asynchronisme sont des concepts très larges. Ils se concentrent sur la question de savoir si l'occurrence ou l'exécution d'un événement entraînera une attente temporaire de l'ensemble du processus lorsque plusieurs tâches et événements se produisent. Je pense qu'on peut faire une analogie entre synchronisation et asynchronisme avec le mot-clé synchronized en Java. Lorsque plusieurs threads accèdent à une variable en même temps, l'accès de chaque thread à la variable est un événement. Pour la synchronisation, ces threads doivent accéder à la variable un par un. Le processus d'un thread accédant à la variable , autre. les threads doivent attendre ; pour être asynchrones, plusieurs threads ne doivent pas accéder à la variable un par un, mais peuvent y accéder en même temps.

Par conséquent, je pense personnellement que la synchronisation et l'asynchronisme peuvent s'exprimer de plusieurs manières, mais la clé à retenir est que lorsque plusieurs tâches et événements se produisent, que l'occurrence ou l'exécution d'un événement entraîne l'ensemble du processus. attendez temporairement. De manière générale, l'asynchrone peut être obtenu grâce au multi-threading, mais n'oubliez pas de ne pas assimiler le multi-threading à l'asynchrone. L'asynchrone n'est qu'un modèle de macro, et l'utilisation du multi-threading pour obtenir l'asynchrone n'est qu'un moyen, et l'implémentation asynchrone peut également être réalisée. grâce au multi-traitement.

2. Qu'est-ce que le blocage ? Qu'est-ce que le non bloquant ?

La différence entre la synchronisation et l'asynchronisme a été introduite plus tôt dans cette section, examinons la différence entre le blocage et le non-blocage.

Le blocage signifie : lorsqu'un événement ou une tâche est en cours d'exécution, il émet une opération de requête, mais comme les conditions requises pour l'opération de requête ne sont pas remplies, il y attendra jusqu'à ce que les conditions soient remplies ;

Non bloquant signifie : lorsqu'un événement ou une tâche est en cours d'exécution, il émet une opération de requête. Si les conditions requises pour l'opération de requête ne sont pas remplies, un message indicateur sera immédiatement renvoyé pour informer que les conditions ne sont pas remplies. , et ça ne continuera pas. Attendez là.

C'est la différence entre le blocage et le non-blocage. C'est-à-dire que la principale différence entre le blocage et le non-blocage est que lorsqu'une demande d'opération est faite, si les conditions ne sont pas remplies, elle attendra indéfiniment ou renverra un message d'indicateur.

Donnez un exemple simple :

Si je veux lire le contenu d'un fichier, s'il n'y a pas de contenu lisible dans le fichier à ce moment, pour la synchronisation, il attendra toujours là. Jusqu'à ce qu'il y ait du contenu lisible dans le fichier ; pour le non-blocage, un message d'indicateur sera renvoyé directement pour informer qu'il n'y a pas de contenu lisible dans le fichier.

Certains amis sur Internet assimilent respectivement la synchronisation et l'asynchronisme au blocage et au non-blocage. En fait, ce sont deux concepts complètement différents. Notez que comprendre la différence entre ces deux ensembles de concepts est très important pour comprendre le modèle IO ultérieur.

L'objectif de la synchronisation et de l'asynchronisme est de savoir si l'exécution d'une tâche entraînera une attente temporaire de l'ensemble du processus pendant l'exécution de plusieurs tâches

L'objectif du blocage et du non-blocage est ; lors de l'émission d'une demande Pendant l'exploitation, si les conditions de l'opération ne sont pas remplies, si un message indicateur sera renvoyé pour informer que les conditions ne sont pas remplies.

Comprendre le blocage et le non-blocage peut être compris par analogie avec le blocage de threads Lorsqu'un thread effectue une opération de requête, si la condition n'est pas remplie, il sera bloqué, c'est-à-dire qu'il attendra que la condition soit remplie. rencontré.

3. Qu'est-ce qui bloque les IO ? Qu’est-ce que les E/S non bloquantes ?

Avant de comprendre les IO bloquantes et les IO non bloquantes, examinons d'abord comment le processus d'opération IO spécifique est effectué.

De manière générale, les opérations d'E/S comprennent : la lecture et l'écriture sur le disque dur, la lecture et l'écriture sur le socket, et la lecture et l'écriture sur les périphériques.

Lorsque le thread utilisateur lance une opération de requête IO (cet article prend l'opération de requête de lecture comme exemple), le noyau vérifiera si les données à lire sont prêtes pour bloquer les IO, si les données ne le sont pas. prêt, il continuera à attendre jusqu'à ce que les données soient prêtes ; pour les IO non bloquantes, si les données ne sont pas prêtes, un message d'indicateur sera renvoyé pour informer le thread utilisateur que les données en cours de lecture ne sont pas prêtes. Lorsque les données sont prêtes, les données sont copiées dans le thread utilisateur, de sorte qu'une opération complète de demande de lecture IO soit terminée, c'est-à-dire qu'une opération complète de demande de lecture IO comprend deux étapes :

 1) Afficher. data Est-ce prêt ?

 2) Copier les données (le noyau copie les données dans le thread utilisateur).

Ensuite, la différence entre le blocage (IO bloquant) et le non bloquant (IO non bloquant) est que dans la première étape, si les données ne sont pas prêtes, devez-vous continuer à attendre en vérifiant si les données sont prêtes , ou renvoie directement Un message de drapeau.

L'IO traditionnelle en Java bloque l'IO, comme la lecture de données via le socket. Après avoir appelé la méthode read(), si les données ne sont pas prêtes, le thread actuel sera bloqué lors de l'appel de la méthode read jusqu'à ce que cela se produise. est des données et s'il s'agit d'E/S non bloquantes, lorsque les données ne sont pas prêtes, la méthode read() doit renvoyer un message d'indicateur pour informer le thread actuel que les données ne sont pas prêtes, au lieu d'y attendre tout le temps.

4. Qu'est-ce que les E/S synchrones ? Qu’est-ce que les E/S asynchrones ?

Jetons d'abord un coup d'œil aux définitions des E/S synchrones et des E/S asynchrones. Les définitions des E/S synchrones et des E/S asynchrones dans le livre "Programmation réseau Unix" sont les suivantes :

 . Une opération d'E/S synchrone entraîne le blocage du processus demandeur jusqu'à ce que cette opération d'E/S soit terminée.
 Une opération d'E/S asynchrone n'entraîne pas le blocage du processus demandeur.

Cela peut être vu au sens littéral : IO synchrone signifie que si un thread demande une opération IO, le thread sera bloqué avant que l'opération IO ne soit terminée

IO asynchrone signifie que si un ; le thread demande une opération IO, le thread sera bloqué avant que l'opération IO ne soit terminée, les opérations IO n'entraîneront pas le blocage du thread de demande.

En fait, les modèles IO synchrones et IO asynchrones sont destinés à l'interaction entre les threads utilisateur et le noyau :

Pour les IO synchrones : après que l'utilisateur a émis une opération de requête IO, si les données sont pas prêt, Il est nécessaire de vérifier en permanence si les données sont prêtes via le thread utilisateur ou le noyau. Lorsque les données sont prêtes, les données sont copiées du noyau vers le thread utilisateur

IO asynchrone : uniquement. l'opération de demande IO est émise par le thread utilisateur. Les deux étapes de l'opération IO sont complétées automatiquement par le noyau , puis une notification est envoyée pour informer le thread utilisateur que l'opération IO est terminée. C’est-à-dire que dans les IO asynchrones, il n’y aura pas de blocage des threads utilisateur.

C'est la principale différence entre les E/S synchrones et les E/S asynchrones. La différence clé entre les E/S synchrones et les E/S asynchrones se reflète dans le fait que la phase de copie des données est terminée par le thread utilisateur ou par le noyau. Par conséquent, les E/S asynchrones doivent bénéficier d’une prise en charge sous-jacente du système d’exploitation.

Notez que les IO synchrones et les IO asynchrones sont deux concepts différents des IO bloquantes et des IO non bloquantes.

Les IO bloquantes et les IO non bloquantes se reflètent dans le fait que lorsque l'utilisateur demande une opération IO, si les données ne sont pas prêtes, le thread utilisateur recevra toujours un message d'indicateur s'il attend que les données soient soyez prêt. En d'autres termes, les E/S bloquantes et les E/S non bloquantes se reflètent dans la première étape de l'opération d'E/S, comment elles sont traitées lors de la vérification si les données sont prêtes.

5. Cinq modèles d'IO

Cinq modèles d'IO sont mentionnés dans le livre "Unix Network Programming", à savoir : IO bloquants, IO non bloquants, IO multiplexés, signaux lecteurs IO et IO asynchrones.

Présentons respectivement les similitudes et les différences de ces 5 modèles IO.

1. Modèle IO de blocage

Le modèle IO le plus traditionnel, c'est-à-dire que le blocage se produit pendant le processus de lecture et d'écriture des données.

Lorsque le thread utilisateur émet une requête IO, le noyau vérifiera si les données sont prêtes. Sinon, il attendra que les données soient prêtes et le thread utilisateur sera bloqué . état , et le thread utilisateur remettra le processeur. Lorsque les données sont prêtes, le noyau copiera les données dans le thread utilisateur et renverra le résultat au thread utilisateur, puis le thread utilisateur libérera l'état de bloc.

Des exemples typiques de modèles d'E/S bloquants sont :




1

1


data = socket.read();

data = socket.read(); td >


Si les données ne sont pas prêtes, elles seront toujours bloquées dans la méthode de lecture.

2. Modèle IO non bloquant

Lorsque le thread utilisateur lance une opération de lecture, il n'est pas nécessaire d'attendre, mais un résultat est obtenu immédiatement. Si le résultat est une erreur, il sait que les données ne sont pas encore prêtes et peut donc renvoyer l'opération de lecture. Une fois que les données du noyau sont prêtes et qu'une requête est à nouveau reçue du thread utilisateur, il copie immédiatement les données dans le thread utilisateur, puis revient.

Donc en fait, dans le modèle IO non bloquant, le thread utilisateur doit constamment demander au noyau si les données sont prêtes, ce qui signifie que les IO non bloquantes ne remettront pas le CPU, mais le feront toujours occuper le CPU.

Le modèle IO non bloquant typique est généralement le suivant :




1

1

2

3

4

5

6

7


<a href="//m.sbmmt.com/wiki/121.html" target="_blank">while</a>(true){

    data = socket.read();

    <a href="//m.sbmmt.com/wiki/109.html" target="_blank">if</a>(data!= error){

        处理数据

        <a href="//m.sbmmt.com/wiki/130.html" target="_blank">break</a>;

    }

}

2

3

4

5

6

7

<a href="//m.sbmmt.com/wiki/121.html" target=" _blank"> while</a>(true) { code><p class="numéro de ligne2 index1 alt1"><code class="espaces java"> data = socket.read(); <p class="numéro de ligne3 index2 alt2"><code class="espaces java"> <a href="http://www.%20php.cn%20/wiki/109.html" target="_blank">if</a>(data!= erreur){

Traitement des données

<a href="//m.sbmmt.com/wiki/130.html" target="_blank"> pause a></a>;

}

}


Mais il y a un problème très sérieux avec les E/S non bloquantes dans la boucle while, vous devez constamment demander si les données du noyau sont prêtes, ce qui le fera. provoquer l'utilisation du processeur Le taux est très élevé, donc en général, alors que les boucles sont rarement utilisées pour lire des données.

3. Modèle IO multiplexé

Le modèle IO multiplexé est le modèle le plus couramment utilisé à l'heure actuelle. Java NIO est en fait une E/S multiplexée.

Dans le modèle IO multiplexé, il y aura un thread qui interroge en permanence l'état de plusieurs sockets. Ce n'est que lorsque le socket a réellement des événements de lecture et d'écriture que les opérations de lecture et d'écriture IO réelles sont réellement appelées. Étant donné que dans le modèle d'E/S multiplexées, un seul thread peut être utilisé pour gérer plusieurs sockets, le système n'a pas besoin de créer de nouveaux processus ou threads, ni de maintenir ces threads et processus, et seulement lorsqu'il y a de véritables lectures et connexions de socket. Écrire des événements Les ressources IO ne seront utilisées que lorsque le temps est écoulé, ce qui réduit considérablement l'utilisation des ressources.

Dans Java NIO, selector.select() est utilisé pour demander s'il y a un événement d'arrivée pour chaque canal. S'il n'y a pas d'événement, il y sera toujours bloqué, donc ceci. La méthode entraînera le blocage du thread utilisateur.

Peut-être que certains amis diront que je peux utiliser le multi-threading + blocage des IO pour obtenir des effets similaires. Cependant, en multi-threading + blocage des IO, chaque socket correspond à un thread, ce qui entraînera beaucoup de ressources. En cas d'utilisation, et en particulier pour les connexions longues, les ressources du thread ne seront jamais libérées. S'il y a de nombreuses connexions plus tard, cela entraînera un goulot d'étranglement des performances.

En mode IO multiplexé, plusieurs sockets peuvent être gérés via un seul thread. Ce n'est que lorsque le socket a réellement des événements de lecture et d'écriture que les ressources seront occupées pour les opérations de lecture et d'écriture réelles. Par conséquent, les E/S multiplexées sont plus adaptées aux situations où le nombre de connexions est important.

De plus, la raison pour laquelle les IO multiplexées sont plus efficaces que le modèle IO non bloquant est que dans les IO non bloquantes, l'état du socket est constamment demandé via le thread utilisateur, tandis que dans les IO multiplexées, interrogeant le le statut de chaque socket est effectué par le noyau, et cette efficacité est bien supérieure à celle des threads utilisateur.

Cependant, il convient de noter que le modèle IO multiplexé utilise l'interrogation pour détecter si un événement est arrivé et répond aux événements arrivant un par un. Par conséquent, pour le modèle IO multiplexé, une fois que le corps de la réponse à l'événement est volumineux, les événements suivants ne seront pas traités pendant une longue période et l'interrogation des nouveaux événements sera affectée.

4. Modèle IO piloté par signal

Dans le modèle IO piloté par signal, lorsque le thread utilisateur lance une opération de demande IO, une fonction de signal sera enregistrée pour Le socket correspondant. Ensuite, le thread utilisateur continuera à s'exécuter lorsque les données du noyau sont prêtes, un signal sera envoyé au thread utilisateur. Une fois que le thread utilisateur aura reçu le signal, il appellera les opérations de lecture et d'écriture IO dans le signal. fonction pour effectuer l’opération de demande d’E/S réelle.

5. Modèle IO asynchrone

Le modèle IO asynchrone est le modèle IO le plus idéal dans le modèle IO asynchrone, lorsque le thread utilisateur lance l'opération de lecture, elle peut démarrer immédiatement . D'autres choses. D'un autre côté, du point de vue du noyau, lorsqu'il reçoit une lecture asynchrone, il reviendra immédiatement, indiquant que la demande de lecture a été lancée avec succès, donc aucun bloc ne sera généré pour le thread utilisateur. Ensuite, le noyau attendra que la préparation des données soit terminée, puis copiera les données dans le thread utilisateur. Lorsque tout cela sera terminé, le noyau enverra un signal au thread utilisateur pour lui indiquer que l'opération de lecture est terminée. En d'autres termes, le thread utilisateur n'a pas besoin de savoir comment l'ensemble de l'opération IO est réellement effectué. Il lui suffit d'abord de lancer une requête. Lorsqu'il reçoit le signal de réussite renvoyé par le noyau, cela signifie que l'opération IO est terminée. et les données peuvent être utilisées directement.

En d'autres termes, dans le modèle IO asynchrone, aucune des phases de l'opération IO ne bloquera le thread utilisateur. Les deux phases sont automatiquement complétées par le noyau, puis un signal est envoyé pour informer le thread utilisateur que le thread utilisateur est activé. l’opération est terminée. Il n'est pas nécessaire d'appeler à nouveau la fonction IO dans le thread utilisateur pour une lecture et une écriture spécifiques. Ceci est différent du modèle basé sur le signal, lorsque le thread utilisateur reçoit le signal, cela indique que les données sont prêtes, puis le thread utilisateur doit appeler la fonction IO pour effectuer la lecture et la lecture réelles. Opérations d'écriture ; dans le modèle IO asynchrone, la réception du signal indique que l'opération IO est terminée et qu'il n'est pas nécessaire d'appeler la fonction iO dans le thread utilisateur pour les opérations de lecture et d'écriture réelles.

Notez que les E/S asynchrones nécessitent une prise en charge sous-jacente du système d'exploitation. Dans Java 7, les E/S asynchrones sont fournies.

Les quatre premiers modèles d'E/S sont en fait des E/S synchrones, et seul le dernier est une E/S véritablement asynchrone, car qu'il s'agisse d'E/S multiplexées ou d'un modèle piloté par signal, la deuxième étape du fonctionnement d'E/S provoquera Le thread utilisateur est bloqué, c'est-à-dire que le processus de copie des données par le noyau entraînera le blocage du thread utilisateur.

6. Deux modèles de conception d'E/S hautes performances

Parmi les modèles de conception de services réseau traditionnels, il existe deux modèles classiques :

L'un est multithread, l'autre est le pool de threads.

Pour le mode multi-thread, c'est-à-dire que lorsque le client arrive, le serveur va créer un nouveau thread pour gérer les événements de lecture et d'écriture du client, comme le montre la figure suivante :

Bien que ce mode soit simple et pratique à gérer, comme le serveur utilise un thread pour traiter chaque connexion client, il consomme beaucoup de ressources. Par conséquent, lorsque le nombre de connexions atteint la limite supérieure et qu'un autre utilisateur demande une connexion, cela provoquera directement un goulot d'étranglement des ressources et, dans les cas graves, cela peut directement provoquer le crash du serveur.

Par conséquent, afin de résoudre les problèmes causés par le modèle un thread, un client, la méthode du pool de threads est proposée, ce qui signifie créer un pool de threads de taille fixe et créer un client Le thread. pool prend un thread inactif pour le traitement. Lorsque le client termine les opérations de lecture et d'écriture, il cède l'occupation du thread. Par conséquent, cela évite le gaspillage de ressources provoqué par la création de threads pour chaque client, afin que les threads puissent être réutilisés.

Mais le pool de threads a aussi ses inconvénients. Si la plupart des connexions sont des connexions longues, cela peut entraîner l'occupation de tous les threads du pool de threads pendant un certain temps, puis lorsqu'un autre utilisateur demande une connexion. , car il n'y a pas de Si les threads inactifs disponibles sont utilisés pour le traitement, la connexion client échouera, affectant ainsi l'expérience utilisateur. Par conséquent, le pool de threads est plus adapté à un grand nombre d’applications de connexion courte.

Par conséquent, les deux modèles de conception d'E/S hautes performances suivants ont émergé : Reactor et Proactor.

En mode Reactor, les événements d'intérêt seront d'abord enregistrés pour chaque client, puis un thread interrogera chaque client pour voir si un événement se produit. Lorsqu'un événement se produit, chaque client sera traité séquentiellement. lorsque tous les événements seront traités , ils seront transférés pour continuer l'interrogation, comme le montre la figure suivante :

Comme on peut le voir d'ici, le ci-dessus Les IO multiplexées parmi les cinq modèles IO adoptent le mode Reactor. Notez que la figure ci-dessus montre que chaque événement est traité séquentiellement. Bien entendu, afin d'améliorer la vitesse de traitement des événements, les événements peuvent être traités via plusieurs threads ou pools de threads.

En mode Proactor, lorsqu'un événement est détecté, une nouvelle opération asynchrone sera lancée puis transmise au thread du noyau pour traitement. Lorsque le thread du noyau terminera l'opération IO, une notification sera envoyée pour informer. que l'opération est terminée. On peut savoir que le modèle IO asynchrone adopte le mode Proactor.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal