This is the second day of my participation in the August More text Challenge. For details, see: August More Text Challenge

demand

The merchant has multiple stores and needs to send fixed format SMS messages to the members of the stores at fixed time every day.

It’s not like you can’t use it

LV1 after seeing the demand: this is not easy, at most half a day, let’s draw a flowchart according to the demand first (inner OS: I am not very professional) :

Is the flow chart clear? Here, let’s get started with copy-and-paste art:

public void sendSmsMessage(a) {
    shopDao.findAll().forEach(shop -> {
      String telNums = memberDao
          .findByShopId(shop.getId())
          .stream()
          .map(Member::getTelNum)
          .collect(Collectors.joining(","));
      httpSendSmsMessage.batchSendMessage("Happy birthday to you!", telNums);
    });
}
Copy the code

After the implementation of LV1 was completed, I submitted PR and waited for the praise from the boss. Unexpectedly, after only a few minutes, the boss refused LV1’s PR and carefully attached the advantages and disadvantages of the scheme:

advantages

  1. Can be used
  2. Development cycle end

disadvantages

  1. If an error occurs in one store, it will affect text messaging in all stores.
  2. It can only run normally and correctly. Once an error occurs, it can only be manually intervened.

Do not disturb each other version

LV1 stared at the advantages and disadvantages sent by the big guy, carefully thought about it, and realized that his demand was too simple. After some searching, he found that the problem of mutual influence between stores could be solved through an asynchronous way. Now that the solution has been found, let’s just do it.

With flowcharts and knowledge of asynchrony, the code was quickly written:

public void eachOtherSendSmsMessage(a) {
    CountDownLatch latch = new CountDownLatch((int) shopDao.count());
    shopDao.findAll().forEach(shop -> {
      asyncBetterThinkService.sendSmsMessage(shop.getId(), latch);
    });
    try {
      latch.await();
    } catch (InterruptedException e) {
      log.error("Thread interrupted while waiting.", e); }}Copy the code

After completing the code, LV1 was upgraded to LV3, although the first defect was solved, LV3 was once again lost in thought about how to solve the second one.

Error retry version

Again after some time, search and discuss LV3 will be divided into two types of errors, one kind is a third-party messaging interface error, one kind is their internal program error, when the first kind of error occurs, the program should automatically retry and exceed set retries, judged to be errors, save the log, another kind of error occurs, does not automatically retry, save the log directly. With the solution in mind, let’s start with a flowchart:

The implementation code is as follows:

- the main methodpublic void errorSendSmsMessage(a) {
    CountDownLatch latch = new CountDownLatch((int) shopDao.count());
    shopDao.findAll().forEach(shop -> {
      try {
        asyncBetterThinkService.errorRetrySendSmsMessage(shop.getId(), latch);
      } catch (Exception e) {
        // If a non-third party error occurs, the error log is saved
        SendSmsMsgError error = new SendSmsMsgError();
        error.setReason("xxxx"); error.setShopId(shop.getId()); }});try {
      latch.await();
    } catch (InterruptedException e) {
      log.error("Thread interrupted while waiting.", e); }} -- asynchronous methods@Async
public void errorRetrySendSmsMessage(String shopId, CountDownLatch latch) {
    String telNums = memberDao
        .findByShopId(shopId)
        .stream()
        .map(Member::getTelNum)
        .collect(Collectors.joining(","));
    // The number of retries per thread is independent
    int selfRetry = retry;
    boolean successRetry = false;
    while (selfRetry > 0) {
      try {
        httpSendSmsMessage.batchSendMessage("Happy birthday to you!", telNums);
        // After the send is successful, the retry is terminated directly
        selfRetry = 0;
        successRetry = true;
      } catch (Exception e) {
        log.error("Sending SMS error", e);
      }
      if (selfRetry > 0) {
        try {
          // Wait for two seconds and try again
          Thread.sleep(TimeUnit.SECONDS.toMillis(2));
        } catch(InterruptedException e) { e.printStackTrace(); } selfRetry--; }}if(! successRetry) {// Save the error message
      SendSmsMsgError error = new SendSmsMsgError();
      error.setReason("xxxx"); error.setShopId(shopId); } latch.countDown(); } -- manual retry methodpublic void handleRetry(String msgErrorId) {
    sendSmsMsgErrorDao.findById(msgErrorId).ifPresent(sendSmsMsgError -> {
        String telNums = memberDao
          .findByShopId(sendSmsMsgError.getShopId())
          .stream()
          .map(Member::getTelNum)
          .collect(Collectors.joining(","));
        httpSendSmsMessage.batchSendMessage("Happy birthday to you!", telNums);
    });
}
Copy the code

After this, LV3 confidently submitted the PR, and the big guy quickly approved LV3’s request.

The end of the

In fact, even in the end, our scheme is not optimal. For example: Not consider too many stores or member of the query time and send the time-consuming, and so on, but the author outlets and member number when the actual situation of this project will not too much, so the scheme is combined with the actual needs and development cycle of the project, if the development cycle is very nervous, the author thinks that because of the query takes to data volume too much problem can be extended.

Finally, as an aside, don’t spend a lot of time comparing how good or bad a language is. In the end, a language is just a tool, and each tool has its own area of expertise. What really keeps you from growing is the way you think and how deep you solve problems.

Code address: gitee.com/lanrain/art…