728x90
반응형

나는 개인 싱글서버로 웹서버를 개발중이다.

그런데 어제 스케줄 실행에서 로그를 기록하는 것을 서버에 적용한 후 스케줄이 중복 실행되는 걸 발견했다.

큰 이상을 유발할 수 있는 상황은 아니었으나 그래도 깔끔하지 못하니 수정하고 싶었다.

 

검색을 해보니 Tomcat, spring 설정의 문제를 확인해 보라는 내용이 있었지만, 내 경우는 적용할 수 가 없었다.

(이건 내 지식이 얕기 때문에 이해를 제대로 하지 못해 적용하지 못했을 것이다.)

 

추가로 검색을 해보니 멀티 서버의 경우에서 스케줄 중복을 방지하는 방법을 발견했다.

넓게 본다면 비슷한 의도로 중복실행 방지를 가능하게 처리하는 방법이 될 것으로 생각하고 적용 했으며, 잘 반영 되었다.

 

* 참고한 좋은글

 

ShedLock 사용하기

www.baeldung.com/shedlock-spring 같은 잡을 수행하는 각각 다른 서버에서의 인스턴스 A,B가 있을 때, A,B 둘 중 하나가 수행하도록, 2개 이상의 서버에서 중복 수행을 방지하도록 Lock 을 걸게 하는 라이브

eunbc-2020.tistory.com

 

 

* 내 서버 환경 : spring boot 2.4.3, 스케줄 적용 중

 

자세한건 위 글을 참고하시고, 내 기준에서 추가로 보강한 내용만 적는다.

 

1. 테이블 생성 (mysql)

1
2
3
4
5
6
7
8
CREATE TABLE IF NOT EXISTS `bookkeeping`.`SHEDLOCK` (
  `NAME` VARCHAR(64NOT NULL COMMENT '스케줄잠금이름',
  `LOCK_UNTIL` TIMESTAMP(3NULL COMMENT '잠금기간',
  `LOCKED_AT` TIMESTAMP(3NULL COMMENT '잠금일시',
  `LOCKED_BY` VARCHAR(255NULL COMMENT '잠금신청자',
  PRIMARY KEY (`NAME`))
ENGINE = InnoDB
COMMENT = '스케줄 잠금'
cs

 

2. Bean 주입

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
 
import net.javacrumbs.shedlock.core.LockProvider;
import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider;
 
@Configuration
public class SchedulerConfig {
 
    @Bean
    public LockProvider lockProvider(JdbcTemplate dataSource) {
        return new JdbcTemplateLockProvider(dataSource);
    }
}
cs

 

이상.

오늘도 잘 해결해서 보람차다. ㅠㅠ 

 

 

3. 21.11.24 추가 이슈.

스케줄 실행시 언제나 에러가 난다는 것이다.

스케줄의 name 값으로 select를 먼저하고, 확인해서 insert, update로 분기해서 진행해야 하는데 무조건 insert 후 에러가 나면 update를 하는거다.(어쨌든 에러가 나도 실행은 잘 되긴 한다.)

하지만 이 excption이 눈에 거슬려서 고쳐보려 했지만 해결 방법을 아직 찾지는 못했다.

* 에러 전문

1. INSERT INTO shedlock(name, lock_until, locked_at, locked_by) VALUES('job_10min', '11/22/2021 12:28:00.003', '11/22/2021 12:20:00.050', 'DESKTOP-VMFC4B1')

{FAILED after 16 msec}

java.sql.SQLIntegrityConstraintViolationException: Duplicate entry 'job_10min' for key 'PRIMARY'

at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:117)

at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)

at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:953)

at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1092)

at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1040)

at com.mysql.cj.jdbc.ClientPreparedStatement.executeLargeUpdate(ClientPreparedStatement.java:1347)

at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdate(ClientPreparedStatement.java:1025)

at net.sf.log4jdbc.sql.jdbcapi.PreparedStatementSpy.executeUpdate(PreparedStatementSpy.java:1080)

at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)

at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeUpdate(HikariProxyPreparedStatement.java)

at org.springframework.jdbc.core.JdbcTemplate.lambda$update$2(JdbcTemplate.java:965)

at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:651)

at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:960)

at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:981)

at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.update(NamedParameterJdbcTemplate.java:328)

at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.update(NamedParameterJdbcTemplate.java:333)

at net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateStorageAccessor.lambda$insertRecord$0(JdbcTemplateStorageAccessor.java:65)

at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140)

at net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateStorageAccessor.insertRecord(JdbcTemplateStorageAccessor.java:63)

at net.javacrumbs.shedlock.support.StorageBasedLockProvider.doLock(StorageBasedLockProvider.java:81)

at net.javacrumbs.shedlock.support.StorageBasedLockProvider.lock(StorageBasedLockProvider.java:65)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

at java.lang.reflect.Method.invoke(Unknown Source)

at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)

at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208)

at com.sun.proxy.$Proxy61.lock(Unknown Source)

at net.javacrumbs.shedlock.core.DefaultLockingTaskExecutor.executeWithLock(DefaultLockingTaskExecutor.java:63)

at net.javacrumbs.shedlock.spring.aop.MethodProxyScheduledLockAdvisor$LockingInterceptor.invoke(MethodProxyScheduledLockAdvisor.java:85)

at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)

at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)

at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692)

at packageName.core.schedule.cron.ScheduleCron$$EnhancerBySpringCGLIB$$5253d941.job_10min()

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

at java.lang.reflect.Method.invoke(Unknown Source)

at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84)

at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)

at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:95)

at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)

at java.util.concurrent.FutureTask.run(Unknown Source)

at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(Unknown Source)

at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source)

at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)

at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)

at java.lang.Thread.run(Unknown Source)

 

 

 

728x90
반응형

+ Recent posts