Tmax 5 SP1
1. 추가 기능
Tmax 5 SP1에서 새롭게 추가된 주요 기능은 다음과 같다.
1.1. HMS
HMS(Hybrid Messaging System)는 Sun Microsystems에서 재정한 Java 기반의 메시징 시스템 표준이다. JMS(Java Messaging Service)의 개념과 동작방식을 반영한 시스템으로 송신자(Sender)와 수신자(Receiver) 사이의 느슨한 결합(loosely coupled)을 가능케 하는 통신 매개체이다.
HMS는 TP-Monitor인 Tmax 시스템에서 동작하므로 사용자는 TP-Monitor의 기능과 메시징 시스템의 기능을 유연하게 결합하여 사용할 수 있다.
1.3. WebTJCA
J2EE의 JCA(J2EE Connector Architecture) 스펙에서 리소스 어댑터 역할을 하는 라이브러리 패키지로 사용자는 이 라이브러리를 통해서 Tmax와 통신을 할 수 있다.
1.5. TDL 기능 확장
전반적인 TDL(Tmax Dynamic Library) 기능이 개선되었으며 특히 다음과 같은 TDL의 기능이 확장되었다.
-
동적 라이브러리 링킹(Dynamic Library Linking)
OS에서 제공하는 동적 라이브러리 링킹(Dynamic Library Linking) 기법을 이용하여 변경이 빈번한 업무 모듈의 중단 없이 업데이트 정보를 제공할 수 있다.
-
인덱싱 기법 도입
인덱싱 기법을 도입하여 모듈 로컬 캐쉬의 검색 성능을 개선하였고, 공유 메모리 해시 테이블(Hash Table) 검색을 최소화하였다.
1.6. 웹 서비스 게이트웨이의 Outbound 기능 제공
Outbound 기능은 기존에 Tmax 서비스를 특별한 변경 없이 웹 서비스로 사용하기 위한 게이트웨이용이었으나, Tmax 5 SP1부터는 Tmax에서 외부의 웹 서비스를 호출할 수 있는 Outbound 기능도 제공한다.
1.8. ENGINE
1.8.1. IRT 트랜잭션 지원 기능
4.0 SP1에서 IRT(Intelligent Routing) 기능이 추가되었으나 비트랜잭션 서버 그룹과 게이트웨이에 대해서만 동작이 가능했다. 5.0 SP1에서는 트랜잭션에 대해서도 IRT를 지원함으로써 보다 강력한 라우팅 기능을 제공한다.
-
멀티 노드에서의 IRT
멀티 노드 환경에서 부하 분산에 대한 이전 방식(4.0 SP1 이전)은 아래와 같다.
-
로컬 노드의 서버가 종료되었을 경우, CLH에서 이를 판단하고 리모트 노드의 서버로 재스케줄한다. 따라서 에러 없이 클라이언트의 요청을 모두 처리할 수 있다.
-
리모트 노드의 서버가 종료된 경우, 로컬 노드의 CLH는 리모트 노드에서의 서버의 상태를 알지 못하기 때문에 리모트 노드로 해당 리퀘스트를 보내게 되고 클라이언트는 TPENOREADY 에러를 전달 받게 된다.
이와 같은 문제를 해결하고자 정적 부하 분산에 대한 리모트 서버의 상태를 관리하여, 처리할 수 있는 서버로 재스케줄되는 기능이 바로 멀티 노드에서의 IRT 기능이다.
따라서 리모트 노드의 서버 프로세스가 비정상적으로 종료되어 서비스를 처리할 수 없게 되면, 처리할 수 있는 서버로 재스케줄 된다.
-
-
멀티 도메인 환경에서의 IRT
멀티 도메인 환경에서는 게이트웨이 서버 프로세스가 종료된 경우뿐 아니라 해당 게이트웨이의 연결 상태에 따라서도 TPENOREADY 에러가 발생할 수 있다.
멀티 도메인에서의 IRT는 각 도메인 게이트웨이의 연결 상태까지 체크하여 연결이 끊어져 있는 상태라면 처리할 수 있는 게이트웨이로 재스케줄된다.
-
IRT 트랜잭션
기존의 IRT는 비트랜잭션 서버 그룹과 게이트웨이에 대해서만 동작하였으나 5.0 SP1에서는 트랜잭션에 대해서도 가능하도록 하였다.
1.8.2. 운영 중 SERVER의 MAX 값 변경 기능 추가
CFL 당시의 SERVER 절의 MAX 값을 늘리는 것이 가능하도록 기능을 추가하였다. 이전 버전에서는 줄이는 것만 가능했다. 모든 서버가 가능한 것이 아니다. MAX 값의 변경을 위해서 새로운 서버 타입을 생성한다.
STD_DYN, UCS_DYN을 추가하였다. 이 SVRTYPE으로 설정한 svr만 MAX 값 변경이 가능하다. 나머지는 기존과 동일하게 동작한다. STD_DYN은 TCS이며 UCS_DYN은 UCS이다. 기존 사이트는 동일하게 동작하며 새로운 설정을 적용한 곳만 영향을 받도록 설정을 분리했다.
주의 사항
-
설정의 변화
-
서버절의 SVRTYPE 추가 - STD_DYN, UCS_DYN
-
-
spri의 변화
-
tmadmin에서 spri를 볼 때 MAX 값을 조정하면 spri가 연속하지 않는다.
-
다른 svr에서 이미 한번 사용한 spri의 MAX 값을 조정하면 다른 svr에서도 사용할 수 있다.
-
-
제한
-
16384 버전에서는 사용 불가능한 기능이다.
-
MAX 값을 감소 시킬 경우 MIN 값보다 작게 줄일 수는 없다.
-
MAX 값을 감소 시킬 경우 감소할 값에 해당하는 spr index 이후의 spr은 다운 상태이어야 한다.
예) 초기에 설정된 spri가 8193, 8194, 8195, 8192 순 일 때, MAX를 2로 줄이기 위해서 8195, 8192는 다운 상태이어야 한다.
-
1.8.3. Tibero 트랜잭션 지원
XA는 2PC(Two Phase Commit)를 통한 분산 트랜잭션 처리를 하기 위해 X/Open에서 명시한 표준이다. XA는 각 DBMS 벤더별로 제공하고 있으며 이 표준 규약을 통해서 이기종 간의 트랜잭션이 보장된다.
Tmax에서는 현재 Oracle, Informix, DB2, SYBASE와의 연동에 의한 분산 트랜잭션 기능을 제공하고 있으며 5.0 SP1에서는 Tibero와의 연동에 의한 분산 트랜잭션 기능 또한 제공한다.
환경설정
다음은 분산 트랜잭션을 위한 Tibero의 환경설정 방법과 환경설정의 예제이다.
환경설정 방법
*SVRGROUP SVG_TIBERO [DBNAME = TIBERO,] [OPENINFO = "TIBERO_OPEN_INFORMATION",] [CLOSEINFO = "TIBERO_CLOSE_INFORMATION",] [TMSNAME = "TIBEROTMS_PROCESS_NAME"]
환경설정 예제
*SVRGROUP SVG_TIBERO DBNAME = TIBERO, OPENINFO = "TIBERO_XA:user=tibero,pwd=tmax, sestm=60,db=tibero4,LogDir=/data1/tmax/log/xalog"
XA STUB 라이브러리의 추가
Tibero XA Switch에 대한 XA STUB 라이브러리가 추가되었다. 파일명은 libtbs이다. tms와 XA 서버를 작성할 때 반드시 해당 라이브러리를 링크해 주어야 한다.
다음은 TMS Makefile의 예이다.
# TMS Makefile for AIX 64bit TBLIBDIR = $(TB_HOME)/client/lib TBLIB =-ltbxa -ltbertl -ltbcli -lm -lpthread TARGET = tms_tbr APOBJ = dumy.o APPDIR = $(TMAXDIR)/appbin TMAXLIBD= $(TMAXDIR)/lib64 TMAXLIBS= -ltms -ltbs TMAXINC = -I$(TMAXDIR) CFLAGS = -q64 LDFLAGS = -brtl all : $(TARGET) $(TARGET): $(APOBJ) $(CC) $(CFLAGS) $(LDFLAGS) -o $(TARGET) $(TMAXINC) -L$(TMAXLIBD) $(TMAXLIBS) $(APOBJ) -L$(TBLIBDIR) $(TBLIB) $(SYSLIBS): mv $(TARGET) $(APPDIR)/. $(APOBJ): $(CC) $(CFLAGS) $(LDFLAGS) -c dumy.c clean: -rm -f *.o core $(APPDIR)/$(TARGET)
1.8.4. Rolling Down 기능
클라이언트의 요청을 처리하고 있던 Tmax 시스템이 다운될 경우, 기존 버전에서는 현재 처리 중인 요청에 대해서만 응답을 처리하여 전달한 후, 큐에 쌓여 있는 요청에 대하여 TPECLOSE 에러를 전달하였다.
하지만 Tmax 5 SP1에서는 Tmax 엔진을 다운시키기 이전에 요청된 모든 클라이언트에 대하여 정상적으로 응답을 주기 위한 기능이 추가되었다.
사용 방법
$ tmdown [-R][-n]
클라이언트 요청 메시지의 유실을 막기 위한 옵션이다. 해당 옵션으로 Tmax 시스템을 종료하는 경우, CLL 은 클라이언트로부터의 Listen Port를 차단하고 이미 처리되고 있는 요청에 대해서만 응답을 준 후 종료한다.
상세 설명
NODEA와 NODEB가 멀티 노드 (혹은 멀티 도메인)로 구성되어 있으며, 총 100개의 클라이언트가 NODEA에 접속되어 있다고 가정할 경우의 처리 과정은 다음과 같다.
-
NODEA의 Tmax 시스템을 종료시킬 경우
tmdown –R –n NODEA
-
NODEA의 CLL은 클라이언트로부터의 Listen Port를 차단한다.
-
NODEA에서 기존에 처리되고 있던 요청에 대해서는 처리를 완료한 후 클라이언트에게 처리 결과를 전달한다.
-
NODEA의 Tmax 시스템이 종료된다. NODEA에 접속되어 있는 Tmax 클라이언트들에게 모두 정상 응답을 준 후 종료된다.
-
큐에 쌓여 있는 요청에 대해서는 TMAX_BACKUP_ADDR로 설정된 NODEB에 요청을 보낸다.
-
NODEB에서는 NODEA로부터 받은 요청을 처리한 후, 처음 해당 리퀘스트를 요청한 클라이언트에게 처리 결과를 직접 전달한다.
-
NODEA에 접속되어 있는 모든 클라이언트는 정상 응답을 받는다.(100개의 클라이언트가 모두 정상 응답을 받아야 함)
-
-
NODEB의 Tmax 시스템을 종료시킬 경우
tmdown –R –n NODEB
-
100개의 클라이언트 요청을 NODEA의 CLH가 약 50:50 으로 NODEA와 NODEB에 분배한다.
-
tmdown -R –n NODEB 로 NODEB 의 Tmax 시스템을 종료시킨다.
-
NODEB의 CLL은 클라이언트로부터의 Listen Port를 차단한다.
-
NODEB에서 기존에 처리되고 있던 요청에 대해서는 처리를 완료한다.
-
클라이언트는 NODEA에 접속되어 있는 상황이므로 해당 처리 결과를 NODEA의 CLH에게 전달한다. NODEA의 CLH 는 클라이언트에게 처리 결과를 전달한다.
-
NODEB의 큐에 쌓여 있는 요청에 대해서는 TMAX_BACKUP_ADDR로 설정된 NODEA에 요청을 보낸다.
-
NODEB의 Tmax 시스템이 종료된다.
-
NODEA에서는 NODEB로부터 받은 요청을 처리한 후, 처음 해당 리퀘스트를 요청한 클라이언트에게 처리 결과를 전달한다.
-
NODEA에 접속되어 있는 모든 클라이언트는 정상 응답을 받는다.(100개의 클라이언트가 모두 정상 응답을 받아야 함)
-
주의 사항
NODEA의 요청을 NODEB가 처리하기 위해서는 NODEA에 접속한 클라이언트의 TMAX_BACKUP_ADDR, TMAX_BACKUP_PORT가 NODEB로 설정되어 있어야 한다. 그렇지 않을 경우, NODEA의 Tmax 시스템이 종료되는 순간, 아직 처리되지 않은 클라이언트 요청에 대하여 TPESYSTEM 에러를 전달하게 된다.
환경설정
해당 기능을 사용하기 위해서는 노드 사이 COUSIN 환경구성이 반드시 설정되어 있어야 한다.
멀티 노드
*SVRGROUP svg1 NODENAME = NODE1, COUSIN = "svg2" svg2 NODENAME = NODE2
멀티 도메인
*SVRGROUP svg1 NODENAME = NODE1, COUSIN = "GW1" *GATEWAY GW1 RGWADDR = "192.168.1.48" // NODEB의 IPADDR
Tmax 클라이언트
TMAXDIR=/EMC01/starbj81/tmax TMAX_HOST_ADDR=192.168.1.43 // NODEA TMAX_HOST_PORT=8350 TMAX_BACKUP_ADDR=192.168.1.48 // NODEB TMAX_BACKUP_PORT=8350
WebT 환경설정
WebT를 이용하여 Rolling Down 기능을 사용하기 위해서는 반드시 아래의 세 가지가 모두 설정되어 있어야 한다.
-
USE_ROLLING_DOWN 설정
-
<run.sh>
java -classpath ./webt50.jar:. -DUSE_ROLLING_DOWN=true WebtClient
-
<source에서 설정>
System.setProperty("USE_ROLLING_DOWN", "true");
-
-
WebT 환경설정 메시지 헤더 설정
-
<webt.properties>
// 커넥션 풀을 사용할 때 connectionPool.group1.header.type=extendedV4 // 단일 커넥션을 사용할 때 headerType=extendedV4
-
<JeusMain.xml에서 설정할 경우>
<header-type>extendedV4</header-type>
-
<source에서 설정할 경우>
connection.setHeaderType("extendedV4");
-
-
백업 설정
-
<webt.properties>
connectionPool.anylink.hostBackupAddr=192.168.1.48 //NODEB connectionPool.anylink.hostBackupPort=8350
-
<JeusMain.xml>
<backup-host-name>192.168.1.48</backup-host-name> <backup-port>8350</backup-port>
-
1.8.5. Rolling Down 에러 코드 추가
기존의 Rolling Down 기능
클라이언트의 요청을 처리하고 있던 Tmax 시스템이 다운될 경우, 4.0 SP3 Fix#7 이전 버전에서는 현재 처리 중인 요청에 대해서만 응답을 처리하여 전달한 후, 큐에 쌓여 있는 요청에 대하여 TPECLOSE 에러를 전달하였다.
4.0 SP3 Fix#7 버전에 Rolling Down 기능이 추가되면서 해당 기능을 사용할 경우 (tmdown -R) Tmax 엔진을 다운시키기 이전에 요청된 모든 클라이언트에 대하여 정상적으로 응답을 줄 수 있다.
개선된 Rolling Down 기능
해당 버전에서는 TPERDOWN / TPERCLOSE 에러 코드가 추가됨으로써 기존 버전보다 더 효율적인 방식으로 Rolling Down 기능을 사용할 수 있다.
에러 코드 | 설명 |
---|---|
TPERDOWN |
Rolling Down이 발생한 경우, 기존 서버 큐에 쌓여 있는 요청들에 대하여 tpgetrply가 호출되면 TPERDOWN 에러가 셋팅 되며, tpacall이 호출되면 송신되었던 데이터가 tpgetrply의 수신 버퍼에 다시 담긴다. |
TPERDCLOSE |
더 이상 서버큐에 적체된 요청이 없는 상황에서 tpgetrply가 호출된 경우 TPERDCLOSE 에러가 셋팅 된다. |
사용자 가이드
개선된 Rolling Down 기능을 사용하여 사용자는 아래와 같은 방법으로 해당 기능을 사용할 수 있다.
NODEA의 Tmax 시스템 을 종료시킬 경우 (tmdown –R –n NODEA)
-
클라이언트 환경 파일(가칭 : tmax.env)에 [MAIN], [BACKUP] 환경 파일을 설정한다.
[MAIN] TMAXDIR=/user/tmaxqam/tmax TMAX_HOST_ADDR=192.168.1.44 TMAX_HOST_PORT=8155 [BACKUP] TMAXDIR=/user/tmaxqas/tmax TMAX_HOST_ADDR=192.168.1.44 TMAX_HOST_PORT=8255
-
[MAIN] 에 접속한다.
-
tpacall을 담당할 Send thread와 tpgetrply를 담당할 Recv thread를 생성한 후 [MAIN] context를 공유한다.
-
각각의 쓰레드가 동시에 tpacall / tpgetrply 요청을 계속적으로 보낸다.(서비스는 1초 이상 수행)
-
서버 큐에 요청이 적체된다.
-
tmdown –R 옵션으로 [MAIN]으로 설정된 Tmax를 종료한다.
-
Rolling Down이 수행된 이후 최초의 Send thread는 TPERDOWN 에러를 수신 및 해당 에러 수신할 때 당분간 tpacall 요청을 보내지 않는다.
-
Rolling Down이 수행된 이후 호출되는 tpgetrply 요청에 대하여 TPERDOWN 에러를 수신 및 해당 에러 수신 시 수신 버퍼(rcvbuf)에 요청 데이터가 담긴다.
-
사용자는 tpgetrply 때 전달 받은 수신 데이터를 [BACKUP]으로 지정된 시스템에 접속할 때 다시 요청할 데이터로 간주하고 모두 저장한다.
-
더 이상 서버 큐에 요청 데이터가 없을 경우 tpgetrply에 TPERDCLOSE 에러가 발생한다.
-
해당 에러가 발생하면 [BACKUP]으로 지정된 Tmax 로 접속 (tmaxreadenv, tpstart)
-
9번에서 저장한 데이터를 다시 송신 데이터로 하여 거래한다.
1.8.6. Loss Service 호출 기능
서비스 호출을 한 클라이언트나 서비스가 종료 또는 재시작되면 CLH에서 호출된 서비스로부터 온 응답메시지를 폐기(Discard)한다. 응답메시지가 폐기되면서 사용자가 지정한 서비스(Loss Service)를 호출(tpacacall with TPNOREADY | TPNOTRAN)할 수 있는 기능이 추가되었다. 또한 서버나 클라이언트 응답 큐가 쌓인 상태에서 종료되었을 때 폐기되는 메세지에 대해서도 Loss Service가 호출된다.
사용방법
CLHOPT 항목에 –L 옵션을 추가하여 사용한다. '-L 서비스명’으로 추가를 하며 서비스명에 응답메시지가 폐기될 때 호출되는 Loss Service를 지정한다.
*NODE Tmax01 TMAXIDR = …, CLHOPT = "-L LOSS_SVC" *SERVICE SVC01 SVRNAME = T4036_001_01 LOSS_SVC SVRNAME = T4036_001_01
Loss Service로 지정된 서비스는 TPSVCINFO의 cltid를 통해 추가 정보를 전달받게 된다.
cltid의 값은 다음과 같다.
-
cltid.clientdata[1]: 폐기된 응답의 tperrno이다.
-
cltid.clientdata[2]: 폐기된 응답의 tpurcode이다.
-
cltid.clientdata[3]: 폐기된 응답의 서비스 인덱스값이다. tpgetsvcname()함수의 인자로 사용된다.
Loss Service 호출 조건
CLH에서 지정된 서비스로 메시지를 전달할 때, 다음과 같은 조건을 만족해야 한다.
-
tpcall 또는 tpacall에 대한 응답이어야 한다.
-
정상 응답이나 에러 응답에 상관없이 데이터가 존재해야 한다.
-
CLH가 비정상적으로 종료될 경우에는, 전달되지 않을 수도 있다.
1.8.7. 노드의 동적 추가
기존에 tmadmin의 cfgadd 명령어를 이용하여 서비스, 서버, 서버 그룹을 동적으로 추가할 수 있는 기능에 더해 해당 버전에서는 노드를 동적으로 추가할 수 있도록 기능이 추가되었다. 또한 COUSIN 서버 그룹이 속한 노드도 동적으로 추가할 수 있다.
추가 방법은 다음과 같다.
-
환경 파일 작성
<tmconfig.m>
*DOMAIN tmax1 SHMKEY =@SHMEMKY@, MINCLH=1, MAXCLH=3, TPORTNO=@TPORTNO@, BLOCKTIME=300, MAXCPC =100, RACPORT=@TRACPORT@ *NODE @HOSTNAME@ TMAXDIR = "@TMAXDIR@", APPDIR = "@TMAXDIR@/appbin", @RMTNAME@ TMAXDIR = "@RMTDIR@", APPDIR = "@RMTDIR@/appbin", *SVRGROUP svg1 NODENAME = "@HOSTNAME@",COUSIN="svg2",LOAD=2 svg2 NODENAME = "@RMTNAME@",LOAD=1 *SERVER svr2 SVGNAME = svg1 *SERVICE TOUPPER SVRNAME = svr2 TOLOWER SVRNAME = svr2
<tmconfig_add.m>
*NODE @RMTNAME2@ TMAXDIR = "@RMTDIR2@", APPDIR = "@RMTDIR2@/appbin", *SVRGROUP svg1 NODENAME = "@HOSTNAME@",COUSIN="svg2,svg3",LOAD=2 svg3 NODENAME = "@RMTNAME2@",LOAD=1
-
racd 기동
추가된 새로운 노드에 racd를 기동한다.
node3>$ racd -k
-
환경 파일 컴파일
새로운 노드가 추가된 환경 파일 tmconfig_add.m을 컴파일한다.
컴파일을 할 때 –o 옵션을 주어 다른 이름의 바이너리 환경 파일을 만든다.
node1>$cfl -i tmconfig_add.m -a tmconfig_add.m –o tmchg CFL is done successfully for node(node1) CFL: rcfl start for rnode (node2) CFL is done successfully for node(node2) CFL: rcfl start for rnode (node3) CFL is done successfully for node(node3)
-
서버 컴파일
새로 추가할 노드의 서버를 컴파일한다.
node3>$ gst node3>$ compile c svr2
-
동적 노드 추가
tmadmin의 cfgadd 명령어를 사용해 동적으로 노드를 추가한다. 주의할 점은 각각의 노드에서 tmadmin –l로 모두 해당 명령어를 실행한다.
아래는 기존에 node1, node2가 존재하는 상황에서 node3를 추가할 경우에 대한 예이다.
# node1 $ node1>tmadmin –l -m --- Welcome to Tmax Admin (Type "quit" to leave) --- $$2 node1 (tmadm): cfgadd -i tmchg (I) TMM0211 General Infomation : CFGADD started [TMM0902] (I) TMM0211 General Infomation : CFGADD completed [TMM0907] config is successfully added # node2 $ node2>tmadmin –l -m --- Welcome to Tmax Admin (Type "quit" to leave) --- $$2 node2 (tmadm): cfgadd -i tmchg (I) TMM0211 General Infomation : CFGADD started [TMM0902] (I) TMM0211 General Infomation : CFGADD completed [TMM0907] config is successfully added
-
새로 추가된 노드의 기동
새로 추가된 노드 (node3)를 기동한다.
Node3>tmboot -n tmaxh4 -f tmchg TMBOOT for node(tmaxh4) is starting: Welcome to Tmax demo system: it will expire 2008/11/23 Today: 2008/9/24 TMBOOT: TMM is starting: Wed Sep 24 11:11:59 2008 TMBOOT: CLL is starting: Wed Sep 24 11:11:59 2008 (I) TMM0211 General Infomation : node register (nodeno = 0(0)) success [TMM0404] (I) TMM0211 General Infomation : node register (nodeno = 1(1)) success [TMM0404] TMBOOT: CLH is starting: Wed Sep 24 11:11:59 2008 (I) CLH9991 Current Tmax Configuration: Number of client handler(MINCLH) = 1 Supported maximum user per node = 7966 Supported maximum user per handler = 7966 [CLH0125] TMBOOT: TLM(tlm) is starting: Wed Sep 24 11:11:59 2008 TMBOOT: SVR(svr2) is starting: Wed Sep 24 11:11:59 2008
-
새로 추가된 노드의 확인
노드가 정상적으로 추가 되었는지 확인한다.
node1>tmadmin TMADMIN for rnode (node2): starting to connect to RAC TMADMIN for rnode (node3): starting to connect to RAC --- Welcome to Tmax Admin (Type "quit" to leave) --- $$1 node1 (tmadm): ti Tmax System Info: DEMO version 4.0 SP #3 Fix #8: expiration date = 2008/11/22 maxuser = UNLIMITED, domaincount = 1, nodecount = 3, svgrpcount = 3, svrcount = 9, svccount = 6 rout_groupcount = 0, rout_elemcount = 0 cousin_groupcount = 1, cousin_elemcount = 3 backup_groupcount = 0, backup_elemcount = 0 Tmax All Node Info: nodecount = 3: ---------------------------------------------------------------- no name portno racport shmkey shmsize minclh maxclh ---------------------------------------------------------------- 0 node1 8350 3155 88350 225760 1 3 1 node2 8350 3155 88350 225760 1 3 2 node3 8350 3155 88350 225760 1 3 $$2 (tmadm): st -s CLH 0: -------------------------------------------------------------- svc_name svr_name count cq_cnt aq_cnt q_avg avg status -------------------------------------------------------------- TOLOWER svr2 0 0 0 0.000 0.000 RDY TOUPPER svr2 0 0 0 0.000 0.000 RDY Msg from rnode(node2): CLH 0: -------------------------------------------------------------- svc_name svr_name count cq_cnt aq_cnt q_avg avg status -------------------------------------------------------------- TOLOWER svr2 0 0 0 0.000 0.000 RDY TOUPPER svr2 0 0 0 0.000 0.000 RDY Msg from rnode(node3): CLH 0: -------------------------------------------------------------- svc_name svr_name count cq_cnt aq_cnt q_avg avg status -------------------------------------------------------------- TOLOWER svr2 0 0 0 0.000 0.000 RDY TOUPPER svr2 0 0 0 0.000 0.000 RDY
1.8.8. 구버전 AUTOTRAN 호환 기능
AUTOTRAN은 비명시적인 트랜잭션(implict transaction)을 사용할 때 서비스 호출이 성공인 경우에는 commit, 실패인 경우에는 rollback을 자동으로 해주는 기능의 사용 여부에 대한 옵션이다.
구버전 동작 방식
3.8.9 이전 버전에서는 XA 서버가 tx_begin 없이 호출될 경우 자동으로 트랜잭션이 시작되었으며(기본값 AUTOTRAN=Y), 트랜잭션은 일종의 로컬 트랜잭션 형태로 수행되었다. 즉 외부 호출인 경우 트랜잭션이 함께 묶이지 않게 되며 결과적으로 현재의 TPNOTRAN으로 호출하는 것과 동일하게 동작하였다.
현 버전 동작 방식
하지만 3.8.9 버전에서는 AUTOTRAN=Y일 경우 글로벌 트랜잭션이 자동으로 시작되어 외부 호출이 있을 경우 자동으로 트랜잭션으로 묶이게 된다. 또한 기본값이 N으로 변경되었기 때문에 업그레이드를 할 때 해당 값을 명시적으로 설정해 주어야만 한다.
업그레이드 예
아래의 프로그램은 3.8.5 버전에서 3.8.9 이상의 버전으로 업그레이드할 경우의 예이다.
<SVC1>
SVC1 { EXEC SQL INSERT into EMP; tpcall("SVC2"); tpreturn(TPFAIL); }
<SVC2>
SVC2 { EXEC SQL INSERT into EMP2; tpreturn (TPSUCCESS) }
CLI pcall(SVC1) -> SVC1 tpcall(SVC2) -> SVC2인 구조에서 3.8.9 이전 버전에서는 로컬 트랜잭션으로 SVC2의 호출 성공 여부와 SVC1의 호출 성공이 별개로 동작하는데 반해, 3.8.9 이후 버전부터는 이 사항이 글로벌 트랜잭션으로 묶이게 되어, SVC2의 호출이 실패할 경우, SVC1 자체도 호출 실패로 처리된다.
따라서 3.8.9 이후의 버전에서는 SVC1의 EMP 테이블과 SVC2의 EMP2 테이블이 모두 rollback되지만 3.8.9 이전 버전에서는 SVC1의 EMP 테이블은 rollback, SVC2의 EMP2 테이블은 commit되는 결과를 가져온다.
3.8.9 이전 버전의 AUTOTRAN과 이후 버전의 AUTOTRAN의 차이점은 "로컬 트랜잭션에서 글로벌 트랜잭션으로 변경"으로 요약된다.
따라서 업그레이드 된 환경에서 이전 방식과 동일하게 동작하고 싶을 경우 애플리케이션 레벨의 수정이 불가피하다. 이전 버전과 동일한 방식으로 동작하고 싶다면 SVC1을 다음과 같이 수정하면 된다.
SVC1 { EXEC SQL INSERT into EMP; tpcall("SVC2",..., TPNOTRAN); tpreturn(TPFAIL); }
1.8.9. SysMaster 지원 기능 추가
SysMaster 이벤트 핸들러
라이브러리명
libsvrevt.a, libsvrevt.so
사용자 함수
_tmax_event_handler() 함수는 SVRTYPE이 EVT_SVR인 경우, SLOG가 발생하면 호출되는 콜백 함수이다.
-
프로토타입
int _tmax_event_handler(char *progname, int pid, int tid, char *msg, int flags);
-
파라미터
파라미터 설명 *progname
이벤트를 발생시킨 프로그램 이름이다.
pid
process id이다.
tid
thread id이다. 예비용이다.
*msg
이벤트 메시지이다.
flags
예비용이다.
-
반환값
반환값은 현재 사용되고 있지 않다. 예비용이다.
환경변수
-
NODE 절
TMMOPT에 –h 옵션을 통해 이벤트 핸들러 로그 레벨을 설정한다.
TMMOPT = "-h i | w | e | f" (default: e) - i : fatal, error, warn, info - w : fatal, error, warn - e : fatal, error - f : fatal
-
SERVER 절
SVRTYPE을 EVT_SVR로 설정, MIN/MAX는 모두 1, 노드당 하나의 EVT_SVR, SVRTYPE=EVT_SVR을 사용할 수 있다.
예제
<환경 파일>
*DOMAIN tmax1 SHMKEY =@SHMEMKY@, MINCLH=1, MAXCLH=3, TPORTNO=@TPORTNO@, BLOCKTIME=30 *NODE @HOSTNAME@ TMAXDIR = "@TMAXDIR@", APPDIR = "@TMAXDIR@/appbin", PATHDIR = "@TMAXDIR@/path", TMMOPT = "-h i", SMSUPPORT = Y, SMTBLSIZE = 1000 *SVRGROUP svg1 NODENAME = "@HOSTNAME@" *SERVER evtsvr SVGNAME = svg1, SVRTYPE = EVT_SVR
<서버 프로그램>
#include <stdio.h> #include <stdlib.h> #include <usrinc/atmi.h> #include <time.h> int tpsvrinit(int argc, char *argv[]) { printf("[EVTHND] started\n"); return 1; } int svrdone() { printf("[EVTHND stopped\n"); return 1; } int _tmax_event_handler(char *program, int pid, int tid, char *msg, int flags) { time_t t1; struct tm *tm; time(&t1); tm = localtime(&t1); printf("[EVTHND] %s.%d.%02d%02d%02d:%s\n", program, pid, tm->tm_hour, tm->tm_min, tm->tm_sec, msg); return 0; }
<Makefile.evt>
# Server makefile TARGET = $(COMP_TARGET) APOBJS = $(TARGET).o NSDLOBJ = $(TMAXDIR)/lib64/sdl.o LIBS = -lsvrevt -lnodb OBJS = $(APOBJS) $(SVCTOBJ) SVCTOBJ = $(TARGET)_svctab.o CFLAGS = -Ae +DA2.0W +DD64 +DS2.0 -O -I$(TMAXDIR) APPDIR = $(TMAXDIR)/appbin SVCTDIR = $(TMAXDIR)/svct LIBDIR = $(TMAXDIR)/lib64 # .SUFFIXES : .c .c.o: $(CC) $(CFLAGS) -c $< # # server compile # $(TARGET): $(OBJS) $(CC) $(CFLAGS) -L$(LIBDIR) -o $(TARGET) $(OBJS) $(LIBS) $(NSDLOBJ) mv $(TARGET) $(APPDIR)/. rm -f $(OBJS) $(APOBJS): $(TARGET).c $(CC) $(CFLAGS) -c $(TARGET).c $(SVCTOBJ): cp -f $(SVCTDIR)/$(TARGET)_svctab.c . touch ./$(TARGET)_svctab.c $(CC) $(CFLAGS) -c ./$(TARGET)_svctab.c # clean: -rm -f *.o core $(APPDIR)/$(TARGET)
Sysmaster Trace
GID 구조 (12byte)
-
GID0 (4bytes)
제품 내의 클라이언트별 고유 번호 (WebtoB의 경우 cli id) domain id, node id, hth #, slot id 등으로 제품에 접속한 클라이언트를 구별하기 위한 번호이다.
-
GID1 (4bytes)
부분으로 나뉘어 구성되어 있다. 상위 3byte는 seq #, 하위 1byte는 제품 고유 ID이다.
-
SEQNO (4bytes)
상위 2 byte는 비동기 호출의 branch #로 사용한다.
하위 2 byte는 모든 호출의 seq #로 사용한다.
환경 파일 추가
*NODE nodename SMSUPPORT = Y | (N) SMTBUFSIZE = num
-
SMSUPPORT = Y | N
-
Sysmaster Trace 기능 지원 여부를 선택하는 옵션으로 Y일 경우 Trace 기능을 지원하며, N일 경우 지원하지 않는다.
-
-
SMTBLSIZE = num
-
CLH별 SysMaster 트레이스 최대 저장 건수이다. (기본값 : 50000)
-
Tmadmin 기능 추가 (smtrc)
-
사용법
$$ node0 (tmadm) : smtrc [–a] GID0 GID1
항목 설명 - a
해당 옵션을 사용하면 기존 정보 외에 서버 프로세스 인덱스 (spri), 사용자 CPU 사용 시간, 시스템 CPU 사용 시간, 리턴 정보 등을 모두 표시한다.
GID0
SysMaster의 GID 상위 4byte를 Hexa decimal로 표기한다.
GID1
SysMaster의 GID 하위 4byte를 Hexa decimal 로 표기한다.
'st –p –x’를 실행할 경우, 서비스가 수행 중이면 해당 GID를 출력한다.
-
예제
$$1 tmaxh4 (tmadm): CLH 0: -------------------------------------------------------------- svr_name svgname spr_no status count avg svc PID fail_cnt err_cnt min_time max_time SysMaster_GID -------------------------------------------------------------- evtsvr svg1 36 RDY 0 0.000 -1 17980 0 0 0.000 0.000 00000000-00000000-00000000 svr1 svg1 37 RUN 0 0.000 SDLTOUPPER 17981 0 0 0.000 0.000 00000000-00000101-00000000 svr2 svg1 38 RUN 0 0.000 SDLTOUPPER2 17982 0 0 0.000 0.000 00000000-00080101-00000000 svr3 svg1 39 RUN 0 0.000 SDLTOUPPER3 17983 0 0 0.000 0.000 00000000-00100101-00000000 svr_sys svg1 40 RDY 3 0.000 -1 17984 0 0 0.000 0.000 00000000-00000000-00000000 ---------------------------------------------------------------- TOTAL COUNT = 3 TOTAL SVCFAIL COUNT = 0 TOTAL ERROR COUNT = 0 TOTAL AVG = 0.000 TOTAL RUNNING COUNT = 3 $$1 tmaxh4 (tmadm): smtrc 0 0101 CLH 0: ------------------------------------------------------------- sysmaster_global_id status svc_name ------------------------------------------------------------- 00000000:00000101:00000000 SVC_RUNNING SDLTOUPPER2 45670701 tmaxi4 (tmadm): smtrc -a 0 0101 CLH 0: ---------------------------------------------------------------- sysmaster_global_id status svc_name ctime svctime spri ucpu scpu ---------------------------------------------------------------- 00000000:00000101:00000000 SVC_RUNNING SDLTOUPPER2 14:05:32:159 0.000 38 0.000 0.000 00000000:00000101:00010000 SVC_RUNNING SMTRACE 14:05:32:159 0.000 40 0.000 0.000 00000000:00000101:00010000 SVC_DONE SMTRACE 14:05:32:159 0.000 40 0.000 0.000
Tmadmin API 추가 (서버 라이브러리)
#include <usrinc/tmadmin.h> /* TMADM_SMTRC return structures */ struct tmadm_smtrc_body { int seqno; int clhno; char status[TMAX_NAME_SIZE]; char name[TMAX_NAME_SIZE]; }; struct tmadm_smtrc { /* fixed header */ struct tmadm_header header; /* fixed body */ struct tmadm_smtrc_body trc[1]; }; /* TMADM_SMTRC with TMADM_AFLAG return structures */ struct tmadm_smtrcall_body { int seqno; int clhno; char status[TMAX_NAME_SIZE]; char name[TMAX_NAME_SIZE]; int spri; int reserved; struct timeval curtime; struct timeval svctime; struct timeval ucputime; struct timeval scputime; }; struct tmadm_smtrcall { /* fixed header */ struct tmadm_header header; /* fixed body */ struct tmadm_smtrcall_body trc[1]; }; typedef struct { int gid1; int gid2; int seqno; } tmax_smgid_t; int tmadmin(int cmd, void *arg, int opt, long flags);
-
TMADM_SMTRC cmd 추가
-
서비스가 수행 중이면 해당 GID를 출력한다.
-
결과를 이어서 가져오기 지원하지 않는다. (offset 미지원)
-
결과를 가져오기 위한 공간을 충분히 할당한 후 호출해야 한다.
-
-
flags
-
TPNOFLAGS
-
tmadm_smtrc 구조체를 사용한다.
TMADM_AFLAG
-
tmadm_smtrcall 구조체를 사용한다.
-
기존 정보 외에 spri, reserved, curtime, svctime, ucputime, scputime 등의 정보를 추가로 제공한다.
opt는 사용하지 않는다.
-
-
tmgetsmgid
tmgetsmgid() 함수는 현재 자신의 gid를 얻어오기 위한 함수이다.
-
프로토타입
#include <usrinc/tmadmin.h> int tmgetsmgid (tmax_smgid_t *gid);
-
예제
<svr02.c : TPNOFLAGS 사용>
#include <stdio.h> #include <stdlib.h> #include <usrinc/atmi.h> #include <usrinc/tmadmin.h> #include "../sdl/demo.s" GETGID(TPSVCINFO *msg) { tmax_smgid_t smgid; int ret; char *buf; buf = (char *)tpalloc("STRING", NULL, 0); if(buf == NULL) tpreturn(TPFAIL, -1, NULL, 0, 0); ret = tmgetsmgid(&smgid); memcpy(buf, (char *)&smgid.gid1, 4); memcpy(buf+4, (char *)&smgid.gid2, 4); memcpy(buf+8, smgid.seqno, 4); tpreturn(TPSUCCESS, 0, (char *)buf, strlen(buf), 0); } SMTRACE(TPSVCINFO *msg) { struct tmadm_smtrc *smtrc; int max = 10, size; int gid1, gid2, n, i; struct smtrace *ptr; char *buf; buf = (char *)tpalloc("CARRAY", NULL, 0); if(buf == NULL) tpreturn(TPFAIL, -1, NULL, 0, 0); ptr = (struct smtrace *)msg->data; gid1 = ptr->gid1; gid2 = ptr->gid2; size = sizeof(struct tmadm_smtrc) + (max-1) * sizeof(struct tmadm_smtrc_body); smtrc = (struct tmadm_smtrc *)malloc(size); if(smtrc == NULL) { printf("smtrc is null\n"); tpreturn(TPFAIL, -1, NULL, 0, 0); } memset(smtrc, 0x00, size); smtrc->header.version = _TMADMIN_VERSION; smtrc->header.size = size; smtrc->header.reserve_int[0] = gid1; smtrc->header.reserve_int[1] = gid2; n = tmadmin(TMADM_SMTRC, smtrc, TPNOFLAGS, TPNOFLAGS); if(n < 0) { free(smtrc); tpreturn(TPFAIL, -1, NULL, 0, 0); } for(i=0; i<smtrc->header.num_entry; i++) { sprintf(buf, "SMTRACE[%d] : gid[%x-%x-%x] seqno[%x] clhno[%x] status [%s] name[%s]\n", i, gid1, gid2, ptr->seqno, smtrc->trc[i].seqno, smtrc->trc[i].clhno, smtrc->trc[i].status, smtrc->trc[i].name); } free(smtrc); tpreturn(TPSUCCESS, 0, (char *)buf, strlen(buf), 0);
<svr02_a.c : TMADM_AFLAG 사용>
#include <stdio.h> #include <stdlib.h> #include <usrinc/atmi.h> #include <usrinc/tmadmin.h> #include "../sdl/demo.s" GETGID_A(TPSVCINFO *msg) { tmax_smgid_t smgid; int ret; char *buf; buf = (char *)tpalloc("STRING", NULL, 0); if(buf == NULL) tpreturn(TPFAIL, -1, NULL, 0, 0); ret = tmgetsmgid(&smgid); memcpy(buf, (char *)&smgid.gid1, 4); memcpy(buf+4, (char *)&smgid.gid1, 4); memcpy(buf+8, smgid.seqno, 4); tpreturn(TPSUCCESS, 0, (char *)buf, strlen(buf), 0); } SMTRACE_A(TPSVCINFO *msg) { struct tmadm_smtrcall *smtrcall; int max = 10, size; int gid1, gid2, n, i; struct smtrace *ptr; char *buf; buf = (char *)tpalloc("CARRAY", NULL, 0); if(buf == NULL) tpreturn(TPFAIL, -1, NULL, 0, 0); ptr = (struct smtrace *)msg->data; gid1 = ptr->gid1; gid2 = ptr->gid2; size = sizeof(struct tmadm_smtrcall) + (max-1) * sizeof(struct tmadm_smtrcall_body); smtrcall = (struct tmadm_smtrcall *)malloc(size); if(smtrcall == NULL) { printf("smtrcall is null\n"); tpreturn(TPFAIL, -1, NULL, 0, 0); } memset(smtrcall, 0x00, size); smtrcall->header.version = _TMADMIN_VERSION; smtrcall->header.size = size; smtrcall->header.reserve_int[0] = gid1; smtrcall->header.reserve_int[1] = gid2; n = tmadmin(TMADM_SMTRC, smtrcall, TMADM_AFLAG, TMADM_AFLAG); if(n < 0) { free(smtrcall); tpreturn(TPFAIL, -1, NULL, 0, 0); } printf("smtrcall->header.num_entry = %d\n", smtrcall->header.num_entry); printf("smtrcall->header.num_left = %d\n", smtrcall->header.num_left); n = 0; for(i=0; i<smtrcall->header.num_entry + smtrcall->header.num_left; i++) { sprintf(buf+n, "SMTRACE[%d] : gid[%x-%x-%x] seqno[%x] clhno[%x] status[%s] name[%s] spri[%d] curtime[%ld], svctime[%ld], ucputime[%ld], scputime[%ld]\n", i, gid1, gid2, ptr->seqno, smtrcall->trc[i].seqno, smtrcall->trc[i].clhno, smtrcall->trc[i].status, smtrcall->trc[i].name, smtrcall->trc[i].spri, smtrcall->trc[i].curtime.tv_sec, smtrcall->trc[i].svctime.tv_sec, smtrcall->trc[i].ucputime.tv_sec, smtrcall->trc[i].scputime.tv_sec); n = n + strlen(buf); } free(smtrcall); tpreturn(TPSUCCESS, 0, (char *)buf, strlen(buf), 0); }
<svr01.c>
#include <stdio.h> #include <usrinc/atmi.h> #include <usrinc/tmadmin.h> #include "../sdl/demo_sdl.h" SDLTOUPPER(TPSVCINFO *msg) { int i, ret, cd; struct smtrace *stdata; tmax_smgid_t smgid; char *buf; long rcvlen; buf = (char *)tpalloc("CARRAY", NULL, 0); ret = tmgetsmgid(&smgid); if(ret < 0) tpreturn(TPFAIL, -1, NULL, 0, 0); stdata = (struct smtrace *)msg->data; stdata->gid1 = smgid.gid1; stdata->gid2 = smgid.gid2; stdata->seqno = smgid.seqno; cd = tpacall("SMTRACE", msg->data, 0, 0); /* TMADM_AFLAG 사용 시 */ /* cd = tpacall("SMTRACE_A", msg->data, 0, 0); */ ret = tpgetrply(&cd, (char **)&buf, (long *)&rcvlen, 0); if(ret < 0) tpreturn(TPFAIL, -1, NULL, 0, 0); sleep(20); tpreturn(TPSUCCESS,0,(char *)buf, strlen(buf),0); }
< client.c>
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <usrinc/atmi.h> #include <usrinc/tmadmin.h> #include "../sdl/demo.s" main(int argc, char *argv[]) { struct smtrace *sndbuf, *rcvbuf; long rcvlen, sndlen; int ret; if (tpstart((TPSTART_T *)NULL) == -1){ printf("tpstart failed\n"); exit(1); } ... if (tpcall("SDLTOUPPER", (char *)sndbuf, 0, (char **)&rcvbuf, &rcvlen, 0) == -1){ printf("Can't send request to service SDLTOUPPER =>\n"); tpfree((char *)sndbuf); tpfree((char *)rcvbuf); tpend(); exit(1); } printf("rcvbuf = %s\n", rcvbuf); tpfree((char *)sndbuf); tpfree((char *)rcvbuf); tpend(); }
트레이스 로깅 기능
노드 절에 SMLOGSVC와 SMLOGINT를 지정할 경우, 지정한 서비스를 주기적으로 호출하여 트레이스 정보를 로깅할 수 있다.
환경설정
Domain 이름 [SMLOGSVC=service-name] [SMLOGINT=interval-time-value]
-
SMLOGSVC
-
로깅을 담당할 서비스명을 지정한다. 지정하지 않을 경우 TRACE LOGGING 기능은 동작하지 않는다.
-
-
SMLOGINT
-
로깅 주기를 지정한다. 단위는 초이며 지정하지 않을 경우 기본값은 30초이다.
-
로깅 정보 구조체
<usrinc/tmadmin.h>
/* SysMaster Trace Log structure */ typedef struct { tmax_smgid_t gid; int clhno; char status[TMAX_NAME_SIZE]; char name[TMAX_NAME_SIZE]; int spri; int reserved; struct timeval curtime; struct timeval svctime; struct timeval ucputime; struct timeval scputime; } tmax_smtrclog_t;
tmget_smtrclog_count
tmget_smtrclog_count() 함수는 현재 로깅할 데이터의 개수를 반환한다.
-
프로토타입
#include <usrinc/tmadmin.h> int tmget_smtrclog_count(void *handle)
-
파라미터
파라미터 설명 *handle
로깅 서비스에서 전달 받은 데이터의 포인터(msg->data)이다.
-
반환값
반환값 설명 현재 로깅된 데이터의 개수
함수 호출에 성공한 경우이다.
-1
함수 호출에 실패한 경우이다.
(tperrno에 에러 상황에 해당하는 값이 설정된다.)
tmget_smtrclog
tmget_smtrclog() 함수는 로깅 데이터를 구조체 버퍼에 저장한다.
-
프로토타입
#include <usrinc/tmadmin.h> int tmget_smtrclog(void *handle, tmax_smtrclog_t *buf, int *count)
-
파라미터
파라미터 설명 *handle
로깅 서비스에서 전달받은 데이터의 포인터(msg→data)이다.
*buf
로깅 데이터를 가져오기 위한 버퍼이다.
*count
로깅 건수가 저장된다. 입력할 때는 버퍼의 최대 로깅 건수를 설정하며 출력할 때는 저장된 로깅 건수가 저장된다.
-
반환값
반환값 설명 1
함수 호출에 성공한 경우이다.
-1
함수 호출에 실패한 경우이다.
(tperrno에 에러 상황에 해당하는 값이 설정된다.)
-
예제
#include <stdio.h> #include <stdlib.h> #include <usrinc/atmi.h> #include <usrinc/tmadmin.h> SMLOGSERVICE(TPSVCINFO *msg) { tmax_smtrclog_t *smtrclog; int ret, count=0, i; char *buf; smtrclog = (tmax_smtrclog_t *)tpalloc("CARRAY", NULL, 1024); if(smtrclog == NULL) { printf("smtrclog tpalloc fail [%s]\n", tpstrerror(tperrno)); } buf = (char *)tpalloc("STRING", NULL, 0); if(buf == NULL) tpreturn(TPFAIL, -1, NULL, 0, 0); memset(buf, 0x00, 1024); memset(smtrclog, 0x00, 1024); count = tmget_smtrclog_count((char *)msg->data); printf("\n###########################\n\n"); printf("tmget_smtrclog_count = %d\n", count); count = 100; ret = tmget_smtrclog(msg->data, smtrclog, &count); printf("count = %d\n", count); for(i=0; i<count; i++) { printf("smtrclog[%d].gid = %d-%d-%d\n", i, smtrclog[i].gid.gid1, smtrclog[i].gid.gid2, smtrclog[i].gid.seqno); printf("smtrclog[%d].clhno = %d\n", i, smtrclog[i].clhno); printf("smtrclog[%d].status = %s\n", i, smtrclog[i].status); printf("smtrclog[%d].name = %s\n", i, smtrclog[i].name); printf("smtrclog[%d].spri = %d\n", i, smtrclog[i].spri); printf("\n"); } printf("###########################\n\n"); strcpy(buf, "success\n"); tpreturn(TPSUCCESS, 0, (char *)buf, 0, 0); }
1.8.10. UCS Writable FD Scheduling
tpissetfd_w
UCS 방식 서버 프로세스의 FDSET을 검사하여 파라미터로 주어진 소켓 FD에 보낼 데이터가 있는지 확인하는 함수이다. tpissetfd() 함수가 Readable FDSET을 검사하는 반면, 이 함수는 Writable FDSET을 검사한다. UCS 방식 프로세스의 외부 소켓 스케줄링에 사용된다.
-
프로토타입
#include <ucs.h> int tpissetfd_w(int fd)
-
파라미터
파라미터 설명 fd
Writable FDSET에서 검사할 소켓 FD 값이다.
-
반환값
반환값 설명 1
함수 호출에 성공한 경우이다.
-1
함수 호출에 실패한 경우이다.
(tperrno에 에러 상황에 해당하는 값이 설정된다.)
-
오류
에러 코드 설명 [TPESYSTEM]
Tmax 시스템 에러가 발생. 자세한 정보는 로그파일에 기록됨
[TPEOS]
OS 에러 발생
tpsetfd_w
UCS 방식 서버 프로세스의 FDSET에 파라미터로 주어진 소켓 FD를 등록하는 함수이다. tpsetfd() 함수가 Readable FDSET에 등록하는 반면, 이 함수는 Writable FDSET에 등록한다. UCS 방식 프로세스의 외부 소켓 스케줄링에 사용된다.
-
프로토타입
#include <ucs.h> int tpissetfd_w(int fd)
-
파라미터
파라미터 설명 fd
Writable FDSET에 등록할 소켓 FD 값이다.
-
반환값
반환값 설명 1
함수 호출에 성공한 경우이다.
-1
함수 호출에 실패한 경우이다.
(tperrno에 에러 상황에 해당하는 값이 설정된다.)
-
오류
에러 코드 설명 [TPESYSTEM]
Tmax 시스템 에러가 발생한 경우이다. 자세한 정보는 로그파일에 기록된다.
[TPEOS]
OS 에러가 발생한 경우이다.
tpclrfd_w() 함수
UCS 방식 서버 프로세스의 FDSET에서 파라미터로 주어진 소켓 FD를 제거시키는 함수이다. tpclrfd() 함수가 Readable FDSET에서 제거하는 반면에, 이 함수는 Writable FDSET에서 제거한다. UCS 방식 프로세스의 외부 소켓 스케줄링에 사용된다.
-
프로토타입
#include <ucs.h> int tpclrfd_w(int fd)
-
파라미터
파라미터 설명 fd
Writable FDSET에서 제거할 소켓 FD 값이다.
-
반환값
반환값 설명 1
함수 호출에 성공한 경우이다.
-1
함수 호출에 실패한 경우이다.
(tperrno에 에러 상황에 해당하는 값이 설정된다.)
-
오류
에러 코드 설명 [TPESYSTEM]
Tmax 시스템 에러가 발생한 경우이다. 자세한 정보는 로그파일에 기록된다.
[TPEOS]
OS 에러가 발생한 경우이다.
1.8.11. IP 주소 기반 접근 제한 기능
Tmax 4.0 SP3 Fix#8 버전에서는 IP 주소를 기준으로 접속을 허용할 클라이언트와 허용하지 않을 클라이언트를 설정할 수 있다.
정적 제한 설정
접속을 허용/거부할 클라이언트의 설정 파일을 각각 tmax.allow, tmax.deny로 작성한다. 해당 파일들을 통하여 CLL에서 TCP 클라이어트의 접근 허용/거부를 선택한다.
접속 허용 클라이언트 설정
$TMAXDIR/path 디렉터리에 접근 허용 파일 tmax.allow를 작성하여 접근을 허용할 클라이언트의 IP 주소를 설정한다.
192.168.1.43 192.168.1.48
접속 거부 클라이언트 설정
$TMAXDIR/path 디렉터리에 접근 거부 파일 tmax.deny를 작성하여 접근을 거부할 클라이언트의 IP 주소를 설정한다.
192.168.1.35 192.168.1.45
적용 규칙
ACL(Access Control List)의 적용 규칙은 아래와 같다.
-
tmax.allow를 먼저 검색한 후 일치하는 ACL이 존재하면 접속을 허용한다.
-
tmax.deny를 검색한 후 일치하는 ACL이 존재하면 접속을 거부한다. 해당 ACL에 속하는 클라이언트가 접속을 시도(TPSTART)하면 TPECLOSE 에러가 발생한다.
-
tmax.allow, tmax.deny 두 파일에 모두 없으면 접속을 허용한다.
사용 문법
-
첫 번째 문자가 ‘#’일 경우 주석으로 처리한다.
-
IP 주소 또는 NETWORK/NETMASK 방식만 허용한다.
예) 192.168.1.1 또는 192.168.1.0/24
-
한 라인당 하나의 ACL만 허용하며 공백이나 탭이 허용되지 않는다.
-
ALL은 모든 IP 주소를 의미하는 예약어이다.
동적 제한 설정
동적으로 접근 허용/거부 클라이언트 IP 주소를 설정하기 위해서는 아래의 tmax_add_acl() 함수를 사용한다.
tmax.allow와 tmax.deny 파일에 설정된 클라이언트 IP 주소 목록 외에 운영 중에 접근 허용/거부 IP 주소를 추가하고자 할 경우 사용할 수 있는 함수이다.
-
프로토타입
#include <usrinc/tmaxapi.h> int tmax_add_acl (int nodeno, char *ip, int mask, int mode, int flags)
-
파라미터
파라미터 설명 nodeno
ACL(Access Control List)이 추가될 노드번호이며 -1로 설정할 경우 로컬 노드에 ACL을 설정함을 의미한다.
ip
접근을 허용/거부할 클라이언트의 IP 주소를 문자열로 설정하며 mask에는 NETMASK(1~32)를 설정한다. NETMASK를 설정하지 않을 경우 TMAX_ACL_IPADDR로 대신 설정한다.
mode
두 가지를 설정할 수 있다. TMAX_ACL_ALLOW를 설정할 경우 접근 허용 리스트에 추가하게 되며, TMAX_ACL_DENY를 설정할 경우 접근 거부 리스트에 추가된다.
flags
현재 사용되지 않는다.
-
반환값
반환값 설명 0보다 큰 값
함수 호출에 성공한 경우이다.
-1
함수 호출에 실패한 경우이다.
-
예제
<deny.m>
*DOMAIN tmax1 SHMKEY = @SHMEMKY@, TPORTNO = @TPORTNO@, RACPORT = @TRACPORT@ *NODE @HOSTNAME@ TMAXDIR = "@TMAXDIR@", APPDIR = "@TMAXDIR@/appbin" @RMTNAME@ TMAXDIR = "@RMTDIR@", APPDIR = "@RMTDIR@/appbin", *SVRGROUP svg1 NODENAME = "@HOSTNAME@" svg2 NDENAME = "@HOSTNAME@", COUSIN = "svg3" svg3 NODENAME = "@RMTNAME@" *SERVER svr_addlist SVGNAME = svg1 svr2 SVGNAME = svg2 *SERVICE TOUPPER SVRNAME = svr2 ALLOW_IP SVRNAME = svr_addlist DENY_IP SVRNAME = svr_addlist
<svr_addlist.c>
#include <stdio.h> #include <arpa/inet.h> #include <usrinc/atmi.h> #include <usrinc/tmaxapi.h> ALLOW_IP(TPSVCINFO *msg) { struct in_addr in; int ip, port, nodeno; int ret; printf("\nALLOW_IP Service is started.\n"); printcliinfo(); getcliinfo(&ip, &port, &nodeno); in.s_addr = ip; // 1번 노드에 ACL(접속 허용 IP 주소)을 설정 ret = tmax_add_acl(1, msg->data, TMAX_ACL_IPADDR,TMAX_ACL_ALLOW, 0); if(ret < 0) { printf("tmax_add_acl is failed.\n"); tpreturn(TPFAIL, 0, (char*)msg->data, 0, 0); } tpreturn(TPSUCCESS, 0, (char*)msg->data, 0, 0); } DENY_IP(TPSVCINFO *msg) { struct in_addr in; int ip, port, nodeno; int ret; printf("\nDENY_IP Service is started.\n"); printcliinfo(); getcliinfo(&ip, &port, &nodeno); in.s_addr = ip; // 1번 노드에 ACL(접속 거부 IP 주소)을 설정 ret = tmax_add_acl(1, msg->data, TMAX_ACL_IPADDR,TMAX_ACL_DENY, 0); if(ret < 0) { printf("tmax_add_acl is failed.\n"); tpreturn(TPFAIL, 0, (char*)msg->data, 0, 0); } tpreturn(TPSUCCESS, 0, (char*)msg->data, 0, 0); }
<cli_acladd.c>
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <usrinc/atmi.h> main(int argc, char *argv[]) { char *sndbuf, *rcvbuf; long rcvlen, sndlen; int ret, cd; if (argc != 2) { printf("Usage: [%s] IP_ADDR\n", argv[0]); exit(1); } if ( (ret = tmaxreadenv( "tmax.env","TMAX" )) == -1 ){ printf( "tmax read env failed\n" ); exit(1); } if (tpstart((TPSTART_T *)NULL) == -1){ printf("tpstart failed. [%s]\n", tpstrerror(tperrno)); exit(1); } if ((sndbuf = (char *)tpalloc("STRING", NULL, 0)) == NULL){ printf("sendbuf alloc failed !\n"); tpend(); exit(1); } if ((rcvbuf = (char *)tpalloc("STRING", NULL, 0)) == NULL){ printf("recvbuf alloc failed !\n"); tpfree((char *)sndbuf); tpend(); exit(1); } // 접속을 거부할 IP 주소를 설정한다. strcpy(sndbuf, argv[1]); printf("client is calling SVC01.\n"); cd = tpcall("DENY_IP", sndbuf, 0, rcvbuf, &rcvlen, 0); if(cd < 0){ printf("tpcall is failed[%s]\n", tpstrerror(tperrno)); tpfree((char *)sndbuf); tpfree((char *)rcvbuf); tpend(); exit(1); } tpfree((char *)sndbuf); tpfree((char *)rcvbuf); tpend(); }
-
결과
client > $ cli_acladd 192.168.1.43
IP 192.168.1.43에서 node2로 접속하는 모든 연결시도(TPSTART)는 TPECLOSE 에러가 발생한다.
1.8.12. 비정상 연결 클라이언트의 강제 해제
비정상 연결 클라이언트가 CLH에 무한정 연결되어 있을 경우 다른 클라이언트가 접속하지 못하는 현상이 발생할 수 있다. 따라서 현 버전에서는 CLH에서 소켓 연결 이후 60초 이내에 TPSTART 연결 메시지가 오지 않는 클라이언트에 대해서는 자동으로 연결을 종료한다.
클라이언트의 연결 종료할 때 다음과 같은 에러가 발생한다.
(I) CLH0209 internal error : disconnect client because client didn't send tpstart msg for 60 sec.(192.168.1.43) [CLH0058]
1.8.13. TMS Recovery 관련 로그 추가
TMS에서 트랜잭션 리커버리를 사용하는 경우, 트랜잭션 리커버리가 시작될 때나 완료될 때 INFO 로그를 출력한다. (서비스 코드 : TMS0221, TMS0222, TMS0223)
출력되는 로그는 다음과 같다.
(I) TMS0211 General Infomation : transaction recovery will be started [TMS0221] (I) TMS0211 General Infomation : transaction recovery was completed [TMS0222] (I) TMS0211 General Infomation : transaction recovery was completed [TMS0223]
1.9. Tuxedo 게이트웨이
1.9.1. Tuxedo Async 게이트웨이의 추가
Tuxedo 게이트웨이 개요
Tuxedo 게이트웨이는 Tmax와 Tuxedo의 양방향 통신을 위한 게이트웨이로 Tuxedo의 도메인 게이트웨이 통신 방법에 따라 통신을 한다.
-
Tmax에서 Tuxedo로의 요청
Tmax의 Tuxedo 게이트웨이는 요청 메시지가 왔을 때 Tmax의 CLH로부터 받는 xid를 Tuxedo의 xid에 맞게 변환한 후 Tuxedo의 도메인 게이트웨이에 요청 메시지와 함께 보낸다.
이후 xa_prepare나 xa_rollback/xa_commit 메시지가 CLH 로부터 오게 되면 이전에 변환한 xid를 참조하여 Tuxedo의 도메인 게이트웨이로 해당 메시지를 보낸다.
-
Tuxedo에서 Tmax로의 요청
Tmax의 Tuxedo 게이트웨이는 Tuxedo의 도메인 게이트웨이로부터 요청 메시지가 왔을 때 Tuxedo로부터 받은 xid를 Tmax의 xid에 맞게 변환한 후 CLH로 메시지를 보내게 된다.
CLIENT Tmax System SVC Tuxedo GateWay Tuxedo System Domain GateWay SVC XA OR Non XA 이후, Tuxedo로부터 xa_prepare나 xa_rollback/xa_commit 요청 메시지가 오면 이전에 변환한 xid를 참조하여 CLH로 해당 메시지를 보낸다.
Tmax 환경설정
<GATEWAY 절>
*GATEWAY GW 명 NODENAME = "nodename", GWTYPE = TUXEDO | TUXEDO_ASYNC, PORTNO = Port-number, RGWADDR = "Tuxedo-domaingw-ipaddr", RGWPORTNO = Tuxedo-domaingw-portno, CLOPT = "string" BACKUP_RGWADDR = "Backup-Tuxedo-ipaddr", BACKUP_RGWPORTNO = "Backup-Tuxedo-domaingw-portno", BACKUP_RGWADDR2 = "Backup-Tuxedo-ipaddr2", BACKUP_RGWPORTNO2 = "Backup-Tuxedo-domaingw-portno2", BACKUP_RGWADDR3 = "Backup-Tuxedo-ipaddr3", BACKUP_RGWPORTNO3 = "Backup-Tuxedo-domaingw-portno3"
-
GWTYPE = string
Tuxedo의 도메인 게이트웨이와 통신하기 위해서는 반드시 TUXEDO나 TUXEDO_ASYNC로 지정해 준다.
-
TUXEDO : 기존 버전에도 존재하던 TYPE으로 Tuxedo와 동기 형태의 채널로 통신한다.
-
TUXEDO_ASYNC : 4.0 SP3 Fix#8에서 새로 추가된 타입으로 비동기 형태의 채널로 통신한다.
-
-
PORTNO = numeric
로컬 Tuxedo 게이트웨이 프로세스가 사용하는 Listen Port이다.
-
RGWADDR = literal
Tmax의 Tuxedo 게이트웨이가 접속하기를 원하는 Tuxedo의 도메인 게이트웨이 프로세스가 실행되고 있는 노드의 IP 주소나 노드명을 등록한다.
-
RGWPORTNO = numeric
Tmax의 Tuxedo 게이트웨이가 접속하기를 원하는 Tuxedo의 도메인 게이트웨이 프로세스가 Listen하고 있는 포트 번호를 등록한다.
-
BACKUP_RGWADDR = literal
백업을 위해 로컬 Tuxedo 게이트웨이가 연결할 Tuxedo의 IP 주소를 지정한다. RGWADDR로 지정된 Tuxedo에 장애가 발생할 경우 해당 항목에 설정한 노드로 접속을 시도한다.
백업 Tuxedo 게이트웨이는 3개까지 지정할 수 있으며, BACKUP_RGWADDR2-BACKUP_RGWPORTNO2, BACKUP_RGWPORT3-BACKUP_RGWPORTNO3로 지정한다.
-
BACKUP_RGWPORTNO = numeric
백업을 위해 로컬 Tuxedo 게이트웨이가 연결할 Tuxedo의 도메인 게이트웨이의 포트 번호를 지정한다. RGWADDR-RGWPORTNO로 지정된 Tuxedo에 장애가 발생한 경우 해당 항목(BACKUP_RGWADDR-BACKUP_RGWPORTNO)에 설정한 노드로 접속을 시도한다.
-
CLOPT = literal
-
-a value
Tuxedo로 연결할 때 전송되는 도메인 ID 값을 설정한다. 반드시 Tuxedo의 환경설정 파일 내에 정의된 이름이어야 한다.
-
-r value
Tuxedo에서 Tmax로 연결할 때 전송되는 Tuxedo의 도메인 ID 값을 설정한다. 이 옵션은 Tuxedo의 도메인 ID 를 검사하여 인증을 하고자 할 때 사용한다. 지정하지 않을 경우 Tuxedo 도메인 ID를 인증하지 않는다. 해당 값에 설정되지 않은 도메인 ID 를 가진 게이트웨이가 접속을 시도하고자 할 때에는 다음의 에러가 발생하여 접속을 거부하게 된다.
(I) GATEWAY0046 socket connect error : incorrent remote domain name [TUXGW0416]
-
예제
<Tmax 환경 파일>
*DOMAIN dom1 SHMKEY = 78350, MINCLH = 1, MAXCLH = 1, TPORTNO = 8350, BLOCKTIME=60 *NODE tmaxh4 TMAXDIR="/data1/tmaxqam/tmax APPDIR="@TMAXDIR@/appbin", *SVRGROUP svg1X NODENAME = "tmaxh4", DBNAME = ORACLE, OPENINFO = "Oracle_XA+Acc=P/scott/tiger+SesTm=60+DbgFl=0x01+LogDir=@TMAXDIR@/log/xalog", TMSNAME = tms_ora *SERVER txsvr SVGNAME=svg1X txsvr2 SVGNAME = svg1X *SERVICE TMAX_INSERT SVRNAME = txsvr TUX_INSERT SVRNAME = TUXGW *GATEWAY TUXGW GWTYPE = TUXEDO_ASYNC, PORTNO = 9521, RGWADDR = "192.168.1.35", RGWPORTNO = 9311, BACKUP_RGWADDR = "192.168.1.35", BACKUP_RGWPORTNO = 9011, BACKUP_RGWADDR2 = "192.168.1.35", BACKUP_RGWPORTNO2 = 9711, NODENAME = @HOSTNAME@, CLOPT="-a TMXDOM -S AA –rTUXDOM", TIMEOUT = 30, CPC = 1
<Tuxedo 환경 파일>
*DM_RESOURCES VERSION=U22 *DM_LOCAL_DOMAINS TUXDOM GWGRP=DOM_GW1 TYPE=TDOMAIN #Tmax GATEWAY 절의 CLOPT="-rTUXDOM 설정 DOMAINID="TUXDOM" BLOCKTIME=3000 MAXDATALEN=56 MAXRDOM=89 SECURITY=NONE BLOB_SHM_SIZE=100000 DMTLOGDEV="/data1/tmaxqas/bea/tuxedo9.1/samples/atmi/dgw/DMTLOG" AUDITLOG="/data1/tmaxqas/bea/tuxedo9.1/samples/atmi/dgw/AUDITLOG" DMTLOGNAME="DMTLOG" CONNECTION_POLICY=ON_STARTUP *DM_REMOTE_DOMAINS TMXDOM TYPE=TDOMAIN DOMAINID="TMXDOM" *DM_TDOMAIN #Tuxedo의 도메인 IP 주소/포트 번호 설정 #Tmax GATEWAY 절의 RGWADDR, RGWPORTNO TUXDOM NWADDR="//192.168.1.35:9311" CONNECTION_POLICY=ON_STARTUP #Tmax의 Tuxedo Gateway IP 와 PORT TMXDOM NWADDR="//192.168.1.42:9521" CONNECTION_POLICY=ON_STARTUP *DM_LOCAL_SERVICES TUX_INSERT *DM_REMOTE_SERVICES TMAX_INSERT2
1.10. Java 게이트웨이
1.10.1. Async Java 게이트웨이
-
Async Java 게이트웨이
Tmax 클라이언트 혹은 서버가 자바로 이루어진 애플리케이션 서비스를 CPC 제약 없이 호출할 수 있도록 Async Java 게이트웨이를 제공한다.
-
WebTASync 라이브러리
외부에서 Tmax의 서비스 요청을 asyncronous하게 할 수 있는 WebTASync 라이브러리를 제공한다.
WebTASync 라이브러리에 대한 자세한 사항은 Tmax 안내서 중에 Tmax WebTAsync User Guide를 참고한다. |
1.11. JTC(Jeus-Tuxedo Connector)
1.11.1. JTC 대화형 통신 기능 추가
추가된 API
API의 사용 방법은 WebT의 대화형 통신과 동일하다. 자세한 사항은 WebT 대화형 통신을 참고한다.
-
tpconnect
TuxCallDescripter tpconnect(boolean recvNext) TuxCallDescripter tpconnect(TuxBuffer sndbuf, boolean recvNext) TuxCallDescripter tpconnect(WebtAttribute attr, boolean recvNext) TuxCallDescripter tpconnect(TuxBuffer sndbuf, WebtAttribute attr, boolean recvNext)
-
tpsend
void tpsend(TuxCallDescripter cd, TuxBuffer tx, boolean recvNext) void tpsend(TuxCallDescripter cd,TuxBuffer tx, WebtAttribute attr, boolean recvNext)
-
tprecv
TuxBuffer tprecv(TuxCallDescripter cd) throws WebtIOException, WebtServiceException, WebtDialogueException TuxBuffer tprecv(TuxCallDescripter cd,WebtAttribute attr) boolean isSendNext() boolean isRecvNext() void tpdiscon(TuxCallDescripter cd) throws WebtIOException, WebtServiceException
webt.properties 설정
log.level=debug log.dir=D:\\tmax log.file=jtc.log defaultCharset=euc-kr tux.remote.name.list=TUXGW1 tux.local.name=TUXGW2 tux.buffer.size=4096 tux.default.timeout=1000 tux.default.txtimeout=30 tux.default.readtimeout=0 tux.TUXGW1.addr=192.168.1.43 tux.TUXGW1.port=9111 tux.TUXGW1.interval=1 tux.TUXGW1.svc=TOUPPER_CONV
예제
import java.io.*; import javax.ejb.*; import javax.naming.*; import java.sql.*; import javax.sql.*; import tmax.jtc.TuxService; import tmax.jtc.TuxServiceException; import tmax.jtc.TuxServiceFailException; import tmax.jtc.io.TuxBuffer; import tmax.jtc.io.TuxFieldBuffer; import tmax.webt.WebtBuffer; import tmax.webt.WebtConnection; import tmax.webt.WebtRemoteService; import tmax.webt.jeus.WebtDataSource; import java.rmi.*; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.servlet.*; import javax.servlet.http.*; import javax.transaction.HeuristicMixedException; import javax.transaction.HeuristicRollbackException; import javax.transaction.NotSupportedException; import javax.transaction.RollbackException; import javax.transaction.SystemException; import javax.transaction.UserTransaction; import tmax.jtc.*; import tmax.jtc.io.*; import tmax.webt.*; public class JtcTest2 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("<html>"); out.println("<head>"); String title = "Request Information APIs"; out.println("<title>" + title + "</title>"); out.println("</head>"); out.println("<body bgcolor=\"white\">"); Context initial; UserTransaction ut = null; TuxService service = new TuxService("TMXDOM","TOUPPER_CONVN"); TuxBuffer sndbuf = service.createStringBuffer(); sndbuf.setString("conversation service"); try{ TuxCallDescripter cd = service.tpconnect(false); if(cd == null) { out.println("tpconnect failed!"); } else { for(int i=0 ; i<10 ; i++) service.tpsend(cd, sndbuf, false); service.tpsend(cd, sndbuf, true); WebtAttribute attr = new WebtAttribute(); attr.setTPNOTIME(true); while(service.isRecvNext()) { TuxBuffer rcvbuf = service.tprecv(cd,attr); out.println(" : " + rcvbuf.getString()); } /* 반복부분 */ if( service.isSendNext()) service.tpsend(cd, sndbuf, true); if( service.isRecvNext()) { TuxBuffer rcvbuf = service.tprecv(cd,attr); out.println(" : " + rcvbuf.getString()); } } catch (WebtException wie) { wie.printStackTrace(System.out); Throwable t = wie.getRootCause(); if (t != null) t.printStackTrace(System.out); System.exit(1); } finally { connection.close(); } } }
1.11.2. JTC Async Listener 구조 기능 추가
tpacall, xa_prepare, xa_rollback에 대한 응답을 콜백 인터페이스의 메소드에서 수신하도록 하는 기능이 추가되었다.
추가된 클래스
-
class TuxAsyncService ( 생성자는 TuxService와 동일 ) public TuxCallDescripter tpacall(TuxBuffer input, WebtAttribute attr, TuxAsyncMsgListener listener) throws WebtException TuxAsyncXAResource getXAResource()
-
class TuxAsyncXAResource int prepare(Xid xid, TuxAsyncMsgListener listener) throws XAException void commit(Xid xid, TuxAsyncMsgListener listener) void rollback(Xid xid, TuxAsyncMsgListener listener)
추가된 인터페이스
-
interface TuxAsyncMsgListener
void handleEvent(TuxBuffer rcvBuffer) void handleError(Exception e)
환경설정
<webt.properties>
log.level=debug log.dir=D:\\tmax log.file=jtc.log defaultCharset=euc-kr tux.remote.name.list=TUXGW1 tux.local.name=TUXGW2 tux.buffer.size=4096 tux.default.timeout=1000 tux.default.txtimeout=30 tux.default.readtimeout=0 tux.TUXGW1.addr=192.168.1.43 tux.TUXGW1.port=9111 tux.TUXGW1.interval=1 tux.TUXGW1.svc=TOUPPER_CONV
<Tuxedo 환경설정>
*RESOURCES IPCKEY 75350 DOMAINID simpapp2 MASTER simple MAXACLGROUPS 50 MAXACCESS*RESOURCES IPCKEY 75350 DOMAINID simpapp2 MASTER simple MAXACLGROUPS 50 MAXACCESSERS 1000 MAXSERVERS 20 MAXGROUPS 100 MAXSERVICES 40 MODEL SHM *MACHINES DEFAULT: TUXDIR="/openframe/phk6254/bea/tuxedo9.1" APPDIR="/openframe/phk6254/bea/tuxedo9.1/samples/atmi/tmax50" TUXCONFIG="/openframe/phk6254/bea/tuxedo9.1/samples/atmi/tmax50/tuxconfig" ULOGPFX="/openframe/phk6254/bea/tuxedo9.1/samples/atmi/tmax50/ulog" TLOGDEVICE="/openframe/phk6254/bea/tuxedo9.1/log/tlog/TLOG" TLOGNAME=TLOG tmaxi4 LMID=simple *GROUPS GROUP1 LMID=simple GRPNO=1 OPENINFO=NONE GROUP2 LMID=simple GRPNO=2 OPENINFO=NONE DOM_XA LMID=simple GRPNO=3 TMSNAME=tms_ora OPENINFO="Oracle_XA:Oracle_XA+Acc=P/scott/tiger+SesTm=60+DbgFl=0xff+ LogDir=/openframe/phk6254/bea/tuxedo9.1/samples/atmi/" *SERVERS DEFAULT: CLOPT="-A -t -r -o svr.out -e svr.out" DMADM SRVGRP=GROUP1 SRVID=1 GWADM SRVGRP=GROUP2 SRVID=1 GWTDOMAIN SRVGRP=GROUP2 SRVID=2 CLOPT="-A -t -o svr.out" txsvr SRVGRP=DOM_XA SRVID=1 *SERVICES TUX_INSERT
<Tuxedo 도메인 환경설정>
*DM_LOCAL_DOMAINS TUXGW1 GWGRP=GROUP2 TYPE=TDOMAIN DOMAINID="TUXGW1" BLOCKTIME=20 CONNECTION_POLICY=INCOMING_ONLY #CONNECTION_POLICY=ON_STARTUP DMTLOGDEV="/openframe/phk6254/bea/tuxedo9.1/samples/atmi/dgw/DMTLOG" AUDITLOG="/openframe/phk6254/bea/tuxedo9.1/samples/atmi/dgw/AUDITLOG" DMTLOGNAME="DMTLOG_TUXGW1" *DM_REMOTE_DOMAINS TUXGW2 TYPE=TDOMAIN DOMAINID="TUXGW2" *DM_TDOMAIN TUXGW1 NWADDR="//192.168.1.35:9111" TUXGW2 NWADDR="//192.168.15.241:9888" *DM_LOCAL_SERVICES TUX_INSERT *DM_REMOTE_SERVICES
예제
tuxedo server
<txsvr.pc>
#include <stdio.h> #include <atmi.h> /* TUXEDO Header File */ #include <userlog.h> /* TUXEDO Header File */ #include <tx.h> #include <fml32.h> #include "Jtcfld.fld.h" #define succ_str "Insert Success" #define fail_str "Insert Failure" EXEC SQL include SQLCA.H; EXEC SQL begin declare section; int h_empno; char h_ename[11]; char h_job[10]; EXEC SQL end declare section; void TUX_INSERT(rqst) TPSVCINFO *rqst; { FBFR32 *sndbuf; char msgbuf[30]; FLDLEN32 flen; sndbuf = (FBFR32 *)rqst->data; h_empno = 0; memset( h_ename, 0x00, sizeof ( h_ename ) ); memset( h_job, 0x00, sizeof ( h_job ) ); Fprint32(sndbuf); h_empno = 9999; Fget32(sndbuf, ENAME, 0, (char *)h_ename, &flen); Fget32(sndbuf, JOB, 0, (char *)h_job, &flen); printf("%s",h_ename); EXEC SQL INSERT INTO emp( empno, ename, job ) VALUES ( :h_empno, :h_ename, :h_job ); if ( sqlca.sqlcode != 0 ){ printf( "insert failed sqlcode = %d\n",sqlca.sqlcode ); strcpy(msgbuf, fail_str); Fchg32(sndbuf, OUTPUT_STR, 0, msgbuf, 0); tpreturn( TPFAIL, -1, (char *)sndbuf, 0, 0 ); } strcpy(msgbuf, succ_str); printf( "insert success\n"); Fchg32(sndbuf, OUTPUT_STR, 0, msgbuf, 0); Fprint32(sndbuf); tpreturn(TPSUCCESS, 0, rqst->data, strlen(rqst->data), 0); }
JTC 클라이언트
<TuxAsyncXA.java>
package com.tmax.tuxedo.async; import javax.transaction.xa.XAException; import javax.transaction.xa.Xid; import tmax.jtc.TuxAsyncMsgListener; import tmax.jtc.TuxAsyncService; import tmax.jtc.TuxAsyncXAResource; import tmax.jtc.external.TuxBootstrapper; import tmax.jtc.io.TuxFieldBuffer; import com.tmax.handler.TuxedoMsgListener; import com.tmax.util.Jtcfld; import com.tmax.util.XAUtil; public class TuxAsyncXA { public static void main(String[] args) { TuxBootstrapper boot = new TuxBootstrapper(); boot.init("resource\\webt.properties"); Xid xid = XAUtil.getUniqueXid(); TuxAsyncService service = new TuxAsyncService("TUXGW1", "TUX_INSERT"); try { service.getXAResource().start(xid, TuxAsyncXAResource.TMSUSPEND); } catch (XAException e) { e.printStackTrace(); } try { TuxFieldBuffer sndbuf = new TuxFieldBuffer(true); int empno = 8080; String ename = "phk6254"; String job = "tmaxqmc"; sndbuf.createField(Jtcfld.EMPNO).add(empno); sndbuf.createField(Jtcfld.ENAME).add(ename); sndbuf.createField(Jtcfld.JOB).add(job); TuxAsyncMsgListener listener = new TuxedoMsgListener(service, xid); service.tpacall(sndbuf, null, listener); Thread.sleep(2000); } catch (Exception e) { e.printStackTrace(); } } }
<TuxPrepareMsgListener.java>
package com.tmax.handler; import javax.transaction.xa.XAException; import javax.transaction.xa.Xid; import tmax.jtc.TuxAsyncMsgListener; import tmax.jtc.TuxAsyncService; import tmax.jtc.io.TuxBuffer; import tmax.webt.WebtException; public class TuxPrepareMsgListener implements TuxAsyncMsgListener { private TuxAsyncService service; private Xid xid; public TuxPrepareMsgListener(TuxAsyncService service, Xid xid) { this.service = service; this.xid = xid; } public void handleError(Exception e) { System.out.println("prepare faile > " + xid); } public void handleEvent(TuxBuffer rcvBuffer) { System.out.println("prepare success > " + xid); try { service.getXAResource().commit(xid, new TuxCommitMsgListener(service, xid)); } catch (XAException e) { e.printStackTrace(); } } }
<TuxPrepareMsgListener.java>
package com.tmax.handler; package com.tmax.handler; import javax.transaction.xa.Xid; import tmax.jtc.TuxAsyncMsgListener; import tmax.jtc.TuxAsyncService; import tmax.jtc.io.TuxBuffer; public class TuxCommitMsgListener implements TuxAsyncMsgListener { private TuxAsyncService service; private Xid xid; public TuxCommitMsgListener(TuxAsyncService service, Xid xid) { this.service = service; this.xid = xid; } public void handleError(Exception e) { System.out.println("fail commit > " + xid); } public void handleEvent(TuxBuffer rcvBuffer) { System.out.println("commit success > " + xid); System.out.println(Thread.currentThread().getName() + " ] rcv "+ rcvBuffer.toString()); } }
<TuxCommitMsgListener.java>
package com.tmax.handler; import javax.transaction.xa.Xid; import tmax.jtc.TuxAsyncMsgListener; import tmax.jtc.TuxAsyncService; import tmax.jtc.io.TuxBuffer; public class TuxCommitMsgListener implements TuxAsyncMsgListener { private TuxAsyncService service; private Xid xid; public TuxCommitMsgListener(TuxAsyncService service, Xid xid) { this.service = service; this.xid = xid; } public void handleError(Exception e) { System.out.println("fail commit > " + xid); } public void handleEvent(TuxBuffer rcvBuffer) { System.out.println("commit success > " + xid); System.out.println(Thread.currentThread().getName() + " ] rcv "+ rcvBuffer.toString()); } }
1.12. 웹 서비스 게이트웨이
웹서비스 게이트웨이 관련 내용은 Tmax 안내서 중에 Tmax Gateway Guide(WebService)를 참고한다.
1.13. TCP/IP 게이트웨이
1.13.1. 콜백 함수
set_error_msg
TCP/IP 게이트웨이를 통한 리모트 노드와의 거래 도중 타임아웃 혹은 네트워크 단절 등으로 에러가 발생한 경우 자동으로 호출되는 함수이다. 해당 함수에서 사용자는 사용자 헤더 또는 사용자 데이터를 수정할 수 있다.
-
프로토타입
#include "custom.h" int set_error_msg(msg_header_t *hp, int err, char *data, int len)
-
반환값
반환값 설명 양수
반환한 길이 만큼의 데이터를 전송한 경우이다. 단 이 경우 CLOPT="-I" 옵션을 설정해야만 적용된다.
0
사용자 헤더 부분까지만 전송한 경우이다.
음수
해당 메세지를 CLH로 전송하지 않은 경우이다.
-
예제
int set_error_msg(msg_header_t *hp, int err, char *data, int len) { msg_body_t * body; body = (msg_body_t *)data; strcpy(body->data, "changed hello data"); /* 에러 메시지에는 데이터가 포함되지 않으므로 데이터 부분까지 전달되기 위해서는 -I 옵션을 사용해야 한다. 사용하지 않을 경우 사용자 헤더까지만 CLH로 전달된다. */ /* 사용자헤더가 없는 경우에 hp와 data의 값이 같을 수 있다. */ strcpy(hp->retsvcname, "RECVSVC_CHANGE"); return len; }
set_error_msg() 함수가 호출되는 에러는 다음과 같다.
에러 코드 | 설명 |
---|---|
[TPECLOSE] |
리모트 노드로 전송 후 응답을 받기 전에 끊어짐 |
[TPETIME] |
리모트 노드로 전송 후 타임 아웃 발생 |
[TPEPROTO] |
ASYNC 모드에서 tpforward API 를 사용하는 경우 외 |
[TPENOENT] |
클라이언트 모드 / 연결을 맺을 경우 OUT 채널 수가 부족한 경우이다. |
[TPENOREADY] |
클라이언트 모드 / 연결을 맺을 경우 리모트 노드가 비정상적인 동작을 한 경우이다. |
[TPEOS] |
메모리 할당 등의 오류이다. |
[TPESYSTEM] |
내부적 오류이다. |
[TPESVCERR] |
put_msg_info에서 0 또는 음수를 반환한 경우이다. |
1.14. 유틸리티
1.14.1. CFL
공유 메모리 우선 체크 기능 비활성 옵션
CFL을 수행할 때, 환경 파일 DOMAIN 절의 SHMKEY 항목에 설정해 준 값이 현재 사용 중이라면 해당 값의 UID를 비교하며, 다르면 다음 에러가 발생한다.
(E) CFL0096 shared memory : different owner 210 [COM3402]: File exists
이 기능을 사용하고 싶지 않을 경우, 다음과 같이 CFL을 수행할 때 -I 옵션을 추가한다.
$ cfl –i sample.m –I
-I 옵션을 추가하면 CFL을 수행할 때 SHMKEY에 설정한 값의 사용 여부 및 UID 동일 여부를 체크하지 않는다.
사용 가능 FD 값 선 체크 기능 옵션
CFL 수행 단계에서, ulimit –n으로 조회할 때 출력되는 현재 시스템의 사용 가능 FD 최대 수를 미리 체크하여 사용자에게 알려준다. CLH 하나 당 열 수 있는 최대 FD 개수를 미리 계산하여 체크한다. Tmax 시스템에서 사용하는 FD 값이 시스템에서 사용 가능한 FD보다 더 크게 설정이 되었다면 다음와 같은 에러가 발생한다.
(E) CFL9990 Current Tmax configuration contains more servers or nodes than current system can support[CFL5056]
이 기능을 사용하기 위해서는 CFL을 수행할 때, 다음과 같이 -r 옵션을 추가한다.
$ cfl –i sample.m -r
-r 옵션을 추가하면 CFL 단계에서 사용 가능 FD를 미리 체크하며, 초과 설정하면 에러가 발생한다.
1.14.2. tmboot의 옵션 보완 및 추가
현 버전에서는 기존의 tmboot –w 옵션을 보완하여 서버 프로세스들을 하나씩 기동시킬 경우 좀 더 효율적으로 동작하도록 하였다.
조건
-
서버 프로세스가 TMM에 접속할 때 LOCK 사용 조건
(LOCK | NOLOCK)
-
서버 프로세스를 기동할 때 WAIT 조건
(NO-WAIT | FINITE-WAIT)
–d 옵션
-
-d val < 0 : LOCK, |VAL| FINITE-WAIT
-
-d val = 0 : NO-LOCK, NO-WAIT
-
-d val > 0 : NO-LOCK, |VAL| FINITE-WAIT
-
기본적으로 –d 옵션의 val이 0이 아닌 경우, 절대값(|VAL|)을 사용하며 단위는 usec이다.
-
FINITE-WAIT일 때 |VAL|값은 각 프로세스마다 최대의 WAIT 시간이다. (전체 프로세스의 총 WAIT 시간이 아님)
-
서버 프로세스가 signal을 줄 경우 WAIT은 해제된다. |VAL| 시간이 되지 않더라도 signal을 받으면 다음 프로세스의 기동을 시도한다.
-
val이 음수인 경우, LOCK을 사용한다.
-
이 옵션이 사용되었을 경우, -w 옵션은 무시된다.
-
–w 옵션
-
-d -1000000(1sec)와 동일한 효과를 가진다.
-
-d 옵션이 없는 경우에만 의미가 있다. –d 옵션이 사용되면 –w 옵션은 무시된다.
–D 옵션
-d 옵션과 거의 유사하지만 FINITE-WAIT일 경우 signal이 오더라도 |VAL|까지는 무조건 WAIT하게 된다.
1.14.3. tmboot –S 옵션의 환경 파일 참조 경로 변경
기존 동작 방식
tmboot 명령어로 Tmax를 기동할 경우 바이너리 환경 파일 $TMAXDIR/config/tmconfig를 $TMAXDIR/path/tmconfig에 복사한 후 $TMAXDIR/path 디렉터리에 있는 환경 파일을 사용하게 된다.
하지만 이미 Tmax 엔진이 기동되어 있는 상황에서 tmboot –S 또는 –s를 이용하여 특정 서버만을 기동하고자 할 경우에는, 기존 버전에서는 $TMAXDIR/config/tmconfig를 참조하여 해당 서버를 기동하게 된다.
이와 같은 동작은 다음과 같은 환경에서 문제가 될 수 있다.
-
환경 파일
<기존 운영 환경 파일>
*SVRGROUP svg1 NODENAME = "tmaxh4" *SERVER svr1 SVGNAME = svg1 svr2 SVGNAME = svg1 *SERVICE TOUPPER1 SVRNAME = svr1 TOUPPER2 SVRNAME = svr2
<운영 도중 변경된 환경 파일>
*SVRGROUP svg1 NODENAME = "tmaxh4" *SERVER svr1 SVGNAME = svg1 svr3 SVGNAME = svg1 svr2 SVGNAME = svg1 *SERVICE TOUPPER1 SVRNAME = svr1 TOUPPER3 SVRNAME = svr3 TOUPPER2 SVRNAME = svr2
-
재컴파일
운영 도중 변경된 환경 파일을 CFL 로 재컴파일한다.
$ cfl –i node1.m
-
추가된 서버 기동
새로 추가된 서버를 다음과 같이 기동한다.
$ tmboot –S svr3
운영 중인 환경에서 환경 파일 변경 후 tmboot –S를 시도하면 다음과 같은 에러가 발생한다.
(E) BOOT3007 maxsvr (1) is over for svr(svr3:svr2): nodeno = 0, svri = 5, cur = 1, ksvr = 1 [BOOT0015]
운영 환경에서 CFL을 하였을 경우, $TMAXDIR/config/tmconfig는 변경된 내용이 적용되지만 실제 공유 메모리에는 변경되기 이전인 $TMAXDIR/path/tmconfig와 동일하게 구성되어 있다. 따라서 tmboot –S로 새로 추가한 서버 프로세스를 기동하면 실제로는 기존에 이미 떠 있는 서버 프로세스를 추가로 기동하는 결과를 가져온다. Tmax 엔진이 기동되어 있는 상황에서의 CFL은 허용되지 않지만 이 실수로 인한 에러가 발생하면 해당 에러는 디버깅을 어렵게 만든다.
현재 동작 방식
Tmax 엔진 동작 중 tmboot –S, -s –g, -q, -t, -A 등의 옵션으로 각 서버를 기동시킬 때 참조하는 tmconfig의 경로를 $TMAXDIR/config/tmconfig가 아닌 $TMAXDIR/path/tmconfig로 수정하였다. (단, 엔진을 기동할 때는 기존의 방법을 그대로 유지)
주의 사항
tmboot -f 옵션을 사용하여 특정 바이너리 환경 파일을 지정하게 되면 기존과 동일하게 동작한다. 즉 $TMAXDIR/config/tmconfig를 참조하여 서버를 기동하게 된다. 현재 변경된 동작 방식이 서버를 동적으로 추가할 때 문제를 일으키기 때문이다.
동적으로 서버를 추가할 때에는 반드시 –f 옵션으로 특정 환경 파일을 지정해 주어 $TMAXDIR/config/의 변경된 바이너리 환경 파일을 참조하도록 한다.
1.14.4. racd의 umask 옵션 추가
racd를 통해 기동되는 process의 경우 umask가 기본적으로 0으로 설정되어 원하지 않는 권한(permission)의 파일이 생성되는 문제점을 보완하여 사용자가 권한을 설정할 수 있도록 하였다.
설정 방법
racd에 '-P umask' 옵션을 지정하여 원하는 umask를 설정한다.
$ racd -P umask
'-P umask' 옵션은 racd를 통해 기동되는 프로세스의 경우 umask를 사용자가 설정하여 원하는 권한의 파일을 생성할 수 있도록 한다.
예제
<환경 파일>
*DOMAIN tmax1 SHMKEY =@SHMEMKY@, MINCLH=1, MAXCLH=3, TPORTNO=@TPORTNO@, BLOCKTIME=30, RACPORT = 3255 *NODE @HOSTNAME@ TMAXDIR = "@TMAXDIR@", APPDIR = "@TMAXDIR@/appbin", PATHDIR = "@TMAXDIR@/path", @RMTNAME@ TMAXDIR = "@RMTDIR@", APPDIR = "@RMTDIR@/appbin", PATHDIR = "@RMTDIR@/path", *SVRGROUP svg1 NODENAME = "@HOSTNAME@", COUSIN="svg2" svg2 NODENAME = "@RMTNAME@" *SERVER svr2 SVGNAME = svg1, CLOPT="-o $(SVR).out -e $(SVR).err" *SERVICE TOUPPER SVRNAME = svr2
RMT 노드에 racd 기동한다.
$ export TMAX_RAC_PORT=3255 $ racd –k –P 055
HOST 노드에서 전체 Tmax 기동(tmboot)하고, RMT 노드의 ULOGDIR에 svr2.out의 파일 권한을 확인한다.
1.14.5. 라이브러리 버전 정보 조회 기능
tmaxlibver
UNIX용 라이브러리를 사용하는 경우, 해당 라이브러리만으로 Tmax의 버전을 알 수 없다. 5.0 SP1에서는 tmaxlibver 유틸리티를 통해 Tmax 라이브러리의 버전을 확인할 수 있다.
-
사용법
$ tmaxlibver [-l filename] [-d | -s] [-6] [-L directory] [-o arg] [-h]
항목 설명 -l filename
조회하기 위한 라이브러리의 이름을 지정한다.
-d | -s
라이브러리의 dynamic 또는 static의 여부를 설정한다.
-6
라이브러리가 64bit일 경우 설정한다. 설정하지 않으면 32bit로 동작한다. -6이 설정되고 –L 옵션이 설정되지 않은 경우 $TMAXDIR/lib64 디렉터리에 위치한 라이브러리를 자동 참조한다.
-L directory
조회하기 위한 라이브러리가 위치하는 절대경로 또는 상대경로를 입력한다. 지정하지 않은 경우 -6 옵션에 따라 기본 경로가 달라지게 된다.
-h arg
명령어 도움말 옵션이다.
-
지원 라이브러리
libcli, libclithr, libsvr, libsvrucs에 대한 버전 정보 조회 기능을 사용할 수 있다.
-
예제
$ tmaxlibver -l libsvr.a –s -6 libsvr.a for TMAX Version 5.0 SP #1 64bit binary for AIX 5L
1.15. 클라이언트 / 서버
1.15.1. tpgetsvcname
서비스 인덱스로부터 서비스명을 가져오는 함수이며, 응답 메시지가 폐기될 때 호출되는 Loss Service에서 TPSVCINFO의 cltid.clientdata[3]의 서비스 인덱스값을 파라미터로 사용한다.
tpgetsvcname() 함수는 Tmax 서버에서만 사용할 수 있으며, Tmax 클라이언트에서는 사용할 수 없다. 그리고 반환되는 버퍼는 내부 Static 버퍼이므로 버퍼의 내용을 직접 변경하지 말고 별도의 버퍼에 복사하여 사용하는 것이 바람직하다.
-
프로토타입
#include <tmaxapi.h> char* tpgetsvcname(int svc_index)
-
파라미터
파라미터 설명 svc_index
서비스의 인덱스값이다.
-
반환값
반환값 설명 버퍼의 주소값
성공한 경우 서비스명이 저장된 버퍼의 주소값이 반환된다.
NULL
실패한 경우 NULL이 반환된다.
(tperrno에 에러 상황에 해당하는 값이 설정된다.)
-
예제
#include <stdio.h> #include <usrinc/atmi.h> #include <usrinc/tmaxapi.h> SVC01(TPSVCINFO *msg) { int i; printf("\nSVC01 service is started!\n"); printf("INPUT : data=%s\n", msg->data); for (i = 0; i < msg->len; i++){ msg->data[i] = toupper(msg->data[i]); printf("OUTPUT: data=%s\n", msg->data); } sleep(10); tpreturn(TPSUCCESS,0,(char *)msg->data, 0,0); } LOSS_SVC(TPSVCINFO *msg) { long svcindex; char *svcname; printf("\nLOSS_SVC service is started!\n"); printf("INPUT : data = %s\n", msg->data); printf("TPERROR : %d\n", msg->cltid.clientdata[1]); printf("TPURCODE : %d\n", msg->cltid.clientdata[2]); svcindex = msg->cltid.clientdata[3]; printf("SVC INDEX Of Discarded Message : %ld\n", svcindex); svcname = tpgetsvcname((int)svcindex); if(NULL == svcname) { printf("tpgetsvcname is failed!!\n"); } else { printf("SVC Name Of Discarded Message : %s\n", svcname); } tpreturn(TPSUCCESS, 0, (char*)msg->data, 0, 0); }
1.15.2. tptsleep
TMM으로부터 서버 프로세스 종료 이벤트를 대기하는 함수이다. tpprechk() 콜백 함수에서 대기해야 하는 경우에, 주기적으로 tptsleep()을 호출하여 정상적인 tmdown이 수행될 수 있도록 한다. 타임아웃은 select() 시스템 함수와 동일하게 적용된다.
-
프로토타입
#include <usrinc/tmaxapi.h> int tptsleep(struct timeval *timeout)
-
예제
int tpprechk(void) { struct timeval timeout; int ret; timeout.tv_sec = 5; timeout.tv_usec = 0; while(1) { ret = tptsleep(&timeout); ... return 0; }
1.15.3. tpmcallx
tpmcallx()는 기존 tpmcall()의 확장 기능 제공을 목적으로 함수이다. 기존과 달리 COUSIN 서버 그룹의 서비스로부터 모두 응답을 받을 때까지 기다린다. 또한 응답 성공 여부가 저장되는 svglist 구조체에 r_list가 추가되었으며 flags에 몇 가지 항목이 추가되었다.
-
프로토타입
#include <usrinc/tmaxapi.h> struct svglistx* tpmcallx(char *svc, char *data, long len, long flags)
-
파라미터
flags에 사용 가능한 값은 다음과 같다.
flags 설명 TPNOREPLY
송신만 수행한다. 이 값을 설정하면 기존의 tpmcall과 유사하게 동작한다.
TPBLOCK
송신이 CLH까지 성공적으로 전달되었는지 확인한다.
TPNOTIME
수신할 때 블록 타임값을 무시하고 무한으로 대기한다.
-
반환값
반환값 설명 서버 그룹 리스트
성공한 경우 서비스 호출에 성공한 서버 그룹 리스트를 svglistx 구조체에 설정하여 반환한다.
NULL
실패한 경우 NULL이 반환된다.
(tperrno에 에러 상황에 해당하는 값이 설정된다.)
svglistx 구조체는 다음과 같다.
struct svglistx { int ns_entry; /* number of entries of s_list */ int nf_entry; /* number of entries of f_list */ int nr_entry; /* number of entries of r_list */ int *s_list; /* list of server group numbers */ int *f_list; /* list of tperrno of each server group */ int *r_list; /* list of tpurcode of each server group */ };
변수 설명 ns_entry
tpmcall()에 성공한 서버 그룹의 수이다.
nf_entry
tpmcall()에 실패한 서버 그룹의 수이다.
nr_entry
r_list가 설정된 서버 그룹의 수이다.
*s_list
tpmcall()에 성공한 서버 그룹 일련번호의 배열이다.
*f_list
tpmcall()에 실패한 서버 그룹 일련번호의 배열이다.
*r_list
tpurcode가 설정된 서버 그룹 일련번호의 배열이다.
-
오류
tpmcall()을 참고한다.
-
예제
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <usrinc/atmi.h> #include <usrinc/tmaxapi.h> main(int argc, char *argv[]) { char *sndbuf, *rcvbuf; long rcvlen, sndlen; struct svglistx svglist; struct svglistx *psvglist; int ret, i; psvglist = &svglist; if (argc != 2) { printf("Usage: toupper string\n"); exit(1); } if ( (ret = tmaxreadenv( "tmax.env","TMAX" )) == -1 ){ printf( "tmax read env failed\n" ); exit(1); } if (tpstart((TPSTART_T *)NULL) == -1){ printf("tpstart failed[%s]\n", tpstrerror(tperrno)); exit(1); } if ((sndbuf = (char *)tpalloc("STRING", NULL, 0)) == NULL){ printf("sendbuf alloc failed !\n"); tpend(); exit(1); } if ((rcvbuf = (char *)tpalloc("STRING", NULL, 0)) == NULL){ printf("recvbuf alloc failed !\n"); tpfree((char *)sndbuf); tpend(); exit(1); } strcpy(sndbuf, argv[1]); psvglist = tpmcallx("TOUPPER", sndbuf, 0, TPBLOCK); if(psvglist == NULL){ printf("tpmcall is failed[%s]\n", tpstrerror(tperrno)); tpfree((char *)sndbuf); tpfree((char *)rcvbuf); tpend(); exit(1); } printf("send data: %s\n", sndbuf); printf("ns_entry = %d\n", psvglist->ns_entry); printf("nf_entry = %d\n", psvglist->nf_entry); printf("nr_entry = %d\n", psvglist->nr_entry); for(i=0; i<psvglist->ns_entry; i++) printf("psvglist->s_list[%d] = %d\n", i, psvglist->s_list[i]); for(i=0; i<psvglist->nf_entry; i++) printf("psvglist->f_list[%d] = %d\n", i, psvglist->f_list[i]); for(i=0; i<psvglist->nr_entry; i++) printf("psvglist->r_list[%d] = %d\n", i, psvglist->r_list[i]); tpfree((char *)sndbuf); tpfree((char *)rcvbuf); tpend(); }
1.15.4. 서버 CLOPT 항목의 –B 옵션 추가
Tmax 4.0 SP#3 Fix#2에서 다음의 기능이 추가되었다.
-
멀티 CLH 환경에서 하나의 서버 프로세스에 요청이 동시에 스케줄 되는 경우, CLH 큐 타임아웃 적용이 안되어 처리가 지연되는 문제를 개선하였다.
-
배치성 업무의 경우에는 예외가 필요하며, 서버 절의 CLOPT 항목에 –B 옵션을 적용한 경우 해당 서버 프로세스에 스케줄링 된 경우는 예외적으로 큐 타임아웃을 무시하고 수행된다.
환경설정
다음과 같이 NODE 절의 CLOPT 항목에 -B 옵션을 적용한다.
*SERVER SVRNAME [CLOPT = "-B"]
-B 옵션을 적용하면 하나의 서버 프로세스에 요청이 동시에 스케줄되는 경우, CLH 큐 타임아웃을 적용시키지 않고 처리된다.
1.15.5. 서버 CLOPT 항목의 –X 옵션 추가
오라클에서 이미 롤백된 트랜잭션에 대해서 쿼리가 수행될 경우, 처음에는 ORA-24761 에러를 발생 시키지만, 사용자가 이것을 무시하고 계속해서 다음 쿼리를 수행할 경우, 로컬 트랜잭션으로 처리되면서 정합성에 문제가 발생할 수 있다. 이 경우는 사용자 코드의 문제이지만, 이것을 막기 위해서 –X 옵션을 사용하면 xa_end() 의 결과가 XA_OK가 아닌 경우, 서버 프로세스를 재시작시켜 정합성 문제를 해소할 수 있다.
환경설정
다음과 같이 NODE 절의 CLOPT 항목에 –X 옵션을 적용한다.
*SERVER SVRNAME [CLOPT = "-X"]
XA 서버의 경우, tpreturn() 내에서 xa_end()가 실패하면 기본적으로 XA 채널의 RESET을 수행한다. -X 옵션을 적용할 경우 xa_end()가 실패하면 Fatal 에러 메시지(서비스 코드 : CSC5608)를 출력한 후 서버 프로세스를 종료시킨다.
1.15.6. 표준출력 파일의 날짜가 변경되어도 닫히는 옵션 추가
서버가 수행되고 있는 동안 날짜가 변경되었을 때, 서버동작의 userlog로 인한 표준출력 파일이 삭제 명령을 수행하여도 지워지지 않는 동작을 수정하였다. 즉 로깅파일이 날짜가 변경되면 닫히지 않던점을 -c 옵션을 통해 날짜변경을 체크하여 삭제할 때 정상적으로 삭제가 수행된다.
환경설정
다음과 같이 -c 옵션을 적용한다.
*SVRGROUP svg1 NODENAME = "tmaxi4" *SERVER DEFAULT: MIN = 10, MAX = 30, ASQCOUNT = 1, MAXQCOUNT = 1000, MAXRSTART = 10000, LIFESPAN = IDLE_1800, CLOPT = "-o $(SVR)_$(CDATE).out -e $(SVR)_$(CDATE).out -c" svr2userlog1 SVGNAME = svg1
1.15.7. RDP 서버의 -q 옵션 제한 없음(-1) 기능 추가
tpflush()를 할 경우, WRITE 큐에 전송되지 않은 데이터가 있더라도 TPSENDTOCLI 큐에서 WRITE 큐로 데이터를 무조건 이동할 수 있는 옵션 [-q -1 ]을 추가하였다.
realmt SVGNAME = svg1, SVRTYPE = REALSVR_MT, CPC = 13, CLOPT="-o /home/tmax/realmt.log -q -1"
이 옵션은 RDPMT에서만 적용되며, 메모리 증가에 제한이 없어지므로 권장 사항은 아니며 테스트 용도로만 사용해야 한다.
1.15.8. 클라이언트 에러 로그 파일 로깅
환경 변수 또는 클라이언트 환경 파일(tmax.env)에 로그 파일 경로를 설정한다. 해당 환경 파일 설정 시 파일명 뒤에 해당 클라이언트의 PID(Process ID)가 자동으로 붙는다.(파일명.pid)
환경설정
<.profile에 설정할 경우>
TMAX_DEBUG=directory/filename
<tmax.env에 설정할 경우>
TMAX_DEBUG=directory/filename
예제
<tmax.env>
TMAX_DEBUG=/data1/tmaxqa/tmax/work/client/cli00
Tmax 클라이언트가 tpstart를 할 때 메인 노드 접속에 실패하였을 경우 /data1/tmaxqa/tmax/work/client/cli00.18176이라는 이름의 파일이 생성되며 다음과 같은 메시지가 파일 안에 저장된다.
(E) CLI3003 unable to connect to main server : 192.168.1.43 [CLI0106][Connection refused] (E) CLI3003 unable to connect to main server : 192.168.1.48 [CLI0106][Connection refused]
1.15.9. STRING 버퍼의 NULL 종료 문자 보호 기능
tpalloc / tprealloc API를 이용하여 STRING 타입의 버퍼를 할당하고 할당된 버퍼에 대하여 사용자가 NULL을 위한 마지막 1byte 공간까지 데이터를 입력하고 서비스를 요청하였을 경우 기존에는 TPEINVAL(클라이언트), TPESVCERR (서버) 에러가 발생하였다. 현 버전에서는 해당 상황이 발생하면 1byte 크기의 버퍼가 추가로 할당되며 해당 공간에 자동으로 NULL 문자가 설정된다.
환경설정
<.profile 에 설정할 경우>
export TMAX_STRING_NULL=Y
주의사항
-
해당 기능은 tpalloc/tprealloc으로 할당된 버퍼에 한해서만 지원된다.
-
서버와 클라이언트가 동일하게 동작한다.
-
반드시 8.9.2에서 설명한 환경 변수를 Y로 설정해야 한다.
1.16. 환경설정
1.16.1. DUMMY 서버그룹, 서버 설정 추가
도메인 게이트웨이에 COUSIN을 설정할 때, DUMMY 서버그룹, 서버가 사용되는데, 기존의 임시로 서버그룹 이름을 "__dummy"로 사용하는 방식을 개선하였다. (기존의 "__dummy"로 설정된 서버그룹의 서버들은 tmboot를 실행할 때 기동되지 않는다.)
설정 방법
*SVGGROUP DUMMY = N|Y (기본값: N) *SERVER DUMMY = N|Y (기본값: N)
주의사항
SVGGROUP 절의 DUMMY 설정은 SERVER 절에 승계되지 않으므로, SERVER 절에서 각각 지정해야 한다.
예제
*DOMAIN tmax1 SHMKEY = 89255, MINCLH=1, MAXCLH=3, TPORTNO=9255, BLOCKTIME=10000, MAXCPC = 100, MAXGW=10, RACPORT = 3155 *NODE tmaxh4 TMAXDIR = "@TMAXDIR@", APPDIR = "@TMAXDIR@appbin", *SVRGROUP ### tms for Oracle ### dgw_svg NODENAME = "tmaxh4", COUSIN = "gw1, gw2", LOAD = -2,DUMMY=Y *SERVER dummy SVGNAME =dgw_svg , DUMMY=Y *SERVICE TOUPPER SVRNAME = dummy *GATEWAY gw1 GWTYPE = TMAXNONTX, PORTNO = 7500, RGWADDR="192.168.1.43", RGWPORTNO = 6500, NODENAME =tmaxh4 , CPC = 1, LOAD=-2 gw2 GWTYPE = TMAXNONTX, PORTNO = 7510, RGWADDR="192.168.1.48", RGWPORTNO = 6510, NODENAME = tmaxh4, CPC = 1, LOAD=-2
1.18. WebT
1.18.1. WebtField.get()의 동작이 TMAX의 fbget()과 일치하도록 수정
WebtField, WebtFieldSet에서 get()을 한 데이터는 tmax에서 해당 필드 버퍼를 fbprint()했을 때 나타나지 않는다. get()을 한 데이터는 다시 사용하지 않으므로 나타나지 않는 것이 잘못된 동작이라고 할 수 없으나, Tmax의 fbget()과 동작이 일치하지 않는다. 이를 수정하여 Tmax 서비스에서 필드를 fbprint()로 출력하였을 때 get()된 데이터 앞에 ( r )이 표시되어 있는 상태로 출력될 수 있도록 수정하였다.
1.18.2. Webt.properties 방식의 AutoClose 기능을 추가
webt.properties에 jeus.servlet.webt.autoClose.enable=true를 설정 할 경우 서비스 종료 후 닫지 않는 connection을 AutoClose하는 기능을 추가하였다.
2. 변경 기능
2.1. Engine
2.1.1. tmdown -S로 서버 종료할 때 ASQCOUNT 동작 변경
기존의 동작 방식
tmdown –S로 특정 서버의 프로세스를 전체 종료할 경우, 해당 서버가 수행 중(RUN)이라면 해당 서버 프로세스가 수행을 마칠 때까지 대기한 후에 전체 프로세스가 종료하게 된다. ASQCOUNT가 설정된 경우 이 대기 시간 동안 클라이언트의 요청이 들어오게 되면 ASQCOUNT에 의해 서버 프로세스가 자동으로 추가 기동되며 tmdown –S 명령어가 성공적으로 종료되었다 하더라도 추가 기동된 서버 프로세스는 계속 남아 있게 된다. 기존의 방식대로 동작하게 되면 tmdown –S의 결과가 사용자가 의도한 것과 다르게 동작하게 된다.
현재 동작 방식
현 버전에서는 tmdown –S로 특정 서버의 프로세스를 전체 종료할 경우, 해당 서버로의 클라이언트 서비스 요청이 있다 하더라도 ASQCOUNT에 의해서 서버 프로세스가 추가 기동되지 않는다.
주의 사항
tmdown –S로 서버가 종료 대기 중인 상황에서의 클라이언트 서비스 요청은 서버 큐에 큐잉되며, 이는 타임아웃 설정을 통하여 자동 정리될 수 있도록 적절하게 사용자가 조정하여야 한다.
2.1.2. CLH 로그 메시지 개선
다음 예와 같이 CLH2052, CLH2053 에러를 출력할 때 뒤에 서비스명이 출력되도록 개선하였다.
CLH2052 msg discarded due to closed client(clientId) connection : svc = svcname CLH2053 msg discarded due to closed server connection : svc = svcnam
2.1.3. 도메인 소켓파일 권한 관련 개선
HP-UX 환경에서만 노드 절의 IPCPERM을 따르지 않고 도메인 소켓 파일 권한이 0777(srwxrwxrwx)로 생성되는 것을 다른 OS처럼 IPCPERM을 따르도록 수정하였다.
환경설정
*DOMAIN tmax1 SHMKEY =89255, MINCLH=1, MAXCLH=3, TPORTNO=9255, BLOCKTIME=5, MAXSVC=10, IPCPERM = 0777
주의사항
해당 설정은 UMASK와 IPCPERM에 동시에 영향을 받는다.
2.2. 유틸리티
2.2.1. CFL을 실행할 때 -a 옵션으로 SVG의 COUSIN 설정 갱신
기존에는 노드를 추가하여 COUSIN 설정을 추가할 때 원본 설정 파일을 수정해서 다시 CFL을 실행한 후 tmadmin에서 cfgadd를 하는 방식이었다.
5.0에서는 –a 옵션이 들어가지 않으면 cfgadd를 수행하지 못하도록 기능을 보완하여 위 방식으로는 COUSIN 설정 추가를 할 수 없으므로 SVRGROUP 절의 COUSIN 설정만 갱신(Update)하도록 기능을 보완하였다.
기능 제약
-
추가하는 SVRGROUP 절은 COUSIN과 NODENAME만 설정한다.
-
갱신은 COUSIN 설정만 할 수 있다.
-
갱신할 수 있는 COUSIN은 이미 정의된 COUSIN을 포함해야 한다. (순서를 변경할 수 없다. )
2.3. TDL (Tmax Dynamic Library)
2.3.1. tdlcall의 성능개선
TDL 공유 메모리 해시 테이블(Hash Table)의 충돌이 많을 경우, CPU 점유율이 높아지면서 발생하는 성능 저하 현상을 개선하였다.
-
모듈 내부적으로 로컬 캐싱 기능을 인덱싱 기법으로 도입하여 성능을 개선하였다.
-
모듈 로컬 캐시 내부에 TDL 공유 메모리 슬롯 위치를 포함하여 공유 메모리 해시 테이블 검색을 최소화하였다.
-
tdlcall에서 모듈 로컬 캐쉬가 VERSION3인 경우에 인덱싱 성능을 보완하였다.
주의사항
모듈이 많아질수록 증가하던 검색 시간 및 CPU 점유율을 충돌이 많은 모듈을 구분해 비교할 필요가 있다.
3. 버그패치
3.1. 클라이언트/서버
3.1.1. tmadmin API
tmadmin의 다음의 에러를 수정하였다.
-
st -p 조회 에러 수정
SERVER 절 혹은 GATEWAY 절의 CPC = 2 이상인 경우 tmadmin API로 st -p(TMADM_SPR_STAT) 조회할 때 SIGSEGV 에러가 발생하는 현상을 수정하였다.
-
st -s 조회 에러 수정
st -s(TMADM_SVC_STAT) 조회할 때 NRDY인 서비스가 RDY로 보이는 현상을 수정하였다.
3.1.2. tpstart API
tpstart()를 실행할 때 내부적으로 메모리를 할당하고 tpend()를 실행할 때 메모리 해제를 수행하지 않음으로써 메모리 부족이 발생하는 에러를 수정하였다.
3.1.3. tmax_is_restart API
ASQCOUNT나 tmadmin API에 의해 기동된 서버에서 tmax_is_restart API를 호출할 때 TRUE로 나오는 에러를 수정하였다.
3.1.4. tpqsvcstat API
tpqsvcstat API를 사용할 때 음수를 반환하거나 tpqsvcstat API 사용 이후 tpdeq API를 사용할 때 서버가 비정상 종료하는 현상을 수정하였다.
3.1.5. Multi Thread 클라이언트 라이브러리
Multi Thread / Multi Context 환경에서 Tmax API를 사용할 때 다음와 같은 에러가 빈번하게 발생하는 에러를 수정하였다.
(E) CLH0200 magic number errorfrom client(70.12.204.147): 0 0 0 0 [CLH0516] (E) CLI0209 internal error : unknown message type :1002 [CSC5713] (E) CLI2008 tpcall reply arrived after timeout. Msg discarded : 1003 1 [CSC5708]
3.1.6. SysMaster GID 트레이스 지원 기능 확장
실행 중인 서비스에 대하여 조회할 수 있는 SysMaster GID를 기존 tpcall뿐만 아니라 tpacall로 호출된 서비스들에 대해서도 지원한다.
3.1.8. static-library 빌드 문제
서버 소스에 tpprechk() 함수 없이 tpsvrinit() 함수를 추가한 뒤 static-library로 빌드하면 빌드 도중에 tpsvrinir() 함수가 중복되는 에러 발생 문제를 수정하였다.
3.1.9. FDL 파일 로드 에러가 발생한 경우 메모리 누수 현상
FBPRINT, FBGET_FLDNAME, FBGET_FLDKEY API와 같이 FDLKEY와 FDLNAME의 매칭이 필요한 함수를 사용하면 FDL 파일을 로드할 때 FREAD()이 실패하면 메모리 누수가 발생하는 현상을 수정하였다.
3.1.10. UCS 서버의 메모리 핸들링 에러
SMSUPPORT = Y인 UCS의 usermain 내에서 tprelay()를 호출하면 메모리 핸들링 에러가 발생하는 문제 수정하였다.
3.2. Engine
3.2.1. Rolling Down 기능의 개선
-
수정 내용
-
클라이언트 실패 및 비정상적으로 종료되었을 때, CLH가 종료되지 않는 에러를 수정하였다.
-
Rolling Down(tmdown -R)을 할 때 CUSTOM_GATEWAY 타입의 서버가 종료되지 않는 에러를 수정하였다.
-
클라이언트에서 TMAX_BACKUP_ADDR, TMAX_BACKUP_PORT가 제대로 설정되어 있지 않을 경우 클라이언트가 받는 tperrno가 TPENOREADY가 TPESYSTEM으로 변경되는 에러를 수정하였다.
-
서버 프로세스의 수가 2 이상이며 서버 큐에 요청이 누적되어 있는 경우 응답 순서가 바뀌는 에러를 수정하였다.
-
-
제한 사항
-
일반 클라이언트와 Multi Thread 클라이언트의 tpacall/tpcall만 지원한다.
-
반드시 해당 버전(4.0 SP3 Fix#8)의 클라이언트를 사용해야 한다.
-
부하 상황에서는 정상 다운이 어려울 수도 있다.
-
트랜잭션(XA), tpacall(TPBLOCK), 대화형 통신, 구버전 클라이언트, 압축, 암호화 기능과 함께 지원되지 않는다.
-
NON-XA 도메인 게이트웨이만 지원한다.(XA 도메인 게이트웨이 및 커스텀 게이트웨이 등은 지원되지 않는다.)
-
3.2.2. CLHQTIMEOUT 에러 메세지
CLHQTIMEOUT을 설정하지 않은 노드에서 해당 관련 에러 메세지가 발생하는 문제점을 수정하였다.
다음의 경우에 에러 메시지 가 발생한다.
-
로컬 도메인
A 서비스 존재, CLHQTIMEOUT이 설정되지 않은 경우 발생한다.
-
리모트 도메인
멀티 노드로 각 노드에 B 서비스가 COUSIN으로 존재, CLHQTIMEOUT을 설정한 경우 발생한다.
-
CLH 서버 큐
A 서비스 내에서 B 서비스에 부하를 주어 호출하는 상황에서 B 서비스가 응답을 늦게 주어 CLH 서버큐에 적재되어 있는 상태에서 B 서비스를 다운하면 로컬 도메인에서 다음 메세지 출력한다.
CLH.11160.190613:(E) CLH2093 server queue is purged due to CLHQTIMEOUT:SVRNAME [itpasyncgw] CLID[0x1604] [CLH0802]
3.2.3. 서버 그룹 동적 추가 관련 에러
cfgadd를 통해 COUSIN으로 구성된 XA 서버 그룹을 동적으로 추가할 경우 해당 서비스 호출 시 tx_commit()이 성공해도 실제로 commit이 되지 않을 수 있는 에러를 수정하였다.
3.2.4. COUSIN 서비스 동적 추가 관련 에러
mksvr을 통해 COUSIN 서비스를 동적으로 추가했을 경우 COUSIN과 상관없는 노드를 재기동하면 동적으로 추가된 서비스 정보를 알지 못하는 에러를 수정하였다.
다음의 상황에서 발생한다.
-
NodeA, NodeB, NodeC로 구성
-
NodeA의 svgA와 NodeB의 svgB를 COUSIN으로 구성
-
NodeC의 svgC를 단독으로 구성
-
SvgA와 SvgB에 속한 서비스를 mksvr로 동적 추가
-
SvgC에 속한 서비스를 mksvr로 동적 추가
-
클라이언트가 NodeC에 접속하여 svgC의 서비스 호출
-
svgC에서 svgA 또는 svgB의 서비스 호출
-
NodeC 재기동
-
6,7번을 실행할 때 TPENOENT 에러 발생
3.2.5. 노드 장애 감지 지연 에러
멀티 노드(3개 이상) 환경에서 특정 노드 장비에 장애가 발생할 경우 일부 노드에서 장애 감지가 지연되면서 백업 서버 기동이 지연되고 관련 서비스 호출 역시 블록되는 현상을 개선하였다.
NCLHCHKTIME이 설정되어 있는 경우 노드 장애 메시지 Broadcast가 비정상적으로 동작하여 일부 노드 장애 감지rk 지연된다.(최대 10분까지 지연된다.)
예를 들면 다음과 같은 환경에서 발생한다.
-
NODE 절
설정 순서가 node1, node2, node3, node4(node3가 node4의 백업)
-
DOMAIN 절
NLIVEINQ 와 NCLHCHKTIME 설정
3.2.6. 펜딩 트랜잭션 처리 강화
클라이언트가 tx_begin 후 트랜잭션 로깅 요청 상태에서 서버 프로세스가 비정상적으로 종료되면 해당 트랜잭션이 오랫동안 Pending 상태(TXTIME * 3)로 남는 문제를 개선하였다.
3.2.7. 트랜잭션 XID 사용 에러
클라이언트 -> 서비스1 -> 서비스2를 호출하는 환경에서, 서비스1이 속한 서버의 tpsvrinit() 함수에서 tx_begin, tx_commit을 호출하는 경우, 서비스1이 tx_begin 후 서비스2 를 호출하면 서비스2에서는 서비스1의 tpsvrinit()에서 사용하던 트랜잭션 XID를 사용하게 되어 발생하는 에러를 수정하였다.
3.2.8. CLH 공유 메모리 에러
UCS 타입 서버의 usermain() 내에서 tpreturn을 사용하는 경우, CLH 공유 메모리의 일부가 깨지는 에러를 수정하였다.
이러한 현상이 발생할 때 tmadmin에서 특정 CLH에 대한 서비스나 서버 프로세스 상태 정보 조회가 제대로 되지 않으며, tpreturn(TPEXIT) 이후 재시작한 서버는 TPENOREADY가 발생할 수 있음
3.2.9. 멀티 노드 환경에서의 TMM 사이의 채널 연결 에러
멀티 노드 환경에서 TMM 사이의 TM_NCLH_START_NOTIFY를 처리할 때, 특정 케이스에 정상 처리 되지 못하는 현상을 수정하였다.
또한 TMM의 노드 Status 판단 기준에 NPING_REQUESTED와 NPING_REQUESTED2가 추가되었다.
3.2.10. ASQCOUNT를 적용할 때 서비스 NRDY 현상
ASQCOUNT를 적용할 때 서버 전체가 재기동되는 상황에서의 서비스 NRDY 현상을 수정하였다.
-
조건
-
CLH가 심한 부하를 받는 상황
-
SERVER 절에 ASQCOUNT 적용 / SERVICE 절에 SVCTIME 적용
-
-
현상
-
데이터베이스 장애와 같이 SVCTIME이 길어지는 상황이 발생한다.
-
계속되는 클라이언트의 요청으로 인한 서버 큐에 리퀘스트가 적체된다.
-
SVCTIMEOUT이 모든 서버에서 거의 동시에 발생한다.
-
모든 서버가 거의 동시에 종료된다. (이때 순간적으로 서비스 상태가 NRDY가 됨)
-
서버 큐에 메시지가 누적되어 있으므로 ASQCOUNT에 의한 서버가 추가로 기동된다.
-
순간적으로 MAX 값까지의 모든 서버가 재기동되면서 서비스 상태가 NRDY에서 RDY로 바뀌지 않는다.
-
3.2.11. CLH, CLL, TMM을 강제 종료할 때 CORE 발생 에러
LOGCLH, CLL, TMM을 KILL 시그널로 강제 종료하면 CORE가 발생하며 종료되거나 이상한 문자를 출력하며 종료되는 문제점을 수정하였다.
3.2.12. CLH 구헤더 호환 관련 버그
3.X 구버전 클라이언트에서 5.0 버전으로 여러 건의 요청(tpacall)이 동시에 전송되는 경우 비정상적인 요청 메시지가 전달되는 현상을 수정하였다.
3.2.15. 서버 종료 시 서버 큐가 소멸되는 현상
서버 큐에 리퀘스트가 누적되어 있는 상황에서 해당 서버가 종료/재기동 된 경우, 해당 큐 COUNT가 소멸되는(cq_count 가 0이 되는) 현상을 수정하였다.
3.2.16. 동적 서비스 부하 분산 에러
MKSVR로 생성된 동적 서비스의 서버가 COUSIN으로 구성된 경우, 기동할 때 NOT READY로 인식하여 부하 분산이 되지 않는 에러를 수정하였다.
3.2.17. SLOG의 로그 기능 확장
엔진 및 서버를 재기동할 때 환경 파일의 MAXRSTART항목의 값도 함께 출력하도록 수정하였다.
-
기존 TMM3004 에러코드에 MAXRSTART 내용 추가
(I) TMM3004 CLH (clh) is restarted the 1th time (MAXRSTART = 2) [TMM0149]
-
MAXRSTART에 도달했을 때 추가적인 로그 생성
(I) TMM3034 CLH MAXRSTART reached: clh [TMM0166]
3.2.18. 서버 그룹 지정 요청 기능 IRT 문제
멀티 노드에서 서버 그룹 지정 요청 기능(tpcallsvg, tpacallsvg, tpsvgcall, tpmcall, tpmcallx)을 사용할 때 IRT가 적용되어 다른 서버 그룹으로 호출되는 오류를 수정하였다.
3.2.19. tpacall with TPBLOCK flags 처리 기능
Tmax 서비스 호출(tpacall TPBLOCK)을 할 때 Tmax 서비스가 존재하지 않거나 기동되어 있지 않은 경우 정상적으로 실패 응답을 전달하도록 수정하였다. 기존에는 실패 응답을 전달하지 않고 블럭되었다.
3.2.20. COUSIN 서버 그룹에 서버 동적 추가할 때 비정상 동작
COUSIN 서버 그룹에 서버를 동적으로 추가한 뒤 추가한 서버로 클라이언트에서 서비스를 수행할 때 실패 응답을 받는 에러를 수정하였다.
3.3. Utility
3.3.2. tmboot -w를 실행할 때 SVR 바이너리 부재 관련 버그
tmboot -w를 수행할 때 다음의 상황이 발생하면 무조건 1초 간격으로 서버가 기동되는 현상을 수정하였다.
-
서버 바이너리가 존재하지 않을 경우
-
구현되지 않은 서비스가 환경 파일에 정의되어 있을 경우
3.3.3. tmboot -D의 실행이 특정 플랫폼에서 정확하지 않은 현상
-
다른 프로세스가 많이 동작 중인 유닉스 서버에서 tmboot -D로 설정한 시간이 2배 정도 더 걸리는 에러를 수정하였다.
-
tpprechk()에서 -1을 반환하는 서버가 존재하면 tmboot -D를 실행할 때 설정한 시간 간격을 무시하고 바로 다음 서버를 기동하는 에러를 수정하였다.
-
서버 바이너리가 부재하는 경우 tmboot -D나 -d를 실행할 때 설정한 시간 간격을 무시하고 바로 다음 서버를 기동하는 에러를 수정하였다.
3.3.4. tmdown 정지 에러
부하 상황에서 서비스 타임아웃으로 인해 서버 프로세스가 빈번히 TPEXIT되는 상황에서 tmdown을 수행하면 Tmax 엔진이 종료되지 않고 정지되는 현상과 관련한 에러를 수정하였다.
3.3.5. tmboot의 MAXUSER 체크 문제
tmboot 실행 단계에서 라이센스의 MAXUSER와 현재 환경설정의 MAXUSER의 체크를 실패하면 기동 과정이 종료되는 에러를 수정하였다.
3.3.6. CFL의 에러 체크 기능
다음과 같이 단락의 마지막이 콤마(,)로 끝나는 경우에 에러 메세지가 출력되지 않는 현상을 수정하였다.
*SERVER svr2 SVGNAME = svg1,
3.3.7. CFL을 수행할 때 잘못된 에러 메세지 발생
CFL을 수행할 때 환경 파일에서 노드에 정의된 MAXGWSVR의 수보다 많은 수의 게이트웨이가 해당 노드에 선언되었을 때 잘못된 에러 메세지가 출력되는 것을 수정하였다.
기존 메세지 : (E) CFL3008 server group [svgname] is defined as duplicate COUSIN [CFL0310] 수정 메세지 : (E) CFL3110 more GW(5) than MAXGWSVR(5) are defined at node(nodename) [CFL0941]
3.4. 도메인 게이트웨이
3.4.1. 게이트웨이 운영 중 Recovery 처리 에러
-
Tmax 트랜잭션 도메인 게이트웨이를 사용하는 중 DOM1(5.0 SP1) CLOPT에 -h 옵션 설정하여 DOM2(3.x)를 구성한 후, DOM2에서 DOM1로 트랜잭션을 요청할 경우, DOM1에서는 prepare 완료 후 commit에 대한 응답을 주지 않았던 에러를 수정하였다.
-
구버전 Tmax(3.x)와 도메인 게이트웨이를 구성할 때 pending 상황이면 Recovery가 수행되지 않았던 에러를 수정하였다.
3.4.2. 구버전(3.x) Tmax와 트랜잭션을 처리할 때 정합성 에러
멀티 노드 환경에서 구버전(3.x) Tmax로 이루어진 도메인과 트랜잭션 게이트웨이를 이용해 트랜잭션을 처리할 때 로컬 도메인의 트랜잭션만 commit되는 에러를 수정하였다.
다음의 환경에서 정합성 에러가 발생한다.
-
로컬 도메인 : NODE1(gw1), NODE2(gw2)
-
리모트 도메인 : NODE1-1(gw1-1, gw2-1)
-
gw1, gw2가 COUSIN 으로 구성
-
gw1을 내림
-
로컬 도메인에 접속하여 글로벌 트랜잭션 발생
-
로컬 도메인의 트랜잭션만 commit (정합성 에러 발생)
3.4.3. 도메인 게이트웨이 Fail Back 에러
도메인 게이트웨이를 COUSIN으로 구성하여 2개의 노드와 연동할 경우 도메인 게이트웨이에 독립적 채널 기능을 사용하면(CLOPT = -i) Fail Back이 되지 않는 에러를 수정하였다.
3.4.4. CLOPT = -c -i 옵션 동시 사용 오동작
GATEWAY 절 CLOPT 항목에 -c, -i 옵션이 함께 설정되어 있는 경우 -i 옵션이 정상적으로 동작하지 않는 문제점을 수정하였다.
3.5. 관리 도구
3.5.1. tmadmin에서 서버를 조회할 때 CLH 인덱스 순으로 정렬
tmadmin의 st -v로 서버 상태를 조회할 때 해당 결과가 항상 CLH 인덱스 순으로 출력되도록 수정하였다.
3.7. WebT
3.7.1. TPBLOCK flag를 적용할 때 blocktime 적용
-
setTPtimeout으로 blocktime을 지정하고 tpcall, tpacall을 TPBLOCK flag를 주어 불렀을 때, 세팅된 blocktime이 반영이 되지 않고 응답이 끝날 때까지 기다리던 문제를 수정하였다.
-
tpcall, tpacall이 응답을 기다리고 있을 때 해당 스레드가 인터럽트되면 처리를 하지 않는 오류를 수정하였다.
3.7.2. 서비스가 대량으로 호출될 때 selector 에러
tpcall이나 tpacall을 대량으로 반복 호출하였을 때 10,000건을 전후하여 ‘java.io.IOException: Unable to establish loopback connection’에러가 발생하는 문제를 수정하였다.
3.7.3. 비요청 메시지 처리 세션이 종료될 때 handleError()
WebT 클라이언트에서 비요청 메시지를 수신하지 못하거나 에러가 발생한 경우 handleError()가 호출되지 않는 에러를 수정였다.
3.7.4. WebTAsync의 요청에 응답을 주지 못하는 에러
WebTAsync에서 tpcall, prepare, commit을 할 때 JEUSGWA에서 응답을 주지 않고 메시지를 discard시키고 WebTAsync에서는 계속 기다리다가 타임아웃되는 오류를 수정하였다.
3.7.5. 응답 Worker Thread Pool의 최대값까지 스레드가 늘어나지 않는 에러
다음의 jtmax1.worker.thread.min과 jtmax1.worker.thread.max를 각각 다르게 설정하면 jtmax1.worker.thread.max에 설정한 값까지 Thread Pool이 늘어나도록 수정하였다.
[webtasync] jtmax1.worker.thread.min = 5 , jtmax1.worker.thread.max = 20
3.7.6. 네트워크 재연결시 recovery 에러
트랜잭션 처리 중 네트워크 종료로 인하여 pending이 발생하면 네트워크에 재연결했을 경우 recovery를 수행하지 않는 에러를 수정하였다.
3.7.7. NullpointerException 발생
JEUSGWA는 등록 메시지를 보내고 tpacall 메시지도 보내기 때문에 등록 메시지가 처리되기 이전에 tpacall 메시지를 처리할 때 NullpointerException이 발생하는 에러를 수정하였다.
3.7.8. 한 채널만 종료 후 메시지를 전송할 때 NullpointerException 발생
CPC 2이상, setRegister(true)로 설정한 경우, -A(alive check)옵션에 의하여 한 채널을 종료하면, JTmax에서 관리하고 있는 domain_id 부분을 삭제하여 서비스를 요청할 때 원래 있던 채널로 서비스를 요청하게 되어 NullpointException 발생한다.
내부적으로 관리하고 있는 domain_id에 관한 정보를 같은 정보를 가진 domain_id의 커넥션은 종료 후에 없에도록 JTmax 에러를 수정하였다.
3.8. Java 게이트웨이
3.8.1. flag가 TPBLOCK인 tpacall의 실패 응답을 수신하지 못하는 에러
WebT에서 JavaGW를 통해서 다운 중이거나 없는 서비스를 호출할 때 정상적으로 실패 응답을 받도록 수정하였다.
3.8.2. tmadmin에서 JEUSGWA의 avg 시간이 나오지 않는 에러
tmadmin의 st -p를 수행할 때 JEUSGWA의 서비스의 avg 시간이 나오지 않는 에러를 수정하였다.
3.8.3. 멀티 도메인 환경에서 recovery를 수행할 때 다른 도메인의 xid를 처리하는 에러
dom1에서 발생한 pending 트랜잭션을 처리할 때 WebTAsync를 재기동하면 recovery를 수행하는 과정에서 dom1에서 처리하던 트랜잭션의 pending xid를 dom2에 보내서 dom2에서 이를 처리한다. 이렇게 처리된 xid는 dom1에는 없는 xid이기 때문에 rollback decision을 내려주어 데이터 정합성이 어긋나는 에러가 발생한다.
다음과 다음과 같은 설정을 추가해야 한다.
-
JTmaxServer
JTmaxServer server = new JTmaxServer(name, port, maxConnection,2,this); server.setRegister(true);
-
JEUSGWA
CLOPT = "-t"
3.8.4. 구버전과 통신할 때 대용량 데이터 전송 에러
JEUSGW를 설정할 때 구버전과의 통신을 위해서 CLOPT 항목에 '-h 1’을 설정한 경우 대용량 데이터를 전송할 때 다음과 같은 에러가 발생하는 것을 수정하였다.
GATEWAY.4068028.132758:(I) GATEWAY2062 remote gateway closed: 111.60.1.62 [JGW0205] GATEWAY.4068028.132758:(E) GATEWAY0050 write error: rgw closed [JGW0018]
3.8.5. tpacall TPBLOCK TPNOREPLY 수행할 때 응답을 받지 못하는 에러
다음과 같은 환경에서 WebT 클라이언트가 다량의 서비스를 호출한 경우 응답을 받지 못하는 에러를 수정하였다.
-
WebT에서 JEUSGW로 tpacall( TPNOREPLY | TPBLOCK )을 설정
-
Tmax 서비스에 sleep 60, CLHQTIMEOUT 설정
3.8.6. JavaGW에 -n -A 옵션이 동시에 있는 경우 TPESYSTEM 발생 에러
Tmax에서 WebTAsync(JTmax) 로 요청 중 JavaGWA에 -n 옵션과 -A 옵션이 같이 있을 경우 alive check 메시지가 전송되고 응답을 받지 않은 상태에서 CLH로부터 요청이 들어온 경우 TPESYSTEM 발생하는 에러가 수정되었다.
3.9. Tuxedo 게이트웨이
3.9.1. tpacall 제거 현상
Tuxedo에서 Tmax 서비스를 호출할 때 tpacall(TPNOREPLY 등의 flag) 설정이 Tmax의 서비스까지 전달되도록 수정하였다.