아래 스크립트를 크론으로 추가해서 실행하면 현재 시스템의 상태를 로그를 통해 알 수 있다. 

로그를 시스템의 상황을 볼 수 있다

#!/bin/bash

#Today=`date +%Y%m%d%H%M`

LOGDIR=/logs/was/monitoring/mem

if [ ! -d "$LOGDIR" ]; then
        echo Cannot Stat $LOGDIR
        echo Run: mkdir $LOGDIR
        exit;
fi

Today=`date '+%Y/%m/%d %H:%M:%S'`
DT=`date '+%Y%m%d'`
MemUsed="$(echo 'MEM:'`free -m | grep 'Mem' | awk '{printf "%-2d% (%5d/%5dMB)", ($3/$2)*100, $3, $2}'`)"
RSS="$(echo 'RSS:'`ps aux | grep piasSvr | grep -Ev 'catalina.sh|.out|grep|cronolog' | awk '{printf "%-6d", $6/1024}'`'MB')"
CpuUsed="$(echo 'CPU:'`/usr/bin/vmstat 1 2| sed 1,3d | awk '{printf "%-2d%", 100-$15}'`)"
THREAD="$(echo 'THREAD_NUM:'`ps -ef | grep apache | grep httpd | wc -l | awk '{printf "%-4d", $0}'`)"
    ESTABLISHED_SERVICE="$(echo 'ESTABLISHED_NUM:'`netstat -an | grep 18009 | grep ESTABLISHED | wc -l | awk '{printf "%-4d", $0}'`)"
    ESTABLISHED_TOTAL="$(echo 'ESTABLISHED_TOTAL:'`netstat -an | grep ESTABLISHED | wc -l | awk '{printf "%-4d", $0}'`)"

DATA="$Today | $MemUsed | $CpuUsed | $THREAD | $ESTABLISHED_TOTAL | $ESTABLISHED_SERVICE | $RSS"
echo "$DATA" >> $LOGDIR/${HOSTNAME}.memcpu.${DT}.log
echo "$DATA"

 

리눅스는 크론탭 최소로 조작이 가능한 단위가 분이다. (스프링 스케줄러는 초까지 가능) 

 

그래서 섬세한 실행이 필요하면 아래와 같이 추가하면 리눅스 크론탭에서도 초단위로 조작이 가능하다. 

 

 

crontab -l 

* * * * * /bin/sh /tony/cpu_mem.sh 
* * * * * ( sleep 10; /bin/sh /tony/cpu_mem.sh )
* * * * * ( sleep 20; /bin/sh /tony/cpu_mem.sh )
* * * * * ( sleep 30; /bin/sh /tony/cpu_mem.sh )
* * * * * ( sleep 40; /bin/sh /tony/cpu_mem.sh )
* * * * * ( sleep 50; /bin/sh /tony/cpu_mem.sh )

위와 같이 추가하면 10초단위로 실행이 된다. 

설명 

 

담당하고 있는 프로젝트에 고도화를 진행하면서 새로운 연동 서비스가 추가 되었다. 해당 서비스와 연동을 하면서 트래픽 증가가 예상이 되어서 로드러너를 통해 과부하 테스트를 진행을 했다. 로드러너 테스트를 진행하면서 동시다발적으로 에러들이 발생을 해서 관련 내용을 정리하고자 한다. 

 

서버 구성도는 on-premise환경, 3tier 형식으로 구축되어있다. 추가로 테스트는 WEB1 -> WAS1 -> DB + 시뮬서버1 으로 과부하 테스트를 했다. 

 

 

문제 

1. 502 Bad gateway 

- 웹에서 ping를 보냈는데 웹서버에 설정된 시간안에 응답을 못받으면 502에러가 발생했다. (웹 설정은 worker.xxx.ping_timeout)

 

로드러너 실행시 간헐적으로 502 발생 (4~5시간마다 1번)
웹의 mod_jk.log에 자세한 내용이 찍힌다

- 100명의 유저로 200tps정도 테스트시 Tomcat의 Thread pool의 개수가 부족하나 싶어서 톰캣의 threadpoolSize를 늘려줬다. 하지만 결과는 동일 ㅠㅠ 

 

2. Hikair pool restart (deadlock)

- Tomcat thread pool의 size를 늘려도 동일한 502발생. 502 발생시 서비스 로그를 확인해보니 서비스로그가 안남으면서 정상적으로 요청을 처리할때 첫째줄에 Hikair pool restart이라고 적혀 있었다. 

- 구글에서 찾아보니 tomcat thread pool size비해 hikari pool size가 적어서 발생할 수 있다는 방안이 있었다. (아래 링크 참고) 

https://techblog.woowahan.com/2664/

- hikair pool size 정하는 공식으로 설정하고 로드러너 다시 실행

Pool size 적용 공식

- 하지만 동일한 502... 

 

해결 

해결책은 정말 간단했다. CPU였다. HTTP 통신은 CPU를 사용한다. 해당 서버에 CPU 사용량이 100% 이상 넘어갈시 해당 서버에 요청이 들어와도 정상 처리를 못한다. 서버에 CPU에 증가량의 원인은 다양하지만 대표적인건 thread pool의 size그리고 로그 출력이다. 로그의 레벨만 제대로 설정하면 cpu의 사용량이 기본 60%에서 30%까지 떨어지는걸 눈으로 확인할 수 있다. (CPU확인 스크립트는 포스트 참고 = https://tonyzorz.tistory.com/139)

 

추가적으로 JVM의 힙설정 + 웹의 retry, timeout 시간을 늘리면 또한 처리가 안될시 방안이 될수 있다. 

1. apache 웹 설정 

- workers.properties

  workers.template.socket_timeout=60

  workers.template.socket_connect_timeout=60000

  worker.template.ping_timeout=60000

 

2. tomcat 설정 

- startup.sh 

  -  JAVA_OPTS="$JAVA_OPTS -XX:InitiatingHeapOccupancyPercent=50 -XX:ParallelGCThreads=2"

 

로드러너 해결하기 위해 2주정도 시간을 헤맨것같다.. 다른 사람들은 제 포스트를 보고 cpu도 의심해보면 좋을것 같다! 

 

'실무정리' 카테고리의 다른 글

리눅스 CPU, MEM, ESTABLISHED 확인 스크립트  (0) 2023.07.16
리눅스 크론탭 10초 간격  (0) 2023.07.16

'Java' 카테고리의 다른 글

자바 람다  (0) 2023.01.10

'스프링' 카테고리의 다른 글

스프링 autocommit  (0) 2023.01.18
    public void accountTransfer(String fromId, String toId, int money) throws SQLException {
        Connection con = dataSource.getConnection();

        try {
            con.setAutoCommit(false);
            
            // 비즈니스 로직 수행 
            Member fromMember = memberRepository.findById(con, fromId);
            Member toMember = memberRepository.findById(con, toId);

            memberRepository.update(con, fromId, fromMember.getMoney() - money);
            validation(toMember);
            memberRepository.update(con, toId, toMember.getMoney() + money);
            
            con.commit();
            
        } catch (Exception e) {
            con.rollback();
            throw new IllegalStateException(e);
        } finally {
            if (con != null) {
                try {
                    con.setAutoCommit(true); // 커넥션을 풀에 다시 반환하기 전에 autocommit default 값 true로 세팅하기
                    con.close();
                } catch (Exception e) {
                    log.info("error", e);
                }
            }
        }
        
    }

커넥션 트랜잭션 시작시 만약 autocommit를 false로 세팅했을시 위 코드와 같이 커넥션 풀에 커넥션을 반환시 기본 autocommit인 true로 세팅해주고 반환해야한다. 

 

DriverManager처럼 데이터풀을 안사용하는 커넥션 라이브러리면 상관은 없지만 현재는 hikari풀을 많이 사용한다. 

'스프링' 카테고리의 다른 글

계층 정의  (0) 2023.01.18

- stream, filter, predicate 사용 예제

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class Playground {
    
    public static List<String> friends = Arrays.asList("taewon", "brian", "timmy", "turner", "ed", "edd", "eddy", "samuel", "jackson", "miranda", "Samantha", "sarah");
    
    public static void main(String[] args) {
        List<String> startsWithT = friends.stream()
                .filter(checkIfStartsWith("t"))
                .collect(Collectors.toList());

        List<String> startsWithE = friends.stream()
                .filter(checkIfStartsWith("e"))
                .collect(Collectors.toList());
    }
    
    public static Predicate<String> checkIfStartsWith(String letter) {
        return name -> name.startsWith(letter);
    }
}

 

- 총 길이 찾기 

import java.util.Arrays;
import java.util.List;

public class Playground {
    
    public static List<String> friends = Arrays.asList("taewon", "brian", "timmy", "turner", "ed", "edd", "eddy", "samuel", "jackson", "miranda", "Samantha", "sarah");
    
    public static void main(String[] args) {
        int sum = friends.stream()
                .mapToInt(name -> name.length())
                .sum();
    }
    
}

 

- 긴 이름 기준 조건으로 이름 찾기, optional 

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class Playground {
    
    public static List<String> friends = Arrays.asList("taewon", "brian", "timmy", "turner", "ed", "edd", "eddy", "samuel", "jackson", "miranda", "Samantha", "sarah");
    
    public static void main(String[] args) {
        Optional<String> longestName = friends.stream()
                .reduce((name1, name2) -> name1.length() >= name2.length() ? name1 : name2);
        
        longestName.ifPresent(System.out::println);
    }
    
}

 

 

 

 

출처 : 

https://tomining.tistory.com/48

'Java' 카테고리의 다른 글

자바 예외 계층  (0) 2023.01.31

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Document</title>

</head>

<body>

<div id="app">

    {{str}} parent instance === {{num}}

    <app-header v-bind:child="num"></app-header>

    <app-content v-on:what="child_to_parent"></app-content>

</div>    

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<script>

    var appHeader = {

        template: '<div>child component === {{child}}</div>',

        props: ['child']

    }

    var appContent = {

        template: '<div>content<button v-on:click="passem">pass</button></div>',

        methods: {

            passem: function(){

                this.$emit('what', 10);

            }

        }

    }

    new Vue({

        el: '#app',

        data: {

            str: 'hi',

            num: 0,

        },

        components: {

            'app-header'appHeader,

            'app-content'appContent,

        },

        methods: {

            child_to_parentfunction(e){

                console.log(e);

                this.num = e;

            }

        }

    })

</script>

</body>

</html>

'ETC' 카테고리의 다른 글

Linux study  (0) 2020.04.17
Linux commands  (0) 2020.04.02
프로세스 (chapter 2)  (0) 2020.03.13
운영체제(chapter 1)  (0) 2020.03.13
시스템 소프트웨어  (0) 2020.03.12

+ Recent posts