Introducing 

Prezi AI.

Your new presentation assistant.

Refine, enhance, and tailor your content, source relevant images, and edit visuals quicker than ever before.

Loading…
Transcript

성능테스트

어떤 구조?

2월 22일

어쩌다 만들어졌나?

nGrinder V.2.1

잦은 릴리즈

많은 플랫폼

nLocation

1.5

Arcus

1.0

nLocation

Arcus

BLOC

3.2

BLOC

OWFS

1.0

OWFS

...

Lucy

1.7

Lucy

nLucene

1.2

nLucene

The Grinder를 이용한 테스트

* startConsole.sh

GRINDERPATH=/Users/kwangsub/Dev/app/grinder-3.4

GRINDERPROPERTIES=$GRINDERPATH/grinder.properties

CLASSPATH=$GRINDERPATH/lib/grinder.jar:$CLASSPATH

PATH=$JAVA_HOME/bin:$PATH

java -Xmx1024m -cp $CLASSPATH net.grinder.Console

* startAgent.sh

X 필요한 만큼 반복

GRINDERPATH=/Users/kwangsub/Dev/app/grinder-3.4

GRINDERPROPERTIES=$GRINDERPATH/grinder.properties

CLASSPATH=$GRINDERPATH/lib/grinder.jar:$CLASSPATH

PATH=$JAVA_HOME/bin:$PATH

java -Xmx1024m -cp $CLASSPATH -Dgrinder.hostID="api.agent" net.grinder.Grinder $GRINDERPROPERTIES

CUBRID

8.4

3rd-party

GROUND

GrinderAnalyzer

복사 or 다운로드

CUBRID

USF

3.0

수십 ~ 수백번 반복 ㅜ.ㅜ

USF

XE 1.5

XE

API G/W

1.2

API G/W

(1) 시나리오를 가지고 부하 발생 명령

Java 기반 오픈소스

통합, 커스터마이징 용이

지속적인 성능검증

?

(3) Console의 명령을 받아 부하를 발생

(2) 여러대 Agent에 시나리오를 배포

컴포넌트형식 분리

시나리오 테스트

뭔가를 찾아보자!

Java Process와 Thread로 VUser생성

Enter를 치는 가상의 유저

while(true) {

target.request();

}

OK!

통합테스트셋을 구축하자.

while(true) {

target.request();

}

누가? 어떻게? Request를 만드나?

Process와 Thread의 곱으로 Worker개수가 만들어집니다.

(Virtual User)

Java 8개

* 1 Agent의 Worker 수 == Process * Thread

(Virtual User)

Virtual User 개수 :

* 전체 Agent의 Worker 수 == Process * Thread

* Agent

(Virtual User)

Jython스크립트를 이용한 시나리오

쇼핑몰에서 상품을 구매하는 절차

+

로그인 - 검색 - 상품보기 - 장바구니 - 결제

테스트 시작!

Ver.2.3

Ver.2.4

Ver.2.3

Ver.2.4

500 TPS

501 TPS

USF

500 TPS

250 TPS

USF

800 TPS

900 TPS

BLOC

800 TPS

370 TPS

BLOC

고뇌하기 시작

안도하기 시작

성능 테스트

코드 수정/빌드때마다 측정해야 하는데...

현실! 플랫폼, 개발자들 보다는 서비스, QA 우선

테스트 측정 도구를 많이 설치해서 Pool을 늘려라!?

억!소리나는 상용 성능측정툴

요런 시나리오를 Jython스크립트로 만든단 얘깁니다~

request = test.wrap(HTTPRequest(url="http://대박.com"))

class TestRunner:

def __call__(self):

request.GET("/helloworld.jsp")

부하를 받는 장비, 예를들어 시스템이나 서비스가 될 수 있음

성능이 떨어지진 않았나?

시나리오테스트를 잘 만족하나?

- http://grinder.sourceforge.net

프로젝트 시작!

자주 성능측정할 수 있게

  • 테스트 쉽게
  • 모니터링 쉽게
  • 관리 쉽게

X

더이상의 Terminal

http://grinder.sourceforge.net/index.html

질문할 수 있는 공간이 있습니다.

http://nhnopensource.org/ngrinder_forum

One more thing

정의

하루 고작 2~3명 접속하는 서비스를 만들고 싶은신가요?

1만명, 1000만명이 월활하게 사용할 수 있는 그런 서비스

상상해보세요!

로그인 - 검색 - 상품보기 - 장바구니 - 결제

http://nhnopensource.org/ngrinder

신나게 상품을 고르고 결제를 하려는데...

기다리다가...

예상을 해야하는데...

예상? 잘 안됩니다.

세상엔 변수가 너무 많아요. 이런경우

<- 요런게 변수

접속자수 증가 ? 폭발?

유사한 서비스와 비교

vs.

50 TPS

작년에 오픈할때 평균 나왔어!

아 그래? 나도 그 정도야...?

while(true) {

target.request();

}

우린 개발자!

어떻게?

while(true) {

target.request();

}

<

ENTER? 광클? F5?

CPU 90% ? 100%?

while(true) {

target.request();

}

...

부하를 발생시켰더니...

이제 예상이 가능합니다

1대로 부족하다?

100 Vuser

500 ms Res. Time

50 TPS

장비 증설,

CPU, 메모리 업그레이드,

병목을 찾든...

80% CPU

측정할 수 있게 도움을 주는

https://github.com/nhnopensource

정리하며...

어떻게 테스트?

실전 시나리오, Tip

1. Test랩핑, TPS 1 증가방식

Q : "call이 한번 되었는데 tps가 2씩 증가해요..."

기본 예

못난 예

1. Test랩핑, TPS 1증가 방식

# _*_ coding: utf8 _*_

from net.grinder.script import Test

from net.grinder.plugin.http import HTTPRequest

from net.grinder.script.Grinder import grinder

test1 = Test(1, "First Test")

wrapper = test1.wrap(HTTPRequest("http://11.22.33.44"))

class TestRunner:

def __call__(self):

url = "/ngrinder/get.nhn?username=kwangsub&password=4321"

result = wrapper.GET(url)

# _*_ coding: utf8 _*_

from net.grinder.script import Test

from net.grinder.plugin.http import HTTPRequest

from net.grinder.script.Grinder import grinder

test1 = Test(1, "First Test")

wrapper = test1.wrap(HTTPRequest("http://11.22.33.44"))

class TestRunner:

def __call__(self):

url = "/ngrinder/get.nhn?username=kwangsub&password=4321"

result = wrapper.GET(url)

grinder.sleep(1000)

print wrapper.GET(url).getText()

# 디버깅용

GET은 누구의 메소드일까요?

Test라는 객체 건들면 올라갑니다

2. 간단한 HTTP 호출

* POST 방식

* GET 방식

2. 간단한 HTTP 호출

nGrinder...

# _*_ coding: utf8 _*_

from net.grinder.script import Test

from net.grinder.plugin.http import HTTPRequest

from net.grinder.script.Grinder import grinder

test1 = Test(1, "Dummy GET")

requestGet = test1.wrap(HTTPRequest(url="http://11.22.33.44:8080"))

class TestRunner:

def __init__(self):

print "Init"

def __call__(self):

url = "/ngrinder/get.nhn?username=kwangsub&password=4321"

result = requestGet.GET(url)

grinder.sleep(1000)

# _*_ coding: utf8 _*_

from net.grinder.script import Test

from net.grinder.plugin.http import HTTPRequest

from HTTPClient import NVPair

from net.grinder.script.Grinder import grinder

test = Test(1, "Dummy POST")

requestPost = test.wrap(HTTPRequest(url="http://11.22.33.44:9080"))

class TestRunner:

def __call__(self):

url = "/ngrinder/post.nhn"

params = [NVPair("username", "kwangsub"),

NVPair("password", "4321") ]

result = requestPost.POST(url, params)

앞으로

NHN에서는 얼마나 사용했을까?

3. Random변수 사용, Java API사용

The Grinder기반 성능 측정도구이며 오픈소스

Java API 사용 가능

Random

이 말인즉, 내 lib도 사용 가능?!

3. HTTPRequest, 성공/실패 분기

# _*_ coding: utf8 _*_

from net.grinder.script import Test

from net.grinder.plugin.http import HTTPRequest

from net.grinder.script.Grinder import grinder

from java.util import Random

test = Test(1, "test")

request = test.wrap(HTTPRequest(url="http://11.22.33.44:8080"))

random = Random()

class TestRunner:

def __call__(self):

params = []

params.append("param1")

params.append("param2")

params.append("param2")

randomInt = random.nextInt(len(params))

grinder.sleep(1000)

request.GET("/ngrinder/index.nhn?param=" + params[randomInt])

from your.java.code import Something

...

test = Test(1, "TEST")

obj1 = test.wrap(Something())

class TestRunner:

def __call__(self):

test.hello()

Jython스크립트를 이용하여 시나리오를 이용

hello는 누구의 메소드일까요?

+

JVM상에서 Process와 Thread를 이용해 반복적인 Request를 만듬

4. 1초에 한번씩 호출

2012년 2월 17일

# _*_ coding: utf8 _*_

from net.grinder.script import Test

from net.grinder.plugin.http import HTTPRequest

from net.grinder.script.Grinder import grinder

test1 = Test(1, "Test")

request1 = test1.wrap(HTTPRequest(url="http://11.22.33.44"))

log = grinder.logger.output

class TestRunner:

def __call__(self):

url = "/nhn/performance/1k.xml"

result = request1.GET(url)

last_test_time = grinder.statistics.forLastTest.time

remain = last_test_time % 1000

grinder.sleep(1000 - remain)

Process나 Thread를 늘려가며 RampUp

테스트 단위는 ms

1초 - 수행시간 만큼 Sleep()

Duration은 Process가 동작하는 시간

5. HTTPResponse, 성공/실패 분기

5. Random 변수 사용

설정정보와 CPU, Memory등 리소스 정보가 Cubird에 저장

19,577

74

# _*_ coding: utf8 _*_

# WebPlatformTeam

from net.grinder.script import Test

from net.grinder.plugin.http import HTTPRequest

from net.grinder.script.Grinder import grinder

test1 = Test(1, "hello")

url1 = "11.22.33.44"

request1 = test1.wrap(HTTPRequest(url=url1))

log = grinder.logger.output

class TestRunner:

def __call__(self):

grinder.statistics.delayReports = 1

grinder.sleep(1000)

url = "/nhn/performance/1k.xml"

result = request1.GET(url)

if result.statusCode > 200 or result.text == "null" or result.text.find("hello") < 0:

grinder.statistics.forLastTest.success = 0

else:

grinder.statistics.forLastTest.success = 1

카피 설치, 건 테스트

성공일때 1

실패일때 0

부하를 주는 Agent도 부하를 받을 수 있음

6. Ramp Up

- 점차 부하를 늘려주고 싶을 때

* Process 방식

Java Process를 늘리는 방식

* Duration은 Process의 시간을 나타냅니다.

Thread시간 아닙니다!

?

* Thread 방식

from net.grinder.script.Grinder import grinder

from net.grinder.common import Logger

def log(message):

grinder.logger.output(message, Logger.TERMINAL)

class TestRunner:

def initialSleep( self):

sleepTime = grinder.threadNumber * 5000

grinder.sleep(sleepTime, 0)

def __call__( self ):

if grinder.runNumber == 0: self.initialSleep()

grinder.sleep(500)

log("in __call__()")

모든쓰레드를 띄워놓고 sleep()시켰다

시간 지나면 하나하나 깨우는 방식

ex) sleepTime = grinder.threadNumber * 5000

http://grinder.sourceforge.net/g3/script-gallery.html#threadrampup.py

오픈 소스! It' free!

Demo

소스는 열려있습니다.

마음껏 가져다 쓰시고 기여도 많이 해주세요.

class TestRunner:

def __call__(self):

self.getRegister()

self.postSubmit()

self.getComplete()

def getRegister(self):

result1 = request1.GET("http://deview.kr/register")

def postSubmit(self):

result2 = request2.POST("http://deview.kr/submit")

grinder.sleep(1000);

def getComplete(self):

result3 = request3.GET("http://deview.kr/complete")

from net.grinder.script import Test

from net.grinder.plugin.http import HTTPRequest

test1 = Test(1, "Register page")

request1 = test1.wrap(HTTPRequest())

test2 = Test(2, "Submit page")

request2 = test2.wrap(HTTPRequest())

test3 = Test(3, "Complete page")

request3 = test3.wrap(HTTPRequest())

class TestRunner:

def __call__(self):

self.getRegister()

self.postSubmit()

self.getComplete()

def getRegister(self):

result1 = request1.GET("http://deview.kr/register")

def postSubmit(self):

result2 = request2.POST("http://deview.kr/submit")

def getComplete(self):

result3 = request3.GET("http://deview.kr/complete")

1. request.GET("http://deview.kr/register")

2. request.POST("http://deview.kr/submit")

3. request.GET("http://deview.kr/complete")

테스트

리포트

몇명의 VUser를 이용해 어떻게 부하를 발생할지 설정합니다.

완료된 테스트의 요약결과 리포트를 보여주고 상세한 데이터와 그래프를 보여줍니다.

- Agent 선택

(일을 하는 물리장비)

- VUser 개수

(Enter를 누르는 가상유저)

nGrinder 제공 그래프

- 시나리오 스크립트

- 모니터링 대상 장비

* 주의

- 테스트 시간 (Duration or Count)

- Ramp Up

- 그외 변수 설정, Path등록 등

The Grinder

vs.

* Duration은 Process의 시간을 나타냅니다.

Thread시간 아닙니다!

원하는 만큼 Agent 접속

시나리오

CSV Export

로그인 - 검색 - 상품보기 - 장바구니 - 결제

원하는대로~

① http://대박.com/login

② http://대박.com/search

엑셀 그래프

③ http://대박.com/view?id=1

④ http://대박.com/basket

④ http://대박.com/pay

* 각 요청을 뒤죽 바꿔서 시나리오를 잡을 수 도 있음.

프로젝트

시나리오, 결과요약, 리포팅을 프로젝트단위로 관리합니다.

- TPS현황(차트, 표)

- Resource사용현황(CPU, Memeory등)

- 성능 테스트 묶음관리

- Jython을 이용한 시나리오

새로운 프로젝트 생성

Agent를 모니터링하는 이유

- library jar 추가

- 테스트 히스토리

기존 프로젝트와 스크립트들

- 웹기반 에디터 제공

- 결과 리포트 관리

- 스크립트 유효성 체크

lib연결

- 시나리오 관리

The Grinder

VS.

Agent설치위치/log

발표자 : 김광섭

vs.

load average : 136.57

load average : 1.36

주의. Targe뿐 아니라 Agent도 부하를 받을 수 있다.

The Grinder

VS.

진행중인 테스트현황 그래프, Target, Agent장비의 리소스그래프를 보여줍니다.

시나리오를 생성/편집하고 필요한 Lib를 추가합니다.

모니터링

스크립트

만족할때까지 반복!

while(true) {

target.request();

}

Learn more about creating dynamic, engaging presentations with Prezi