webflux가 netty 기반이라서, 아직 (Spring5) 기반에서는 request-timeout 설정이 없음.

각 request에서 mono.timout 이 설정이 가능하고,

아니면, 아래와 같이 WebFilter 설정을 통해서 전체 request-timeout 설정이 가능함.

 

그런데, 만약 비동기식으로 처리되고, 서버에서 on-line 배치 방식으로 처리된다면,

아래 WebFilter는 의미가 없을 테고, 해당 Mono에서 timeout 설정을 해야함.

@Slf4j
@Component
public class RequestTimeoutWebFilter implements WebFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
    	// Request Timeout 설정. WebFlux는 아직 application.yml에서 request-timeout 설정이 없음.
    	// 최근 Tracking 이력 max값 199초. 최대 300초 기준으로 5분으로 산정함.
    	log.info("#################### WebFilter - start");
    	Mono<Void> mono = chain
    			.filter(exchange)
//    			.timeout(Duration.ofMinutes(5)); // 5 분
   		        .timeout(Duration.ofSeconds(10)); // test 
    	log.info("#################### WebFilter - end");
    	return mono;
    }
}

 

end.

 

728x90

서비스가 stop할 경우, 다시 start 하기

 

batch에서 service_name 로 변수 정의된 서비스가 stop되면, restart 하게 하는 batch 입니다.

window 용이라, encoding을 euc-kr로 했습니다.

* 주의사항
1. batch를 관리자 권한으로 실행하세요.
 - 바로가기를 만드 후, [속성] > [고급] 에서 "관리자 권한으로 실행" 체크 후, 바로가기를 실행하면 편합니다.

 

@echo off
rem service 가 stop 되면 restart 위한 batch 입니다. encoding: euc-kr
setlocal

:init
rem cmd 실행경로설정. for logfile
cd C:\testcmd
set service_name=AMD External Events Utility
set logfile=monitor_service.log
@echo [%date% %time%] Start monitoring the "%service_name%" service.>> %logfile%
@echo [%date% %time%] Start monitoring the "%service_name%" service.
@echo [%date% %time%] "%service_name%"를 stop하려면, batch를 먼저 중지하세요.
@echo [%date% %time%] "%service_name%"를 start 후, 다시 batch를 관리자 권한으로 시작하세요.

rem monitoring loop
:loop
rem sleep 시간 설정(초). 시간이 너무 짧으면 service 가 stop 할 때 실패하션 종료되지 않을 수 있음.
timeout 10 > NUL
rem service가 STOPPED 인지 조사함. STOPPED이면 1 아니면 0
for /f "tokens=*" %%a in ('sc query "%service_name%" ^| find /c "STOPPED"') do set result=%%a
if %result% == 1 goto restart
goto loop

rem restart
:restart
@echo [%date% %time%] %service_name% restart >> %logfile%
sc start "%service_name%" >> %logfile%
goto loop

 

ps1. 스크립트를 짜기는 했는데, windows service에서 exit 코드로 restart 옵션이 있네요.

결국 스크립트를 사용하지 않고, 프로그램에서 exit : 2 이면 restart 하도록 windows service 를 설정하였습니다. ㅠㅠ;;;;

nssm set 서비스명 AppExit 2 Restart

 

ps2. java 또는 다른 프로그램을 windows service로 사용하려면 아래 프로그램 참조하세요.

https://nssm.cc/

 

NSSM - the Non-Sucking Service Manager

NSSM - the Non-Sucking Service Manager nssm is a service helper which doesn't suck. srvany and other service helper programs suck because they don't handle failure of the application running as a service. If you use such a program you may see a service lis

nssm.cc

ps3. 추가로 windows service가  2대의 서버에서 active - standby 로 동작도 가능하네요. 궁금하신 분은 아래글 참조하세요.

https://m.blog.naver.com/sik7854/221840130271

 

Windows 이중화 - Teaming

이중화란?? 시스템의 신뢰성을 올리기 위해서 같은 기능을 가진 시스템을 두 개 준비하여 활용하는 것을 말...

blog.naver.com

end.

 

728x90

1. Mono -> Flux 전환 / flatMapMany 예

@Test
public void flatMapMany_test001() {
// List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
  List<Integer> list = new ArrayList<>();
  for(int i = 0; i < 100000; i++)
    list.add(i);

  // Publisher가 Mono 라서 List가 길어도, 1번으로 emit 되서, subscribe하면 mono -> Flux 전환된 데이터 모두 출력된다.
  Mono.just(list)
      .flatMapMany(n -> Flux.fromIterable(n))
      .map(n -> n * 10)
      .subscribe(n -> log.info(n + ""));
}

@Test
public void flatMapMany_test002() {
  List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

  // Publisher가 Mono 라서 List가 길어도 1번으로 emit 되서, subscribe하면 mono -> Flux 전환된 데이터 모두 출력된다.
  Mono.just(list)
      .flatMapMany(n -> Flux.fromIterable(n).map(x -> x * 10))
//   .map(n -> n * 10)
      .subscribe(n -> log.info(n + ""));
}

 

2. Mono -> Flux 전환 / flatMapMany 예

@Test
	public void test_sql03() {
		String COUNT_KEY = "COUNT_KEY";
		Map<String, Integer> cntMap = new HashMap<>();
		cntMap.put(COUNT_KEY, 0);
		
		String sql3 = "SELECT ORDER_ID, CUSTOMER_ID, STATUS, NVL(SALESMAN_ID, -1) AS SALESMAN_ID, ORDER_DATE FROM ORDERS ORDER BY ORDER_ID";
		
		AtomicReference<Long> startTime = new AtomicReference<>();
		
		Mono.from(
					ConnectionFactories.get(
						ConnectionFactoryOptions.builder()
							.option(ConnectionFactoryOptions.DRIVER, "oracle")
//							.option(OracleR2dbcOptions.DESCRIPTOR, "(DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.20)(PORT = 1521)) (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.29)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = XEPDB1) ) )")
							.option(ConnectionFactoryOptions.HOST, "127.0.0.1")
							.option(ConnectionFactoryOptions.PORT, 1521)
							.option(ConnectionFactoryOptions.DATABASE, "XEPDB1")
							.option(ConnectionFactoryOptions.USER, "ot")
							.option(ConnectionFactoryOptions.PASSWORD, "1111")
							.build()
						).create()
				)
				.doOnNext(connection -> log.debug(connection.toString()))
				.flatMapMany(connection -> 
					Mono.from(connection.createStatement(sql3).execute())
						.flatMapMany(result -> 
								result.map(row -> {
									Orders orders = new Orders(row.get("ORDER_ID", Integer.class), row.get("CUSTOMER_ID", Integer.class), row.get("STATUS", String.class), row.get("SALESMAN_ID", Integer.class), row.get("ORDER_DATE", LocalDateTime.class));
									return orders;
								}
							)
						)
					    .doFinally((st) -> {
					    	connection.close();
					    	log.debug("### connection.close()");
					    })
				).doOnSubscribe(x -> startTime.set(System.nanoTime()))
				.doOnNext(n -> {
					cntMap.put(COUNT_KEY, cntMap.get(COUNT_KEY) + 1 ); // count row
				})
				.doAfterTerminate(() -> {
					log.info("Time taken : " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime.get()) + " milliseconds.");
					log.debug("## count = " + cntMap.get(COUNT_KEY));					
				})
				.subscribe(n -> log.debug(n.toString()));
		
		try {
			Thread.sleep(5000L);
		} catch (InterruptedException e) {
		}
	}

 

	@Test
	public void test_sql05() {
		log.info("### test_sql04() - start");
		
		String COUNT_KEY = "COUNT_KEY";
		Map<String, Integer> cntMap = new HashMap<>();
		cntMap.put(COUNT_KEY, 0);
		
		String sql3 = "SELECT ORDER_ID, CUSTOMER_ID, STATUS, NVL(SALESMAN_ID, -1) AS SALESMAN_ID, ORDER_DATE FROM ORDERS ORDER BY ORDER_ID";
		
		AtomicReference<Long> startTime = new AtomicReference<>();
		
		ConnectionFactory confactory = connectionFactory();
		
			
		Flux<Orders> xx = Mono.from(confactory.create())
		    .flatMapMany(connection ->     // return Flux. Flux로 받으려면 Mono의 flatMapMany로 Mono로 받으면 Mono의 FlatMap 으로.
				Mono.from(connection.createStatement(sql3).execute())		
				    .flatMapMany(result -> // return Flux
							result.map(row -> {
								Orders orders = new Orders(row.get("ORDER_ID", Integer.class), row.get("CUSTOMER_ID", Integer.class), row.get("STATUS", String.class), row.get("SALESMAN_ID", Integer.class), row.get("ORDER_DATE", LocalDateTime.class));
								return orders;				    	
							})
				    )
				    .doFinally((st) -> {
				    	connection.close();
				    	log.debug("### connection.close()");
				    })
		    );
				
		xx.subscribe(n -> log.debug(n.toString()));
				
		// 한건 테스트. block 으로 뽑음.
//		Orders yyy = xx.blockFirst();
//		log.debug("### yyy = " + yyy.toString());

		try {
			Thread.sleep(15000L);
		} catch (InterruptedException e) {
		}
	}
    
      public ConnectionFactory connectionFactory() {
	    ConnectionFactory connectionFactory = ConnectionFactories.get(ConnectionFactoryOptions.builder()
	        .option(ConnectionFactoryOptions.DRIVER, "pool")
	        .option(ConnectionFactoryOptions.PROTOCOL, "oracle")
			.option(OracleR2dbcOptions.DESCRIPTOR, "(DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.19)(PORT = 1521)) (ADDRESS = (PROTOCOL = TCP)(HOST = 127.0.0.1)(PORT = 1521)) ) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = XEPDB1) ) )")
			.option(ConnectionFactoryOptions.USER, "ot")
			.option(ConnectionFactoryOptions.PASSWORD, "1111")
	        .build());
	    
//	    Mono<Connection> con1 = (Mono<Connection>) connectionFactory.create();
//	    Flux<Connection> con2 = (Flux<Connection>) connectionFactory.create();
	    
	    int initialSize = 1;
	    int maxSize = 5;
	    ConnectionPoolConfiguration configuration = ConnectionPoolConfiguration.builder(connectionFactory)
	        .maxIdleTime(Duration.ofMinutes(30))
	        .initialSize(initialSize)
	        .maxSize(maxSize)
	        .maxCreateConnectionTime(Duration.ofSeconds(1))
//	        .maxAcquireTime(Duration.ofSeconds(1))
//	        .validationQuery("SELECT 1 FROM DUAL")
//	        .maxValidationTime(Duration.ofSeconds(2))
	        .build();
	    		    
	    return new ConnectionPool(configuration);
	  }
728x90

'Programming Language > Reactive Programing' 카테고리의 다른 글

Reactor Hooks 관련 글 모음  (0) 2023.10.06

 

https://www.oracle.com/kr/database/technologies/appdev/xe.html

 

Oracle Database Express Edition | Oracle 대한민국

모두를 위한 무료 Oracle Database 개발자, DBA, 데이터 과학자, 교육자 등 데이터베이스를 탐구하는 모두에게 Oracle Database Express Edition(XE)은 데이터베이스 입문에 매우 이상적인 방법입니다. Oracle Datab

www.oracle.com

end.

 

728x90

'Database > Oracle' 카테고리의 다른 글

참조 index 소개  (1) 2023.10.12
Oracle 중간값 계산하기  (0) 2023.04.25
오라클 테이블 명세생성 조회 쿼리  (0) 2022.08.17
오라클 버전 확인 쿼리  (0) 2022.08.17
subtotal,cube,grouping_id  (0) 2021.04.01

+ Recent posts