Heim  >  Artikel  >  Java  >  Einführung in Ausnahme und Ergebnis (Codebeispiel)

Einführung in Ausnahme und Ergebnis (Codebeispiel)

不言
不言nach vorne
2019-03-12 15:52:003982Durchsuche

Der Inhalt dieses Artikels ist eine Einführung in Exception and Result (Codebeispiele). Ich hoffe, dass er für Freunde hilfreich ist.

Bei der Entwicklung verteilter Systeme müssen wir häufig verschiedene Statuscodes und Fehlermeldungen an den äußersten Aufrufer übergeben. Dieser Aufrufer ist normalerweise die http/api-Schnittstelle und Fehlermeldungen wie Anmeldefehler, Parameterfehler usw.

Die von der äußersten Schnittstelle bereitgestellten Daten liegen normalerweise in einem JSON-Format vor, das {code, msg, data} ähnelt, und darüber gibt es keinen Streit.

Bei RPC-Aufrufen zwischen Knoten und Methodenaufrufen innerhalb von Knoten in einem verteilten System werden Fehlerinformationen jedoch normalerweise mithilfe von ServiceException oder Result übertragen. Was ist der Unterschied zwischen diesen beiden Methoden und welche ist besser? Welches ist schlimmer? Dieser Artikel konzentriert sich auf Entwicklungseffizienz und Systemleistung, um dieses Problem zu untersuchen.

ErgebnisEinleitung

Dies ist eine relativ übliche Methode zur Übermittlung von Fehlerinformationen. Einige große Hersteller legen sie sogar direkt als technische Spezifikationen fest und zwingen alle dazu, sich an das Team zu wenden verfolgt diesen Ansatz. Gängige Ergebnisvorlagen lauten wie folgt:

@Data
public class Result<T> {
    private int code; // 也可以是String等
    private String msg;
    private T data;
}

Die Anwendung in der Systementwicklung sieht normalerweise so aus:

Result<UserModel> userModelResult = userService.query(userId);
if (!userModelResult.isSuccess() || userModelResult.getData != null) {
    return Result.fail(userModelResult); // 透传错误
}
UserModel userModel = userModelResult.getData();
if (userModel.getStatus() != UserStatusEnum.NORMAL) {
    return Result.fail("user unavaliable"); // 用户不可用
}
// ... 正常使用UserModel

In einer komplexeren verteilten Microservice-Umgebung gibt es viele ähnliche Codes, die jeweils abhängige Dienste aufrufen werden von einer ähnlichen Fehlertoleranzlogik begleitet.

Dieser Modus ähnelt der Fehlercodeverarbeitung in der Golang-Sprache. Dafür wird Golang auch kritisiert, das heißt, bei jedem Schritt muss eine Fehlerbeurteilung vorgenommen werden.

Die grausamere Realität ist, dass trotz der Ergebniskapselung immer noch Ausnahmen vom Back-End-System transparent übertragen werden. In den praktischen Anwendungen, denen ich ausgesetzt war, ist diese Art von abnormaler transparenter Übertragung, die das Ergebnispaket durchbricht, keineswegs ein Einzelfall Ich habe einen TC vom innersten Handelszentrum erhalten. Das Geschäft war abnormal und mehr als 5 Teams wurden bei der Behebung des Problems verfolgt.

Einführung in ServiceException

Wie der Name schon sagt, verwendet diese Methode Ausnahmeinterrupts, um normale Logik und Ausnahmelogik aufzuteilen.

In der Systementwicklung erfordern die meisten Fehler eine direkte Unterbrechung der Dienste und eine direkte Fehlerrückmeldung an den Benutzer. Aus diesem Grund müssen wir bei der Verwendung von Result oft so etwas schreiben wie if(result.isFail(. )){return...} Code wie diesen. Mit ServiceException können wir den größten Teil des ähnlichen Codes weglassen.

Normalerweise kann ServiceException wie folgt definiert werden:

@Getter
public class ServiceException extends RuntimeException {
    private final int code;
    private final String msg;
    public ApiException() {
        this(-1, null);
    }
    public ApiException(Code code) {
        this(code, null);
    }
    public ApiException(Code code, String msg) {
        super(msg);
        this.code = code;
        this.msg = msg;
    }
}

Wenn interne Komponenten des Systems auf ungewöhnliche Situationen wie Datenverlust, unbefugten Zugriff, Anmeldefehler, Kontosperrung usw. stoßen, werden sie direkt ausgelöst ServiceException unterbricht die Logik und verwendet dann den äußersten Filter oder Aspekt, der die Ausnahme abfängt, den Code und die Nachricht extrahiert und an den Benutzer zurückgibt.

Die tatsächlich verwendete Codelogik ähnelt dieser:

UserModel userModel = userService.query(userId); // userID不存在、不可用等隐藏在异常中
// ... 使用userModel

Diese Methode ist offensichtlich elegant und rationalisiert und hilft bei der Verbesserung der Entwicklungseffizienz und der späteren Wartung.

Es gibt jedoch viele Gerüchte auf dem Markt, die behaupten, dass die Verwendung abnormaler Interrupts die Leistung beeinträchtigt. Einige Leute sind sogar durch einfache Leistungstests zu dem Schluss gekommen, dass die Leistung abnormaler Interrupts hunderte Male schneller ist als die Rückgabe von Ergebnissen.

Leistungstest

Um Leistungsprobleme zu beheben, habe ich auch einen einfachen Test durchgeführt. Den spezifischen Testcode finden Sie unter:

https://. github.com/sisyphsu/b...

Apropos Benchmarks: Ich beneide die mit der Golang-Sprache gelieferte Testbibliothek.

Die Geschäftslogik im Test ist sehr einfach: Rufen Sie einfach System.currentTimeMillis() einmal auf und geben Sie einen langen Zeitstempel zurück.

Im Leistungstest werden der Rückgabewert Result und die Throw-Ausnahme verwendet. Für den Leistungstest zum Auslösen von Ausnahmen werden Aufrufstapeltests unterschiedlicher Tiefe hinzugefügt Ausnahme: Es ist notwendig, den Stapel des aktuellen Threads zu analysieren. Je tiefer der Aufrufstapel ist, desto größer ist der verursachte Leistungsverlust. Die spezifischen Werte für die Stapeltiefe sind 1, 10 und 100:

Test.test                  avgt    5  0.027 ± 0.001  us/op
Test.testException         avgt    5  1.060 ± 0.045  us/op
Test.testDeep10Exception   avgt    5  1.826 ± 0.122  us/op
Test.testDeep100Exception  avgt    5  9.802 ± 0.411  us/op

Auf den ersten Blick ist der Leistungsverlust einer Ausnahmestapeltiefe von 100 tatsächlich 360-mal so hoch wie bei normalen Methodenaufrufen Aus diesem Grund wird der Schluss gezogen, dass Java-Ausnahmeinterrupts schwerwiegende Leistungseinbußen verursachen.

Analysieren Sie die Auswirkungen der Leistung

Aber Sie müssen auf die Zeiteinheit achten, die nur Mikrosekunden, ein Tausendstel einer Millisekunde und ein Millionstel einer Sekunde beträgt.

Unter der Annahme, dass der Durchsatz einer einzelnen CPU eines Mikrodienstes 1000 QPS beträgt und 10 % davon illegale Anforderungen sind, beträgt der Leistungsverlust durch abnormale Unterbrechungen nur ein Zehntausendstel und die Auswirkung auf die Servicezeit beträgt nur 0,001 Just Millisekunden.

Im Leistungstest dient die Geschäftszeit nur dazu, die Systemzeit zu ermitteln, was etwa 25 ns dauert. Aus diesem Grund erreicht der durch abnormale Unterbrechungen verursachte Leistungsverlust ein erschreckendes „Hundertefaches“. Was aber, wenn sich die Geschäftszeit von 25 ns auf 25 us oder 25 ms ändert?

Lassen Sie uns noch einmal über Leistungsengpässe sprechen.

Wenn wir die Systemleistung analysieren, müssen wir deren Größenordnung und Leistungsengpässe verstehen und daran denken, in das Dilemma der Leistungsoptimierung zu geraten.

Um ein grobes Beispiel zu geben: Bei regulären Diensten dauern DB-Vorgänge mithilfe von Indizes zwischen 1 und 10 Millisekunden, der Zugriff auf verteilten Cache dauert zwischen 3 und 30 Millisekunden und der Netzwerkleistungsverlust von Microservice RPC liegt zwischen 3 und 10 Millisekunden. Der Netzwerkaufbau zwischen Client und Server dauert zwischen 5 und 300 Millisekunden und so weiter. In diesem Fall ist die Optimierung für ein Leistungsrisiko von 0,001 Millisekunden so, als würde man Sesamkörner aufheben und Wassermelone verlieren.

Ich habe einmal ein zugrunde liegendes Netzwerkprotokoll ähnlich wie TCP geschrieben. In diesem Hochfrequenzszenario bringt die Algorithmusoptimierung eine Leistungsoptimierung von 0,1 Mikrosekunden, was bedeutet, dass der Durchsatz pro Sekunde um ein Vielfaches oder sogar um ein Vielfaches verbessert wird. Im Niederfrequenzszenario verteilter Anrufe ist dieser Leistungsvorteil jedoch nutzlos.

Ein weiteres Beispiel: Als meine Kollegen und ich vor ein paar Jahren über das Design von DB-Datentabellen diskutierten, hatten wir einen heftigen Streit darüber, welche Länge von int für den Auftragsstatus verwendet werden sollte. Denken Sie jetzt darüber nach it, 1 optimiert für den Bestellstatus Bytes, spart es im Laufe der Jahre nur weniger als 1 MB Speicherplatz.

Abnormale Interrupts in RPC

Bei Remote-Aufruf-Frameworks wie Dubbo und HSF ist bei der Verwendung abnormaler Interrupts zur Übertragung von Fehlerinformationen zu beachten, dass der Ausnahmetyp so gestaltet sein muss universal, also der Basistyp, auf den jeder Microservice verweist.

Es wird in den technischen Spezifikationen einer bestimmten Fabrik erwähnt:

1) Bei Verwendung der Ausnahmerückgabemethode tritt ein Laufzeitfehler auf, wenn der Aufrufer sie nicht abfängt.

2) Wenn Sie keine Stack-Informationen hinzufügen, sondern nur eine neue benutzerdefinierte Ausnahme und Ihre eigene Fehlermeldung hinzufügen, wird dies dem aufrufenden Ende bei der Lösung des Problems nicht viel helfen. Wenn Stack-Informationen hinzugefügt werden, ist bei häufigen Aufruffehlern auch der Leistungsverlust der Datenserialisierung und -übertragung ein Problem.

Ich bin mit dieser technischen Spezifikation ziemlich unzufrieden.

Zuallererst müssen geschäftliche Ausnahmen vom Anrufer transparent an die äußerste Schicht übermittelt werden. Ausnahmen wie nicht vorhandene Daten, Anmeldefehler und Benutzersperrung sind oft nutzlos, wenn sie vom Anrufer abgefangen werden die Mitte.

Der zweite Punkt ist der Leistungsverlust. Welche Art von Leistungsverlust wird diese niederfrequente Datenserialisierung und Intranetübertragung mit sich bringen? Die Übermittlung von Stapelinformationen an den Anrufer ist auch hilfreich für die Fehlerbehebung. Laut Paket habe ich die dritte und vierte Ebene direkt umgangen, um den Fehler auf der untersten Ebene zu finden hat viel Zeit gespart.

Fazit

In verteilten Microservices kann die Verwendung von Ausnahme-Interrupts den Geschäftscode erheblich vereinfachen und nur minimale Auswirkungen auf die Leistung haben.

Mit Unterstützung von @NotNull, @Nullable und anderen Annotationen kann die verteilte Entwicklung so schnell und bequem sein wie der Wind. In komplexen Servicenetzwerken können Geschäftsausnahmen Entwicklern auch dabei helfen, Fehler genau zu lokalisieren und die peinliche Situation zu vermeiden, Fehlerpunkte Schicht für Schicht entlang der Aufrufkette aufzuspüren.

Das obige ist der detaillierte Inhalt vonEinführung in Ausnahme und Ergebnis (Codebeispiel). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:segmentfault.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen