이전에는 ChainedTransactionManager를 사용 했는데 2.5버전 이후부터 Deprecated되어서 소스에 줄이 생기는 것이 보기 싫어서 JtaTransactionManager로 변경을 했습니다.

그리고 TransactionManager를 변경을 하면서 ChainedTransactionManager와 JtaTransactionManager의 동작 방식이 전혀 다르다는 것을 알게 되었습니다.

 

ChainedTransactionManager와 JtaTransactionManager는 모두 트랜잭션 관리를 위한 도구이지만, 사용 목적과 작동 방식에서 차이가 있습니다. 이 두 트랜잭션 매니저의 차이점을 설명하겠습니다.

1. JtaTransactionManager

개요

  • **JtaTransactionManager**는 JTA(Java Transaction API)를 기반으로 하는 트랜잭션 매니저로, 분산 트랜잭션을 관리하기 위해 사용됩니다. JTA는 여러 자원을 사용하는 트랜잭션을 관리하기 위한 표준 Java API입니다.
  • 사용 예: JTA는 주로 복수의 데이터베이스, 메시지 큐, 그리고 다른 자원 관리자들 간에 분산 트랜잭션을 처리해야 하는 엔터프라이즈 애플리케이션에서 사용됩니다.

주요 특징

  • 글로벌 트랜잭션 관리: JTA는 여러 자원을 하나의 글로벌 트랜잭션으로 묶어 관리할 수 있습니다. 이는 예를 들어, 두 개 이상의 데이터베이스에 걸쳐서 하나의 트랜잭션을 실행할 때 유용합니다.
  • 트랜잭션 관리자와의 통합: JtaTransactionManager는 JTA를 지원하는 애플리케이션 서버(예: WebLogic, WebSphere, JBoss)에서 주로 사용되며, 컨테이너 관리형 트랜잭션을 처리할 수 있습니다.
  • 트랜잭션 전파: JtaTransactionManager는 트랜잭션 전파(Propagation)를 통해 트랜잭션 범위를 제어할 수 있으며, 이미 시작된 트랜잭션을 다른 자원으로 확장할 수 있습니다.
  • 트랜잭션 격리 수준 제한: 기본적으로 JTA는 특정 격리 수준을 직접 지원하지 않으며, 이를 설정하려면 allowCustomIsolationLevels 옵션을 활성화해야 합니다.

2. ChainedTransactionManager

개요

  • **ChainedTransactionManager**는 여러 개의 트랜잭션 매니저를 체인으로 연결하여 하나의 트랜잭션처럼 동작하도록 만드는 트랜잭션 매니저입니다. 이 매니저는 Spring Framework에서 제공되며, 서로 다른 트랜잭션 매니저를 순차적으로 실행하여 일관된 트랜잭션 처리를 가능하게 합니다.
  • 사용 예: ChainedTransactionManager는 두 개 이상의 트랜잭션 매니저(예: DataSourceTransactionManager, JmsTransactionManager)를 함께 사용해야 하는 상황에서 사용됩니다.

주요 특징

  • 복수의 로컬 트랜잭션 관리: 각 자원에 대해 로컬 트랜잭션 매니저를 사용하고, 이를 하나의 트랜잭션으로 묶어서 관리할 수 있습니다. 예를 들어, 하나의 데이터베이스와 JMS 메시지 브로커에 대해 각각의 트랜잭션 매니저를 사용하는 경우입니다.
  • 순차적 트랜잭션 관리: ChainedTransactionManager는 순차적으로 등록된 트랜잭션 매니저를 실행합니다. 각 트랜잭션 매니저가 성공해야만 전체 트랜잭션이 성공으로 간주됩니다.
  • 부분 실패 처리: 여러 트랜잭션 매니저 중 하나가 실패하면, 앞서 실행된 트랜잭션도 롤백되어야 합니다. 이 때문에 ChainedTransactionManager는 복잡한 트랜잭션 관리가 필요할 때 신중하게 사용해야 합니다.

주요 차이점

  1. 사용 목적:
    • JtaTransactionManager: 분산 트랜잭션 관리(복수의 자원 관리자에 걸쳐 있는 트랜잭션)에서 주로 사용됩니다.
    • ChainedTransactionManager: 여러 개의 로컬 트랜잭션 매니저를 하나의 트랜잭션으로 묶어서 관리할 때 사용됩니다.
  2. 작동 방식:
    • JtaTransactionManager: 글로벌 트랜잭션을 관리하며, JTA를 지원하는 애플리케이션 서버 환경에서 주로 사용됩니다.
    • ChainedTransactionManager: 여러 개의 트랜잭션 매니저를 체인으로 연결하여 순차적으로 트랜잭션을 처리합니다.
  3. 트랜잭션 범위:
    • JtaTransactionManager: 전체 글로벌 트랜잭션 범위에서 모든 자원을 관리합니다.
    • ChainedTransactionManager: 개별적으로 정의된 트랜잭션 매니저들을 하나의 연계된 트랜잭션으로 관리합니다.
  4. 격리 수준 지원:
    • JtaTransactionManager: 기본적으로 격리 수준 설정을 지원하지 않으며, 필요 시 allowCustomIsolationLevels 옵션을 활성화해야 합니다.
    • ChainedTransactionManager: 각 트랜잭션 매니저가 자신의 격리 수준을 관리합니다.

결론

  • **JtaTransactionManager**는 주로 엔터프라이즈 애플리케이션에서 글로벌 분산 트랜잭션을 처리하는 데 사용됩니다.
  • **ChainedTransactionManager**는 복수의 로컬 트랜잭션을 하나의 트랜잭션으로 묶어 관리해야 할 때 사용되며, Spring 환경에서 여러 자원에 걸친 트랜잭션을 처리할 때 유용합니다.
  • **ChainedTransactionManager**은 하나의 트랜젝션으로 처리 되는 것이 아닌 트랜잭션 매니저를 하나로 묶어서 관리를 하기 때문에 트랜잭션이 실패를 할 수 있다.
  • **JtaTransactionManager**은 트랜잭션 자원을 하나로 묶어서 글로벌로 관리를 하기 때문에 **ChainedTransactionManager** 보다는 안정성이 높다.

Spring Boot Batch에서는 Tibero Database를 지원하지 않는다.

 

Batch 프레임워크에서 DataSource에 Tibero를 설정하고 어플리케이션을 실행하면 Tibero는 지원하지 않는다고 콘솔에 에러가 난다.

에러 내용
Caused by: java.lang.IllegalArgumentException: DatabaseType not found for product name: [Tibero]

 

Spring Boot Batch 프레임워크에서 Tibero를 사용하기 위해서 별도의 설정이 필요하다.

 

1. Spring Boot Batch에서 지원하는 Database Type

DB2, DB2AS400, DB2VSE, DB2ZOS, DERBY, H2, HANA, HSQL, MARIADB, MYSQL, ORACLE, POSTGRES,
SQLITE, SQLSERVER, SYBASE

 

2. application.properties 설정

spring.batch.initialize-schema=never
spring.batch.job.enabled=false

spring.batch.initialize-schema : batch server가 시작되면 프레임워크에 설정한 Database에 Spring Boot Batch에서 사용하는 테이블을 생성하는데 Tibero를 지원하지 않기 때문에 batch server가 시작되고 spring boot가 올라갈때 지원하지 않는

타입이라고 오류가 나기 때문에 사용하지 않도록(never) 설정

 

spring.batch.job.enabled : job.enabled 설정은 Tibero와 관계된 설정은 아니고 Batch Server가 시작될때 최초에 Job가 실행되지 않게 false로 설정을 해야 한다.

Batch는 대부분 스케줄러를 사용해서 정해진 시간에 Batch가 실행 되도록 설계를 하는데 spring.batch.job.enabled을 false로 하지 않으면 Batch Server가 실행 될때 최초에 등록된 Job를 실행 시켜서 문제가 발생한다.

 

3. BatchConfig.class 설정

DataSource와 TransactionManager를 DefaultBatchConfigurer를 상속 받아서 설정을 해줘야 한다.

import javax.sql.DataSource;
import org.springframework.batch.core.configuration.annotation.DefaultBatchConfigurer;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.explore.support.JobExplorerFactoryBean;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.repository.support.JobRepositoryFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;

@Configuration
public class BatchConfig extends DefaultBatchConfigurer{

        private final DataSource dataSource;
        private final PlatformTransactionManager transactionManager;

        @Autowired
        public BatchConfig(DataSource dataSource, PlatformTransactionManager transactionManager) {
                this.dataSource = dataSource;
                this.transactionManager = transactionManager;
        }

        @Override
        protected JobRepository createJobRepository() throws Exception {
                JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
                factory.setTransactionManager(transactionManager);
                factory.setDataSource(dataSource);
                factory.setTablePrefix("BATCH_");
                factory.setIsolationLevelForCreate("ISOLATION_REPEATABLE_READ");
                factory.afterPropertiesSet();
                return factory.getObject();
        }

        @Override
        protected JobExplorer createJobExplorer() throws Exception{
                JobExplorerFactoryBean factory = new JobExplorerFactoryBean();
                factory.setDataSource(dataSource);
                factory.setTablePrefix("BATCH_");
                factory.afterPropertiesSet();
                return factory.getObject();
        }

        @Override
        public void setDataSource(DataSource dataSource) {
                super.setDataSource(dataSource);
        }

}

 

1. 오류 내용

Timeout/setRollbackOnly of ACTIVE coordinator !

 

이클립스에서 Service에 브레이크 포인트를 지정 하고 디버깅 모드로 디버깅 시 위 오류가 발생 하는 경우가 있다.

 

인텔리제이에서는 오류가 발생 하지 않으나 이클립스에서만 발생함.

 

2. 해결 방안

서비스에 선언된 @Transactional에 timeout 설정

@Transactional (timeout = 300) //5분

 

timeout = 300은 트랜잭션이 300초(5분) 내에 완료되지 않으면 자동으로 롤백되도록 설정


트랜잭션이 오래 걸릴 수 있는 작업에 대해 타임아웃을 설정함으로써, 시스템 자원을 효율적으로 관리할 수 있다.

1. 윈도우에서 Pcap을 테스트 하기 위해서는 Pcap이 설치 되어 있어야 한다.

  • 설치파일 : https://www.winpcap.org/install/ 에서 인스톨파일 다운로드(WinPcap_4_1_3.exe)
  • 다운로드한 WinPcap을 관리자권한으로 설치

2. Centos, Rocky Linux에서 pcap을 사용하려면 libpcap 설치

libpcap-1.10.4.tar.gz 수동 설치 할 경우 (Root 권한으로 진행)

1. 파일 다운로드
wget http://www.tcpdump.org/release/libpcap-1.10.1.tar.gz

2. 압축해제
tar -xzf libpcap-1.10.1.tar.gz

3. 압축해제된 폴더로 이동해서 설치 (configure 시 설치경로 /usr 지정)
./configure --prefix=/usr
make
make install

4. 설치 완료 후 설치 경로에 라이브러리 있는지 확인
ls /usr/include/pcap.h
ls /usr/lib64/libpcap.so

5. /usr/include/pcap.h에 없고 /usr/local/include/pcap.h에 있으면 심볼 생성
ln -s /usr/local/include/pcap.h /usr/include/pcap.h

6. usr/lib64 path 설정 (다른 방법으로도 전역으로 생성하면 됨)
echo 'export LD_LIBRARY_PATH=/usr/lib64:$LD_LIBRARY_PATH' >> ~/.bashrc
source ~/.bashrc

 

  • pcap 설치 확인 및 테스트
간단하게 C로 인터페이스 출력하는 코드로 설치가 정상적으로 됐는지 확인

1. vi로 테스트 코드 작성
vi pcaptest.c

2. pcaptest.c 내용
#include <pcap.h>
#include <stdio.h>

int main() {
    pcap_if_t *alldevs, *dev;
    char errbuf[PCAP_ERRBUF_SIZE];

    // 모든 네트워크 장치 목록을 가져옴
    if (pcap_findalldevs(&alldevs, errbuf) == -1) {
        printf("Error finding devices: %s\n", errbuf);
        return 1;
    }

    // 첫 번째 장치를 선택 (기본 장치)
    dev = alldevs;
    if (dev == NULL) {
        printf("No devices found.\n");
        pcap_freealldevs(alldevs); // 메모리 해제
        return 1;
    }

    printf("Interface: %s\n", dev->name);

    // 장치 목록에서 사용한 메모리 해제
    pcap_freealldevs(alldevs);

    return 0;
}

3. pcaptest.c 컴파일
gcc -o pcaptest pcaptest.c -lpcap

4. pcaptest.c 컴파일을 하면 pcaptest 파일이 생성되면 실행이 정상적으로 되는지 확인
./pcaptest

5. ./pcaptest 실행하면 나오는 경과
Interface: eth0   <---eth0은 서버 환경에 따라서 다를 수 있음

 

3. Java Spring 구현

  • pom.xml에 pcap4j 라이브러리 Maven Dependency 추가
<dependency>
        <groupId>org.pcap4j</groupId>
         <artifactId>pcap4j-core</artifactId>
         <version>1.7.4</version>
</dependency>
<dependency>
         <groupId>org.pcap4j</groupId>
         <artifactId>pcap4j-packetfactory-static</artifactId>
         <version>1.7.4</version>
</dependency>

 

  • PcapHandleWrapper.class 작성
import org.pcap4j.core.PcapHandle;

public
class PcapHandleWrapper {

         private PcapHandle handle;
         public PcapHandleWrapper(PcapHandle handle) {
                  this.handle = handle;
         }
          public PcapHandle getHandle() {
                    return handle;
          }
          public void setHandle(PcapHandle handle) {
                    this.handle = handle;
          }
}

 

  • 테스트 샘플
import java.net.InetAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.pcap4j.core.BpfProgram;
import org.pcap4j.core.PcapHandle;
import org.pcap4j.core.PcapNetworkInterface;
import org.pcap4j.core.PcapNetworkInterface.PromiscuousMode;
import org.pcap4j.core.Pcaps;
import org.pcap4j.packet.EthernetPacket;
import org.pcap4j.packet.IpV4Packet;
import org.pcap4j.packet.Packet;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import egovframework.example.utils.PcapHandleWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;

@Controller
@RequestMapping("/mactest")
@RequiredArgsConstructor
@Log4j2
public class MacAddrSampleController {

    private static PcapHandleWrapper handleWrapper;



     @ResponseBody
     @GetMapping(value = {"/pcap4j"})
     public String pcap4j5(HttpServletRequest req ,HttpServletResponse rep,
          //client IP
          @RequestParam(name = "targetIP", defaultValue = "192.168.35.224") String targetIP) throws Exception {

          //Was가 동작하고 있는 server IP
          String serverIP = "192.168.35.28";

          ExecutorService executor = Executors.newSingleThreadExecutor();

         
try {


               //Was가 동작하고 있는 server IP
               InetAddress addr = InetAddress.getByName(serverIP);
               PcapNetworkInterface device = Pcaps.getDevByAddress(addr);

               if (device == null) {
                    System.out.println("네트워크 인터페이스를 선택하지 않았습니다.");
                     return "네트워크 인터페이스를 선택하지 않았습니다.";
               }

              
// 캡처 설정

               int snaplen = 65536; // 최대 캡처할 패킷 크기
               PromiscuousMode mode = PromiscuousMode.PROMISCUOUS; // Promiscuous 모드
               int timeout = 50; // 타임아웃 (단위: ms)
               PcapHandle handle = device.openLive(snaplen, mode, timeout);

               handleWrapper = new PcapHandleWrapper(handle);

               String filter = "ip host " + targetIP;

               handle.setFilter(filter, BpfProgram.BpfCompileMode.OPTIMIZE);

               executor.submit(() -> {

                    try {

                             
while (true) { // 무한 루프


                                   Packet
packet = handleWrapper.getHandle().getNextPacket();


                                       
if (packet != null) {


                                                     EthernetPacket
ethernetPacket = packet.get(EthernetPacket.class);

                                                       if (ethernetPacket != null) {

                                                            // IP 패킷으로 변환
                                                            IpV4Packet ipV4Packet = packet.get(IpV4Packet.class);

                                                           
if (ipV4Packet != null) {


                                                                
// 목적지 MAC 주소 추출

                                                                 String dstAddr = ipV4Packet.getHeader().getDstAddr().getHostAddress();
                                                                 String dstMacAddrIp = ethernetPacket.getHeader().getDstAddr().toString();

                                                                      if(dstAddr != null && targetIP.equals(dstAddr)) {
                                                                           System.out.println("Destination IP Address: " + dstAddr);
                                                                           System.out.println("Destination MAC Address: " + dstMacAddrIp);
                                                                       }

                                                             }

                                                       }

                                         }
                                  }

                    } catch (Exception e) {
                         e.printStackTrace();
                    } finally {
                         handleWrapper.getHandle().close();
                    }
               });


          // ExecutorService 종료 대기
          Runtime.getRuntime().addShutdownHook(new Thread(() -> {

               try {

                    executor.shutdown();

                    if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
                         executor.shutdownNow();
                    }

               } catch (InterruptedException e) {
                    executor.shutdownNow();
               }

          }));


         } catch (Exception e) {
               e.printStackTrace();
         }

          return "pcap 스레드 동작 중";

     }


}

웹 프로젝트에서 테스트를 진행 했기 대문에 웹 호출로 스레드를 구동하고 나면 모니터링이 시작되고

웹페이지에 접속하는 클라이언트의 정보를 pcap으로 통해 가져 올 수 있다.

 

* 같은 망에서만 mac 정보를 가져올 수 있고 망이 다르면 가져올 수 없다.

https://kkubi-story.tistory.com/21

 

pcap 동작 원리

pcap(Packet Capture) 도구를 사용해도 망이 다르면 MAC 주소 정보를 가져올 수 없다. 이는 네트워크의 기본적인 동작 원리에 따른 것이다. 이유 설명 1. MAC 주소의 역할과 ARP 프로토콜

kkubi-story.tistory.com

 

pcap(Packet Capture) 도구를 사용해도 망이 다르면 MAC 주소 정보를 가져올 수 없다. 이는 네트워크의 기본적인 동작 원리에 따른 것이다.

이유 설명
1. MAC 주소의 역할과 ARP 프로토콜
MAC 주소는 이더넷 같은 로컬 네트워크(LAN)의 데이터 링크 계층에서 장치를 식별하기 위해 사용됩니다. ARP (Address Resolution Protocol)는 IP 주소를 MAC 주소로 변환하는 프로토콜로, 동일한 서브넷 내에서만 작동합니다. 즉, 같은 LAN 내에서만 ARP 요청을 통해 상대방의 MAC 주소를 알 수 있습니다.

2. 라우터와 MAC 주소
인터넷과 같은 외부망(WAN)에서는 패킷이 여러 라우터를 거쳐 전달됩니다. 각 라우터는 패킷을 다음 홉으로 전달할 때 자신의 MAC 주소를 사용해 패킷을 포워딩합니다. 따라서, 외부망에서 전달되는 패킷에는 실제 최종 목적지 장치의 MAC 주소가 포함되지 않으며, 대신 라우터의 MAC 주소만 포함됩니다.

3. PCAP의 한계
pcap 라이브러리나 Wireshark 같은 패킷 캡처 도구는 네트워크 인터페이스를 통해 수신된 패킷을 캡처합니다. 하지만 네트워크 구조상:

내부망(LAN): 같은 서브넷 내에서는 ARP 패킷을 캡처할 수 있고, 이를 통해 MAC 주소를 얻을 수 있습니다.
외부망(WAN): 패킷이 라우터를 거치기 때문에, 최종 목적지의 MAC 주소는 캡처할 수 없습니다. 외부망에서 캡처되는 패킷의 MAC 주소는 라우터의 MAC 주소가 됩니다.

4. 네트워크 시나리오에 따른 MAC 주소 가시성
내부망(LAN) 시나리오:
동일한 서브넷에 있는 두 장치 간의 통신에서는 MAC 주소를 ARP 요청을 통해 알 수 있습니다.
pcap으로 캡처한 패킷에서 상대방의 MAC 주소를 확인할 수 있습니다.

외부망(WAN) 시나리오:
네트워크 패킷은 라우터를 통해 전달되며, 각 라우터가 자신의 MAC 주소를 사용해 패킷을 다음 홉으로 전달합니다.
pcap으로 캡처한 패킷에서는 라우터의 MAC 주소만 보이며, 최종 목적지의 MAC 주소는 볼 수 없습니다.

5.요약
**내부망(LAN)**에서는 MAC 주소를 얻을 수 있지만,
**외부망(WAN)**에서는 MAC 주소를 얻을 수 없습니다.
이러한 동작은 네트워크 프로토콜의 특성에 따른 것이며, pcap이나 다른 네트워크 도구들도 이 원칙에 따릅니다. MAC 주소는 데이터 링크 계층에서만 유효하기 때문에, 다른 서브넷이나 인터넷을 통해 전송되는 패킷에는 원래의 MAC 주소 정보가 포함되지 않습니다.

'Development Story > Linux' 카테고리의 다른 글

nginx 수동 설치 재기동  (0) 2024.08.19
Linux Maven 설치  (0) 2024.03.04
Linux Java(jdk) 설치  (0) 2024.03.04
linux 방화벽 오픈  (2) 2024.02.29
KVM 간편 정리  (0) 2024.02.28

Wildfly로 세션 클러스트링을 할 경우에는 jboss-web.xml에 설정이 추가적으로 필요하다.

 

<jboss-web>
       <context-root>/</context-root>
        <replication-config>
                <replication-trigger>SET</replication-trigger>
                <replication-granularity>SESSION</replication-granularity>
        </replication-config>
</jboss-web>

 

replication-config 부분이 추가적으로 필요하다.

 

web.xml에는 distributable를 추가 해야 한다.

<distributable/>

start : nginx 설치경로/sbin/nginx

reload : nginx 설치경로/sbin/nginx -s reload

stop : nginx 설치경로/sbin/nginx -s stop

'Development Story > Linux' 카테고리의 다른 글

pcap 동작 원리  (0) 2024.08.29
Linux Maven 설치  (0) 2024.03.04
Linux Java(jdk) 설치  (0) 2024.03.04
linux 방화벽 오픈  (2) 2024.02.29
KVM 간편 정리  (0) 2024.02.28
stage('Deploy to Wildfly') {
            steps {
                sh '''#!/bin/bash
wildfly_plugin_version=2.0.1.Final

# 서버 접속 정보
wildfly_hostname1=172.16.7.10
wildfly_port=9990
wildfly_username=admin
wildfly_password=admin123
server_group=group
rollout_name=rollout

pushd target
shopt -s nullglob
for file in *.war; do
  war_filename="${file}"
  break
done
popd

cat << EOF > deploy.xml
<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>example.com</groupId>
  <artifactId>wildfly-deploy</artifactId>
  <version>1</version>
  <build>
    <plugins>
      <plugin>
        <groupId>org.wildfly.plugins</groupId>
          <artifactId>wildfly-maven-plugin</artifactId>
          <version>${wildfly_plugin_version}</version>
          <configuration>
            <filename>target/${war_filename}</filename>
            <hostname>${wildfly_hostname1}</hostname>
            <port>${wildfly_port}</port>
            <username>${wildfly_username}</username>
            <password>${wildfly_password}</password>
            <commands>
                <command>/server-group=${server_group}:restart-servers(blocking=true)</command>
                <command>deploy ./target/${war_filename} --force --headers={rollout name=${rollout_name}}</command>
            </commands>
          </configuration>
        </plugin>
      </plugins>
  </build>
</project>
EOF

exec mvn -f deploy.xml wildfly:execute-commands
'''
            }
        }

+ Recent posts