[Prefilter - order 1]
→ Beast를 거치는 API와 System에 대한 정보로부터 요청 정보가 어디로 가는지에 대한 정보를 추출하는 역할. (Transaction 객체에 넣어주기 위함)
package com.ktds.act.apigw.filter;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.UUID;
import com.ktds.act.apigw.handler.HandlerFactory;
import com.ktds.act.apigw.license.LicenseCheck;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import com.ktds.act.apigw.api.db.entity.AdmApiDply;
import com.ktds.act.apigw.filter.function.GetExchangeRequest;
import com.ktds.act.apigw.filter.function.RequestMethod;
import com.ktds.act.apigw.filter.model.ComnonDto;
import com.ktds.act.apigw.filter.model.RequestDto;
import com.ktds.act.apigw.filter.model.StoreDto;
import com.ktds.act.apigw.filter.model.TransactionDto;
import com.ktds.act.apigw.system.SystemFactory;
import com.ktds.act.apigw.system.db.entity.AdmSys;
import com.ktds.act.apigw.system.db.entity.Edpt;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;
@Component
@Slf4j
public class PreFilter implements GlobalFilter, Ordered, InitializingBean{
@Value("${spring.application.name}")
private String appName;
private static String hostNm = null;
private static String hostIp = null;
@Autowired
private ModifyRequestBodyGatewayFilterFactory modifyRequestBodyFilter;
@Autowired
private GetExchangeRequest getExchangeRequest;
// @Autowired(required = false)
// private LicenseCheck licenseCheck;
@Value("${common.license.necessary}")
private boolean lChknecessary;
@Value("${common.header.txid}")
private String txidForHeadNm;
@Value("${common.header.clientip}")
private String clientIpForHeadNm;
@Override
public void afterPropertiesSet() throws Exception {
hostNm = InetAddress.getLocalHost().getHostName(); **//현재 Beast가 띄워져 있는 host (Pc)이름 받아오기**
hostIp = InetAddress.getLocalHost().getHostAddress(); **//현재 Beast가 띄워져 있는 IP 받아오기**
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
long startTime = System.currentTimeMillis();
// 요청 헤더의 주요정보 추출
String txId = getHeaderF(exchange.getRequest().getHeaders(), txidForHeadNm); **//헤더 이름의 txId를 받아온다.**
String clientIp = getHeaderF(exchange.getRequest().getHeaders(), clientIpForHeadNm); **//고객 IP를 받아온다.**
if(!StringUtils.hasText(clientIp)){
clientIp = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress(); **//고객 IP가 없을때**
}
// TODO 실 Client IP 추출 로직으로 검증시 확인 필요
// XForwardedRemoteAddressResolver resolver = XForwardedRemoteAddressResolver.maxTrustedIndex(1);
// InetSocketAddress inetSocketAddress = resolver.resolve(exchange);
// String clientIp = inetSocketAddress.getAddress().getHostAddress();
// 트랜잭션 ID 추출 및 생성
if(!StringUtils.hasText(txId)) {
txId = UUID.randomUUID().toString(); **//헤더 이름이 없을때 UUID로 txId를 생성.**
}
// 쓰레드명을 Txid로 변경
//Thread.currentThread().setName(txId);
log.info("PreFilter.filter - Start");
// 요청 정보 추출
String reqMthd = exchange.getRequest().getMethod().name(); **//HTTP 요청 method를 받아와서 넣어준다.**
String reqUri = exchange.getRequest().getURI().getPath(); **//요청 URI에서 Path를 받아와서 넣어준다.**
//COMPLETED: SI api 식별 방식을 해당 방식과 동일하게 적용해도 되는지 확인 필요.
// API 식별
//AdmApiDply api = ApiFactory.getApi(reqMthd, reqUri);
AdmApiDply api = null;
if(exchange.getAttributes().containsKey("api")) {
api = exchange.getAttribute("api"); **//exchange객체에 "api"가 있을때 (어떻게 생겼는지 확인)**
}
// System 식별
AdmSys system = null;
Edpt systemEdpt = null;
String out = null;
if(api!=null){
system = SystemFactory.getSystem(api.getSysId()); **//SystemFactory에서 api에서 systemId얻어오는게 있나 봄.**
if(system!=null){
systemEdpt = system.getEdpt(); **//System의 Endpoint gateway를 거쳐 어느 IP:port로 가게 될지 넣어준다.**
}
out = api.getOut(); **//어느 서버로 연결시켜줄지는 endpoint에서 여기서는 URI의 path를 받아와서 넣어준다.**
}
// 트랜젝션 객체 생성 **(Beast Api-gateway의 기능에서 필요한 정보들을 함축해 놓은 객체)**
TransactionDto transaction = TransactionDto.builder()
.comn(ComnonDto.builder()
.applId(appName)
.hostNm(hostNm)
.hostIp(hostIp)
.txId(txId)
.api(api!=null?AdmApiDply.copy(api):null) //오 copy 메소드가 존재했네?
.build())
.req(RequestDto.builder() **//Client의 요청에 따른 DTO**
.stTm(startTime)
.svcIp(clientIp)
.meth(reqMthd)
.in(reqUri)
.out(out)
.edpt(systemEdpt)
.build())
.store(StoreDto.builder() **//이건 뭐지?**
.cstmLog(new HashMap<String, String>())
.data(new HashMap<String, Object>())
.build()
)
.build();
// 트랜젝션 객체 저장 **위의 정보들을 exchange객체에 태워서 필터를 순차적으로 태워야 하니까**
exchange.getAttributes().put(TransactionDto.TRANSACTION, transaction);
//license check
// if(lChknecessary){
// if(!licenseCheck.isLicensed()){
// transaction.setError(AgwErrorDto.UNAUTHORIZED);
// return Mono.error(new AuthenticationException(AgwErrorDto.UNAUTHORIZED.getErrMsg()));
// }
// }
log.info("PreFilter.filter - End");
// 헤더 추출
RequestMethod.extractHeaderAndQueryParams(exchange, transaction);
if(api!=null){
if(HandlerFactory.isHandlerGroupBodyRef(api.getReqHndlr())) {
// ModifyRequestBodyGatewayFilterFactory를 사용하여 requestbody추출 -> transaction객체 저장
return modifyRequestBodyFilter
.apply(
new ModifyRequestBodyGatewayFilterFactory.Config()
.setRewriteFunction(byte[].class, byte[].class, getExchangeRequest)) // extractRequestBody -> extractRequest : header, body, querystring
.filter(exchange, chain);
}
}
log.info("Transaction : " + transaction.toString());
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 1;
}
// 헤더 값 추출
private String getHeaderF(HttpHeaders header, String key){
if(header.containsKey(key)) {
return header.getFirst(key);
}
return "";
}
}
블로킹 가능성이 있는 부분
@Override
public void afterPropertiesSet() throws Exception {
hostNm = InetAddress.getLocalHost().getHostName(); **//현재 Beast가 띄워져 있는 host (Pc)이름 받아오기**
hostIp = InetAddress.getLocalHost().getHostAddress(); **//현재 Beast가 띄워져 있는 IP 받아오기**
}
<aside> 💬 PC의 이름이나 주소를 알기 위해서는 시스템 콜을 해야할 경우가 있다.
InetAddress.getLocalHost().getHostName() 메소드는 실행 환경에 따라서 시스템 콜을 호출할 수도 있습니다. 이 경우에는 시스템 콜이 블로킹될 수 있습니다.
예를 들어, 리눅스나 유닉스에서는 gethostname() 시스템 콜을 호출해서 호스트 이름을 가져옵니다. 이 시스템 콜은 호스트 이름을 결정하는 데 네트워크 연결이 필요하지 않기 때문에 보통 블로킹되지 않습니다.
하지만, 만약 시스템에서 DNS 조회를 사용해서 호스트 이름을 가져오도록 설정한 경우에는, DNS 조회의 응답이 지연될 수 있어서 블로킹될 가능성이 있습니다. 따라서, 이 경우에는 InetAddress.getLocalHost().getHostName() 메소드는 블로킹될 수 있습니다.
</aside>
long startTime = System.currentTimeMillis(); //I/O가 발생하지만, 엄청 빠르게 전환 논블로킹처럼
<aside>
💬 System 콜
System.currentTimeMillis()는 운영체제의 gettimeofday() 시스템 콜을 호출하여 현재 시간을 얻어옵니다. 그러므로 이 메소드를 호출하는 것은 운영체제와의 상호작용을 필요로 하며, 이 과정에서 I/O 작업이 발생합니다. 하지만 이 작업은 매우 짧은 시간 동안만 수행되므로, 블로킹되지 않습니다.
System.currentTimeMillis() 메소드는 시스템 호출을 통해 시스템의 현재 시간을 가져옵니다. 시스템 호출은 운영체제와 상호작용을 필요로 하며, 이 작업에서 I/O 작업이 발생합니다. 따라서 System.currentTimeMillis() 메소드를 호출하는 것은 I/O 작업이 발생한다고 볼 수 있습니다.
</aside>