The Java Sharing Stack takes four code optimization records from the online environment and shares them with you

Python WeChat Subscription Applet Course Video

https://blog.csdn.net/m0_56069948/article/details/122285951

Python Actual Quantitative Transaction Finance System

https://blog.csdn.net/m0_56069948/article/details/122285941

Preface

Because new projects have been completed over a period of time and are now stable, I have recently been assigned to the company's Operations and Maintenance Group to maintain another project that includes day-to-day issues for handling customer feedback and optimizing system defects.
After nearly two weeks of maintenance, I have dealt with a BUG at the code level and optimized three issues in addition to the daily problems. I have summarized these four issues into four coding tips to share with you, I hope it will help you. If you encounter similar problems in the future, you can look over them here and see, it will certainly save a lot of time.

Skill

1. stream Grouping

Many people know that streams of java8 are very useful, but many people don't really use them, or search a lot of data or use them badly. In the final analysis, many Baidu data have no suitable cases, which makes people seem to understand or not understand. I've extracted a code snippet for streaming grouping from an online project to help you understand it at a glance.

First, I'll show you the table structure, which is, of course, simplified and easy to understand for the sake of case study.

  • Doctor Information Sheet
iddoctor_namephonephoto_urlarea_code
1Zhang San13612345678https://head.img.com/abc.pngEAST
2Li Si15845678901https://head.img.com/xyz.pngWEST
  • Courtyard table
idarea_codearea_name
1EASTEast Court District
2SOUTHNanyuan District
3WESTWest Court District
4NORTHNorth Courtyard

Requirements: Query the doctor information list to show the hospital district name.

Before I optimized, my last colleague wrote this:

// Query list of doctors
List doctorVOList = doctorService.findDoctorList();

// Walk through the list of doctors and load the hospital district name.
doctorVOList.forEach((vo)->{
 // Yard code
 String areaCode = vo.getAreaCode(); 
 // Query hospital information based on yard code
 HospitalAreaDTO hospitalAreaDTO = areaService.findOneByAreaCode(areaCode);
 // Place in hospital district name
 vo.setAreaName(hospitalAreaDTO.getAreaName());
});

// Return
return doctorVOList;

As you can see, he traverses the list of doctors, then goes back to each doctor's hospital and asks for the name of each doctor's hospital. That means if there are 100 doctors, then he has to inquire about the hospital table 100 times, although MySQL8. After 0+, queries become more efficient. This kind of small table query has less impact, but as a mature online project, this kind of code is novice level, I dare to pack a ticket that many people have written.

After optimization:

// Query list of doctors
List doctorVOList = doctorService.findDoctorList();

// Grouping yard lists into memory using areaCode as key
Map> areaMap = areaService.findAll().stream()
 .collect(Collectors.groupingBy(e-> e.getAreaCode()));


// Walk through the list of doctors and load the hospital district name.
List doctorVOList = new ArrayList<>();
doctorVOList.forEach((vo)->{
 // Yard code
 String areaCode = vo.getAreaCode(); 
 // Get the name of the hospital from map according to the code of the hospital
 String areaName = areaMap.get(areaCode).get(0).getAreaName();
 // Place in hospital district name
 vo.setAreaName(areaName);
});

// Return
return doctorVOList;

As you can see, stream grouping is used to encode the hospital area information as key, and the hospital area information as value is put into memory. When traversing the list of doctors, you can get the corresponding hospital area name directly from the memory according to the hospital area code. It only queries once before and after, which greatly improves efficiency and saves database resources.
This can be used in scenarios where traversal queries like this need to find the value of an attribute from other small tables.

2. stream Sorting

The sorting is actually simple, which is to sort the list of doctors according to a number of rules required by the client, where the rules are as follows: descending order according to whether online or not, ascending order according to the title of the doctor, and ascending order according to the number of the doctor.
mybatis is used in the project, so the previous writing was to write sql statements directly, but if the sql statements are more complex, it is not maintainable to hand them over to other colleagues later.
In fact, it's comfortable to sort directly in memory by stream ing once the list is found, so I optimized the sql statement writing for this part of my project to query and sort directly in code.
stream multi-attribute different rule ordering:

// Query List
List respDTOList = findHomePageDoctorList();

// sort
List sortList = respDTOList.stream()
 .sorted(
 Comparator.comparing(HomePageDoctorsDTO::getOnlineFlag, Comparator.reverseOrder())
 .thenComparing(HomePageDoctorsDTO::getScheduleStatus, Comparator.reverseOrder())
 .thenComparing(HomePageDoctorsDTO::getDoctorTitleSort)
 .thenComparing(HomePageDoctorsDTO::getDoctorNo)
 )
 .collect(Collectors.toList());

// Return
return sortList;

The above section of code is OK, which is very simple. ReverseeOrder() denotes descending order and default ascending order if not written.
One thing to note here is that a lot of information on the Internet is useful:
Comparator.comparing(HomePageDoctorsRespDTO::getOnlineFlag).reverse()
This is a wrong way to descend. You can check or try the use of reverse(). It is just the reverse, not the descending order. It is like changing from left to right to right. The descending order must be written in the code above. This is a pit to be aware of.

3. Asynchronous Threads

Asynchronous threads Many people know that you can just use the @Async comment, but many people don't know the limitations of using this comment and tend to think you've used it, and you don't actually walk an asynchronous thread at all.

  1. The @Async annotation can only be labeled on void methods;
  2. The @Async annotation must be labeled with a public modifier;
  3. The @Async annotation method and caller are in the same class and will not work.

Neither of these conditions is necessary, even if the first two are met, or asynchronous threads are not allowed.
The project I maintained was to satisfy the first two, but it didn't really work. That means the idea of my colleague writing this code is good. I want to make the interface more efficient without occupying the main thread, but I don't really test it sufficiently myself to think it's valid. I'm sure a lot of people have done the same.
Here, I optimize the next, to give you the most scientific writing, to ensure the effectiveness, here I take text message notification as an example.

First, define a class that specifically writes asynchronous methods called AsyncService.

/**
 * Asynchronous method service, does not affect the main program.
 */
@Service
public class AsyncService {
    private final Logger log = LoggerFactory.getLogger(AsyncService.class);
    
    @Autowired
    private PhoneService phoneService;
    
    /**
     * Send text messages to inform patients of check-up time
     * @param dto Patient Information
     * @param consult Consulting Information
     */
    @Async
    public void sendMsgToPatient(PatientDTO patientDTO, ConsultDTO consultDTO) {
        // Message Content
        String phone = patientDTO.getTelphone();
        String msg = "Hello,"+ patientDTO.getName() +",Successfully booked for you"
        + consultDTO.getDeviceType() +"Check, time is"+ consultDTO.getCheckDate() 
        +",We hope you can make a good schedule for the inspection. Visit card number:"+ consultDTO.getPatientId() 
        +",Check items:" + consultDTO.getTermName();
        
        // Send message
        phoneService.sendPhoneMsg(phone, msg);
    }
}

Note here that using the public modifier, the void method, the preceding restrictions have been discussed.

Second, we declare in the configuration class that the @EnableAsync annotation opens an asynchronous thread.

@Configuration
@EnableAsync
public class AsyncConfiguration implements AsyncConfigurer {

    // Specific implementation
    // ....
    
}

Finally, we can call it in the business method.

public BusinessResult doBusiness(PatientDTO patientDTO, ConsultDTO consultDTO) { 
    // Processing business logic, omitted here...
    // ....
    
    //Asynchronous SMS notification patient check-up time
    asyncService.sendMsgToPatient(patientDTO, consultDTO);
}

In this way, the text messaging service will be asynchronous threads, and even if other similar services require asynchronous calls, they can also be placed in AsyncService for unified processing.

One more thing to note is that asynchronous threads in the above way actually go through the default thread pool, which is not recommended because there may be an insufficient number of threads blocking during heavy use, so we need to further optimize for a custom thread pool.
Here, we use the ThreadPoolTaskExecutor recommended in the Ali Development Manual.

@Configuration
@EnableAsync
public class AsyncConfiguration implements AsyncConfigurer {

    private final Logger log = LoggerFactory.getLogger(AsyncConfiguration.class);

    @Override
    @Bean(name = "taskExecutor")
    public Executor getAsyncExecutor() {
        log.debug("Creating Async Task Executor");
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(8);
        executor.setMaxPoolSize(50);
        executor.setQueueCapacity(1000);
        executor.setThreadNamePrefix("async-Executor-");
        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new SimpleAsyncUncaughtExceptionHandler();
    }
}

Here, we set the number of core threads 8, the maximum number of threads 50, and the task queue 1000, respectively, with the thread name starting with async-Executor-
These configurations can actually be extracted and placed in the yml file, depending on the size of the asynchronous threads used in the project and the level of the server itself. There are not too many asynchronous threads used in this project, mainly when sending and subscribing to SMS notifications, and the server itself is 8-core 16G, so this setting is relatively consistent.

4. Unified exception management

Unified exception management is my main point. This time I maintained a project in which I wrote simply unbearable, online troubleshooting problems a lot of important information can not be seen, the check code found that unified exception management is clearly used, but the writing is just layman's level, I have a stomach ache.

First, let me talk about specifications:

  1. After unified exception management, if it is not necessary, try...catch will never happen again. If you must try...catch, make sure log.error(e) records log print stack information and throw s an exception, otherwise nothing will be visible on the line where the code block is out of order;
  2. After unified exception management, do not use generic response objects such as ResultUtil directly to return interface-level validation errors. Error (500, "Query xx Failure"), which will result in the ineffectiveness of Unified Exception Management, because this is the normal return of an object, not an exception, so we should throw the new BusinessException (Query xx Failure) directly when checking for errors to actively throw an exception in order to be caught;
  3. After unified exception management, Spring's ResponseEntity package is best used in the global exception management class to ensure that the HTTP state is not 200 but the correct exception state when an exception occurs, so that the front-end engineer can judge the interface connectivity based on the HTTP state, and then the success of the interface data acquisition based on the business state.

Here, I'll share the optimized global exception handling code with you:

First, we customize three common exceptions.

Check for exceptions to parameters, inherit runtime exceptions.

/**
* Incorrect parameter exception
*/
public class BadArgumentException extends RuntimeException {
    public BadArgumentException(){
        super();
    }

    public BadArgumentException(String errMsg){
        super(errMsg);
    }
}

Check permission exceptions, inherit runtime exceptions.

/**
* No Access Exception
*/
public class NotAuthorityException extends RuntimeException {
    
    public NotAuthorityException(){
        super("No access rights.");
    }
 
    public NotAuthorityException(String errMsg){
        super(errMsg);
    }
}

Business logic exception, inherit runtime exception RuntimeException.

/**
* Business logic exception
*/
public class BusinessException extends RuntimeException {

    public BusinessException(){
        super();
    }

    public BusinessException(String errMsg){
        super(errMsg);
    }
    public BusinessException(String errMsg,Throwable throwable){
        super(errMsg,throwable);
    }

}

Second, we declare a global exception handling class.

/**
* Unified exception handling
*/
@RestControllerAdvice
@Slf4j
public class ExceptoinTranslator {

    /**
 * Permission Exception
 */
    @ExceptionHandler(value = {AccessDeniedException.class,NotAuthorityException.class})
    public ResponseEntity handleNoAuthorities(Exception ex){
        return ResponseEntity.status(HttpCodeEnum.FORBIDDEN.getCode()).body(
            ResultUtil.forbidden(ex.getMessage())
        );
    }
    
    /**
 * Parameter error exception
 */
    @ExceptionHandler(value = BadArgumentException.class)
    public ResponseEntity handleBadArgument(Exception ex){
        return ResponseEntity.status(HttpStatus.BAD_REQUEST.value()).body(
            ResultUtil.custom(HttpStatus.BAD_REQUEST.value(), ex.getMessage())
        );
    }
    
    /**
 * Interface parameter check exception
 */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public ResponseEntity handleArguNotValid(MethodArgumentNotValidException ex){
        FieldError fieldError=ex.getBindingResult().getFieldErrors().get(0);
        String msg = !StringUtils.isEmpty(fieldError.getDefaultMessage()) ? fieldError.getDefaultMessage():"Illegal parameters";
        return ResponseEntity.status(HttpStatus.BAD_REQUEST.value()).body(
            ResultUtil.custom(HttpStatus.BAD_REQUEST.value(), msg)
        );
    }
    
    /**
 * Illegal parameter exception
 */
    @ExceptionHandler(value = ConstraintViolationException.class)
    public ResponseEntity handleConstraintViolation(ConstraintViolationException ex){
        String err=ex.getMessage();
        Set> set=ex.getConstraintViolations();
 if(!set.isEmpty()){
 err= set.iterator().next().getMessage();
 }
 String msg = StringUtils.isEmpty(err)?"Illegal parameters":err;
 return ResponseEntity.status(HttpStatus.BAD\_REQUEST.value()).body(
 ResultUtil.custom(HttpStatus.BAD\_REQUEST.value(), msg)
 );
 }
 
 /**
 * Illegal parameter exception
 */
 @ExceptionHandler(value = {IllegalArgumentException.class})
 public ResponseEntity handleIllegalArgu(Exception ex){
 String err=ex.getMessage();
 String msg = StringUtils.isEmpty(err)?"Illegal parameters":err;
 return ResponseEntity.status(HttpStatus.BAD\_REQUEST.value()).body(
 ResultUtil.custom(HttpStatus.BAD\_REQUEST.value(), msg)
 );
 }

 /**
 * Business logic handles exceptions, which are also the most commonly used proactively thrown exceptions.
 */
 @ExceptionHandler(value = BusinessException.class)
 public ResponseEntity handleBadBusiness(Exception ex){
 return ResponseEntity.status(HttpStatus.INTERNAL\_SERVER\_ERROR.value()).body(
 ResultUtil.custom(HttpStatus.INTERNAL\_SERVER\_ERROR.value(), ex.getMessage())
 );
 }

 /**
 * HTTP Request method does not support exceptions
 */
 @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
 public ResponseEntity methodNotSupportException(Exception ex){
 return ResponseEntity.status(HttpStatus.METHOD\_NOT\_ALLOWED.value()).body(
 ResultUtil.custom(HttpStatus.METHOD\_NOT\_ALLOWED.value(), "Request method not supported!")
 );
 }

 /**
 * Handling of all but the above exceptions will enter here
 */
 @ExceptionHandler(value = Exception.class)
 public ResponseEntity handleException(Exception ex){
 log.error("[ExceptoinTranslator]>>>> Global exception: ", ex);
 return ResponseEntity.status(HttpStatus.INTERNAL\_SERVER\_ERROR.value()).body(
 ResultUtil.custom(HttpStatus.INTERNAL\_SERVER\_ERROR.value(), "An internal error has occurred!")
 );
 }
 
}

The above global exception handling includes the most likely occurrences of the project: several parameter exceptions, permission exceptions, HTTP methods that do not support exceptions, custom business exceptions, other exceptions, which are basically enough, and other exceptions can be customized if you want to be more detailed.

Two things to focus on here are:
1. We use Spring's ReponseEntity for outer wrapping instead of directly using ResultUtil as the custom response object. This ensures that the business state returned by our interface is consistent with the HTTP state of the interface itself. The front end can judge the connectivity of the interface. If you don't understand the difference, you can see the HTTP state in the upper right corner with a little Postman. When you return with a custom response object, it will always be 200;
2. Last of all other exceptions. For class capture, make sure to log.error(ex) logging so that you can see the specific stack information when you are sorting online.

summary

  1. stream grouping can be used to improve query efficiency.
  2. stream sorting avoids treading pits;
  3. Asynchronous threads best use;
  4. Unify best practices for exception management.

My original articles are pure-handed, mostly from work, I feel like a drop of help is just a click-and-click!

Just pay attention and stop getting lost!
Point collection, no more hesitation!
Tip a recommendation, dream come true!
A little compliment, every day!

  • About bloggers: Comments and private messages will be answered in the first place. perhaps Direct Private Trust I.
  • Copyright Statement: All articles in this blog, except special statements, use BY-NC-SA License agreement. Reprint please indicate the source!
  • Support blogger: If you feel the article is helpful to you, you can click on the bottom right corner of the article ** [Recommended] (javascript:void(0) 😉]** Once.

Tags: Python computer

Posted by ozzysworld on Sun, 17 Apr 2022 01:48:37 +0930