jmeter를 사용한 가변길이 TCP Binary 패킷 처리 방법

Use Dynamic Binary Packet for Jmeter

jmeter를 사용한 가변길이 TCP Binary 패킷 처리를 설명합니다.

Test 시나리오

  • 테스트할 서버는 로컬에 1234 port를 listen하고 있습니다.
  • 요청 패킷에 요청 seq가 포함됩니다.
  • 요청/응답 header 패킷은 동일합니다.
struct header_ {
    unsigned char version;      // 1 byte
    short int seq;              // 2 byte
    unsigned char str;          // 1 byte
    short int type;             // 2 byte
    short int bodyLen;          // 2 byte
}
  • body는 일반 문자열입니다.
    • 요청 패킷 전문은 CSV 파일을 읽어 내용을 만듭니다.
  • thread(socket connection) 는 10개로 합니다.

apache jmeter 소스 받기

jmeter의 sampler에서 tcp 가변길이 패킷을 처리할 수 있는 방법이 없으므로, plug-in을 만들어야 합니다. Java Request plug-in을 만들기 위해 jmeter 소스를 받습니다.

1. github 에서 fork

https://github.com/apache/jmeter

2. 내 PC에 checkout

git clone https://github.com/lmk/jmeter

3. branch 생성

git branch DynamicLengthTCP

4. eclipse import 준비

ant setup-eclipse-project

5. eclipse > File > Import > Existing Projects into Workspace

코드 추가

1. org.apache.jmeter.protocol.java.test 아래 DynamicLengthTCP, SingletonSocketMap class 추가

  • DynamicLengthTCP
    • AbstractJavaSamplerClient 상속받아 구현했습니다.
    • 자세한 내용은 JavaTest class 참고했습니다. (JavaTest를 그대로 복사해서 생성)
    • Jmeter에서 생성한 Binary 패킷을 plug-in으로 가져오기 위해서는 hex string으로 변환해서 넘어옵니다. (BinaryTCPClientImpl 참고) "RequestData" 저장된 요청 패킷을 byte[]로 변환합니다.(여기)
    • 요청 패킷을 전송합니다.
    • 응답 패킷을 header 먼저 받고, body 길이만큼 응답 패킷을 받습니다.
    • 받은 Binary 패킷과 상태 정보를, jmeter에서 받을 수 있도록 SampleResult에 담습니다.
  • SingletonSocketMap
    • Thread별 Socket 재사용을 위한 class 입니다.

2. git add 및 commit, push

Export jar

1. Package Explorer 에서 추가한 파일 선택

  • DynamicLengthTCP.java
  • SingletonSocketMap.java

2. Export > JAR File

3. \lib\ext 경로 아래 저장할 jar 파일명 입력

  • \lib\ext에 jar 파일을 넣어두면, jmeter 실행시 plugin으로 인식해서 읽어들입니다.
  • github에서 받은 소스의 \lib\ext 경로가 아니라, jmeter 실행파일이 위치한 경로입니다.
  • 제 경우는 소스는 "D:\src\jmeter"에 받았고, jmeter 실행파일은 "D:\Tools\apache-jmeter-5.0"에 받았으므로, jar 파일을 저장할 경로는 "D:\Tools\apache-jmeter-5.0\lib\ext" 였습니다.

Test

1. bin/jmeter.bat 실행

2. "Test Plan" 우클릭 > Add > Thread Group추가

- Number of Threads를 10 으로 수정합니다.
- Loop Count는 Forver 체크하면 Stop 할때까지 반복하지만, 테스트니까 기본값 1로 유지합니다.

3. "Thread Group" 우클릭 > Add > Config Element > Counter 추가.

  • 요청 패킷 seq 증가를 위한 것입니다.
  • 시작값, 증가값은 1로 설정합니다.
  • seq 는 패킷의 자료형 크기보다 작게 max 값을 정합니다.
    • 여기서는 short 형이므로 30000으로 잡았습니다.
  • Exported Variable Name에 jmeter 내부에서 사용할 변수명 "reqCount"를 입력합니다.

4. "Thread Group" 우클릭 > Add > Config Element > CSV Data Set Config 추가.

  • Filename에 "msg.txt"로 지정합니다.
    • msg.txt 파일 내용은 아와 같습니다.
    메시지1
    메시지2
    메시지3
    
    • 여기서는 row전체를 하나의 field를 사용합니다.
  • Variable NAmes에 jmeter 내부에서 사용할 변수명 "msg"를 입력합니다.
  • Recyle on EOF: row가 끝나면 처음부터 다시 읽도록 True로 설정합니다.
  • Stop thread on EOF: row가 끝나도 테스트를 계속 할것이므로 False로 설정합니다.

5. 테스트 결과를 쉽게 보기 위해 Listener 추가

  • "Thread Group" 우클릭 > Add > Listener > View Results Tree
  • "Thread Group" 우클릭 > Add > Listener > Summary Report
  • "Thread Group" 우클릭 > Add > Listener > Response Time Graph

6. "Thread Group" 우클릭 > Add > Sampler > Java Request

  • Classname은 이번에 만든 DynamicLengthTCP 선택합니다.
  • HostIP: 접속할 서버 IP 127.0.0.1 입력합니다.
  • Port: 접속할 서버 Port 1234 입력합니다.
  • Connect Timeout: 접속할때 처리할 timeout 시간은 대략 200 입력합니다.
  • Response Timeout: 응답 패킷을 받을때 timeout 시간은 대략 200 입력합니다.
  • Re-use connect: 하나의 thread에서 connect socket을 재사용할지 여부인데, True를 입력합니다.
  • Set NoDelay, SO_LINGER는 socket 옵션 참고합니다. (모르겠으면 flase, 0)
  • Size of response header: 응답 패킷의 해더 사이즈를 입력합니다 (여기서는 8)
  • Offset of response body length: 응답 해더에서 body 사이즈가 포함된 위치를 입력합니다 (여기서는 6)
  • Size of response body length: body 사이즈가 저장된 크기를 입력합니다 (여기서는 2)
    • DynamicLengthTCP 에서는 사이즈가 1, 2, 4 일때만 처리 가능합니다.

7. "Java Request" 우클릭 > Add > Pre Processors > BeanShell PreProcessor

  • 요청 패킷을 만들기 위해 BeanShell을 작성합니다.
import java.io.*;
import org.apache.jmeter.protocol.tcp.sampler.*;

try {
  // msg from CSV Data Set Config
  String str = vars.get("msg");
  byte[] msg = str.getBytes();

  byte[] pkt = new byte[8 + msg.length];
  
  // version
  pkt[0] = (byte)0x01;
  
  // seq from Counter
  short seq = Short.parseShort(vars.get("reqCount"));
  pkt[1] = (byte)(seq>>>8);
  pkt[2] = (byte)seq;
  
  // constString
  pkt[3] = (byte)0x00;
  
  // type
  pkt[4] = (byte)0x00;
  pkt[5] = (byte)0x01;
  
  // len
  pkt[6] = (byte)(msg.length>>>8);
  pkt[7] = (byte)msg.length;
  
  // msg
  System.arraycopy(msg, 0, pkt, 8, msg.length);
  
  // byte array to hex string
  char[] hexArray = "0123456789ABCDEF".toCharArray();
  char[] hexChars = new char[pkt.length *2];
  for(int i=0; i<pkt.length; i++) {
  	int v = pkt[i] & 0xff;
  	hexChars[i * 2] = hexArray[v>>>4];
  	hexChars[i * 2 + 1] = hexArray[v & 0x0f];
  }
  
  str = new String(hexChars);
  vars.put("RequestData", str);
  
  log.info("Request data size: " + str.length());
}catch(Exception e) {
  log.info("Detect BeamShell PreProcessor Exception"+ e);
  prev.setTopThread(true);
}

8. "Java Request" 우클릭 > Add > Pre Processors > BeanShell PreProcessor

  • 응답패킷 파싱을 위해 BeanShell을 작성합니다.
import java.nio.ByteBuffer;
import org.apache.jmeter.samplers.SampleResult;

byte[] resData = prev.getResponseData();
int resSize = resData.length;

byte[] buf1 = {resData[4], resData[5]};
byte[] buf2 = {resData[6], resData[7]};

short type = ByteBuffer.wrap(buf1).getShort();
short bodySize = ByteBuffer.wrap(buf2).getShort();

log.info("Rsponse Size: " + resSize +  ", type: " + type+ ", bodySize: " + bodySize);

9. Run > Start

  • 테스트 및 결과 확인합니다.


Windows 에서 포트 포워딩 기능 사용하기.

VMWare등을 사용하는데, Bridged 모드로 설정하지 않고 (IP 여유가 없거나, 설정하기 귀찮을때..)

기본값인 NAT 모드로 설정해두고 아래 명령으로 하시면 됩니다.


CMD 창을 관리자 모드로 열고,


* Forwarding 설정


> netsh interface portproxy add v4tov4 listenport=80 listenaddress=192.168.0.100 connectport=80 connectaddress=192.168.229.100

 


* Forwarding 해제


> netsh interface portproxy delete v4tov4 listenport=80 listenaddress=192.168.0.100

 


* 설명

    - listenport: 내 PC에서 Listen할 Port

    - listenaddress: 내 PC에서 Listen할 IP

    - connectport: VM에 포워딩할 포트

    - connectaddress: VM의 IP

리눅스 배포판별 docker 설치 방법

Docker 설치

공식 문서: http://docs.docker.com/installation/

  • 64 bit 기준으로 테스트 해봤습니다.


Red Hat Enterprise 6.2

공식 문서에는 rhel 7 부터 지원하지만, 아래 방법으로 설치 가능

  • yum 저장소를 centos 로 수정.
rpm -ivh  http://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm

vi /etc/yum.repos.d/epel.repo
# cd /etc/yum.repos.d
# cp rhel-debuginfo.repo rhel-debuginfo.repo.bak
# vim rhel-debuginfo.repo

baseurl=http://download.fedoraproject.org/pub/epel/6/$basearch
#mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-6&arch=$basearch
  • update
yum repolist
yum update
  • Disable selinux & reboot
vi /etc/selinux/config
SELINUX=disabled

reboot now
  • 인증키 생성
rpm --import http://mirror.centos.org/centos/6/os/x86_64/RPM-GPG-KEY-CentOS-6
  • update
yum install libdevmapper.so.1.02
  • docker 설치
yum -y install docker-io
reboot now


Cent OS 6.6

yum install http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
yum install docker-io


Ubuntu 14.04

apt-get update
apt-get upgrade
apt-get install -y less vim openssh-server curl

apt-get install docker.io
    or
curl -sSL https://get.docker.com/ | sh


Debian 8

공식 문서대로는 설치가 안됨.

apt-get update
apt-get upgrade
apt-get install -y less vim openssh-server curl
wget http://ftp.kr.debian.org/debian/pool/main/a/apparmor/libapparmor1_2.9.0-3_amd64.deb
dpkg -i libapparmor1_2.9.0-3_amd64.deb
wget http://ftp.kr.debian.org/debian/pool/main/a/apt/libapt-pkg4.12_1.0.9.8.1_amd64.deb
dpkg -i libapt-pkg4.12_1.0.9.8.1_amd64.deb
wget http://ftp.kr.debian.org/debian/pool/main/a/apt/apt-transport-https_1.0.9.8.1_amd64.deb
dpkg -i apt-transport-https_1.0.9.8.1_amd64.deb
curl -sSL https://get.docker.com/ | sh



최적의 makrdown editor는 sublime text

OSX에서 다시 Windows환경으로 넘어오면서, makrdown editor를 찾아 해맸다.

결론은 OS 구분없이 Sublime Text 였다.

[Sublime Text 3] + [MarkdownEditing] 플러그인 + [OmniMarkdownPreviewer] 플러그인 + [Table Editor] 플러그인 조합이다.

문제는 Windows 환경에선 한글 입력창이 이질감이 느껴진다는 것인데,

그것도 [IMESupport] 플러그인으로 완벽하진 않지만 쓸만해 졌다.

하지만, OS 차이로 일부 단축키가 바뀐건 아직 적응이 잘 안된다.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
2017-04-04

[Visual Studio Code] + [Preview] 플러그인 조합을 사용해보고 있는데, 이것도 좋다.


문자열 치환

  • 하나의 파일에서 치환

    • vi 사용:%s/old/new/g
    • sed 사용sed -i 's/old/new/g' ./file.html
  • 여러 파일을 한꺼번에 치환find ./ -name "*.html" -exec sed -i 's/old/new/g' {} \;
  • 특정 파일만 제외하고 치환find . ! \( -name '*.jar' -prune \) -exec sed -i 's/old/new/g' {} \;


shell script로 구현하는 URL 상태 체크

Shell Script로 특정 URL의 상태를 체크하고자 합니다.


curl을 이용하면 간단 하네요.

curl은 결과를 exit code로 리턴 합니다.

exit codes list는 아래 man page를 참고하세요.

http://curl.haxx.se/docs/manpage.html )


#! /usr/bin/sh

function check {
     if [ $? -ne 0 ] ; then
         echo "Error occurred getting URL $1:"
         if [ $? -eq 6 ]; then
             echo "Unable to resolve host"
         fi
         if [$? -eq 7 ]; then
             echo "Unable to connect to host"
         fi
         exit 1
     fi

}
curl -s -o "/dev/null" $1
check;


[ 출처 ]

http://answers.google.com/answers/threadview/id/276934.html

shell script로 구현하는 multi tail


multi tail 유틸은 이미 있습니다.


http://www.vanheusden.com/multitail/


하지만, 설치를 해야 하죠


간단하게 shell script로 구현 하는 방법이 있어서 소개 합니다.



$ vi multitail.sh

#!/bin/ksh

function sig_int
{
   echo 'sig_int'
   kill `jobs -p`
}

for file in "$@"
do
  tail -f $file &
done
trap 'sig_int' 2

wait


이상입니다.

솔라리스 메일 전체 삭제

솔라리스에서 메일 전체 삭제 벙법.

콘솔에서 아래와 같이 입력하면 된다.


$ printf "d*q"|mailx -N


[출처] 

http://www.unix.com/unix-dummies-questions-answers/38598-delete-all-mails-solaris.html

CVS Commit 로그를 남겨보자

-. 요구사항 파악 및 설계

적용 패키지 작성을 위해 수정된 파일 목록을 추출 할 방법을 찾아 보기로 했다.
업무 때문에 소스 관리를 CVS 로 하고 있다. CVS의 단점은 개별 파일이력은 확인 할 수 있는데, 
수정된 파일 목록을 뽑기가 쉽지 않다는 것이다.

구글링을 해보니 CVS에서 Commit 시점에 뭔가 처리할 수 있다는 것을 알았다.

먼저, CVS 서버에 접속해서 저장소의 CVSROOT 디렉토리를 보자

-rwxrwxr-- 1 cvs cvs       544  4월 22 09:23 checkoutlist

-rwxrwxr-- 1 cvs cvs       694  3월 21  2009 checkoutlist,v

-rwxrwxr-- 1 cvs cvs       882  4월 26 16:03 commitinfo

-rwxrwxr-- 1 cvs cvs       959  3월 21  2009 commitinfo,v

-rwxrwxr-- 1 cvs cvs       993  3월 21  2009 config

-rwxrwxr-- 1 cvs cvs      1192  3월 21  2009 config,v

-rwxrwxr-- 1 cvs cvs       602  2월  8 16:32 cvswrappers

-rwxrwxr-- 1 cvs cvs       801  3월 21  2009 cvswrappers,v

-rwxrwxr-- 1 cvs cvs      1025  3월 21  2009 editinfo

-rwxrwxr-- 1 cvs cvs      1224  3월 21  2009 editinfo,v

-rwxrwxr-- 1 cvs cvs 292081967  4월 29 18:14 history

-rwxrwxr-- 1 cvs cvs      1245  4월 21 11:32 loginfo

-rwxrwxr-- 1 cvs cvs      1367  3월 21  2009 loginfo,v

-rwxrwxr-- 1 cvs cvs      1151  3월 21  2009 modules

-rwxrwxr-- 1 cvs cvs      1350  3월 21  2009 modules,v

-rwxrwxr-- 1 cvs cvs       564  3월 21  2009 notify

-rwxrwxr-- 1 cvs cvs       763  3월 21  2009 notify,v

-rwxrwxr-- 1 cvs cvs       640  4월 25 12:43 passwd

-rwxrwxr-- 1 cvs cvs       649  3월 21  2009 rcsinfo

-rwxrwxr-- 1 cvs cvs       848  3월 21  2009 rcsinfo,v

-rwxrwxr-- 1 cvs cvs       879  3월 21  2009 taginfo

-rwxrwxr-- 1 cvs cvs      1078  3월 21  2009 taginfo,v

-rwxrwxrw- 1 cvs cvs       187 11월 10 17:15 val-tags

-rwxrwxr-- 1 cvs cvs      1026  3월 21  2009 verifymsg

-rwxrwxr-- 1 cvs cvs      1225  3월 21  2009 verifymsg,v


위와 같은 파일들을 볼 수 있는데.. 
여기서 commitinfo 란 파일이 CVS에서 Commit 하기 바로전에 실행되는 파일이다.

방향은 정해졌다. 
작업 구성은 아래와 같은 순서로 하기로 했다.

     1. CVS가 Commit 되는 시점에 파일명을 DB에 저장한다.
         -> CVSROOT/commitinfo에 등록할 쉘 스크립트를 작성.
     2.  저장된 DB를 조회할 간단한 WEB Page 제작
         -> 평소 관심을 갖고 있던, jQuery를 이용.

[ 1. CVS가 Commit 되는 시점에 파일명을 DB에 저장한다 ]

commitinfo 파일에 아래와 같이 내용을 추가 한다.

$ vi CVSROOT/commitinfo 

ALL /home/cvs/myproject/CVSROOT/cvscommitlog.sh 


DB에 테이블을 생성 한다.

CREATE TABLE CVS_COMMIT
(
   SEQ              NUMBER(10) NOT NULL,
   USERID           VARCHAR2(20) NOT NULL,
   REG_DATE         DATE DEFAULT SYSDATE,
   PROJECT          VARCHAR2(50) NOT NULL,
   FILENAME         VARCHAR2(500) NOT NULL
);

* index는 REG_DATE, TO_CHAR(reg_date, 'YYYY/MM/DDHH24:MI'), USERID, PROJECT 를 걸었다.

commitinfo 파일이 실행할 쉘스크립트를 작성한다.

#!/bin/sh
export ORACLE_BASE=/oracle
export ORACLE_HOME=$ORACLE_BASE/product/10.2.0/db_1
export ORACLE_SID=ORACLE_SID
export PATH=$PATH:$ORACLE_HOME/bin
export ORACLE_OWNER=oracle
export LANG=c
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ORACLE_HOME/lib;

DATE=`date "+%Y-%m-%d %H:%M:%S"`
DT=`date "+%Y%m%d"`
LOG_FILE="/home/cvs/cvs_log/commit.$DT.log"
DIR=$1
USER=$USER
PROJECT=`echo $DIR | cut -d/ -f4`
#(echo ""; id;) >> $LOG_FILE
shift
for file in $*
do
    echo "$PROJECT, $USER, $DATE, $DIR/$file" >> $LOG_FILE
        result=`$ORACLE_HOME/bin/sqlplus oracle_user/oracle_pwd@oracledb <<EOF 
        INSERT INTO CVS_COMMIT 
        VALUES ( (SELECT NVL(MAX(SEQ), 0) +1 FROM CVS_COMMIT), 
                       '$USER', TO_DATE('$DATE', 'YYYY-MM-DD hh24:mi:ss'), 
                       '$PROJECT', '$DIR/$file');
        COMMIT;
        EOF
        `
done



[ 2.  저장된 DB를 조회할 간단한 WEB Page 제작 ]
jsp 파일 하나와 html파일 하나로 이루어졌고, jQuery와 JSON을 이용 했다.
DATE Picker는 jQuery Plugin 중에 하나를 이용했다.

html 내용 보기


jsp 내용 보기



iTunes 없이 iPhone에 음악 전송


나같은 경우 MacBook, 업무용 Notebook, 집 Desktop 이렇게 3개의 컴퓨터를 사용하고 있다.

MacBook에는 주소록과 사진, 집 Desktop에는 동영상, 업무용 Notebook에는 음악들을 
iPhone과 동기화 하고 싶은데, 이런xx!! iPhone은 하나의 iTunes에만 동기화가 가능하다.
( 시도 했다가, iPhone의 음악과 Apps를 몽창 말아 먹었다 ㅡㅡ; )

아직은 iTunes에 적응을 못해서 인지, 불편하기만 하다.

웹 서핑중 iTunes 없이 iPhone에 음악을 전송할 수 있는 어플이 있어서 소개 한다.


CopyTransManager라는 프로그램으로 무료다!

설치시 기본언어로 설치하면, 한글도 지원된다.

일단 업무용 Notebook을 이용하여, 앨범 생성도 해보고, 음악도 전송하여 봤는데.. 잘 된다. ^^
( iPhone 버전 4.1(8B117), iTunes 버전 10.0.1.22, CopyTransManager(Free) 버전 v0.924 )

웹을 통해 사용기를 훑어 보니, 이 어플을 사용했다가 iPhone을 초기화 했다는 글들이 보이는 걸로 봐서는 아직은 불안한 어플인것 같기도 하다.