Récemment, mon équipe de programmeurs et moi-même avons découvert un obstacle mineur au mécanisme de demande de l'application. Après quelques vérifications, j'ai trouvé la cause première.
Le pool de threads pour le traitement des demandes pour l'application était plein et n'a pas pu traiter les demandes supplémentaires. Après avoir vidé les threads, j'ai trouvé que la pile de threads était bloquée dans la zone d'écriture du journal. Ce n'est pas un nouveau problème que la pile de threads soit bloquée dans la zone d'écriture du journal, mais je pensais que c'était le résultat de quelque chose d'autre. Par conséquent, sur la base de l'expérience passée, nous avons supposé que le problème ne se trouvait pas dans la zone d'écriture du journal.
Après avoir utilisé beaucoup de dépannage et de frustrations sans trouver la cause racine du problème, nous avons examiné le code source et essayé d'obtenir des indices sur la cause du problème. .. J'ai trouvé que mon code a un verrou de journal. À partir de ce verrou de journal, j'ai trouvé qu'il y avait un bloc dans ArrayBlockingQueue.put de la pile de threads. Une enquête plus approfondie a révélé qu'il s'agissait d'une file d'attente de blocage d'une longueur de 1024. Cela signifie que s'il y a 1024 objets dans cette file d'attente, les demandes de placement suivantes seront bloquées.
Le programmeur qui a écrit ce code semblait penser que lorsque la BlockingQueue était pleine, et que les données devaient être traitées. Le code en question est ci-dessous.
if (blockingQueue.retainCapacity() < 1) { //todo } blockingQueue.put
Il y a deux parties principales au problème ici.
Un verdict complet va directement à "mettre" au lieu de "autre".
La logique de traitement est // todo ... même lorsque la file d'attente est pleine.
Le code ci-dessus montre que ce programmeur particulier n'est pas familier avec l'interface BlockingQueue. Vous n'avez pas besoin de prendre cette décision au préalable pour obtenir ce résultat. Une meilleure façon serait d'utiliser blockingQueue.offer. Si cela renvoie "faux", vous pouvez implémenter la gestion des exceptions appropriée.
BlockingQueue est une structure de données couramment utilisée en mode production / consommateur. Les types les plus couramment utilisés sont ArrayBlockingQueue, LinkedBlockingQueue et SynchronousQueue.
La principale différence entre ArrayBlockingQueue et LinkedBlockingQueue réside dans les objets placés dans la file d'attente. L'un est utilisé pour les tableaux et l'autre est utilisé pour les tables liées. D'autres différences sont également mentionnées dans les notes de code.
Les files d'attente liées ont généralement un débit plus élevé que les files d'attente basées sur des baies, mais avec des performances prévisibles médiocres pour la plupart des applications simultanées.
SynchronousQueue est une BlockingQueue spéciale. Il est utilisé lors de l'offre. Si aucun autre thread ne prend ou n'interroge actuellement, l'offre échouera. Il échouera également si aucun autre thread n'exécute l'offre en même temps pendant la prise. Ce mode spécial convient aux threads des files d'attente et aux pools de threads non fixes qui ont des exigences de réponse élevées.
Les scénarios commerciaux en ligne nécessitent un mécanisme de délai d'expiration dans tous les domaines où la concurrence et l'accès externe sont bloqués. Je ne sais pas combien de fois j'ai vu l'absence de mécanisme de délai d'expiration à la racine d'un obstacle majeur aux affaires en ligne. Le commerce en ligne se concentre sur le traitement et la réalisation rapide des demandes. Par conséquent, une défaillance rapide est le principe le plus important dans la conception de systèmes d'entreprise en ligne et de programmation de code. Selon ce principe, l'erreur la plus évidente dans le code ci-dessus est d'utiliser put à la place de offer, qui utilise le mécanisme de timeout. Alternativement, dans les scénarios non critiques, l'offre doit être utilisée directement, et le résultat de «faux» peut être considéré comme provoquant le déclenchement ou la journalisation d'exceptions directes.
Pour les scénarios BlockingQueue, vous devez limiter la longueur de la file d'attente en plus du mécanisme de délai d'expiration. Sinon, Entier. MAX_VALUE est utilisé par défaut. Dans ce cas, un bogue dans le code provoquera l'arrêt de la mémoire.
Lorsque nous parlons de BlockingQueue, nous mentionnons également la zone la plus utilisée de BlockingQueue, le pool de threads.
ThreadPoolExecutor a un paramètre BlockingQueue. Si ArrayBlockingQueue ou LinkedBlockingQueue est utilisé ici et que coreSize et poolSize du pool de threads sont différents, le threadpool proposera d'abord le BlockingQueue une fois que le thread coreSize est occupé. Sera envoyé. En cas de succès, le processus se termine. Cependant, ce scénario ne répond pas toujours aux besoins des entreprises en ligne.
Les entreprises en ligne fonctionnent dans un environnement au rythme rapide et nécessitent un traitement rapide plutôt que de placer des demandes dans des files d'attente. En fait, il est préférable pour les affaires en ligne si les demandes ne sont pas toutes mises en file d'attente. Ce type de structure d'entreprise en ligne est facilement cassé, et c'est un moyen relativement simple mais efficace de rejeter directement les demandes qui dépassent la capacité du système et d'émettre un message d'erreur à la place. .. Mais c'est un mécanisme limité.
Lorsque vous écrivez du code hautement concurrent et distribué, gardez à l'esprit qu'il est important de prêter attention non seulement à la conception du système, mais également aux détails du code. Ce faisant, vous pouvez éviter les problèmes ci-dessus.