Avant-propos :
J'ai récemment démarré un projet et j'étais responsable de la conception architecturale et de la mise en œuvre du projet. À l'origine, l'entreprise créait de nombreuses API pour des personnes extérieures à l'entreprise, mais lorsque des personnes extérieures l'utilisent, le lien d'interface est donné à d'autres. Il n'y a pas de cryptage ni de contrôle de concurrence. Quelle que soit la machine sur laquelle se trouve le programme d'interface, il est donné. aux autres. La propriété intellectuelle est là et il n’y a pas de plateforme pour la gérer. Par conséquent, je sais clairement que la valeur de ces interfaces est difficile à découvrir (quelle interface est la plus utilisée par les autres et quelle interface est la moins utilisée par les autres).
Uniquement pour les besoins de « surveillance », nous avons introduit redis comme couche intermédiaire. Tout d'abord, nous avons amélioré le processus d'enregistrement de l'interface utilisateur et haché une clé via les informations et l'adresse de l'utilisateur. Cette clé est la correspondante. Avec une adresse, stockez cette paire (clé - adresse) dans redis. Vient ensuite nginx. Le processus de nginx dans notre projet est à peu près comme ceci :
1 Une fois que l'utilisateur s'est enregistré, il obtient sa clé et y accède via une URL qui contient la clé qui est complètement différente de l'originale. URL
2. nginx capture la clé spéciale de l'utilisateur, puis le programme récupère l'adresse cible de redis en fonction de cette clé, puis nginx accède à l'adresse réelle au nom de l'utilisateur, puis renvoie.
(Les avantages de ce processus sont nombreux)
(1) La véritable adresse est cachée et le programme peut intervenir dans l'accès de l'utilisateur en dehors du serveur en amont, améliorant la sécurité et intervenant dans le processus. Cela peut être très compliqué
(2) Obtenir les informations utilisateur et les stocker dans Redis. Le serveur en amont conserve les journaux stockés dans Redis dans Oracle via un programme de minuterie et les supprime, puis les analyse et les analyse plus en détail. les visualise
Voici le problème
Ce projet est encore en phase de test. Les ressources sont un serveur de serveur Windows et un serveur centos6.5. Pendant la phase de test, il y a environ 100 000 concurrence. en 10 secondes, il vient d'être déployé. Il n'y a toujours pas eu de problème pendant un jour ou deux, mais la connexion Redis a ensuite échoué. En ce qui concerne l'accès aux processus, la situation suivante apparaîtra. (Sous serveur windows)
De nombreux liens TCP FiN_WAIT_2 apparaissent.
(Partage vidéo d'apprentissage : Tutoriel vidéo Redis)
Analyse
1 Redis utilise un seul thread pour gérer les connexions, ce qui signifie qu'il le fera certainement. Les deux situations suivantes se produisent.
2. Évidemment, cela est dû à de nombreuses ressources non publiées entre nginx et redis. Vérifiez l'état TCP FIN_WAIT_2 et expliquez :
Dans les applications HTTP, il y a un problème avec le SERVEUR. ferme la connexion pour une raison quelconque, telle que le délai d'attente de KEEPALIVE. De cette façon, le SERVEUR qui se ferme activement entrera dans l'état FIN_WAIT2, mais il y a un problème avec la pile de protocole TCP/IP. L'état FIN_WAIT2 n'a pas de délai d'attente. (contrairement à l'état TIME_WAIT). ), donc si le CLIENT n'est pas fermé, cet état FIN_WAIT_2 restera jusqu'au redémarrage du système, et de plus en plus d'états FIN_WAIT_2 provoqueront le crash du noyau.
Eh bien, je n'ai pas bien étudié à l'université. Voici les changements de statut de la connexion http
Migration du statut du client
FERMÉ->SYN_SENT->ESTABLISHED ->FIN_WAIT_1 ->FIN_WAIT_2->TIME_WAIT->CLOSEDb.
Migration de l'état du serveur
FERMÉ->LISTEN->SYN reçu->ESTABLISHED->CLOSE_WAIT -> LAST_ACK->CLOSED
Clients interrompus et connexions persistantes
Certains clients ont des problèmes pour gérer les connexions persistantes (akakeepalives). Lorsque la connexion devient inactive et que le serveur ferme la connexion (sur la base de la directive KeepAliveTimeout), le client est programmé pour ne pas renvoyer FIN et ACK au serveur. Cela signifie que la connexion restera dans l'état FIN_WAIT_2 jusqu'à ce que l'un des événements suivants se produise :
Le client ouvre une nouvelle connexion au même site ou à un site différent, ce qui entraînera la fermeture complète du socket Connexion précédente.
L'utilisateur quitte le programme client, ce qui, sur certains (peut-être la plupart ?) des clients, amène le système d'exploitation à fermer complètement la connexion.
Délai d'expiration FIN_WAIT_2, sur les serveurs avec le délai d'expiration de l'état FIN_WAIT_2 défini.
Si vous avez de la chance, cela signifie que le client défectueux fermera complètement la connexion et libérera les ressources de votre serveur.
Cependant, il existe certains cas où le socket n'est jamais complètement fermé, comme par exemple un client d'accès commuté qui se déconnecte du FAI avant de fermer le programme client.
De plus, certains clients peuvent rester inactifs plusieurs jours sans créer de nouvelles connexions, et ainsi garder le socket valable plusieurs jours même s'il n'est plus utilisé. Il s'agit d'un bug dans l'implémentation TCP du navigateur ou du système d'exploitation.
Les raisons sont :
1. Connexion longue et lorsque la connexion est toujours à l'état IDLE et provoque SERVERCLOSE, le défaut de programmation CLIENT n'envoie pas les paquets FIN et ACK au SERVEUR
2 , APACHE1.1 et APACHE1.2 ont ajouté la fonction linger_close(), qui a été introduite dans le post précédent. Cette fonction a peut-être causé ce problème (je ne sais pas pourquoi)
Solution. :
1. Ajouter un mécanisme de timeout à l'état FIN_WAIT_2. Cette fonctionnalité n'est pas reflétée dans le protocole, mais a été implémentée dans certains OS
tels que : LINUX, SOLARIS, FREEBSD, HP-UNIX, IRIX, etc.
2. Ne compilez pas avec linger_close()
3. Utilisez plutôt SO_LINGER, qui peut être bien géré dans certains systèmes
4. Augmentez la mémoire mbuf utilisée pour stocker l'état de la connexion réseau afin d'éviter un crash du noyau
5. DÉSACTIVER KEEPALIVE
Au vu de cette situation, nous avons eu plusieurs discussions et quelques conclusions sont :
1 Définissez le pool de connexion de nginx et redis, et définissez le temps de conservation sur 10 respectivement. Secondes, 5 secondes, mais le résultat est toujours le même
2. Aucun keepalive, c'est-à-dire qu'aucun pool de connexion n'est utilisé, c'est-à-dire que close() est fermé à chaque fois qu'il est utilisé. qu'il y a moins de connexions, mais le pool de connexions n'est pas utilisé, ce qui signifie qu'il doit être ouvert et fermé 100 000 fois en 10 secondes, ce qui coûte trop cher
3. Ajout d'un cluster redis. au système de cluster d'origine peut résoudre le problème, mais 100 000 fois en 10 secondes. En fait, il n'y en a pas beaucoup. Cela peut être une astuce et le problème n'est pas trouvé
4. Définissez la limite de temps d'inactivité de Redis. , et le résultat est le même.
Solution :
n'est en fait pas une solution, car le mécanisme de mémoire de redis est abandonné et la propre technologie de mémoire de nginx est utilisée. La plupart des optimisations en ligne de Redis ne sont pas applicables et ce problème doit être analysé et résolu.
Recommandations associées : Tutoriel sur la base de données Redis
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!