Heim  >  Artikel  >  Java  >  So implementiert SpringBoot die Modulprotokollspeicherung

So implementiert SpringBoot die Modulprotokollspeicherung

WBOY
WBOYnach vorne
2023-05-11 09:37:05968Durchsuche

1. Kurzbeschreibung

Es gibt ungefähr drei Möglichkeiten, Modulprotokolle zu implementieren:

  • AOP + benutzerdefinierte Anmerkungsimplementierung#🎜🎜 Nr Rufen Sie dazu die Methode zum Protokollieren auf.

  • Hier diskutieren wir hauptsächlich die dritte Implementierungsmethode.

  • Angenommen, wir müssen einen Vorgang implementieren, um das Anmeldeprotokoll aufzuzeichnen, nachdem sich der Benutzer angemeldet hat.
  • Die Aufrufbeziehung ist wie folgt:

Der Kerncode hier wird in der Methode LoginService.login() unter festgelegt das Ende der Transaktion Nach der Ausführung:

// 指定事务提交后执行
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
    // 不需要事务提交前的操作,可以不用重写这个方法
    @Override
    public void beforeCommit(boolean readOnly) {
        System.out.println("事务提交前执行");
    }
    @Override
    public void afterCommit() {
        System.out.println("事务提交后执行");
    }
});

Hier kapseln wir diesen Code in eine Toolklasse, Referenz: 4.TransactionUtils.

Wenn eine Transaktion in der Methode LoginService.login() aktiviert ist und nach dem Absenden der Transaktion nicht angegeben wird, kommt es zu Problemen mit der asynchronen Protokollverarbeitungsmethode und neuen Transaktionen:

# 🎜🎜 #So implementiert SpringBoot die Modulprotokollspeicherung

Asynchrone Ausführung: Da die Haupttransaktion möglicherweise nicht abgeschlossen wird, werden die neu hinzugefügten oder geänderten Dateninformationen in der Haupttransaktion möglicherweise nicht gelesen ## 🎜🎜#Neue Dinge tun: Sie können neue Transaktionen über das Transaktionsweitergabeverhalten Propagation.REQUIRES_NEW erstellen. Das Ausführen von Protokollierungsvorgängen in der neuen Transaktion kann die folgenden Probleme verursachen:

#🎜 🎜#

Da die Standardtransaktionsisolationsstufe der Datenbank wiederholbares Lesen ist, bedeutet dies, dass nicht festgeschriebene Inhalte nicht zwischen Dingen gelesen werden können, sodass die neuen oder geänderten Dateninformationen in der Haupttransaktion nicht gelesen werden können. 🎜🎜#

Wenn die neu geöffnete Transaktion dieselbe Tabelle wie die vorherige Transaktion bedient, führt dies dazu, dass die Tabelle gesperrt wird.
  • Nichts tun, direkt synchron anrufen: das problematischste Problem, das zu folgenden Problemen führen kann:
  • #🎜 🎜 #

  • Fängt keine Ausnahmen ab, was direkt zum Rollback aller Vorgänge auf der Schnittstelle führt;
    • Ausnahmen werden abgefangen, z Wie bei PostgreSQL schlagen in derselben Transaktion alle verbleibenden Datenbankvorgänge fehl und lösen Ausnahmen aus, solange ein Ausführungsfehler auftritt.
    • 2.LoginController
    @RestController
    public class LoginController {
        @Autowired
        private LoginService loginService;
        @RequestMapping("/login")
        public String login(String username, String pwd) {
            loginService.login(username, pwd);
            return "succeed";
        }
    }
  • 3.Action

    /**
     * <p> @Title Action
     * <p> @Description 自定义动作函数式接口
     *
     * @author ACGkaka
     * @date 2023/4/26 13:55
     */
    public interface Action {
            /**
            * 执行动作
            */
            void doSomething();
    }

    4.TransactionUtils#🎜. 🎜 #
    import org.springframework.transaction.support.TransactionSynchronization;
    import org.springframework.transaction.support.TransactionSynchronizationManager;
    /**
     * <p> @Title TransactionUtils
     * <p> @Description 事务同步工具类
     *
     * @author ACGkaka
     * @date 2023/4/26 13:45
     */
    public class TransactionUtils {
        /**
         * 提交事务前执行
         */
        public static void beforeTransactionCommit(Action action) {
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void beforeCommit(boolean readOnly) {
                    // 异步执行
                    action.doSomething();
                }
            });
        }
        /**
         * 提交事务后异步执行
         */
        public static void afterTransactionCommit(Action action) {
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void afterCommit() {
                    // 异步执行
                    action.doSomething();
                }
            });
        }
    }
  • 5.LoginService
      @Service
      public class LoginService {
          @Autowired
          private LoginLogService loginLogService;
          /** 登录 */
          @Transactional(rollbackFor = Exception.class)
          public void login(String username, String pwd) {
              // 用户登录
              // TODO: 实现登录逻辑..
              // 事务提交后执行
              TransactionUtil.afterTransactionCommit(() -> {
                  // 异步执行
                  taskExecutor.execute(() -> {
                      // 记录日志
                      loginLogService.recordLog(username);
                  });
              });
          }
      }
    • 6.LoginLogService

      6.1 @Async implementiert asynchron
    • @Service
      public class LoginLogService {
          /** 记录日志 */
          @Async
          @Transactional(rollbackFor = Exception.class)
          public void recordLog(String username) {
              // TODO: 实现记录日志逻辑...
          }
      }
    • Hinweis: @Async muss kooperieren @EnableAsync wird verwendet und @EnableAsync kann zur Startklasse, Konfigurationsklasse oder benutzerdefinierten Thread-Pool-Klasse hinzugefügt werden.

      Ergänzung: Da die @Async-Annotation dynamisch eine geerbte Klasse erstellt, um die Implementierung der Methode zu erweitern, kann dies dazu führen, dass die aktuelle Klasse nicht in den Bean-Container eingefügt wird. BeanCurrentlyInCreationException Sie können die verwenden Folgende Methode: Benutzerdefinierter Thread-Pool + @Autowired
    • 6.2 Benutzerdefinierter Thread-Pool zur Implementierung von asynchronem

      1) Benutzerdefinierter Thread-Pool
    AsyncTaskExecutorConfig.java#🎜 🎜#
    import com.demo.async.ContextCopyingDecorator;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.task.TaskExecutor;
    import org.springframework.scheduling.annotation.EnableAsync;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
    import java.util.concurrent.ThreadPoolExecutor;
    /**
     * <p> @Title AsyncTaskExecutorConfig
     * <p> @Description 异步线程池配置
     *
     * @author ACGkaka
     * @date 2023/4/24 19:48
     */
    @EnableAsync
    @Configuration
    public class AsyncTaskExecutorConfig {
        /**
         * 核心线程数(线程池维护线程的最小数量)
         */
        private int corePoolSize = 10;
        /**
         * 最大线程数(线程池维护线程的最大数量)
         */
        private int maxPoolSize = 200;
        /**
         * 队列最大长度
         */
        private int queueCapacity = 10;
        @Bean
        public TaskExecutor taskExecutor() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setCorePoolSize(corePoolSize);
            executor.setMaxPoolSize(maxPoolSize);
            executor.setQueueCapacity(queueCapacity);
            executor.setThreadNamePrefix("MyExecutor-");
            // for passing in request scope context 转换请求范围的上下文
            executor.setTaskDecorator(new ContextCopyingDecorator());
            // rejection-policy:当pool已经达到max size的时候,如何处理新任务
            // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
            executor.setWaitForTasksToCompleteOnShutdown(true);
            executor.initialize();
            return executor;
        }
    }
2 )Kontextanforderung kopieren

ContextCopyingDecorator.java

import org.slf4j.MDC;
import org.springframework.core.task.TaskDecorator;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import java.util.Map;
/**
 * <p> @Title ContextCopyingDecorator
 * <p> @Description 上下文拷贝装饰者模式
 *
 * @author ACGkaka
 * @date 2023/4/24 20:20
 */
public class ContextCopyingDecorator implements TaskDecorator {
    @Override
    public Runnable decorate(Runnable runnable) {
        try {
            // 从父线程中获取上下文,然后应用到子线程中
            RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
            Map<String, String> previous = MDC.getCopyOfContextMap();
            SecurityContext securityContext = SecurityContextHolder.getContext();
            return () -> {
                try {
                    if (previous == null) {
                        MDC.clear();
                    } else {
                        MDC.setContextMap(previous);
                    }
                    RequestContextHolder.setRequestAttributes(requestAttributes);
                    SecurityContextHolder.setContext(securityContext);
                    runnable.run();
                } finally {
                    // 清除请求数据
                    MDC.clear();
                    RequestContextHolder.resetRequestAttributes();
                    SecurityContextHolder.clearContext();
                }
            };
        } catch (IllegalStateException e) {
            return runnable;
        }
    }
}

3) Benutzerdefinierter Thread-Pool zur Implementierung des asynchronen LoginService

import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
@Service
public class LoginService {
    @Autowired
    private LoginLogService loginLogService;
    @Qualifier("taskExecutor")
    @Autowired
    private TaskExecutor taskExecutor;
    /** 登录 */
    @Transactional(rollbackFor = Exception.class)
    public void login(String username, String pwd) {
        // 用户登录
        // TODO: 实现登录逻辑..
        // 事务提交后执行
        TransactionUtil.afterTransactionCommit(() -> {
            // 异步执行
            taskExecutor.execute(() -> {
                // 记录日志
                loginLogService.recordLog(username);
            });
        });
    }
}

7. Andere Lösungen#🎜🎜 #

7.1 Verwenden Sie programmgesteuerte Transaktionen, um @Transactional zu ersetzen

Wir können auch TransactionTemplate verwenden, um @Transactional zu ersetzen.

# 🎜🎜#Nachdem diese Implementierung eine Ausnahme auslöst, kann die Transaktion auch normal zurückgesetzt werden

Nach der normalen Ausführung kann der Inhalt nach der Transaktionsausführung auch gelesen werden, was machbar ist.

Unabhängig davon, wie einfach es ist, Protokollaufzeichnungen zu implementieren, gibt es wirklich viele Fallstricke. Hier werden nur die bisher aufgetretenen Probleme aufgezeichnet.

Das obige ist der detaillierte Inhalt vonSo implementiert SpringBoot die Modulprotokollspeicherung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

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