Home > Java > javaTutorial > Introduction to Exception and Result (code example)

Introduction to Exception and Result (code example)

不言
Release: 2019-03-12 15:52:00
forward
4053 people have browsed it

This article brings you an introduction to Exception and Result (code examples), which has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.

In distributed system development, we often need to pass various status codes and error messages to the outermost caller. This caller is usually the http/api interface, and error messages such as login Failure, parameter error, etc.

The data exposed by the outermost interface is usually in a json format similar to {code, msg, data}. There is no dispute about this.

However, in RPC calls between nodes and method calls within nodes in a distributed system, error information is usually transmitted using ServiceException or Result. What is the difference between these two methods and which one is better? Which one is worse? This article focuses on development efficiency and system performance to explore this issue.

ResultIntroduction

This is a relatively common way of transmitting error information. Some major manufacturers even directly set them as technical specifications, forcing everyone to The team takes this approach. Common Result templates are as follows:

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

The application in system development usually looks like this:

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
Copy after login

In a more complex distributed microservice environment, there are many similar codes, each Calls to dependent services are accompanied by a similar fault-tolerance logic.

This mode is similar to the error code processing in the Golang language. This is also where Golang is criticized, that is, error judgment must be made at every step.

The more cruel reality is that despite the Result encapsulation, there will still be Exceptions from the back-end system transparently transmitted. In the practical applications I have come into contact with, this kind of abnormal transparent transmission that breaks through the Result encapsulation is by no means an isolated case. When the system I am responsible for calls the most powerful domestic trading system at the back end, I have received a TC from the most internal trading center. The business was abnormal, and more than 5 teams were tracked when troubleshooting the problem.

Introduction to ServiceException

As the name suggests, this method uses exception interrupts to split normal logic and exception logic.

In system development, most errors require direct interruption of services and direct error feedback to the user. Because of this, when we use Result, we often need to write something like if(result.isFail( )){return...} code like this. Using ServiceException, we can omit most of the similar code.

Usually ServiceException can be defined like this:

@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;
    }
}
Copy after login

When internal components of the system encounter abnormal situations such as data loss, unauthorized access, login failure, account lockout, etc., they directly throw ServiceException to interrupt the logic, and then use The outermost Filter or Aspect catches the exception, extracts the code and msg and returns it to the user.

The actual code logic used is similar to this:

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

This method is obviously elegant and streamlined, and is helpful for improving development efficiency and later maintenance.

However, there are many rumors in the market claiming that using abnormal interrupts will affect performance. Some people have even concluded through simple performance tests that the performance time of abnormal interrupts is hundreds of times faster than returning Result.

Performance test

In view of the performance problem, I also conducted a simple test. For the specific test code, please see:

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

JMH is used for performance testing here. Speaking of benchmarks, I really envy the test library that comes with the golang language. It is so convenient.

The internal business logic of the test is very simple, just call System.currentTimeMillis() once and return a long timestamp.

The Result return value and Exception are used in the performance test respectively. For the performance test of throwing exceptions, call stack tests of different depths are added. This is because when Java throws an exception, It is necessary to analyze the stack of the current Thread, and the deeper the call stack, the greater the performance loss caused. The specific stack depth values ​​are 1, 10, and 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
Copy after login

At first glance, the performance loss of an exception stack depth of 100 is indeed 360 times that of ordinary method calls. Some people are indeed based on this reason It is concluded that Java exception interrupts cause serious performance loss.

Analyzing the impact of performance

But you need to pay attention to the time unit, which is only microseconds, one thousandth of a millisecond and one millionth of a second.

Assuming that the single CPU throughput of a certain microservice is 1000QPS, and 10% of them are illegal requests, then the performance loss of abnormal interruption is only one ten thousandth, and the impact on service time is only 0.001 Just milliseconds.

In the performance test, the business time is only to obtain the system time, which takes about 25ns. Because of this, the performance loss caused by abnormal interruptions reaches terrifying "hundreds of times". But what if the business time changes from 25ns to 25us or 25ms?

Let’s talk about performance bottlenecks

When we analyze system performance, we must understand its order of magnitude and performance bottlenecks, and remember to fall into the dilemma of performance optimization.

To give a rough example, in regular services, DB operations using indexes take between 1 and 10 milliseconds, accessing distributed Cache takes between 3 and 30 milliseconds, and the network performance loss of microservice RPC is between Between 3 and 10 milliseconds, the network between the client and the server takes between 5 and 300 milliseconds, and so on. In this case, optimizing for a performance risk of 0.001 milliseconds is like picking up sesame seeds and losing watermelon.

I once wrote about an underlying network protocol similar to TCP. In that high-frequency scenario, algorithm optimization brings 0.1 microsecond performance optimization, which means that the throughput per second is improved by several times or even several times. , but in the low-frequency scenario of distributed calls, this performance benefit is of no use.

Another example, when my colleagues and I were discussing the design of DB data tables a few years ago, we had a red-faced argument over what length of int should be used for the order status. Now think about it, 1 optimized for the order status Bytes, it only saves less than 1MB of disk space over the years. What's the use?

Abnormal interrupts in RPC

For remote calling frameworks such as Dubbo and HSF, when using abnormal interrupts to transfer error information, one thing to note is that the exception type needs to be designed to be universal. , that is, the basic type referenced by each microservice.

It is mentioned in the technical specifications of a certain factory:

1) Using the exception return method, if the caller does not catch it, a runtime error will occur.

2) If you do not add stack information, just new custom exceptions, and add your own understanding of the error message, it will not be of much help to the calling end in solving the problem. If stack information is added, the performance loss of data serialization and transmission is also a problem in the case of frequent call errors.

I am quite dissatisfied with this technical specification.

First of all, business exceptions need to be transparently transmitted to the outermost layer by the caller. Exceptions such as data non-existence, login failure, and user lockout are often of no use if caught by the caller in the middle.

The second thing is the ridiculous performance loss. What kind of performance loss will this low-frequency data serialization and intranet transmission have? Transmitting stack information to the caller is also helpful for troubleshooting. I once received TC's exception stack information. According to the package in the stack, I directly bypassed the third and fourth layers to find the bottom-level error, which can be said to have saved a lot of time. time.

Conclusion

In distributed microservices, using exception interrupts can greatly simplify business code and have minimal impact on performance.

Assisted by @NotNull, @Nullable and other annotations, distributed development can be as fast and convenient as the wind. In complex service networks, business exceptions can also facilitate developers to accurately locate errors and avoid the embarrassing situation of tracing fault points layer by layer along the call chain.

The above is the detailed content of Introduction to Exception and Result (code example). For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:segmentfault.com
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template