Jenkins 로 돌리던 Spring Batch trigger 속도 올리기 (1분 내에 여러번 배치 돌리기)

들어가며

Jenkins로 Batch 를 돌리고 있었는데 1분에 한번씩 돌리던 배치를 10초 이내로 줄여야했다.

Jenkins는 Batch 전용 인터페이스도 아니긴 하지만 어쨌던 다른 데몬을 구현하기는 귀찮았다.  Cron이나 Quartz 도 1분 이내로 빨리 돌리는 방법은 없다. 꼼수는 존재하더라. Jenkins도 꼼수는 존재하지만.. 여튼 빠르게 인터넷을 검색했다.

일단 인터넷에 검색해보니 mkyong 님의 가이드는 존재했다. 인터넷에서 Spring 관련 글을 검색하면 나오는 그것.

정석

1
2
3
4
5
6
7
<task:scheduled-tasks>
  <task:scheduled ref="runScheduler" method="run" fixed-delay="5000" />
</task:scheduled-tasks>
<task:scheduled-tasks>
  <task:scheduled ref="runScheduler" method="run" cron="*/5 * * * * *" />
</task:scheduled-tasks>

2가지 타입이 존재하는데 위의것은 5초의 딜레이를 주는 것이고 아래것은 5초에 한번씩 돌도록 하는 것이다.
배치라면 병행해서 돌면 안되니까 위의 것으로 선택하는게 맞겠다.

그런데… 나는 가이드가 있다는 내용만 개발자에게 넘기고 개발이 완료 되었다고 해서 Merge 하고 테스트 해보니 잘 되어서 운영에 올렸다.

문제 발생

그런데 개발에서 MySQl이 에러를 내기 시작했다.
MySQL이 “Deadlock found when trying to get lock; try restarting transaction” Exception
뭐가 잘못된 것일까 운영에서도 곧 에러가 생길까? 일단 하루가 지났는데 운영에는 별 문제가 없었고 개발을 살펴보고 있다가 MySQL에서는 read 할 때 uncommited, commited, repeatable read 등 여러가지 select option 이 존재하는 것을 확인하였다.

1
2
3
4
5
<batch:job-repository id="5jobRepo"
 data-source="dataSourcePool"
 transaction-manager="transactionManager"
 isolation-level-for-create="READ_COMMITTED"
 table-prefix="BATCH_" />

그래서 Deadlock 이 걸린 원천적인 이유도 확인하지 않고
이걸 해결 하기 위해서 job-repository 의 isolation-level-for-create을 READ_COMMITED 로 바꾸는 실수를 범한다.
나중에 알게 되었지만 1분 이내의 배치 개발은 Mkyong 님의 가이드 중 2번째 것이 선택 되었고 parallel 로 돌기 시작한 것이다.

아차.. 똑같은 배치가 2번 돌때의 데이터가 무너지는 낭패가 발생할 수 있는 상황!!!

일단 LOOP 배치를 내리고 빨리 Mkyong 님의 가이드 중에서 첫번째 것을 적용하기 전에 (소슬 바꾸어야 하므로) Jenkins의 설정을 빠르게 읽어내려갔다.

그러다 JOB이 끝나면 다음 JOB을 트리거 할 수 있는데 자기 자신도 선택 할 수 있다는 사실을 알았다.

JOB과 JOB을 트리거 걸고 그 사이에 sleep 을 준 후 Jenkins 시작시에 JOB을 시작하게 하면 이보다 더 좋은 LOOP Batch 는 없을 것 같다는 생각이 들었다.

해결 방법

1. jenkins에 startup trigger plugin 설치

1-1. JOB에 시작시 실행 옵션을 지정

2. JOB 실행 완료후 POST build 에 같은 JOB을 지정

3. Execute Sheel 에 “sleep 15s” 를 추가하여서 매 JOB 사에 잠깐의 휴식시간을 줌.

개이득

만일 처음부터 Mkyong 님의 가이드 중 fixed-delay를 사용했더라면 어땠을까? 일단 sleep time이 source 에 박힌다. jenkins는 UI상에서 delay time을 조정할 수 있다.  물론 Jenkins를 이용해서 배치를 trigger 하고 있는 상황에서나 가능한 설정이지만 말이다. 유용한 툴은 언제나 유용하다.

Spring 4.1 에 등장한 AbstractJsonpResponseBodyAdvice 를 통해서 jsonp 데이타 응답을 만들 때의 한글 처리.

1
2
3
4
5
6
@ControllerAdvice
public class JSONPAdvice extends AbstractJsonpResponseBodyAdvice {
public JSONPAdvice() {
 super("callback");
 }

아무 클래스나 이러한 것을 만들어 두면 메세지 컨버터 작업을 할 때 다음의 advice를 참고해서 JSONP 형태의 응답을 만들어 낼 수 있게 된다.

다만 이 경우 JSONP의 응답 contentType은  application/javascript  로 고정 된다. 따라서 다음과 같이 Override하면 된다.

 

01
02
03
04
05
06
07
08
09
10
11
12
@ControllerAdvice
public class JSONPAdvice extends AbstractJsonpResponseBodyAdvice {
public JSONPAdvice() {
 super("callback");
 }
@Override
 protected MediaType getContentType(MediaType contentType, ServerHttpRequest request, ServerHttpResponse response) {
 return new MediaType("application", "javascript", Charset.forName("UTF-8"));
 }
}

 

Spring 4 Message Converters 의 처리 방법 분석. UTF-8 한글의 UTF-8 출력.

RESTful 한 응답 처리기를 만들기 위해서는 MessageConverter를 이용한다. 출력물을 적절한 json 형태로 만들기 위해서 컨버터를 사용하는 것이다.

mvc-config.xml 에 다음을 설정하면 스프링은 컨버터를 자동으로 등록한다.

1
<mvc:annotation-driven />

다음의 컨버터 중에서 위에서 부터 하나씩 처리가 가능한지 살피고 처리가 가능하면 처리 하고 브라우저로 내용을 flush 한다. 이 때 브라우저에게 지금부터 내가 보내는 내용은 어떠한 인코딩으로 작성되어 있다는 사실을 알리는데 그것이 header 값 중에 하나인 Content-Type:text/html 이다.

명시적인 message converter 를 설정에 등록하지 않으면 Spring 4는 기본적으로 7개의 컨버터를 등록하여 사용한다.

1
2
3
4
5
6
7
org.springframework.http.converter.ByteArrayHttpMessageConverter@3ade80f2
org.springframework.http.converter.StringHttpMessageConverter@49fc954e
org.springframework.http.converter.ResourceHttpMessageConverter@32be2576
org.springframework.http.converter.xml.SourceHttpMessageConverter@64993315
org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter@2a8ee664
org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter@775509e
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@44234093

controller 의 return type 이 String 이면 StringHttpMessageConverter 가 처리하게 되는데 이 Converter의 supported media type은 다음과 같다.

1
2
text/plain;charset=ISO-8859-1
*/*

한편 브라우저에서는 어떠한 요청을 하면서 다음의 응답을 받을 수 있다고 한다. 크롬 등의 브라우저에서 헤더를 보면 알 수 있다.

1
2
3
4
5
6
7
8
text/html
application/xhtml+xml
image/webp
application/xml;q=0.9
text/plain;charset=ISO-8859-1;q=0.8
application/json;charset=UTF-8;q=0.8
application/*+json;charset=UTF-8;q=0.8
*/*;q=0.8

따라서 Spring 은 text/html 가 가장 적당한 응답이라고 판단한다.  그런데 응답에 인코딩 정의가 안되어 있으니 StringHttpMessageConverter 의 기본 인코딩인 ISO-8859 로 데이타를 변조하면서 한글이 깨지게 된다.

명시적으로 컨버터를 등록해보자.

01
02
03
04
05
06
07
08
09
10
11
<mvc:annotation-driven>
 <mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
 <property name="supportedMediaTypes">
 <list>
 <value>text/html;charset=UTF-8</value>
 </list>
 </property>
 </bean>
 </mvc:message-converters>
 </mvc:annotation-driven>

스프링은 명시적인 메세지 컨버터를 맨 위에 추가한다.

01
02
03
04
05
06
07
08
09
10
org.springframework.http.converter.StringHttpMessageConverter@49fc954e
org.springframework.http.converter.ByteArrayHttpMessageConverter@3ade80f2
org.springframework.http.converter.StringHttpMessageConverter@49fc954e
org.springframework.http.converter.ResourceHttpMessageConverter@32be2576
org.springframework.http.converter.xml.SourceHttpMessageConverter@64993315
org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter@2a8ee664
org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter@775509e
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@44234093

규칙대로 한놈만 걸리면 그것으로 출력을 하고 마무리 한다고 했다.

우리는 명시적인 컨버터와 명시적으로 처리하겠다는 미디어 타입(text/html;charset=UTF-8)도 추가했다.

브라우저가 받아들일 수 있다는 미디어 타입과 비슷한 것을 골라서 스프링은 처리 가능한 미디어타입의 순서를 정리 한다. text/html 을 받을 수 있다고 브라우저가 알려줬으니 스프링은 text/html;charset=UTF-8 도 받을 수 있겠거니 판단한 것이고 아무래도 명시적인 캐릭터셋이 있으니 그것을 우선시 한다.

01
02
03
04
05
06
07
08
09
10
text/html;charset=UTF-8
text/html
application/xhtml+xml
image/webp
application/xml;q=0.9
application/json;charset=UTF-8;q=0.8
text/html;charset=UTF-8;q=0.8
text/plain;charset=ISO-8859-1;q=0.8
application/*+json;charset=UTF-8;q=0.8
*/*;q=0.8

결국 StringHttpMessageConverter 는 출력결과를 실제로는 http response의 context-header 를 text/html;charset=UTF-8 로 사용하고 브라우저는 서버사이드에서 UTF-8 로 출력하겠다고 준비된 한글을 UTF-8 로 스트리밍을 받으면서 한글이 깨지지 않게 된다.

search GIT 으로 clone 을 시도 했는데 다음과 같은 에러가 나오는 경우 : Protocol https not supported or disabled in libcurl while accessing …

에러의 시작

GIT 으로 뭔가 clone을 하려고 했는데 Protocol https not supported or disabled in libcurl while accessing … 에러가 났다. libcurl 이 https 를 지원하지 않는다는 것이다. 난 아무것도 안 했는데? 그냥 git 설치할 때 자동으로 깔아주면 안되나?? 누가 libcurl https (http over ssl)  지원 안되는 버전을 깔아놨나보다.

 

잘못된 curl 찾기

일단 curl 과 libcurl 등을 찾아보아야 한다. 커멘드 창에서 다음과 같이 치는 것이 가장 빠르다.

1
2
C:\Users\Seungho>where curl
C:\Windows\System32\curl.exe

구지 삭제는 하지 말고 다음 단계로 돌입!
혹시 C:\Windows\syswow64 디렉토리가 발견되었더라도 일단 다음으로 넘어가도록 하자.

 

다운로드

curl 싸이트에 가서 libcurl.dll 을 다운로드 받아야 한다.

그런데 왜 이렇게 친철하지 않은가. 많은 파일 중에 libcurl 와 SSL 이 동시에 보이는 것을 다운로드 받아야 한다. 나의 경우는 win 8 64bit를 사용하지만 win64 항목에는 libcurl SSL 이 안 보여서 32비트것으로 다운로드 받았다.

 

압축을 풀면 curl-7.40.0-devel-mingw32\bin 디렉토리 내에있는 모든 파일을 위에서 찾아낸 curl.exe 파일이 위치하는 경로에 모두 붙여 넣는다. windows 64비트라고 하더라도 win32용 dll을 다운로드 받았으므로 사실 system32에 넣는것이 바람직하다고 볼 수 있다.

 

RX100 에서 E-PL7 VF-4 12mm F2.0 으로 옆그레이드??

RX100 이 셀카가 안되어서 가족 사진이 괜찮은게 안 나와서..

여러가지 이유로 옆그레이드를 했다.

 

셀카도 되면서

뷰파인더도 되면서

터치 촬영도 되면서

바디 손떨방이 지원 되면서

가볍기도 하고 렌즈 교환도 되면서 크기는 크지 않은 제품을 알아보다가

E-PL7 + VF-4 를 구매하였다.

바디 손떨방이 있으면서 셀카도 되면서 터치 촬영도 되면서 뷰파인더가 되는 제품은 오로지 이 제품의 이 구성 밖에 없다. 

그런데 사진 결과물을 보니.. 그냥 RX100이랑 별 다를바가 없다.

사진은 얼마나 더 많이 찍어야 스스로 만족할 사진이 나오는 것일까?

du 에 –max-depth 옵션이 없을 때 perl을 이용해서 필요한 출력물만 골라보기 (grep의 또다른 방법)

du -k 하면 서브디렉토리의 용량까지 상세하게 표시된다.
이 때 불필요한 depth 출력은 날려버릴 수 있다. 사실은 불필요한 출력은 날리는게 아니라 필요한 부분만 grep하는 것이다.

du -k | perl -ne ‘print if(tr#/#/# == 1)’

설명하자면

du -k 로 뽑아낸 모든 결과중에서
perl 이 -e explression 표현식에 맞는 것만 출력하라.

tr은 치환하라라는 뜻이고
tr#/#/# 는
“/ 를 / 로 치환하라”는 뜻이다.

그리고 그 치환횟수의 결과가 1개인 것 즉. / 이 한번만 등장할 경우 출력하라는 뜻이 된다.

펄은 구분자로 / 이외에도 어떤 기호든 쓸 수 있다. # | ! 대부분 가능하다.
검색하고자 하는 내용 자체에 / 이 있을 경우 /를 구분자로 써버리면
/\/ 와 같이 escape를 계속 해줘야 해서 귀찮다.

올란도 배터리 DIY 셀프 교체기 교환

올란도 배터리를 셀프 교체 하였다.

 

올란도 배터리 교환에 필요한 공구는

 

– 10mm 스패너,

– 13mm 스패너,

– 일자 드라이버

– 배터리는 델코 din59095를 추천한다.

 

인터넷으로 배터리를 구매하게 되면 공구를 보내준다고 하는데, 배터리 수거 + 공구대여 옵션

판매자 실수로 10, 12 mm 스패너를 보낼 수도 있으니 꼭 13mm 스패너를 달라고 하자.

일자 드라이버는 집에 있는 걸로 쓰면 된다.

일자 드라이버는 일반적인 두께. 좀 얇은 것이면 좋을 듯 하다.

 

 

이제 시작이다.

참고로 배터리는 아주 무거운 편이니 여자는 힘들다. 왠지는 모르겠지만 기본적으로 들어 있는 배터리는 손잡이가 없었다. 손잡이 홈은 있는 걸로 봐서 탈부착 손잡이 인 듯?

그리고 아래 설명하겠지만 남자만의 괴력이 필요한 작업이 하나 있다. 아마 자동차 마다 상태가 랜덤일 수도 있다.

 

일단 차량 안에서.. 운전대 왼쪽 아래 있는 본 네트를 여는 버튼을 당긴다.

 

image

그럼 본네트가 살짝 열리는데 손가락을 아래 방향으로 향하게 해서 사이로 손을 넣으면 손가락 끝에 우측으로 이동시킬 수 있는 걸쇠가 손에 닿는다. 우측으로 밀면 본네트를 들 수 있다.

image

그 다음 일자로 길게 눞혀져 있는 긴~ 받힘대를 본네트 상판 하단에 있는 고정 구멍에 넣어서 본네트가 닫히지 않도록 한다.

image

 

 

이제 진짜 시작이다.

 

다음은 공략 순서도다.

① (-) 단자 너트 제거, 10mm 스패너 사용해서 그냥 너트를 푼다.

② (+) 단자 너트 제거, 아래 상세 설명 참고 10mm, 13mm 스패너와 일자 드라이버 사용

③ 배터리 이탈 방지 10mm 스패너 사용 숫자 부분을 10mm 스패너로 풀고 위로 들어 올린다.

④ 아래 상세 설명 참고. 위로 들어 올린다. ⑤ 를 들어내기 위한 전단계

⑤ 아래 상세 설명 참고. 극악의 힘을 필요로 한다.

 

image

 

(+) 부분 우측에 보면 십자 드라이버로 누를 수 있는 홈이 있다. 이것을 누르면서 + 배터리 접속 부 커버를 열 수 있다. 화살표 부분을 집중 공략. 반대쪽에도 하나 있다.

 

image

 

10mm 스패너로 2번을 풀고 13mm 스패너로 3번을 푼다.

 

image

 

배터리를 들어내려면 주면에 있는 몇가지를 추가로 제거 해 주어야 한다.

 

일단, 아래 화살표 부분을 잘 확인해서 은색 이상하게 생긴 것을 들어서 빼야 한다.

 

image

 

이제.. 마지막 하이라이트.. 배터리 교체를 거의 포기 할 뻔하게 만든 그것..

인터넷을 아무리 검색해도 설명이 나오지 않던 그것..

아래 사진의 주황색 마름모..철판..

image

 

깊숙히 15cm ~ 20cm 정도 쳐다보면 뭔가 철판을 들어내는 것을 막고 있는 1.5cm 정도 되는 네모난 걸쇠 같은 것이 있다.

 

image

 

오른손으로는 마름모 철판을 탕탕탕 치면서 동시에 십자 드라이버로 네모난 검정 걸쇠를 최대한 못살게 굴고 (아래 그림에서는 빨간 네모)

왼손으로 장갑을 끼고 컴컴한 굴을 지나 손가락을 깊히 넣은 후 철판을 차량 앞 방향으로 있는 힘껏 당긴다.

그러면서 왼손의 다른 손가락으로 철판을 위로 들어 올린다.

 

오오오… 이것은… 종합 예술과도 같다.

 

image

 

오오.. 여기서 남자 아니면 절대 못할 것 같다는 생각이 들었다.

 

어느 순간 철컹하고 철판이 들리면 나머지는 순조롭다.

 

배터리를 들고 (꽤 무겁다.. 하지만 이미 체력을 소진한 상태 ㄷㄷ)

교체하고 (이 때, 배터리 양옆에 테이프나 실레콘 제거!!!!)

 

배터리를 연결한다. (+) 단자 (-) 단자 순으로..

 

마지막으로 주의 할 사항이..

방전된 배터리를 뺄 때는 전기 걱정이 없이 (-) 단자 (+) 단자 순으로 스패너를 마구 만졌는데..

 

풀~ 파워 새 배터리는 (-) 단자를 연결할 때 아주 작게 불꽃이 튈 수 있다.

스패너 작업을 할 때.. 불안해서 장갑을 3겹 끼고 살짝 건들여 봤는데 손에 전기 느낌은 없었다. 장갑을 낀 채로 (-) 단자까지 연결을 마무리 했다.

 

부릉부릉!!!

TELNET 이 설치 되지 않은 머신에서 타 서비스 포트 상황 확인 하기 (방화벽 확인) port check without telnet “nc –zv IP port”

[nzin4x@linuxmachine:/app/nzin4x]nc -zv 222.222.222.222 1524
nc: connect to 222.222.222.222 port 1524 (tcp) failed: Connection refused
[nzin4x@linuxmachine:/app/nzin4x]nc -zv 222.222.222.222 1522
Connection to 222.222.222.222 1522 port [tcp/ricardo-lm] succeeded!
[nzin4x@linuxmachine:/app/nzin4x]

 

telnet 이 설치 되지 않은 머신에서

사용할 수 있는 방법이다.

 

curl이 깔려 있다면 이 방법도 사용할 수 있다.

 

curl http://222.222.222.222:1524

curl http://222.222.222.222:1522

 

뭐라도 되는 것과 접속이 실패되는 것 둘로 나눠서 확인 할 수 있다.

Eclipse 가 자주 죽을 때 해볼 만 한 것. softcamp 문서보안 죽이기.

Eclipse 가 로딩 되면서 죽는 현상이 많아서 이것저것 해보다가 꽤 효과가 좋은 것에 대한 기록.

.metadata/.plugins/org.eclipse.debug.core 삭제

.metadata/.plugins/org.eclipse.e4.workbench 삭제

그리고 문서 보안 프로그램

최근 우리 회사는 정직원 이외의 협력지원에게도 문서보안을 설치하게 했는데 이 프로그램 때문에 Eclipse 가 계속 죽는다는 이슈가 있었다.

이제까지는 문서보안과 Eclipse의 연결고리는 생각해볼 수 조차 없었는데…

강한 힌트였다.

 

.metadata/.plugins 내에도 txt 파일이 꽤 있다. 확실한 것은 아니지만 이러한 것을 문서보안이 암호화 하는 것은 아닐까? 그런데 로딩하다가 무언가 이상한 상태에서 Eclipse 가 죽는 것은 아닐까?

 

우리 회사는 오른쪽 아래에 다음과 같은 문서보안 관련 Tray 가 뜨는데 이것을 죽이는 방법은 다음과 같다.

 

Windows 폴더 내의 softcamp 폴더가 핵심.

 

 

 

우측 클릭해서 속성을 열고

 

SYSTEM 계정으로는 모든 것을 거부 시킨다. 그리고 리붓 하면 문서보안의 일부(?) 기능이 작동 안 하게 된다.

이 때, side Effect 가 있는데..

이 프로그램을 쓰는 2-3곳을 다녀보니 실제로 문서 보안이 꺼지는 곳이 있었고 안 꺼지는 곳이 있는데 꺼지는 곳이라면 USB도 사용할 수 있게 된다.

 

내가 지금 일하고 있는 곳은 테스트를 해본 것은 아니지만 Tray는 살아 있다. 아마 다른 디렉토리에 다른 프로세스가 하나 더 있나 보다

나에게 중요한 것은 이제 Eclipse가 죽지 않는다는 것이다.