Initialize RTGS prototype repository
3센터 A-A-A RTGS 실시간총액결제 프로토타입 최초 버전관리 시작. - backend: Kotlin/Gradle 멀티모듈(sequencer, common, 채널/센터 모듈) - frontend: Vite + TS - infra: docker-compose, prometheus/grafana, ELK 네이티브 스크립트 - loadtest(k6), sim(장애/순서/정합성 시나리오), docs(PoC 보고서/복원력방안)
This commit is contained in:
94
loadtest/pacs008.k6.js
Normal file
94
loadtest/pacs008.k6.js
Normal file
@@ -0,0 +1,94 @@
|
||||
import http from 'k6/http';
|
||||
import { check } from 'k6';
|
||||
|
||||
// RTGS 부하 스크립트 - pacs.008(ISO 20022, pacs.008.001.08) 실시간 생성 -> Chanel 접수.
|
||||
// 참가기관(Dbtr/Cdtr Agent MmbId) 무작위 선정, 이체금액 1~200원(개요/PoC 기준).
|
||||
// 프로세스 (0) 사전확인(개인계좌 잔액/수신계좌)은 참가기관 몫이므로 여기(부하생성기=송신기관)가 담당.
|
||||
//
|
||||
// ※ Chanel은 pacs.008.001.08 XSD로 원전문을 검증한다. 반드시 정식 <Document> 구조여야
|
||||
// 접수(TxSts=RCVD)된다. 구식 <RtgsPacs008> 간이 포맷은 XSD 검증 실패로 RJCT.
|
||||
// 핵심 추출 필드: EndToEndId(BMI), DbtrAgt/CdtrAgt MmbId(송/수신), Dbtr/CdtrAcct Othr Id, IntrBkSttlmAmt.
|
||||
//
|
||||
// 실행 예:
|
||||
// steady : k6 run -e RATE=50 -e DURATION=20s loadtest/pacs008.k6.js
|
||||
// spike : k6 run -e MODE=spike loadtest/pacs008.k6.js
|
||||
// 대시보드: k6 run --out influxdb=http://localhost:8096/k6 ... (관측 프로필)
|
||||
|
||||
const BASE = __ENV.CHANEL || 'http://localhost:8091';
|
||||
const DATE = __ENV.BIZDATE || '20260709';
|
||||
const ORGS = ['1001','1002','1003','1004','1005','1006','1007','1008','1009','1010',
|
||||
'1011','1012','1013','1014','1015','1016','1017','1018','1019'];
|
||||
|
||||
function scenarios() {
|
||||
if (__ENV.MODE === 'spike') {
|
||||
// 스파이크(peak) 산정: 도착률을 단계적으로 올려 최대 접수 처리건수 측정(리뷰어2 B).
|
||||
return {
|
||||
spike: {
|
||||
executor: 'ramping-arrival-rate',
|
||||
startRate: Number(__ENV.START || 100),
|
||||
timeUnit: '1s',
|
||||
preAllocatedVUs: 100, maxVUs: 2000,
|
||||
stages: [
|
||||
{ target: Number(__ENV.PEAK || 1000), duration: __ENV.RAMP || '30s' },
|
||||
{ target: Number(__ENV.PEAK || 1000), duration: '20s' },
|
||||
{ target: 0, duration: '10s' },
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
return {
|
||||
steady: {
|
||||
executor: 'constant-arrival-rate',
|
||||
rate: Number(__ENV.RATE || 50),
|
||||
timeUnit: '1s',
|
||||
duration: __ENV.DURATION || '20s',
|
||||
preAllocatedVUs: 50, maxVUs: 500,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const options = { scenarios: scenarios() };
|
||||
|
||||
function pad(n, len) { return String(n).padStart(len, '0'); }
|
||||
|
||||
export default function () {
|
||||
const s = ORGS[Math.floor(Math.random() * ORGS.length)];
|
||||
let r = ORGS[Math.floor(Math.random() * ORGS.length)];
|
||||
while (r === s) r = ORGS[Math.floor(Math.random() * ORGS.length)];
|
||||
|
||||
const amount = 1 + Math.floor(Math.random() * 200);
|
||||
const serial = pad((__VU * 1000000 + __ITER) % 10000000000, 10);
|
||||
const bmi = DATE + s + serial; // 22자리 EndToEndId = 8+4+10
|
||||
const creDtTm = new Date().toISOString().slice(0, 19); // pacs.008 ISODateTime (초 단위, TZ 생략)
|
||||
|
||||
// pacs.008.001.08 정식 전문 (요소 순서는 XSD 시퀀스 준수 - 정상 샘플과 동일 구조).
|
||||
const xml =
|
||||
`<?xml version="1.0" encoding="UTF-8"?>` +
|
||||
`<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pacs.008.001.08">` +
|
||||
`<FIToFICstmrCdtTrf>` +
|
||||
`<GrpHdr>` +
|
||||
`<MsgId>${bmi}</MsgId>` +
|
||||
`<CreDtTm>${creDtTm}</CreDtTm>` +
|
||||
`<NbOfTxs>1</NbOfTxs>` +
|
||||
`<SttlmInf><SttlmMtd>CLRG</SttlmMtd></SttlmInf>` +
|
||||
`</GrpHdr>` +
|
||||
`<CdtTrfTxInf>` +
|
||||
`<PmtId><EndToEndId>${bmi}</EndToEndId></PmtId>` +
|
||||
`<IntrBkSttlmAmt Ccy="KRW">${amount}</IntrBkSttlmAmt>` +
|
||||
`<ChrgBr>SLEV</ChrgBr>` +
|
||||
`<Dbtr><Nm>BANK-${s}</Nm></Dbtr>` +
|
||||
`<DbtrAcct><Id><Othr><Id>ACC-${s}</Id></Othr></Id></DbtrAcct>` +
|
||||
`<DbtrAgt><FinInstnId><ClrSysMmbId><MmbId>${s}</MmbId></ClrSysMmbId></FinInstnId></DbtrAgt>` +
|
||||
`<CdtrAgt><FinInstnId><ClrSysMmbId><MmbId>${r}</MmbId></ClrSysMmbId></FinInstnId></CdtrAgt>` +
|
||||
`<Cdtr><Nm>BANK-${r}</Nm></Cdtr>` +
|
||||
`<CdtrAcct><Id><Othr><Id>ACC-${r}</Id></Othr></Id></CdtrAcct>` +
|
||||
`</CdtTrfTxInf>` +
|
||||
`</FIToFICstmrCdtTrf>` +
|
||||
`</Document>`;
|
||||
|
||||
const res = http.post(`${BASE}/pay/customer`, xml, { headers: { 'Content-Type': 'application/xml' } });
|
||||
check(res, {
|
||||
'status 200': (x) => x.status === 200,
|
||||
'accepted (RCVD)': (x) => x.body && x.body.includes('RCVD'),
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user