Interviewer: Wie viele Anfragen kann ein SpringBoot-Projekt verarbeiten? (Vorsicht vor Gruben)

Inhaltsverzeichnis

 Demo

Antworten

Woher?

usw.

festhalten

Endlich warten


 

Hallo, ich bin Wai Wai.

Dieser Artikel führt Sie durch eine Interviewfrage, auf die Leser stoßen.

Laut Leserbericht lautete die ursprüngliche Frage des Interviewers: Wie viele Anfragen kann ein SpringBoot-Projekt gleichzeitig bearbeiten?

Ich weiß nicht, was Ihre erste Reaktion ist, wenn Sie diese Frage hören.

Ich weiß wahrscheinlich, in welche Richtung er fragen wird, aber bei einer solchen Interviewfrage mit nur einem Satz ist meine erste Reaktion: Wird es Fallstricke geben?

Deshalb werde ich die Fragen nicht voreilig beantworten, sondern zunächst einige Informationen einholen, wie zum Beispiel: Worum geht es in diesem Projekt? Welche Parameter wurden für das Projekt konfiguriert? Welcher Webcontainer wird verwendet? Was ist die bereitgestellte Serverkonfiguration? Welche Schnittstellen gibt es? Wie hoch ist die durchschnittliche Reaktionszeit der Schnittstelle?

Auf diese Weise kann nach einigen Fragen zumindest die Richtung der Interviewfragen grundsätzlich mit dem Interviewer vereinbart werden.

Beispielsweise kann der Interviewer die Frage des vorherigen Interviews nach einigen Ziehungen wie folgt ändern:

Ein SpringBoot-Projekt ohne besondere Konfiguration übernimmt alle die Standardeinstellungen. Wie viele Anfragen kann dieses Projekt gleichzeitig verarbeiten?

Wie viel verträgt es?

Ich weiß es nicht, aber als die Frage so aussah wie oben, fand ich eine Perspektive, um die Antwort zu erforschen.

Da „keine spezielle Konfiguration durchgeführt wurde“, werde ich dann selbst eine Demo erstellen und nach dem Drücken ist sie vorbei?

Sitzen Sie fest und halten Sie sich fest, es kann losgehen.

 Demo

Mit einem Handschlag mache ich zunächst eine Demo.

Diese Demo ist sehr einfach. Erstellen Sie einfach ein neues SpringBoot-Projekt per Idee.

Meine SpringBoot-Version ist 2.7.13.

Das gesamte Projekt hat nur diese beiden Abhängigkeiten:

Im gesamten Projekt gibt es nur zwei Klassen, und das Einzige, was Sie brauchen, ist, leer und klar zu sein.

Der TestController im Projekt verfügt nur über eine getTest-Methode, die zum Testen verwendet wird. Nach Erhalt der Anforderung schläft die Methode direkt eine Stunde lang.

Der Zweck besteht darin, den aktuellen Anforderungsthread direkt zu belegen, damit wir wissen, wie viele Threads im Projekt verfügbar sind:

@Slf4j
@RestController
public class TestController {

    @GetMapping("/getTest")
    public void getTest(int num) throws Exception {
        log.info("{} 接受到请求:num={}", Thread.currentThread().getName(), num);
        TimeUnit.HOURS.sleep(1);
    }
}

Die Datei application.properties im Projekt ist ebenfalls leer:

 

Gibt es auf diese Weise kein SpringBoot „ohne spezielle Konfiguration“?

Basierend auf dieser Demo lautet die Frage des vorherigen Interviews: Wie oft kann ich die getTest-Methode dieser Demo in einem kurzen Zeitraum kontinuierlich aufrufen?

Wird das Problem nicht wieder etwas einfacher?

Wie wird also der vorherige „kontinuierliche Aufruf in kurzer Zeit“ im Code ausgedrückt?

Es ist ganz einfach: Rufen Sie die Schnittstelle einfach weiterhin in der Schleife auf.

public class MainTest {
    public static void main(String[] args) {
        for (int i = 0; i < 1000; i++) {
            int finalI = i;
            new Thread(() -> {
                HttpUtil.get("127.0.0.1:8080/getTest?num=" + finalI);
            }).start();
        }
        //阻塞主线程
        Thread.yield();
    }
}

Wenn Sie einige Stresstest-Tools wie jmeter verwenden, sieht es natürlich überzeugender und professioneller aus. Ich bin hier nur faul und lade einfach den Code hoch.

Antworten

Nach den vorherigen Vorbereitungen sind die Demo- und Testcodes fertig.

Der nächste Schritt besteht darin, zuerst die Demo auszuführen:

 

Führen Sie dann einen MainTest aus.

Wenn MainTest ausgeführt wird, gibt die Demo-Seite schnell und in großem Umfang folgende Protokolle aus:

 Das ist das Protokoll, das ich in der vorherigen getTest-Methode geschrieben habe:

 

Ok, jetzt kommen wir zurück zu dieser Frage:

Ich rufe die getTest-Methode dieser Demo in kurzer Zeit kontinuierlich auf. Wie oft kann ich sie höchstens aufrufen?

Können Sie mir bitte sagen, wie ich die Antwort auf diese Frage bekomme?

Ich bin hier, um mit großer Anstrengung ein Wunder zu vollbringen. Zählen Sie einfach, wie oft das Schlüsselwort „Anfrage erhalten“ im Protokoll erscheint:

 Offensichtlich lautet die Antwort:

 

Wenn der Interviewer Sie also fragt: Wie viele Anfragen kann ein SpringBoot-Projekt gleichzeitig bearbeiten?

Nachdem Sie so getan hatten, als würden Sie sorgfältig nachdenken, sagten Sie bestimmt: 200 Mal.

Der Interviewer nickt leicht und wartet darauf, dass Sie fortfahren.

Auch Sie sind insgeheim froh, zum Glück haben Sie den Artikel von Meister Wai Wai Wai gelesen und eine Antwort vorgetragen. Warten Sie dann, bis der Interviewer weitere Fragen stellt.

Die Atmosphäre wurde plötzlich unangenehm.

Dann gehst du nach Hause und wartest auf die Benachrichtigung.

 

200 Mal ist diese Antwort richtig, aber wenn Sie nur 200 Mal sagen, erscheint diese Antwort etwas umständlich.

Wichtig ist, wie kam dieser Wert zustande?

Den folgenden Teil müssen Sie sich also auch merken.

Woher?

Bevor ich anfange zu untersuchen, wie es dazu kam, möchte ich Ihnen eine Frage stellen: Wem gehören diese 200 Threads oder wer verwaltet diesen Thread?

Ist es Spring Boot?

Sicherlich nicht, SpringBoot ist kein Webcontainer.

Tomcat sollte die 200 Threads verwalten.

Wir können dies auch durch Thread-Dump überprüfen:

 

 Anhand der Thread-Dump-Datei können wir erkennen, dass sich eine große Anzahl von Threads im Ruhezustand befindet. Klicken Sie auf diese Threads, um deren Stapelnachrichten anzuzeigen. Sie können Schlüsselwörter wie Tomcat, Threads und ThreadPoolExecutor sehen:

at org.apache.Tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1791)
at org.apache.Tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
at org.apache.Tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.Tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.Tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)

Basierend auf dem Phänomen, dass „200 Anfragen in kurzer Zeit sofort verarbeitet werden“, kombiniert mit Ihrem auswendig gelernten und sehr soliden Thread-Pool-Wissen, machen Sie zunächst eine mutige Vermutung: Die Standardanzahl der Kern-Threads in Tomcat beträgt 200.

Als nächstes gehen wir zum Quellcode, um zu überprüfen, ob diese Vermutung richtig ist.

Ich habe bereits zuvor erklärt, wie man den Quellcode liest: „Ich versuche, Ihnen durch diesen Artikel eine Möglichkeit beizubringen, den Quellcode zu lesen.“ Das Wichtigste besteht darin, einen gültigen Haltepunkt zu erreichen und dann den Quellcode basierend auf dem Aufrufstapel am Haltepunkt zu finden.

Hier werde ich Ihnen eine weitere Methode beibringen, um den Aufrufstapel ohne Unterbrechungspunkte zu erhalten.

Es wurde bereits gezeigt, dass es sich um einen Thread-Dump handelt.

Rechts ist ein vollständiger Aufrufstapel eines Threads:

Da wir in diesem Aufrufstapel nach dem Quellcode suchen, der sich auf den Tomcat-Thread-Pool bezieht, erscheint in dieser Zeile das relevante Schlüsselwort zum ersten Mal:

org.apache.Tomcat.util.threads.ThreadPoolExecutor.Worker#run

 

Dann setzen wir einen Haltepunkt auf dieser Linie.

Starten Sie das Projekt neu und beginnen Sie mit dem Debuggen.

Nach der Eingabe von runWorker kommt Ihnen dieser Teil des Codes sehr bekannt vor:

Es ist genau derselbe wie der Thread-Pool-Quellcode im JDK.

Wenn Sie mit dem Quellcode des JDK-Thread-Pools vertraut sind, fühlt sich das Debuggen des Tomcat-Thread-Pools wie eine Heimreise an.

Wenn Sie damit nicht vertraut sind, empfehle ich Ihnen, sich so schnell wie möglich damit vertraut zu machen.

Wenn der Haltepunkt ausfällt, können Sie in der getTask-Methode mehrere wichtige Parameter zum Thread-Pool sehen:

org.apache.Tomcat.util.threads.ThreadPoolExecutor#getTask

 

corePoolSize, die Anzahl der Kernthreads, der Wert ist 10.

MaximumPoolSize, die maximale Anzahl von Threads, der Wert beträgt 200.

Und basierend auf dem Parameter MaximumPoolSize werden Sie, wenn Sie sich auf den Code freuen, feststellen, dass der Standardwert 200 ist:

Nun, an diesem Punkt stellen Sie fest, dass Ihre vorherige Vermutung, dass „die Standardanzahl der Kernthreads für Tomcat 200 beträgt“, falsch ist.

Aber keine Panik, kombinieren Sie noch einmal Ihr auswendig gelerntes und sehr solides Threadpool-Wissen.

Und ich habe es in meinem Herzen noch einmal gesagt: Wenn der Thread-Pool die Aufgabe empfängt, aktivieren Sie zuerst die Anzahl der Kernthreads, verwenden Sie dann die Warteschlangenlänge und aktivieren Sie schließlich die maximale Anzahl von Threads.

Da wir zuvor überprüft haben, kann Tomcat 200 Anforderungen gleichzeitig verarbeiten, sein Thread-Pool verfügt nur über 10 Kern-Threads und die maximale Anzahl von Threads beträgt 200.

Dies zeigt, dass mein vorheriger Testfall die Warteschlange gefüllt hat, was dazu geführt hat, dass der Tomcat-Thread-Pool die maximale Anzahl von Threads aktiviert hat:

Nun, es muss so sein!

Die entscheidende Frage lautet nun: Wie groß ist die Standardwarteschlangenlänge des Tomcat-Thread-Pools?

Im aktuellen Debug-Modus kann die Warteschlangenlänge über Alt+F8 angezeigt werden:

wc, dieser Wert ist Integer.MAX_VALUE, also groß?

Ich habe insgesamt nur 1.000 Aufgaben, also ist es unmöglich, voll zu sein?

Ein Thread-Pool:

  • Die Anzahl der Kernthreads, der Wert beträgt 10.

  • Die maximale Anzahl von Threads beträgt 200.

  • Warteschlangenlänge, der Wert ist Integer.MAX_VALUE.

Nachdem 1000 zeitaufwändige Aufgaben eingegangen sind, sollten nur noch 10 Threads funktionieren, und dann sollten die restlichen 990 Threads in die Warteschlange gestellt werden, oder?

Habe ich den stereotypen Aufsatz falsch rezitiert?

Keine Panik zu diesem Zeitpunkt, Suogen Latiao beruhigt sich.

Derzeit ist die Anzahl der Kernthreads bekannt, der Wert beträgt 10. Der Ablauf dieser 10 Threads entspricht unserer Erkenntnis.

Aber als die 11. Aufgabe kam, hätte sie in die Warteschlange gestellt werden müssen, um sich in die Warteschlange zu stellen.

Nun scheint es, dass die maximale Anzahl von Threads direkt aktiviert ist.

Ändern wir also zunächst den Testfall:

Dann stellt sich die Frage: Wie wurde die letzte Anfrage an den Thread-Pool übermittelt?

Wie bereits erwähnt, ist der Thread-Pool-Quellcode von Tomcat im Wesentlichen derselbe wie der von JDK.

Beim Senden von Aufgaben an den Thread-Pool wird die Ausführungsmethode ausgeführt:

org.apache.Tomcat.util.threads.ThreadPoolExecutor#execute(java.lang.Runnable)

Für Tomcat ruft esexecuteInternal diese Methode auf:

org.apache.Tomcat.util.threads.ThreadPoolExecutor#executeInternal

Bei dieser Methode dient die mit ① gekennzeichnete Stelle dazu, zu beurteilen, ob die aktuelle Anzahl der Arbeitsthreads geringer ist als die Anzahl der Kernthreads. Wenn sie geringer ist, rufen Sie direkt die addWorker-Methode auf, um Threads zu erstellen.

Der mit ② markierte Ort dient hauptsächlich dazu, die Angebotsmethode aufzurufen, um zu prüfen, ob der Warteschlange weiterhin Aufgaben hinzugefügt werden können.

Wenn Sie nicht weiter hinzufügen können, bedeutet dies, dass die Warteschlange voll ist. Gehen Sie dann zu der mit ③ markierten Stelle, um zu sehen, ob Sie die addWorker-Methode ausführen können, um Nicht-Kern-Threads zu erstellen, dh die maximale Anzahl von Threads zu aktivieren.

Nachdem wir diese Logik geglättet haben, ist es sehr klar, welchen Teil des Codes wir uns als nächstes ansehen sollten.

Die Hauptsache ist, sich die Logik von workQueue.offer(command) anzusehen.

Wenn es „true“ zurückgibt, bedeutet dies, der Warteschlange beizutreten, und wenn es „false“ zurückgibt, bedeutet es, dass die maximale Anzahl von Threads aktiviert wird.

Diese WorkQueue ist eine TaskQueue, die mir überhaupt nicht bekannt vorkommt:

Natürlich kommt es Ihnen nicht bekannt vor, da es sich hierbei um eine von Tomcat selbst erstellte Warteschlange handelt, die auf LinkedBlockingQueue basiert.

Die Antwort auf die Frage ist in der Angebotsmethode von TaskQueue verborgen.

Deshalb werde ich mich darauf konzentrieren, Sie durch diese Angebotsmethode zu führen:

org.apache.Tomcat.util.threads.TaskQueue#offer

Die mit ① gekennzeichnete Stelle beurteilt, ob die übergeordnete Klasse null ist, und ruft in diesem Fall direkt die Angebotsmethode der übergeordneten Klasse auf. Erklären Sie, dass zur Aktivierung dieser Logik unser übergeordnetes Element nicht null sein darf.

Was ist also dieser Elternteil und woher kommt er?

Das übergeordnete Element ist der Tomcat-Thread-Pool. Durch seine Set-Methode können wir erkennen, dass er nach der Initialisierung des Thread-Pools zugewiesen wird.

Mit anderen Worten: Sie können verstehen, dass im Tomcat-Szenario das übergeordnete Element nicht leer ist.

Die mit ② markierte Stelle ruft die Methode getPoolSizeNoLock auf:

Mit dieser Methode werden mehrere Threads im aktuellen Thread-Pool abgerufen.

Wenn also dieser Ausdruck wahr ist:

parent.getPoolSizeNoLock() == parent.getMaximumPoolSize()

Dies bedeutet, dass die Anzahl der Threads im aktuellen Thread-Pool bereits der maximalen Anzahl konfigurierter Threads entspricht. Rufen Sie dann die Angebotsmethode auf, um die aktuelle Anforderung in die Warteschlange zu stellen.

An der mit ③ gekennzeichneten Stelle wird beurteilt, ob die Anzahl der an den Thread-Pool übermittelten Aufgaben zur Ausführung oder Ausführung geringer ist als die Anzahl der Threads im aktuellen Thread-Pool.

Wenn ja, bedeutet dies, dass im aktuellen Thread-Pool inaktive Threads vorhanden sind, die die Aufgabe ausführen können. Anschließend wird die Aufgabe in die Warteschlange gestellt und vom inaktiven Thread zur Ausführung mitgenommen.

Dann kommt der entscheidende Punkt, die mit ④ markierte Stelle.

Wenn die Anzahl der Threads im aktuellen Thread-Pool geringer ist als die maximale Anzahl der im Thread-Pool konfigurierten Threads, wird „false“ zurückgegeben.

Was passiert, wie bereits erwähnt, wenn die Angebotsmethode „false“ zurückgibt?

Sind Sie direkt zu der mit ③ in der obigen Abbildung markierten Stelle gegangen und haben versucht, Nicht-Kern-Threads hinzuzufügen?

Dies dient dazu, die Konfiguration der maximalen Anzahl von Threads zu ermöglichen.

Also, Freunde, was ist hier los?

Diese Situation unterscheidet sich tatsächlich von dem stereotypen Aufsatz über den Thread-Pool, den wir auswendig gelernt haben.

Der Thread-Pool des JDK wird zunächst mit der Anzahl der Kern-Threads, dann mit der Warteschlangenlänge und schließlich mit der maximalen Thread-Konfiguration konfiguriert.

Der Thread-Pool von Tomcat verwendet zuerst die Kern-Thread-Nummernkonfiguration, dann die maximale Thread-Konfiguration und schließlich die Warteschlangenlänge.

Der Interviewer wird Ihnen also in Zukunft sagen: Sprechen wir über den Arbeitsmechanismus des Thread-Pools?

Stellen Sie einfach zuerst eine Frage: Sprechen Sie über den Thread-Pool von JDK oder den Thread-Pool von Tomcat, da es einen kleinen Unterschied im Betriebsmechanismus der beiden gibt.

Dann schauen Sie sich einfach seinen Gesichtsausdruck an.

Wenn es eine Spur von Zögern gibt und dann leichthin gesagt wird: Vergleichen wir es.

Dann herzlichen Glückwunsch, Sie haben begonnen, eine kleine Initiative zu diesem Thema zu ergreifen.

Um Ihnen schließlich ein tieferes Verständnis des Unterschieds zwischen dem Tomcat-Thread-Pool und dem JDK-Thread-Pool zu vermitteln, gebe ich Ihnen einen Code, der durch Kopieren direkt ausgeführt werden kann.

Wenn Sie die Codezeile taskqueue.setParent(executor) auskommentieren, ist ihr Betriebsmechanismus der Thread-Pool von JDK.

Wenn diese Codezeile vorhanden ist, wird ihr Betriebsmechanismus zum Thread-Pool von Tomcat.

Lass uns spielen.

import org.apache.tomcat.util.threads.TaskQueue;
import org.apache.tomcat.util.threads.TaskThreadFactory;
import org.apache.tomcat.util.threads.ThreadPoolExecutor;

import java.util.concurrent.TimeUnit;

public class TomcatThreadPoolExecutorTest {

    public static void main(String[] args) throws InterruptedException {
        String namePrefix = "歪歪歪-exec-";
        boolean daemon = true;
        TaskQueue taskqueue = new TaskQueue(300);
        TaskThreadFactory tf = new TaskThreadFactory(namePrefix, daemon, Thread.NORM_PRIORITY);
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5,
                150, 60000, TimeUnit.MILLISECONDS, taskqueue, tf);
        taskqueue.setParent(executor);
        for (int i = 0; i < 300; i++) {
            try {
                executor.execute(() -> {
                    logStatus(executor, "创建任务");
                    try {
                        TimeUnit.SECONDS.sleep(2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        Thread.currentThread().join();
    }

    private static void logStatus(ThreadPoolExecutor executor, String name) {
        TaskQueue queue = (TaskQueue) executor.getQueue();
        System.out.println(Thread.currentThread().getName() + "-" + name + "-:" +
                "核心线程数:" + executor.getCorePoolSize() +
                "\t活动线程数:" + executor.getActiveCount() +
                "\t最大线程数:" + executor.getMaximumPoolSize() +
                "\t总任务数:" + executor.getTaskCount() +
                "\t当前排队线程数:" + queue.size() +
                "\t队列剩余大小:" + queue.remainingCapacity());
    }
}

usw.

Wenn Sie den Arbeitsmechanismus des Tomcat-Thread-Pools noch nicht wirklich verstanden haben, haben Sie möglicherweise das Gefühl, dass Sie einen kleinen Fortschritt gemacht haben, wenn Sie dies sehen.

Beachten Sie jedoch, dass ich Nein gesagt habe.

Erinnern Sie sich an die Frage des Interviewers von Anfang an?

Die ursprüngliche Frage des Interviewers lautete: Wie viele Anfragen kann ein SpringBoot-Projekt gleichzeitig bearbeiten?

Dann habe ich über das Funktionsprinzip eines so großen Stapels von Tomcat-Thread-Pools gesprochen. Passt diese Antwort zu dieser Frage?

Ja, bis auf den zu Beginn vorgeschlagenen Wert von 200 stimmt er nicht überein, und selbst in den Augen des Interviewers ist die Antwort völlig irrelevant.

Um diese beiden „nicht übereinstimmenden“ Dinge reibungslos zu verbinden, müssen Sie daher zunächst die Fragen des Interviewers beantworten und dann mit der Erweiterung beginnen.

Antworten Sie beispielsweise so: Ein SpringBoot-Projekt, das keine spezielle Konfiguration durchführt und alle Standardeinstellungen verwendet. Die maximale Anzahl von Anfragen, die dieses Projekt gleichzeitig verarbeiten kann, hängt vom verwendeten Webcontainer ab, und SpringBoot verwendet standardmäßig Tomcat.

Die Standardanzahl der Kernthreads von Tomcat beträgt 10, die maximale Anzahl der Threads beträgt 200 und die Warteschlangenlänge ist unendlich. Da sich sein Betriebsmechanismus jedoch von dem des JDK-Thread-Pools unterscheidet, wird die maximale Anzahl von Threads direkt aktiviert, nachdem die Anzahl der Kern-Threads voll ist. Daher können in der Standardkonfiguration 200 Anfragen gleichzeitig bearbeitet werden.

Bei der tatsächlichen Verwendung sollte dieser Parameter basierend auf der tatsächlichen Servicesituation und zugehörigen Informationen wie der Serverkonfiguration ausgewertet und eingestellt werden.

Diese Antwort ist fast dieselbe.

Wenn Sie jedoch Pech haben und mich treffen, frage ich Sie möglicherweise, um zu überprüfen, ob Sie es wirklich selbst erforscht haben oder nur ein paar Artikel gelesen haben:

Dann ändert sich nichts weiter. Wenn ich nur die Konfiguration von server.tomcat.max-connections=10 hinzufüge, wie viele Anfragen können zu diesem Zeitpunkt maximal verarbeitet werden?

Sie können es wahrscheinlich erraten: 10.

Ja, ich habe 1000 Aufgaben erneut eingereicht und die Konsolenausgabe beträgt tatsächlich 10.

Wie kann also der Parameter max-connections die Anzahl der Anfragen steuern?

Warum ist uns dieser Parameter bei der vorherigen Analyse nicht aufgefallen?

Schauen wir uns zunächst den Standardwert an:

Da der Standardwert 8192 ist, was größer als die maximale Anzahl von Threads 200 ist, ist dieser Parameter nicht auf uns beschränkt, sodass wir ihn nicht beachtet haben.

Wenn wir ihn auf 10 anpassen, was weniger als die maximale Anzahl von Threads von 200 ist, wird er zu einem begrenzenden Element.

Was bewirkt also der Parameter „max-connections“?

Finden Sie es zunächst selbst heraus.

Gleichzeitig gibt es einen solchen Parameter, der Standardwert ist 100:

server.tomcat.accept-count=100

Wofür ist das?

„Es hängt mit der Anzahl der Verbindungen zusammen“, ich kann Sie hier nur daran erinnern, lassen Sie es uns selbst erkunden.

festhalten

Durch die vorherige Analyse wissen wir, dass wir zur Beantwortung der Frage „Die Anzahl der Aufgaben, die ein SpringBoot-Projekt standardmäßig verarbeiten kann“ zunächst den verwendeten Webcontainer klären müssen.

Hier stellt sich also wieder die Frage: Welche Container sind in SpringBoot integriert?

Tomcat、Jetty、Netty、Undertow

Bisher basierten wir alle auf der Tomcat-Analyse. Was ist, wenn wir den Container ändern?

Wenn Sie beispielsweise zu Undertow wechseln, habe ich nur von diesem Ding gehört, es aber nie wirklich verwendet. Für mich ist es eine Blackbox.

Egal, ändern Sie es zuerst.

Um von Tomcat zu Undertow zu wechseln, müssen Sie nur die Maven-Abhängigkeiten ändern und nichts anderes muss geändert werden:

Starten Sie das Projekt erneut. Aus dem Protokoll können Sie erkennen, dass es am Undertow-Container geändert wurde:

An diesem Punkt führe ich die MainTest-Methode erneut aus oder sende 1000 Anfragen:

Aus dem Protokoll ging hervor, dass nur 48 Anfragen bearbeitet wurden.

Es ist sehr verwirrend, was ist mit 48 los, warum ist es keine ganze Zahl, das macht eine Zwangsstörung unangenehm.

Was denken Sie derzeit? Möchten Sie sehen, woher die Zahl 48 kommt?

Was denken Sie?

Habe ich es dir nicht beigebracht, als ich vorher nach Tomcats 200 gesucht habe, stell es einfach auf Undertow.

Klicken Sie auf den Thread-Dump und sehen Sie sich dann die Stapelmeldung an:

Entdecken Sie den Thread-Pool EnhancedQueueExecutor und suchen Sie dann die Parameter beim Erstellen des Thread-Pools in dieser Klasse.

Es ist einfach, diesen Konstruktor zu finden:

Setzen Sie hier also einen Haltepunkt und starten Sie das Projekt neu.

Durch Debug können wir erkennen, dass die Schlüsselparameter alle vom Builder stammen.

Im Builder sind sowohl coreSize als auch maxSize 48 und die Warteschlangenlänge ist Integer.MAX_VALUE.

Schauen Sie sich also an, woher die coreSize im Builder kommt.

Klicken Sie, um festzustellen, dass der Standardwert von coreSize 16 ist:

Keine Panik, unterbrechen Sie einfach das Projekt und starten Sie es neu.

Dann bleiben Sie bei der setCorePoolSize-Methode stehen, und der Eingabeparameter dieser Methode ist der 48, nach dem wir suchen:

Folgen Sie der Rebe, und nachdem Sie den Haltepunkt mehrmals wiederholt und neu gestartet haben, werden Sie feststellen, dass 48 eine Variable mit dem Namen WORKER_TASK_CORE_THREADS ist, die von hier stammt:

Die Variable WORKER_TASK_CORE_THREADS wird wie folgt festgelegt:

io.undertow.Undertow#start

Und der Wert von workerThreads ist hier wie folgt:

io.undertow.Undertow.Builder#Builder

Nehmen Sie die Anzahl der CPUs der Maschine multipliziert mit 8.

Hier bin ich also 6*8=48.

Oh, die Wahrheit ist ans Licht gekommen, so ist 48 entstanden.

langweilig.

Es ist wirklich langweilig, aber da es durch Undertow ersetzt wurde, können Sie seinen NIO ByteBuffer, NIO Channel, BufferPool, XNIO Worker, IO Thread Pool, Worker Thread Pool studieren ...

Dann vergleichen Sie es mit Tomcat,

Es fängt an, interessant zu werden.

Endlich warten

Dieser Artikel basiert auf der Interviewfrage „Wie viele Anfragen kann ein SpringBoot-Projekt gleichzeitig verarbeiten?“

Aber nach unserer einfachen Analyse oben wissen Sie auch, dass die Antwort auf diese Frage anders ausfällt, wenn einige spezifische Voraussetzungen nicht berücksichtigt werden.

Lassen Sie mich Ihnen zum Beispiel ein weiteres Beispiel oder unsere Demo geben. Verwenden Sie einfach die Annotation @Async, und nichts anderes bleibt unverändert:

Starten Sie das Projekt erneut, initiieren Sie den Zugriff und die Protokollausgabe sieht folgendermaßen aus:

Die Anzahl der Anfragen, die gleichzeitig verarbeitet werden können, hat sich von Tomcats Standardwert von 200 auf 8 geändert?

Aufgrund des Thread-Pools, der der @Async-Annotation entspricht, beträgt die Standardanzahl der Kernthreads 8.

Sie sehen also, mit einer kleinen Änderung sieht die Antwort wieder anders aus, und gleichzeitig ist auch der Prozess der internen Verbreitung dieser Anfrage ein anderer, was ein weiterer Punkt ist, der diskutiert werden kann.

Dasselbe gilt auch während des Vorstellungsgesprächs. Beantworten Sie die Fragen nicht überstürzt. Wenn Sie das Gefühl haben, dass die Fragebeschreibung des Interviewers unklar ist, können Sie zunächst vorsichtig nachfragen, um zu sehen, ob Sie einige Standardbedingungen herausfinden können, die er nicht genannt hat.

Wenn die „Standardbedingungen“ stärker ausgewertet werden, ist es wahrscheinlicher, dass Ihre Antwort vom Interviewer akzeptiert wird. Und dieser Ausgrabungsprozess ist auch ein wichtiges Leistungsglied im Interviewprozess.

Darüber hinaus stellt der Interviewer manchmal gerne solche „mehrdeutigen“ Fragen, denn je vager die Frage ist, desto mehr Gruben gibt es. Wenn der Interviewer in die Grube springt, die er gegraben hat, ist das das Ende einer Konfrontation;

Beeilen Sie sich also nicht mit der Beantwortung von Fragen, denken Sie mehr nach und fragen Sie mehr. Ob für den Interviewer oder den Interviewer, ein gutes Interviewerlebnis darf nicht aus einer Frage und Antwort ohne Interaktion bestehen, sondern aus einem Prozess des gegenseitigen Sehens.

Je suppose que tu aimes

Origine blog.csdn.net/weixin_57099902/article/details/131832568
conseillé
Classement