몇년전 회사 시스템 고도화 과정에서 아이디어를 많이 얻은 프로젝트가 있는데 요즘에는 MSA 아키텍쳐 등에 대한 부분 등 많은 내용이 추가되었고 꾸준히 업데이트도 되는 것 같다.

 

그래서 팀에 후배 개발자가 입사하면 공부해보라고 알려주기도 한다.

 

해당 프로젝트는 jhipster라고 하며 주소는 www.jhipster.tech/ 이다.

 - git 주소는 github.com/jhipster/generator-jhipster

 

덧) 몇년전 외국 컨퍼런스 갔을때 하얀 머리의 할아버지가 MSA관련 강의를 열심히 듣고 계시던게 갑자기 생각나네..

Elasticsearch 메모리 증가나 CPU 증가 등의 문제를 튜닝할때 참고

 

경험해봤던 문제의 대부분은 매핑을 오토로해서 문제가 발생함.
 - 텍스트 필드를 집계하지 않도록 하거나 매핑 유형을 키워드로 변경하면 해결 

 

 

Elasticsearch Performance Tuning Practice at eBay

Elasticsearch is an open source search and analytic engine based on Apache Lucene that allows users to store, search, analyze data in near real time. While Elasticsearch is designed for fast queries, the performance depends largely on the scenarios that ap

tech.ebayinc.com

  • 목적/배경
    • 라이브 환경에서 장애가 발생할 수 있는 성능 병목을 미리 확인하는게 좋음
    • 성능 병목을 미리 확인하려면 부하를 발생시켜야하며 여러 툴들이 존재.  툴 예)
  • 간단히 빠르게 부하를 발생시키기 위해서 apache ab도 가끔 사용 중이라 간략히 정리
#설치
yum provides /usr/bin/ab
yum install httpd-tools

#부하발생 샘플(-n은 요청 수, -c는 동시에 요청하는 요청수)
ab -n 10 -c 1 대상URL

#참고
 - 설명: https://httpd.apache.org/docs/2.4/ko/programs/ab.html  
 - 한글 블로그 참고: https://m.blog.naver.com/PostView.nhn?blogId=dlaskarud2&logNo=221726899397&proxyReferer=https:%2F%2Fwww.google.com%2F

글로벌 서비스 개발 중에 갑자기 fiddler로 크롬에서 캡쳐가 안되는 문제가 발생했습니다.

확인해보니, 테스트를 위해서 설치한 VPN 확장도구 때문이네요.

비 활성화 후 정상으로 작동합니다.

 

참고

 - hummingbird.tistory.com/5990

'유용한 프로그램' 카테고리의 다른 글

보안문제 테스트를 위한 툴  (0) 2022.05.04
스크린 캡쳐 프로그램 ShareX- 무료  (1) 2020.09.12
  • pom.xml에 디펜더시 추가(maven 기준)
    • 버전은 maven repo나 git에서 확인
		<!-- https://mvnrepository.com/artifact/io.sentry/sentry-logback -->
        <dependency>
            <groupId>io.sentry</groupId>
            <artifactId>sentry-logback</artifactId>
            <version>3.2.0</version>
        </dependency>

 

  • logback.properties에 설정 추가
    • logback-spring.xml에서 참조하는 properties파일
#sentry에 전송시 environment 구분 값(ex. local, alpha, beta, qa, real)
sentry.environment=입력필요

#sentry 에서 dsn정보 확인 후 입력
sentry.dsn=입력필요
  • logback-spring.xml 에 설정 추가
    • 아래 appender추가 후 전송할 logger에 꼭 appender-ref ref를 추가해야함
<!-- Configure the Sentry appender -->
    <appender name="SENTRY" class="io.sentry.logback.SentryAppender">
        <!-- Optionally change minimum Event level. Default for Events is ERROR -->
        <minimumEventLevel>ERROR</minimumEventLevel>
        <!-- Optionally change minimum Breadcrumbs level. Default for Breadcrumbs is INFO -->
        <minimumBreadcrumbLevel>DEBUG</minimumBreadcrumbLevel>
        <options>
            <!-- NOTE: Replace the test DSN below with YOUR OWN DSN to see the events from this app in your Sentry project/dashboard -->
            <dsn>${sentry.dsn}</dsn>
            <environment>${sentry.environment}</environment>
        </options>
    </appender>

 

 

PVS-Studio Analyzer 셋팅 참고 메모

 

  1. jenkins서버에 라이센스 키 설정은
    1. ./.config/PVS-Studio-Java/PVS-Studio.lic 경로에
  2. maven 프로젝트 기준으로 소스의 pom.xml 아래 설명을 참고하여 내용 추가
    1. Warnings Next Generation플러그인으로 결과 보고서를 만들려면 xml 또는 plog파일만 가능
    2. www.viva64.com/en/m/0044/#IDB449A90AD5

 

 

 

How to Run PVS-Studio Java

PVS-Studio Java static code analyzer consists of 2 main parts: the analyzer core, which performs the analysis, and plugins for integration into build systems and IDEs.

www.viva64.com

 

서비스를 개발하다보면 항상 금칙어처리가 필요하게 됩니다.

금칙어 갯수가 적으면 상관 없는데 갯수가 많으면(특히 중국 서비스하면..;) 성능을 잘 생각해서 처리해야 합니다.

 

관련해서 참고용 TC를 만들어봤습니다.

간단히 만들어서 TC종류는 많지 않고 부족한 부분이 있을 수 있습니다.


1. 금칙어 저장테이블 DDL(참고용)

-- 금칙어 테이블 DDL샘플(Mysql). 글로벌 다국어를 감안하여 금칙어 컬럼은 'utf8mb4_bin'로 정의
CREATE TABLE `bad_word` (
  `pk` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'pk',
  `use_yn` ENUM('Y','N') COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'Y' COMMENT '사용여부',
  `bad_word` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '금칙어',
  PRIMARY KEY (`pk`),
  UNIQUE KEY `UNQ_badWord` (`bad_word`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='금칙어'

 

2. maven dependency

 

<!-- https://mvnrepository.com/artifact/org.ahocorasick/ahocorasick -->
<dependency>
    <groupId>org.ahocorasick</groupId>
    <artifactId>ahocorasick</artifactId>
    <version>0.4.0</version>
</dependency>

3. test case

import org.ahocorasick.trie.Emit;
import org.ahocorasick.trie.Trie;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.Assert;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;

import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.concurrent.TimeUnit;

/**
 * 금칙어 성능 테스트
 *  - 대량(10만개 이상)의 금칙어 키워드 존재시 금칙어 여부 판단에 성능 이슈가 없도록 처리하는 테스트(샘플) 소스
 *  - 아호코라식 알고리즘을 활용: https://en.wikipedia.org/wiki/Aho%E2%80%93Corasick_algorithm
 *
 * @author
 */
class BadWordPerformanceTest {

	private static int initDummyBadwordCnt = 100_000; //더미용 금칙어 초기화 갯수

	private static final String findBadword = "개새끼"; //테스트용 금칙어
	private static final String findBadword2 = "소새끼"; //테스트용 금칙어2

	private static LinkedHashSet<String> badwords = new LinkedHashSet<>(); //linkedhaset이 contain 성능이 가장 좋음: https://dzone.com/articles/java-collection-performance
	private static Trie badwordsTrie; //아호코라식용

	@BeforeAll
	static void init() {

		for (int i = 1; i <= initDummyBadwordCnt; i++) {

			//String randomBadWord = RandomStringUtils.random(30, false, false);
			String randomBadWord = RandomStringUtils.randomAlphanumeric(30);
			badwords.add(randomBadWord);
		}

		System.out.println(String.format("init 금칙어 갯수(컬렉션용): %d", badwords.size()));

		//아호코라식용 초기화
		long startInitAho = System.currentTimeMillis();
		badwordsTrie = Trie.builder().addKeywords(badwords).addKeyword(findBadword).addKeyword(findBadword2).build(); //시간이 많이걸리니까 가능하면 초기화 후 재 사용
		//badwordsTrie = Trie.builder().addKeywords(badwords).addKeyword(findBadword).addKeyword(findBadword2).onlyWholeWords().build(); //시간이 많이걸리니까 가능하면 초기화 후 재 사용
		//badwordsTrie = Trie.builder().ignoreCase().ignoreOverlaps().addKeywords(badwords).build(); //아호코라식용 초기화

		long endInitAho = System.currentTimeMillis();
		System.out.println("아호코라식 초기화 소요시간(ms): " + (endInitAho - startInitAho));
	}

	/**
	 * 아호코라식으로도 완전일치 테스트가 가능하지만 java컬렉션을 이용해서도 구현
	 */
	@Test
	@Timeout(value = 20, unit = TimeUnit.MILLISECONDS)
	public void 금칙어_완전일치_테스트() {

		badwords.add(findBadword); //테스트용 금칙어를 금칙어 셋에 추가해둠(성능 테스트를 위해 만든 대량의 금칙어에 추가)

		final String notExistBadword = findBadword + System.currentTimeMillis(); //확률적으로 존재할 수 없는 금칙어

		long startExactNano = System.nanoTime();
		long startExactms = System.currentTimeMillis();

		Assert.assertTrue(badwords.contains(findBadword));
		Assert.assertFalse(badwords.contains(notExistBadword));

		long endExactNano = System.nanoTime();
		long endExactMs = System.currentTimeMillis();

		System.out.println("\n\n완전일치 금칙어 find 소요시간(nano): " + (endExactNano - startExactNano));
		System.out.println("완전일치 금칙어 find 소요시간(ms): " + (endExactMs - startExactms));

	}

	/**
	 * 성능을 위해서 포함여부 체크는 아호코라식 알고리즘을 사용
	 *  - 구현 java 라이브러리: https://github.com/robert-bor/aho-corasick (maven mvnrepository에는 배포를 안하니 참고해서 직접 구현하거나 소스 내려받아서 빌드 후 사용)
	 */
	@Test
	@Timeout(value = 20, unit = TimeUnit.MILLISECONDS)
	public void 금칙어_포함여부_아호코라식알고리즘기반_테스트() {

		String targetText_1 = "개새끼들이 뛰어놀고 있어요. 소 는 없어요";
		Collection<Emit> emits_1 = excuteAho(targetText_1);
		Assert.assertTrue(emits_1.size() == 1);

		String targetText_2 = "개새끼들이 뛰어놀고 있어요. 옆에는 소새끼들이 있어요";
		Collection<Emit> emits_2 = excuteAho(targetText_2);
		Assert.assertTrue(emits_2.size() == 2);

		String targetText_3 = "개가 뛰어놀고 있어요. 옆에는 소도 있어요";
		Collection<Emit> emits_3 = excuteAho(targetText_3);
		System.out.println(emits_3);
		Assert.assertTrue(emits_3.size() == 0);

	}

	private Collection<Emit> excuteAho(String targetText) {

		System.out.println("\n===== excuteAho: Start ");
		System.out.println("금칙어가 존재하는지 검사할 텍스트:==>" + targetText);

		long startNano = System.nanoTime();
		long startMs = System.currentTimeMillis();

		Collection<Emit> emits = badwordsTrie.parseText(targetText);
		System.out.println("검출된 금칙어 갯수: " + emits.size());
		for (Emit emit : emits) {
			System.out.println(String.format("  금칙어 '%s'에 매칭됨", emit.getKeyword()));
		}

		long endNano = System.nanoTime();
		long endMs = System.currentTimeMillis();

		long duNano = endNano - startNano;
		long duMs = endMs - startMs;

		System.out.println(String.format("아호코라식 기반 금칙어 판별 소요시간. '%d(nano)' | '%d(ms)'", duNano, duMs));
		System.out.println("===== excuteAho: End ");

		return emits;

	}

}

/**
	 * 간단한 컬렉션 contains 성능테스트
	 *  -  https://dzone.com/articles/java-collection-performance
	 *
	 * @param args
	 */
	public static void main(String[] args) {

		long min = 999999999;
		long max = 0;
		for (int t = 0; t < 200; t++) {

			int cnt = 30000;
			LinkedHashSet<String> badwords = new LinkedHashSet<String>(); //min:500 ||  max:15200
			//ArrayList<String> badwords = new ArrayList<String>(); min:148100 ||  max:1110400

			//TreeSet<String> badwords = new TreeSet<String>(); //min:3000 ||  max:31000
			for (int i = 1; i <= cnt; i++) {
				badwords.add(String.valueOf(i));
			}

			long startMs = System.currentTimeMillis();

			long start = System.nanoTime();
			badwords.contains(String.valueOf(cnt));

			long end = System.nanoTime();
			long endMs = System.currentTimeMillis();

			long du = end - start;
			System.out.println(du);

			if (du > max) {
				max = du;
			}

			if (du < min) {
				min = du;
			}

			System.out.println("ms: " + (endMs - startMs));
		}

		System.out.println("\n\n");
		System.out.println("min:" + min + " ||  max:" + max);

	}

+ Recent posts