Nos amis devraient en avoir assez entendu parler des middlewares de messages MQ, tels que : RabbitMQ, RocketMQ, Kafka, etc. Les avantages de l’introduction d’un middleware peuvent jouer un rôle dans la résistance à la concurrence élevée, aux pics et au découplage des activités.
Comme indiqué ci-dessus :
(1) Le service de commande délivre des messages au middleware MQ (2) Le service logistique surveille la consommation des messages du middleware MQ
Discutons dans cet article de la manière d'assurer le service de commande Le message est transmis avec succès au middleware MQ, en prenant RabbitMQ comme exemple.
Mes amis peuvent avoir des questions à ce sujet Si le service de commande lance le service de messagerie, cela ne signifie-t-il pas un succès si le retour est réussi ? Par exemple, le pseudo code suivant :
Dans le code ci-dessus, les messages sont généralement envoyés comme ceci. Pensez-vous qu'il y a un problème ?
Parlons d'un scénario ci-dessous. Que se passera-t-il si le serveur MQ tombe soudainement en panne ? Tous les messages envoyés par notre service de commande ont-ils disparu ? Oui, généralement le middleware MQ enregistre les messages en mémoire afin d'améliorer le débit du système. Si aucun autre traitement n'est effectué, une fois le serveur MQ en panne, tous les messages seront perdus. Cela n’est pas autorisé par l’entreprise et aura un impact important.
Les amis expérimentés diront : je sais qu'une solution consiste à conserver le message lors de l'envoi d'un message dans RabbitMQ, il y aura un paramètre durable qui pourra être défini s'il est défini sur true. , ce sera la persistance.
Dans ce cas, même si le serveur MQ tombe en panne, les messages seront stockés dans le fichier disque après le redémarrage, ils ne seront donc pas perdus. Oui, cela garantit que le message ne sera pas perdu avec une certaine probabilité.
Mais il y aura un scénario dans lequel le message vient d'être enregistré dans la mémoire MQ, mais avant qu'il n'ait le temps d'être mis à jour dans le fichier disque, il plante soudainement. (Merde, cela se produira dans un laps de temps si court. La probabilité est trop faible.) Ce scénario sera très courant dans le processus de livraison continue de messages à grande échelle.
Que faire ? Comment pouvons-nous garantir qu’il sera conservé sur le disque ?
Le problème ci-dessus se pose car personne ne nous dit si la persistance réussit. Heureusement, de nombreux MQ ont des fonctionnalités de notification de rappel, et RabbitMQ dispose d'un mécanisme de confirmation pour nous informer si la persistance réussit ?
Principe du mécanisme de confirmation :
(1) Le producteur du message envoie le message à MQ Si la réception est réussie, MQ renverra un message d'accusé de réception au producteur
(2) Si la réception du message est réussie. échoue, MQ Un message nack sera renvoyé au producteur
Le pseudocode ci-dessus a deux façons de traiter les messages, à savoir le rappel ack et le rappel nack ;
Est-ce que cela garantit que 100 % des messages ne seront pas perdus ?
Jetons un coup d'œil au mécanisme de confirmation. Imaginez si chaque fois que notre producteur envoie un message, MQ doit être conservé sur le disque, puis lancer un rappel d'accusé de réception ou de nack. Dans ce cas, le débit de notre MQ est très faible, car le message doit être conservé sur le disque à chaque fois. L'écriture sur le disque est très lente. Ceci est inacceptable dans les scénarios de concurrence élevée et le débit est trop faible.
Ainsi, l'implémentation réelle du disque persistant MQ est traitée via des appels asynchrones. Il dispose d'un certain mécanisme, par exemple, lorsqu'il y a des milliers de messages, ils seront vidés sur le disque en même temps. Au lieu de vider le disque à chaque fois qu'un message arrive.
Le mécanisme de confirmation est donc en fait un mécanisme d'écoute asynchrone, qui doit assurer le débit élevé du système. Cela signifie qu'il n'est toujours pas garanti à 100 % que le message ne sera pas perdu, car même si le mécanisme de confirmation est ajouté. , le message est toujours dans la mémoire MQ. L'ordinateur est tombé en panne sans clignoter sur le disque et il ne peut toujours pas être traité.
Après avoir tant dit, je ne peux toujours pas le garantir, alors que dois-je faire ? ? ?
En fait, la raison essentielle est qu'il est impossible de déterminer s'il est persisté ? Alors pouvons-nous faire en sorte que le message persiste nous-mêmes ? La réponse est oui, notre plan va encore évoluer.
Le processus dans l'image ci-dessus :
(1) Avant de transmettre le message, le producteur du service de commande conserve d'abord le message dans Redis ou Redis est recommandé pour des performances élevées. Le statut du message est Envoi.
(2) Le message de surveillance du mécanisme de confirmation est-il envoyé avec succès ? Si l'accusé de réception réussit, supprimez ce message dans Redis.
(3) Si nack échoue, vous pouvez choisir de renvoyer le message en fonction de votre propre entreprise. Vous pouvez également supprimer ce message, en fonction de votre décision commerciale.
(4) Une tâche planifiée est ajoutée ici pour extraire le message après un certain temps. Le statut du message est toujours en cours d'envoi. Ce statut indique que le service de commande n'a pas reçu le message de réussite.
(5) Les tâches planifiées délivreront des messages compensatoires. À ce stade, si l'accusé de rappel MQ est reçu avec succès, le message sera supprimé dans Redis.
Ce mécanisme est en fait un mécanisme de compensation. Peu importe que MQ le reçoive réellement, tant que l'état du message dans mon Redis est [Envoi], cela signifie que le message n'a pas été livré correctement. Démarrez ensuite la tâche planifiée pour surveiller et lancer la distribution de la compensation.
Bien sûr, nous pouvons également ajouter un numéro de compensation pour la tâche planifiée. Si cela se produit plus de 3 fois et qu'aucun message d'accusé de réception n'est toujours reçu, définissez directement le statut du message sur [Échec] et laissez les gens vérifier manuellement pourquoi. ?
Dans ce cas, la solution est plus parfaite, garantissant que 100% des messages ne sont pas perdus (bien sûr, cela n'inclut pas la panne de disque, donc une solution maître-esclave peut être utilisée).
Cependant, avec cette solution, il est possible d'envoyer le même message plusieurs fois. Il est très probable que MQ ait déjà reçu le message, mais il y a eu une panne de réseau lors du rappel du message d'accusé de réception, et le producteur ne l'a pas fait. recevez-le.
Ensuite, il faut exiger des consommateurs qu'ils veillent à l'idempotence lorsqu'ils consomment !
Comprenons d'abord ce qu'est l'idempotence ? Dans les applications distribuées, l'idempotence est très importante, c'est-à-dire que si une entreprise est exploitée dans les mêmes conditions, le résultat sera le même quel que soit le nombre de fois qu'elle est exécutée.
Pourquoi existe-t-il un scénario idempotent ? Parce que dans les grands systèmes, ils sont tous déployés de manière distribuée. Par exemple, les activités de commande et les activités d'inventaire peuvent être déployées indépendamment et constituent des services distincts. Lorsqu'un utilisateur passe une commande, le service de commande et le service d'inventaire seront appelés.
En raison du déploiement distribué, il est très probable que lors de l'appel du service d'inventaire, l'appel du service de commande échoue en raison du réseau et d'autres raisons. Cependant, en fait, le service d'inventaire a été traité, mais une exception s'est produite lors du traitement. le résultat a été renvoyé au service des commandes. À ce stade, le système établira généralement un plan de compensation, c'est-à-dire que le service de commande appellera ensuite le service d'inventaire et que l'inventaire sera réduit de 1.
Il y a un problème. En effet, le dernier appel a été réduit de 1, mais le service des commandes n'a pas reçu le résultat du traitement. Maintenant, il est rappelé et il faut le soustraire de 1. Cela ne correspond pas à l'entreprise et c'est une déduction supplémentaire.
Le concept d'idempotence est que peu importe le nombre de fois où le service d'inventaire est appelé dans les mêmes conditions, le résultat du traitement sera le même. Ce n'est qu'ainsi que la faisabilité du plan de compensation pourra être garantie.
Apprenez du mécanisme de verrouillage optimiste de la base de données, tel que :
Selon la version, c'est-à-dire avant d'exploiter l'inventaire, obtenez d'abord le numéro de version du produit actuel, puis exploitez Apportez ce numéro de version. Faisons le tri. Lorsque nous avons exploité l'inventaire pour la première fois, nous avons obtenu la version 1, et lorsque nous avons appelé le service d'inventaire, la version est devenue 2 mais il y a eu un problème lors du retour au service de commande. appel au service d'inventaire. Lorsque le service de commande est passé comme La version est toujours 1, et lorsque l'instruction SQL ci-dessus est à nouveau exécutée, elle ne sera pas exécutée car la version est passée à 2, la condition Where ne sera pas remplie ; Cela garantit que peu importe le nombre d’appels, il ne sera traité qu’une seule fois.
Le principe est d'utiliser la clé primaire de la base de données pour supprimer les doublons. Une fois l'affaire terminée, insérez l'identifiant de la clé primaire
L'identifiant unique. est la seule clé primaire de la table métier, comme l'ID produit
Le code d'empreinte digitale permet de distinguer le code pour chaque opération normale. Le code d'empreinte digitale est généré pour chaque opération ; la méthode d'horodatage + numéro d'entreprise peut être utilisée.
L'instruction SQL ci-dessus :
Si la valeur de retour est 0, cela signifie qu'il n'y a eu aucune opération. Après l'opération commerciale, vous pouvez insérer dans t_check (ID unique + code d'empreinte digitale)
Valeur de retour si elle est supérieure à 0. Après l'opération, retournez directement
Avantages : Implémentation simple
Inconvénients : Goulot d'étranglement de la base de données sous haute concurrence
Solution : Diviser les bases de données et les tables en fonction de l'ID pour le routage de l'algorithme
Utilisez l'opération atomique de redis pour marquer la fin de l'opération. Cette performance est meilleure. Mais il y aura quelques problèmes.
Premièrement : Devons-nous stocker les résultats commerciaux dans la base de données ? Si tel est le cas, le problème clé à résoudre est de savoir comment réaliser l'atomicité dans les opérations de base de données et Redis ?
Cela signifie que l'inventaire est réduit de 1, mais que dois-je faire s'il échoue lorsque l'opération redis termine la marque ? C'est-à-dire que nous devons nous assurer que la suppression de la base de données et Redis réussissent ou échouent ensemble
Deuxième : si la base de données n'est pas supprimée, alors tout est stocké dans le cache. Comment définir la stratégie de synchronisation planifiée ?
Cela signifie que l'inventaire est réduit de 1, et l'inventaire n'est pas supprimé. L'opération redis est directement effectuée pour terminer le marquage, puis un autre service de synchronisation est utilisé pour supprimer l'inventaire. Cela augmente la complexité du système. , et comment définir la stratégie de synchronisation
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!