Maison > Java > javaDidacticiel > le corps du texte

Explication graphique et textuelle détaillée de la réutilisation des E/S en Java

黄舟
Libérer: 2017-05-28 09:23:00
original
1425 Les gens l'ont consulté

Cet article présente principalement les connaissances pertinentes sur la réutilisation de Java IO. Il est très bon et a une valeur de référence. Les amis qui en ont besoin peuvent s'y référer

Pour la capacité de traitement simultané du serveur. , ce dont nous avons besoin est : chaque milliseconde, le serveur peut traiter rapidement les messages sur des centaines de connexions TCP différentes reçues au cours de cette milliseconde. En même temps, il peut y avoir des centaines de milliers de messages sur le serveur qui n'ont pas été reçus au cours de la dernière milliseconde. quelques secondes. Une connexion relativement inactive pour envoyer et recevoir des messages. Le traitement de plusieurs connexions où des événements se produisent en parallèle en même temps est appelé concurrence ; le traitement de dizaines de milliers ou de centaines de milliers de connexions en même temps est une concurrence élevée. Ce que poursuit la programmation simultanée du serveur, c'est de traiter un nombre infini de connexions simultanées tout en maintenant une utilisation efficace des ressources telles que le processeur jusqu'à ce que les ressources physiques soient épuisées.

Il existe de nombreuses implémentations de modèles de programmation simultanée. Le plus simple est fourni avec des "threads", et un thread gère l'intégralité du cycle de vie d'une connexion. Avantages : Ce modèle est assez simple, il peut mettre en œuvre des scénarios commerciaux complexes et, en même temps, le nombre de threads peut être bien supérieur au nombre de processeurs. Cependant, le nombre de threads ne peut pas être augmenté à l’infini. Pourquoi ? Parce que le moment où un thread est exécuté est déterminé par l'algorithme de planification du noyau du système d'exploitation, l'algorithme de planification ne considère pas qu'un certain thread ne peut servir qu'une seule connexion. Il adoptera un gameplay unifié : l'exécutera lorsque la tranche de temps est écoulée, même si. ce fil Une fois exécuté, vous devrez continuer à dormir. Ce va-et-vient de threads de veille et de veille est bon marché lorsque le nombre de fois est petit, mais si le nombre total de threads dans le système d'exploitation est grand, il est coûteux (amplifié), car cette perte de planification technique affectera les threads. L'heure à laquelle le code métier est exécuté. Par exemple, la plupart des threads avec des connexions inactives à l'heure actuelle sont comme nos entreprises publiques. Leur efficacité d'exécution est trop faible. Lorsqu'ils se réveillent et se disputent les ressources CPU, ils se réveillent et dorment. Cela signifie que le nombre de threads d'entreprise privés traitant les connexions actives est réduit et la possibilité d'obtenir le CPU est réduite. Le CPU est le cœur de la compétitivité, et son inefficacité affecte le débit total du PIB. Notre objectif est de traiter des centaines de milliers de connexions simultanément. Lorsque des milliers de threads apparaissent, l'efficacité d'exécution du système ne peut plus répondre à une concurrence élevée.

Pour la programmation à haute concurrence, il n'existe actuellement qu'un seul modèle, qui est essentiellement la seule méthode efficace. Le traitement du message sur la connexion peut être divisé en deux étapes : l'attente que le message soit prêt et le traitement du message. Lors de l'utilisation du socket de blocage par défaut (par exemple, un thread regroupé pour traiter une connexion mentionnée ci-dessus), ces deux étapes sont souvent combinées en une seule, de sorte que le thread qui exploite le code du socket doit dormir pour attendre que le message soit prêt. , cela provoque la mise en veille et le réveil fréquents du thread en cas de concurrence élevée, affectant ainsi l'efficacité de l'utilisation du processeur.

La méthode de programmation à haute concurrence consiste bien entendu à séparer les deux étapes. Autrement dit, la section de code qui attend que le message soit prêt est séparée de la section de code qui traite le message. Bien entendu, cela nécessite également que le socket ne soit pas bloquant. Sinon, le segment de code qui traite le message peut facilement amener le thread à entrer en phase d'attente de veille lorsque les conditions ne sont pas remplies. La question est donc de savoir comment parvenir à cette étape d’attente que le message soit prêt ? Après tout, il attend toujours, ce qui signifie que le fil doit encore dormir ! La solution est d' interroger activement, ou de laisser 1 thread attendre toutes les connexions ! Il s'agit du multiplexage IO. Le multiplexage consiste à attendre que les messages soient prêts, mais il peut gérer plusieurs connexions en même temps ! Il peut également "attendre", ce qui peut également provoquer la mise en veille du thread, mais cela n'a pas d'importance car il est un à plusieurs et il peut surveiller toutes les connexions. De cette façon, lorsque notre thread est réveillé pour exécution, il doit y avoir des connexions prêtes à être exécutées par notre code, ce qui est efficace ! Il n'y a pas tellement de threads en compétition pour traiter la phase "attendre que le message soit prêt", et le monde entier est enfin clair !
Il existe de nombreuses implémentations du multiplexage. Sur linux, avant le noyau 2.4, les principales étaient select et poll. Maintenant, le courant dominant est epoll. Leurs méthodes d'utilisation semblent être très différentes, mais l'essence. c'est pareil.

L'efficacité est également différente, c'est pourquoi epoll a complètement remplacé select.

Parlons brièvement de la raison pour laquelle epoll remplace select.

Comme mentionné précédemment, la solution de base pour une concurrence élevée est d'avoir un seul thread "en attente que les messages soient prêts" pour toutes les connexions. Sur ce point, epoll et select ne sont pas controversés. Mais sélectionnez une chose qui ne va pas. Comme nous l'avons dit dans le premier chapitre de , lorsqu'il existe des centaines de milliers de connexions simultanées, il peut n'y avoir que des centaines de connexions actives chaque milliseconde, tandis que les centaines de milliers de connexions restantes. est inactif pendant cette milliseconde. La méthode d'utilisation de select est la suivante :
Connexions actives renvoyées ==select (toutes les connexions à surveiller)

Quand la méthode select sera-t-elle appelée ? Vous devez appeler ceci lorsque vous pensez avoir besoin de connaître les connexions actives par lesquelles les paquets sont arrivés. Par conséquent, l’appel de select sera appelé fréquemment lorsque la concurrence est élevée. Il faut donc voir si cette méthode fréquemment appelée est efficace, car sa légère perte d'efficacité sera amplifiée par le mot « fréquent ». Est-ce qu'il y a une perte d'efficacité ? Évidemment, il y a des centaines de milliers de connexions à surveiller, et seules des centaines de connexions actives sont renvoyées, ce qui est inefficace en soi. Après avoir été amplifié, vous constaterez que select est totalement incapable de gérer des dizaines de milliers de connexions simultanées.
Regardez quelques photos. Lorsque le nombre de connexions simultanées est inférieur à 1 000, le nombre d'exécutions sélectionnées n'est pas fréquent, et il ne semble pas y avoir beaucoup de différence avec epoll :

Cependant, une fois que le nombre de concurrence augmente, les défauts de select sont infiniment amplifiés par « l'exécution fréquente », et plus le nombre est simultané, plus c'est évident :

Parlons de la façon dont epoll résout le problème. Il utilise intelligemment 3 méthodes pour réaliser ce que fait la méthode select :

New epoll descriptor==epoll_create()

epoll_ctrl(epoll descriptor, add Ou Delete toutes les connexions à surveiller)

La connexion active renvoyée==epoll_wait (descripteur epoll)

Le principal avantage de cette procédure est de faire la distinction entre les appels fréquents et les opérations appelées rarement. Par exemple, epoll_ctrl est appelé moins fréquemment, tandis qu'epoll_wait est appelé très fréquemment. À l'heure actuelle, epoll_wait n'a presque aucun paramètre d'entrée, ce qui est beaucoup plus efficace que select. De plus, il n'augmentera pas le nombre de paramètres d'entrée à mesure que les connexions simultanées augmentent, ce qui entraînera une diminution de l'efficacité d'exécution du noyau.

Comment epoll est-il implémenté ? En fait, c'est très simple. Ces trois méthodes montrent qu'il est plus intelligent que de sélectionner pour éviter d'avoir à transmettre toutes les connexions à surveiller à chaque fois que epoll_wait appelle fréquemment "quelles connexions sont déjà dans la préparation du message". scène". de. Cela signifie qu'il maintient une structure de données en mode noyau pour sauvegarder toutes les connexions à surveiller. Cette structure de données est un arbre rouge-noir, et l'ajout et la réduction de ses nœuds se font via epoll_ctrl. C'est très simple :

L'arbre rouge-noir en bas à gauche de la photo est constitué de toutes les connexions à surveiller. La liste chaînée en haut à gauche affiche toutes les connexions actuellement actives. Par conséquent, lorsque epoll_wait est exécuté, il vérifie uniquement la liste liée en haut à gauche et renvoie la connexion dans la liste liée en haut à gauche à l'utilisateur. De cette façon, l'efficacité d'exécution d'epoll_wait peut-elle être faible ?

Enfin, jetons un coup d'œil aux deux méthodes de jeu ET et LT fournies par epoll, qui sont le déclencheur de bord traduit et le déclencheur horizontal. En fait, ces deux noms chinois sont plutôt appropriés. Ces deux méthodes d'utilisation visent toujours des problèmes d'efficacité, mais elles servent simplement à rendre plus précise la connexion renvoyée par epoll_wait.

Par exemple, nous devons surveiller si le tampon d'écriture d'une connexion est libre Lorsqu'il est "inscriptible", nous pouvons envoyer l'appel de réponse write au client depuis le mode utilisateur. Cependant, peut-être que lorsque la connexion est accessible en écriture, notre contenu de « réponse » est toujours sur le disque. Que se passe-t-il si la lecture du disque n'est pas terminée à ce moment-là ? Le fil de discussion ne doit pas être bloqué, la réponse ne sera donc pas envoyée. Cependant, la connexion peut vous être renvoyée la prochaine fois que vous epoll_wait, et vous devez vérifier si vous souhaitez la traiter. Probablement, notre programme a un autre module qui gère spécifiquement les E/S du disque, et il enverra une réponse lorsque les E/S du disque seront terminées. Ainsi, chaque fois que epoll_wait renvoie cette connexion « accessible en écriture » qui ne peut pas être traitée immédiatement, répond-elle aux attentes des utilisateurs ?

Ainsi, les modes ET et LT ont vu le jour. LT signifie que chaque connexion qui répond au statut attendu doit être renvoyée dans epoll_wait, donc elle traite tout le monde de la même manière et se situe sur une ligne horizontale. Ce n’est pas le cas d’ET qui préfère des liaisons retour plus précises. Dans l'exemple ci-dessus, une fois que la connexion devient accessible en écriture pour la première fois, si le programme n'écrit aucune donnée sur la connexion, alors epoll_wait ne renverra pas la connexion la prochaine fois. ET est appelé edge trigger, ce qui signifie que epoll_wait sera déclenché pour le renvoyer uniquement lorsque la connexion passe d'un état à un autre. On voit que la programmation d'ET est beaucoup plus compliquée. Au moins l'application doit faire attention à éviter que la connexion renvoyée par epoll_wait n'apparaisse : lorsqu'elle est inscriptible, la donnée n'est pas écrite mais elle attend le prochain "inscriptible" ; lorsqu'elle est lisible, les données ne sont pas lues mais elles attendent avec impatience la prochaine fois.

Bien sûr, il n'y aura pas de grande différence de performances dans les scénarios d'application généraux. L'avantage possible d'ET est que le nombre d'appels à epoll_wait sera réduit et, dans certains scénarios, la connexion ne sera pas réveillée lorsqu'elle sera activée. n'est pas nécessaire (ce réveil fait référence au retour de epoll_wait). Mais si c’est comme dans l’exemple que j’ai mentionné ci-dessus, parfois ce n’est pas seulement un problème de réseau, c’est lié au scénario d’application. Bien sûr, la plupart des frameworks open source sont écrits sur la base d'ET. Quant au framework, il poursuit des problématiques purement techniques, et vise bien sûr la perfection

.

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
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!