메모장

블로그 이미지

동팡

https://github.com/ehdvudee

'java'에 해당되는 글 8건

제목 날짜
  • 안전한 비동기 처리 전략(Feat. Spring) 2021.04.02
  • Java Performance를 올리는 코딩 Best Practice 2020.05.20
  • Integer.ParseInt() vs Integer.valueof()1 2017.05.08
  • 자바 Reflect Class, Method 2017.04.29
  • Log4j 2 Manual - Introduction 2017.04.29
  • 자바 제네릭 지우개 2017.03.31
  • 자바 Keytool(셀프 사인) Server/Client 소켓 통신 방법 2017.03.31
  • 로그 간단한 개념 및 라이브러리 2017.03.30

안전한 비동기 처리 전략(Feat. Spring)

개발관련/삽질 2021. 4. 2. 17:41

목차

1부. 안전한 비동기 처리 설계 경험

  • 개요 
  • 비동기 
  • 저널링
  • 비동기 설계 

2부. Spring을 통한 비동기 처리 개발

  • 개요
  • ThreadPoolTaskExecutor
  • ThreadPoolTaskScheduler

1부. 안전한 비동기 처리 설계 경험

개요

TiberoDBMS를 사용하는 상태, 이 DBMS는 너무 느리다. DBA 또는 DB 엔지니어가 설정을 어떻게 했는지 모르는데 진짜 느리다. 아무리 DBMS 동접자가 많다한들 이런식으로 느린거는 진짜 선을 넘은 것이다. 

각설하고.. 고객님께서 삭제 API가 느리다고 콜이 왔다. 이래저래 DB 쪽에 문제가 많았는데.. 많은 과정을 해결하고 비동기 처리 적용을 하는 방향이 나왔음. 

 

CRU(Create, Read, Update) 부분의 경우, 해당 작업 후, 사용의 여지가 있기 때문에 비동기 처리하기에 적합하지 않다. Delete의 경우, 비동기 처리를 해도 무방하였다. 그러면 비동기를 처리해보자.

비동기

비동기를 잘 설명할 수 있는 생활의 예는 우리는 커피 주문 후 주문대에서 계속 기다리지 않고 벨을 받고 다른 곳에서 핸드폰을 하거나 일행과 대화를 하며 "다른 행위"를 한다. 이게 바로 비동기닼ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

 

동기의 경우, 커피 주문 후 주문대에서 아무것도 안하고 커피만 나올 때 까지 기다리는 것이다. 

 

필자의 몇몇 프로젝트는 비동기를 넣은 용례(아래 그림의 작업을 비동기로 처리함)가 존재한다. 아래 그림의 설명은 다음과 같다.

  • A  프로세스 시작
  • B 프로세스 시작 요청
  • B 프로세스 시작 요청 후 대기하는 것이 아닌 A 프로세스 재개
  • A 프로세스 완료 후, B 프로세스 완료 상태 확인 후, 반환

비동기 처리할 때 조심해야할 사항은 무엇일까? 제일 주의해야할 사항은 작업의 무결한 처리이다. 동기 처리의 경우 눈으로 확인이 가능하지만, 비동기 처리의 경우, 눈으로 확인하기 힘들다. 그래서 우리는 "비동기는 실패할 수 있다." 라는 가정하에 개발을 하였다.

저널링

비동기 처리 기술은 아니지만, 파일 시스템의 변경사항을 반영하기 전에, 저널안에 변경 로그(추적할 수 있는 기록)들을 저널에 기입한다. 파일 시스템 변경사항을 복구할 때 해당 저널들을 참고하여 복구한다.

저널링을 잘 이해하기 좋은 것은 Sqlite 트랜잭션 개념 중, "저널 모드"를 이해하면 좋다. Sqlite는 1개 프로세스만 Write 작업(CRU)을 할 수 있다. Sqlite의 트랜잭션을 진행할 때 1개 프로세스가 DB 파일에 Write 작업을 실시한다. 이 때 생성되는 임시 파일이 JOURNAL 파일이다. 반영사항은 DB파일에 기록하고 복구할 파일? 기록들은 JOURNAL에 기록하는 것이다. 만약 Commit이 아닌 Rollback을 진행할 때 JOURNAL 파일을 통해 Rollback을 실시한다. 이런 저널링은 리눅스 파일 시스템 뿐만아니라 많은 곳에서 활용한다.

 

저널링을 하는 이유는 다음과 같다. 작업 중, 얘기치 못한 이유로 작업을 실패했을 때 해당 지점을 찾아서 복구 또는 작업의 완료를 진행하기 위해서이지 않을까 생각이든다. 또는 시점 복구 기법을 사용할 때 이것을 쓰지 않을까? 생각한다.

 

(리눅스 파일 시스템에서 사용된다.) <<- 이 부분 분석/공부 후 추가 기재 필요

 

(번외로 면접에서 비동기 처리 관련 답변을 했었는데 내가 비동기 개발할 때 저널링 기법을 사용했다. 근데 그 때 당시 저널링 방식이 뭔지 몰랐다. 면접관이 묻더라.. "그런 방식을 뭐라하는지 아세요~?" 생각하다 잘 모르겠습니다. 했는데, 저널링이라고 한번 알아보라고 말씀해주셨다. 정말 고마웠다. 이런 피드백... 근데 Sqlite 분석할 때 분석했던 내용이더라... 에휴... 근데 몰랐었음... 그리고 이 글을 작성하는 동기도 주셨지... )

 

저널링의 발전이 DB redo log가 아닐까 생각이 든다... 

비동기 설계 

위에서의 설명과 같이 우리는 완벽한 저널링은 아니더라도 저널링 기법을 본 받아서 비동기 처리 프로세스를 설계해야한다. 

(아래 그림은 기존 설계 사항에서 몇개 삭제하였다.)

순서는 다음과 같다.

  • 클라이언트의 삭제 요청이 들어온다.
  • Server A는 삭제하고자하는 정보들의 식별정보와 삭제 요청한 주체에 대한 정보들을 특정 디렉토리에 기재한다(저널링, 간단한 저널링이다.).
  • 파일 입력 완료 후, 비동기 쓰레드 풀에 notify를 진행한다.
  • Cleint에 삭제 성공을 반환한다(202 Accepted).
  • 비동기 쓰레드 풀은 특정 쓰레드를 지정하여 로직을 진행한다.
  • 삭제하고자하는 식별정보는 파일에 입력했지만 메모리에 보관하고 있기 때문에 조회를 실시하지 않아도 된다.
  • 삭제를 진행한다. 
  • 삭제 트랜잭션은 Server A, B에서 일어나기 때문에 DB A, B 를 접근하여 삭제를 실시한다(이것도 비동기 로직을 태운다 ㅋㅋㅋ).
  • 삭제 완료 후, 결과 감사로깅을 진행한다. 
  • 삭제 트랜잭션을 성공적으로 완료하였다. 
  • 파일에 입력한 것(JOURNAL 파일)을 삭제한다.

위의 사항이 기본 순서이다. 그러나 위의 작업 중 얘기치 못한 시스템 오류로 서버가 종료 되었을 경우, 다음과 같이 진행한다.

  • Destroy 스케줄러는 주기적으로 디렉토리에 있는 파일들을 검사한다. 
  • 파일이 있는 경우, 정상적으로 삭제를 하지 못한 것이다. 
  • 또한 이 때 주의할 사항이 Thread-Safe이다.
  • 비동기 쓰레드가 삭제 하고 있을 때 스케줄러가 접근할 수 있다. 
  • Lock 방식으로 Thread-Safe하게 개발한다. 먼저 파일을 획득하고 선점하면 아무도 접근하지 못한다. 
  • 스케줄러는 Lock을 획득하여 삭제를 시도한다. 
  • 스케줄러는 삭제 트랜잭션을 성공적으로 완료한다.
  • 스케줄러에 대한 결과 값을 감사로깅을 한다.

위의 사항에서 "API 감사로그", "스케줄러 감사로그"를 통해 작업의 완료 여부를 확인할 수 있다.

더 나아가 발전하면 Admin 툴을 이용하여 비동기 실패 여부를 모니터링 기능을 제공할 수 있다.

2부. Spring을 통한 비동기 처리 개발 

개요

Spring을 이용하여 비동기 처리 개발할 때 ThreadPoolTaskExecutor와 ThreadPoolTaskScheduler를 사용하였다. 쉬운 비동기 처리를 위해 ThreadPooltaskExecutor를 사용했으며, 비동기 처리의 실패를 보완하기 위해 ThreadPoolTaskScheduler를 통해 실패한 요청에 대해 재시도하는 로직을 개발하였다. 간략하게 위의 두 가지 사항에 대해 알아보자.

ThreadPoolTaskExecutor

 

Spring에서 설정과 어노테이션의 설정으로 간략하게 비동기 처리를 개발할 수 있다. 스프링의 AOP 전략으로 비동기 처리를 깔끔하게 한다 생각해도 좋다(트랜잭션과 비슷한 원리). 트랜잭션 또한 @Transactional 어노테이션을 통해 트랜잭션 관련 코드가 내 비지니스 로직을 침투를하지 않는다. 이것을 가능하게 하는 것이 SpringAOP이며 Spring 비동기도 마찬가지이다. @Async 어노테이션을 통해 비동기 처리 관련 코드가 내 비지니스 로직을 침투하지 않는다. 

 

@Async 어노테이션은 다음과 같이 지정해야한다.

  • 메소드 레벨 
  • public 메소드
  • Async는 또다른 Async를 호출하면 안된다.

비동기 반환 타입은 다음과 같이 진행할 수 있다.

  • void
  • Future<$너의타입>

java.utils.concurrent.Future을 통해 비동기 반환을 받을 수 있다. 그러나 비동기의 반환의 경우 아래와 같이 기다림이 필요하다.

	Future<String> future = asyncAnnotationExample.asyncMethodWithReturnType();

    while (true) {
        if (future.isDone()) {
            System.out.println("Result from asynchronous process - " + future.get());
            break;
        }
        System.out.println("Continue doing something else. ");
        Thread.sleep(1000);
    }

 

Future를 사용할 경우, 다음의 이점이 존재한다.

  • Future<>.get()을 통해 Exception을 메인 쓰레드(비동기를 호출한 부모 쓰레드)로 전파시켜 Exception 핸들링을 할 수 있다. 
  • 그러나 AsyncUncaughtExceptionHandler를 통해 해결할 수 있다.

SimpleAsyncTaskExecutor를 통해 바로 설정을 할 수 있지만 이런 방법을 통해 설정을 할 수 있다.

 

ThreadPoolTaskScheduler

스케줄러는 말마따나 별도의 쓰레드가 백그라운드에서 주기적으로 작업을 해주는 것이다. 그 이상.. 그 이하.. 설명할 것이 없다.. ThreadPoolTaskScheduler는 ThreadPoolTaskExecutor와 같이 TaskExecutor 인터페이스를 구현하였다. 즉, 비동기 쓰레들을 별도 관리 해준다. 

 

@Scheduled 어노테이션을 통해 간단하게 구현할 수 있지만 좀 복잡하게 설정이 필요한 경우가 존재하여 해당 어노테이션으로 구현하지 않고 일부는 수동으로 설정하였다. 

 

예제는 다음과 같다. 

[SchedulerConfig.class]

	@Bean(name="scheduler")
	public ThreadPoolTaskScheduler threadPoolTaskScheduler() throws IOException {
		ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
		threadPoolTaskScheduler.setPoolSize( 2 );
		threadPoolTaskScheduler.setThreadNamePrefix( "ThreadPoolTaskScheduler" );

		return threadPoolTaskScheduler;
	}
    
    	@Bean
	public ISchedulerOperation intValidOperation( SchedulerAuditLogService schedulerAuditLogService, Invoker invoker, SchedulerDao dao ) {
		if ( settings.getSchedulerAuditMode() ) {
			return new AuditSchedulerOperation( new IntValidSchedulerOperation( dao, invoker ), schedulerAuditLogService );
		} else {
			return new  IntValidSchedulerOperation( dao, invoker );
		}
	}

첫 번째 Bean은 ThreadPoolTaskScheduler이다. 가볍게 보고 넘어가도 괜찮다. 

두 번째 Bean은 프록시, 데코레이터 방식을 통해 빈을 상황에 따라 선택적 주입을 실시한다. 위의 코드를 말로 풀면 스케줄러에 대해 감사로그를 실시할 것 인가 안할 것인가 이다.

 

[Scheduler.class]

public abstract class Scheduler {
	
	boolean state = false;
	
	@Autowired
	private SchedulerDao dao;
	
	@Autowired
	private ThreadPoolTaskScheduler threadPoolTaskScheduler;

	@PostConstruct
	public void scheduleRunnableWithCronTrigger() throws IOException {
		if ( isEnabled() ) {
			threadPoolTaskScheduler.schedule( runner(), getTrigger() );
		}
	}
	
	public SchedulerDao getDao() { return this.dao; }
	
	public abstract Runnable runner();
	
	public abstract Trigger getTrigger();

	public abstract boolean isEnabled();
}

Scheduler를 확장한 구현체의 runner메소드에서 ISchedulerOperation인터페이스의 run메소드를 실행한다. 구현체는 별도 첨부를 하지 않았다.

위의 코드와 설정 파일을 통해 구현체를 아래와 같이 구현할 수 있다.

  • 설정파일에 값을 파싱하여 CronTrigger를 통해 스케줄링 시간을 설정할 수 있다.
  • 설정파일 값을 파싱하여 스케줄링 가동 여부를 설정할 수 있다.
  • 위의 인터페이스와 추상 클래스를 통해 통일된 스케줄러 클래스를 개발할 수 있다.

참고문헌

sqlite.org/atomiccommit.html

www.quora.com/What-is-the-difference-between-a-journaling-vs-a-log-structured-file-system

www.baeldung.com/spring-async

www.baeldung.com/spring-task-scheduler

 

저작자표시 비영리 (새창열림)

'개발관련 > 삽질' 카테고리의 다른 글

Servlet/Spring에서 web.xml은 어떻게 사라졌을까?  (0) 2021.04.28
Apache Commons-DBCP / Hikari-DBCP 정리  (0) 2021.04.05
Oracle/Tibero CURRVAL 사용 주의사항  (0) 2021.03.29
LoRa 1.0 취약점 분석  (0) 2021.02.04
LoRaWAN 1.1 보안 부분 분석(LoRa Spec 6장 일부분 번역)  (0) 2021.02.04
Posted by 동팡

Java Performance를 올리는 코딩 Best Practice

개발관련/(과거)메모 2020. 5. 20. 20:49

옛날 자료 + 추가 정리해서 올림.

 

일단 Performace를 고려한 코딩을 하기 위해서는 우리의 마음가짐을 다음과 같이 한다.

 - 메모리 사용에 대한 생각

 - 시간 복잡도에 대한 생각

 

위와 같은 마음 가짐으로 진행한다.

각설하고 시작한다.

 

 

문자열의 연접 방식

  • String 객체는 불변 객체이다. 한번 초기화한 객체는 불변성을 갖고 있다. 즉, 변경을 하지 못한다. 때문에, 문자열을 연접할 때 매번 메모리 초기화 작업을 진행한다. 문자열 연접을 도와주는 클래스 중, StringBuffer와 StringBuilder가 존재한다.
  • StringBuffer의 경우, append 메소드에 synchronized가 되어있기 때문에, StringBuilder의 append보다 느리다. 그런데! Java5 이상부터의 컴파일러는 String Concat 연산을 StringBuilder 연산으로 최적화(치환) 작업을 진행한다(그러나, 반복문은 얘기가 다르다. 매번 StringBuilder 객체를 생성한다.).
  • 나쁜 예
    • String retStr = str1 + str2 + str3;
  • 좋은 예
    • String retStr = new StringBuilder( str1 ).append( str2 ).append( str3 ).toString();

 

자료형의 기본형 사용

  • Java에서의 자료형은 기본형과 래퍼(객체형) 타입이 존재한다. 기본형 타입의 경우, 값을 직접 제어하기 때문에 Stack 메모리에서 바로 제어할 수 있다. 반면, 래퍼 타입의 자료형은 Heap 메모리에 저장한다. 해당 Heap메모리에서 값을 제어할 때 순서는 다음과 같다. 1) Stack 메모리에서 Heap 메모리의 참조 값 조회 2) 참조 값을 통해 Heap 조회. 때문에, 메모리 효율성은 기본형 타입의 사용이 좋다.

 

로그 레벨의 확인

  • 로그 레벨을 통해 PrintStream을 선택적으로 사용할 수 있다. 예를 들어, Info일 때 debug, trace와 같은 log는 PrintStream 작업을 진행하지 않는다. 그러나, 아래와 같은 로그문은 예외이다.
  • 로그 레벨이 Info가 설정되어 있어도 String concat 작업을 진행한다. 때문에 아래와 같이 변경한다.

변경 전

log.debug(“User [” + userName + “] called method X with [” + i + “]”);

변경 후

if (log.isDebugEnabled()) {

	log.debug(“User [” + userName + “] called method X with [” + i + “]”);         

}

 

String.replace()

  • Java 8이하(8포함) String.replace() 보다는 Apache Common Lang’s StringUilts.replace()를 사용해야한다. 내장 replace보다 Common Lang의 replace가 더 빠르다. 해당 사항은 Java9에서 개선되었다.
  • JMH를 통해 밴치마킹 하면 아래와 같이 확연하게 확인할 수 있다. 테스트모드는 throughput이며, 노란색 부분의 스코어가 높은 것이 좋은 것이다.

 

반복문

1. indexed for vs enhanced for

  • 위와 같이 for문은 2개가 있다. 예전부터 우리가 써왔던 index for문과 Java5에 나온 enhanced for문이다. 일단 아래의 조건이 충족되면 enhanced for문을 사용하자.
    • 증감연산(i++)을 통한 반복 처리
    • Iterable을 구현한 객체에 대한 반복문(array, Collection)
    • Index(I 값)을 핸들링하지 않을 때
  • Enhanced for문을 사용하는 이유는 당연 속도가 더 빠르기 때문이다. i값에 대한 indexing이 없기 때문에 Enhanced for문이 더 빠를 수밖에 없다. 

2. indexed for문 사용할 때

  • Indexed for문에서 for문 탈출 조건을 아래와 같이 설정하면 안된다.

나쁜 예

 

for ( int i=0; i < list.size(); i++ ) {}

좋은 예

int size = list.size();
for ( int i=0; i < size; i++ ) {}

또는 

for ( int i=0, size=list.size(); i < size; i++ ) {}

 

 

EntrySet()의 사용

  • Map 컬렉션에 대해 for문을 이용할 때 가 많다. 단순 key, value를 조회 및 사용할 때 전자보다는 후자를 사용한다. map.get( key )에서 map 객체에서 key에 상응하는 value 값을 찾는 lookup 작업때문에 후자보다 느리다.

나쁜 예

for ( K key : map.keySet() ) { V value = map.get(key); }

 

좋은 예

for ( Entry<K, V> entry : map.entrySet() ) { K key = entry.getKey(); V value = entry.getValue() }

 

EnumMap 또는 EnumSet의 사용

  • Map, Set 인터페이스의 구현체로 보통 HashMap/Set을 사용한다. EnumMap/Set은 Enum과 함께 사용된다. HashMap/Set 보다 EnumMap/Set을 꼭 사용해야할 경우는 key 값이 동적이지 않을 때이다. 대표적으로 AppConfig.properties 같은 어플리케이션 initializing할 때 사용하는 설정 값이다. 여기서 사용하는 Key는 설정 이름이며, 바뀌지 않는다.
  • HashMap은 힙메모리를 더 적게 사용하지만, value를 lookup 할 때 key에 대해 hashCode(), equals()를 실행한다. 반면, EnumMap의 경우, 내부적으로 Enum의 특성을 활용한다. 즉, key.ordinal()를 통해 배열 조회를 실시한다. 즉, key 값이 정적/고정일 때 EnumMap이 더 빠르다.
  • 구현 상세는 다음의 링크를 참고한다.
  • https://www.baeldung.com/java-enum-map

 

Collection의 구현체 선택

우리는 Collection 인터페이스를 정말 많이 사용한다. Collection 인터페이스의 구현체는 여러가지 존재한다. 그러나, “보통”의 경우 다음과 같은 구현체를 사용하는 것을 권고한다. 구현체 상호간 JMH 테스트를 진행하면, 아래의 구현체들이 성능적인 측면에서 우위를 점한다.

  • Set -> HashSet
  • List -> ArrayList
  • Map -> HashMap
  • Queue -> LinkedList
  • Deque -> ArrayDeque

 

Collection 생성자/addAll/add

Set과 같은 Collection 인터페이스의 구현체에 Data를 삽입할 때의 경우이다. 다음의 상황은 다음의 방법을 할 것을 권고한다.

  • Data 한번 삽입(한번 초기화): addAll 보다 생성자를 통해 삽입한다.
  • 후자를 택하라
for (inti=0; i<1000; i++) { list.add( arr[i] ); }

VS

list.addAll( Arrays.asList( arr ) );

 

불필요한 객체 생성의 최소화

  • 딱히 할말이 없다. 불필요한 new 연산은 최대한 간소화한다. 예는 다음과 같다. 아래와 같이 하지말자.
Map<String, Object> returnMap = new HashMap<>();

returnMap = selectUserInfoList( UserVo vo );

 

변수의 재활용

  • “코드의 가독성을 해하지 않는 선”에서 변수를 재활용한다. 예는 아래와 같다.

적용 전

int totalNum = getATotalNum() + getBToTalNum();
int maxNum = getATotalNum() + getBToTalNum();

적용 후

int num = 0;

num = getATotalNum() + getBToTalNum();  vo.setTotalNum( num );
num = getAMaxNum() + getBMaxNum();  vo.setMaxNum( num );

 

싱글턴의 사용

  • 1개의 인스턴스만 사용하는 방식이다. 싱글톤으로 사용할 수 있는 객체일 경우, 싱글톤의 사용을 권고한다. 싱글턴의 사용으로 인해, 객체 생성 비용 감소, 메모리의 효율성을 확보할 수 있다. 싱글턴을 생성할 때 상태 값을 사용하지 말아야한다(클래스 맴버 변수/쓰레드 세이프하지 않음). 여러 쓰레드가 사용할 수 있기 때문이다.

 

Null 및 “” 예외 처리

예외 처리할 때 아래와 같은 코드를 많이 봤을 것이다.

If ( str == null || str.equals( “” ) ) { throw new IllegalArgumentException( " invalid input." ); }

   아래와 같이 변경한다.

if ( str == null || str.isEmpty() ) { throw new IllegalArgumentException( " invalid input." ); }

 

여기까지 읽은 독자의 경우, 여러 생각할 수 있다. 1) 굳이? 2) 하드웨어 좋아짐 3) 변태같음

근데 중요한 사항이 있다. 고성능 어플리케이션은 성능적인 관점의 조각들이 합쳐졌을 때 탄생한다. 즉, 퍼즐과 같다. 위에서 언급한 항목들은 퍼즐 조각 중 한 개다.

다른 퍼즐 조각들은 서버의 튜닝, JVM의 튜닝, WAS의 튜닝, DBMS의 튜닝, 네트워크의 튜닝, 프로토콜의 튜닝, 캐시의 사용 등이 있다.

 

결론은 퍼즐 조각 하나라도 챙겼으면 한다.

 

 

참고문헌

 

https://gist.github.com/benelog/b81b4434fb8f2220cd0e900be1634753

https://www.javaworld.com/article/2150208/a-case-for-keeping-primitives-in-java.html

https://blog.jooq.org/2017/10/11/benchmarking-jdk-string-replace-vs-apache-commons-stringutils-replace/

https://stackoverflow.com/questions/11555418/why-is-the-enhanced-for-loop-more-efficient-than-the-normal-for-loop

https://blog.jooq.org/2015/02/05/top-10-easy-performance-optimisations-in-java/

https://help.semmle.com/wiki/display/JAVA/Inefficient+use+of+key+set+iterator

https://docs.oracle.com/javase/tutorial/collections/implementations/summary.html

https://stackoverflow.com/questions/3321526/should-i-use-string-isempty-or-equalsstring

저작자표시 비영리 (새창열림)

'개발관련 > (과거)메모' 카테고리의 다른 글

다양한 스프링 활용기(지속적인 업데이트 - 수정:20.12.31)  (0) 2020.12.31
REST API 정리  (0) 2020.12.30
IntelliJ(JetBrain) 단축키 표(모음) 공유  (1) 2020.05.18
Tomcat 튜닝 가이드  (0) 2020.05.14
Java에서 HTTPS 좀 알고 구성해보자..  (0) 2020.04.06
Posted by 동팡

Integer.ParseInt() vs Integer.valueof()

개발관련/(과거)메모 2017. 5. 8. 15:35

참고

http://stackoverflow.com/questions/7355024/integer-valueof-vs-integer-parseint

http://stackoverflow.com/questions/13933287/which-one-is-faster-integer-valueofstring-string-or-integer-parseintstring-s

https://docs.oracle.com/javase/tutorial/java/data/autoboxing.html


문득 개발하다가 Integer.ParseInt(), Integer.valueof()가 궁금해서 알아봤다.

 Integer.ParseInt()

Integer.class의 ParseInt 메소드

	public static int parseInt(String arg) throws NumberFormatException {
		return parseInt(arg, 10);
	}


Integer 클래스의 parseInt메소드는 String Object를 인자로 받는다.

그리고 내부적으로 parseInt 메소드를 호출한 후(오버로딩) int형(int primitive)으로 리턴한다.


 Integer.valueOf()

Integer.class의 valueOf 메소드

	public static Integer valueOf(String arg, int arg0) throws NumberFormatException {
		return valueOf(parseInt(arg, arg0));
	}

	public static Integer valueOf(String arg) throws NumberFormatException {
		return valueOf(parseInt(arg, 10));
	}

Integer 클래스의 valueOf 메소드는 String Object를 인자로 받는다.

그리고 내부적으로 parseInt 메소드를 호출한 후 래핑된 Integer Object를 반환한다.


결국 사용자가 원하는 타입이 Object인지, primitive 인지 파악 후, 선택적으로 사용하면 된다.

  Autoboxing, Unboxing

 위의 내용에서 래핑이라는 단어가 사용됐다.

정확히 야기하자면 저 상황에서 래핑은 Autoboxing을 뜻하는 것이다.

Autoboxing은 무엇인가? => get convert int(primitive types) to Integer(Object) automatically.

자동으로 int형을 Integer 형으로 컨버팅 해주는 것이다.

그 반대 상황은 Unboxing이라 칭한다.


그럼 예제를 통해 쉽게 이해해보자

List <Integer> li = new ArrayList<>();
for (int i = 1; i < 50; i += 2)
    li.add(i);

[(List <Integer> li = new ArrayList<>(); ) => ArrayList<> <> 이것은 다이아몬드 연산자로, jdk7 이상 부터 지원한다.]


위의 코드는 컴파일이 '성공적으로' 된다. 

아마 초보자( 저 포함)들도 이런 경우를 많이 봤을 것이다. (그러다면 왜 될까? 생각해보면 좋겠다.)


Autoboxing, Unboxing에 개념이 이것이다.

컴파일 타임 즉 컴파일러는 위의 코드를 자동으로 int형을 Integer형으로 래핑한다.(Autoboxing 한다.)

그리고 런타임 코드는 아래와 같이 변한다.

List<integer> li = new ArrayList<>();
for (int i = 1; i < 50; i += 2)
    li.add(Integer.valueOf(i));


컴파일러가 자동으로 Integer.valueOf(i)를 실행하여 바꿨다.


아래는 primitive type과 그에 상응되는 wrapper class이다.



2개의 메소드의 성능을 알아보려 했으나, 관점자체가 다르다 하더라.... 

그러나 autoboxing할 때 new Integer()를 실행하여 객체 생성을 하므로 prmitive type을 리턴받는게 더 빠르지 않을까 생각한다.


저작자표시 비영리 (새창열림)

'개발관련 > (과거)메모' 카테고리의 다른 글

스프링 빈과 Thread-Safe  (0) 2019.10.01
mybatis- "IllegalArgumentException: invalid comparison: java.util.Date and java.lang.String  (2) 2019.06.28
자바 Reflect Class, Method  (0) 2017.04.29
Log4j 2 Manual - Introduction  (0) 2017.04.29
자바 웹 서버(톰켓, 카탈리나, 재스퍼)  (0) 2017.04.03
Posted by 동팡

자바 Reflect Class, Method

개발관련/(과거)메모 2017. 4. 29. 15:48

참고

http://mysnyc.tistory.com/42

이 친구가 설명한거 응용해서 내거에 적용했다.

자바 Reflect Method 


run.java는 Reflect.java를 상속받는다.(run -> 자식 reflect -> 부모)

즉 run.java는 reflect.java를 확장하며 2개의 코드는 별개의 파일이다.



run.java (main)

public void doOperateOptional() throws Exception {
		String METHODE_NAME_PROCESS;
		Method methodProcess;
		Object[] methodParamObject = null;
		Class[] methodParamClass;
		
		for (VOAttribute a : attributeList) {
			METHODE_NAME_PROCESS = "insert" + a.getTag();
			try {
				methodParamClass = new Class[] { dao.getClass(), a.getClass()};
				methodParamObject = new Object[] { dao, a };
				
				methodProcess = this.getClass().getMethod(
						METHODE_NAME_PROCESS, methodParamClass);  
				methodProcess.invoke(this, methodParamObject);
			} catch( java.lang.NoSuchMethodException e ) {
				
			}
		}	
	}




Reflect.java (run class와 다른 class)

   

public void insertName(Dao dao, VOAttribute a) throws Exception {
		VOManagedObject_Names VOmanagedObject_Names = new VOManagedObject_Names();
		VOmanagedObject_Names.setManagedObjectID(VOmanagedObject.getId())
				.setNameID(a.getID());

		dao.insertManagedObjectNames(VOmanagedObject_Names);		
	}
	
	public void insertObject(Dao dao, VOAttribute a) throws Exception {
		VOManagedObject_Object VOmanagedObject_Object = new VOManagedObject_Object();
		VOmanagedObject_Object.setManagedObjectID(VOmanagedObject.getId())
				.setObjectID(a.getID());
		
		dao.insertManagedObjectObject(VOmanagedObject_Object);
	}
	
	public void insertApplicationnformation(Dao dao, VOAttribute a) throws Exception {
		VOManagedObject_ApplicationInformation VOmanagedObject_ApplicationInformation = 
				new VOManagedObject_ApplicationInformation();
		VOmanagedObject_ApplicationInformation.setManagedobjectID(VOmanagedObject.getId())
				.setApplicationinformationID(a.getID());
		
		dao.insertManagedObjectApplicationInformation(VOmanagedObject_ApplicationInformation);
	}
	
	public void insertCustomAttribute(Dao dao, VOAttribute a) throws Exception {
		VOManagedObject_CustomAttributes VOmanagedObject_CustomAttributes =
				new VOManagedObject_CustomAttributes();
		VOmanagedObject_CustomAttributes.setManagedObjectID(VOmanagedObject.getId())
				.setCustomAttributeID(a.getID());
		
		dao.insertManagedObjectCustomAttributes(VOmanagedObject_CustomAttributes);
	}	


주의: 캡슐화를 위해 접근 지정자(Protected)를 설정할 경우 접근을 못한다.

RunTime 에서는 run.java가 reflect.java를 상속받도록 확장(extends) 해도 안된다.


찾아봤다. 

https://coderanch.com/t/523276/java/Accessing-protected-method-test-case


Method.setAccessible(true) 


삽질하니까 된다. Run.java를 아래와 같이 약간 수정했다. 

@Override
	public void doOperateOptional() throws Exception {
		String METHODE_NAME_PROCESS;
		Method methodProcess;
		Object[] methodParamObject;
		Class[] methodParamClass;
		Class reflectClass = ServerDoGenerateOperation.class;

for (VOAttribute a : attributeList) { METHODE_NAME_PROCESS = "insert" + a.getTag(); try { methodParamClass = new Class[] { dao.getClass(), a.getClass()}; methodParamObject = new Object[] { dao, a }; methodProcess = reflectClass.getDeclaredMethod( METHODE_NAME_PROCESS, methodParamClass); methodProcess.setAccessible(true); methodProcess.invoke(this, methodParamObject); } catch (NoSuchMethodException e ) { } } }


@methodParamClass: invoke하고자 하는 method에 전달 인자의 데이터 형 또는 클래스 이름을 미리 지정한다. 

@methodParam: 전달 인자로 보내고자 하는 데이터(실체)를 미리 저장한다.

- Method 객체에 invoke하고자 하는 ( ServerDoGenerateOperation.class ) 클래스의 메소드(매개변수, 매개변수 ....) 를 지정한다.

@Method.setAccessible(true): Runtime에서도 접근 지정자 private 또는 protected의 접근을 허용한다.

@Method.invoke(Object, param, param....): 현재 객체(this)와 미리 저장한 param 값을 대입하면 끝


getMethod vs getDeclaredMethod

getDeclaredMethod: 호출하고자 하는 클래스의 선언된 메소드만 찾는다.

getMethod: 호출하고자 하는 클래스에 선언된 메소드를 탐색 후, 상위 클래스를 탐색한다.(public method만 찾는다.) 

참고 : http://stackoverflow.com/questions/17953724/using-getdeclaredmethod-to-get-a-method-from-a-class-extending-another


자바 Reflect Class

런타임에서 어떤 클래스를 실행하는지 알 수 있고, 동적으로 클래스를 로드해야할 경우 

나의 경우는 Operation마다 실행해야할 클래스가 다 다른다.


일단

인터페이스 생성 후, Interface에는 doOperate 공통 메소드 선언 (맨 하위 클래스가 오버라이딩 한다.)

String operationClass는 불러오고자 하는 클래스명을 동적으로 받아 온다. 

operationClass는 class.getName + 'Operation명' 이다.

클래스 파일이름을 OperationCreate, OperationDelete 이다. 



String operationClass = decodeHelperSupport.getOperationClass();
Class clazz = Class.forName(operationClass);
ServerDoOperation ServerOperator = (ServerDoOperation)clazz.newInstance();

ServerOperator.doOperate();

그리고 실행, 설명하기 귀찮다. 생략 

궁금하면 댓글 달아주세요. 예제 파일 만들게요



저작자표시 비영리 (새창열림)

'개발관련 > (과거)메모' 카테고리의 다른 글

mybatis- "IllegalArgumentException: invalid comparison: java.util.Date and java.lang.String  (2) 2019.06.28
Integer.ParseInt() vs Integer.valueof()  (1) 2017.05.08
Log4j 2 Manual - Introduction  (0) 2017.04.29
자바 웹 서버(톰켓, 카탈리나, 재스퍼)  (0) 2017.04.03
자바 제네릭 지우개  (0) 2017.03.31
Posted by 동팡

Log4j 2 Manual - Introduction

개발관련/(과거)메모 2017. 4. 29. 15:46

Log4j2 번역 및 정리 작업

출처:  https://logging.apache.org/log4j/2.x/manual/index.html


Introduction

대부분의 대규모 응용프로그램은 자체 logging or tracing API가 포함되어있다.[각주:1] 1996년 초 E.U. SEMPER 프로젝트는 tracing API를 만들기 결정하고, 결국 이 프로젝트의 기능을 향상 및 구체화하여 Log4j Java용 로깅 패키지로 발전했다. 이 패키지는 Apache Software License(ver 2.0)[각주:2] 에 의해 배포된다. 


log statements를 코드에 넣는것은 디버깅을위한 간단한(low-tech) 방법이다. 디버거가 항상 사용 가능하거나 적용 가능하지 않기 때문에 유일한 방법일 수 있다. 대개 멀티 스레드 및 분상 프로그래밍의 경우가 해당된다.


logging은 개발 사이클에 중요한 요소이다. 응용 프로그램 실행에 대한 context 정보를 제공한다. logging을 코드에 넣으면 logging 출력을 위해 개발자가 개입할 필요가 없다. 또한, 로그 출력은 파일처리를 통해 스토리지에 보관하여 차후에 활용할 수 있다.(감사 등)


As Brian W. Kernighan and Rob Pike put it in their truly excellent book "The Practice of Programming":


As personal choice, we tend not to use debuggers beyond getting a stack trace or the value of a variable or two. One reason is that it is easy to get lost in details of complicated data structures and control flow; we find stepping through a program less productive than thinking harder and adding output statements and self-checking code at critical places. Clicking over statements takes longer than scanning the output of judiciously-placed displays. It takes less time to decide where to put print statements than to single-step to the critical section of code, even assuming we know where that is. More important, debugging statements stay with the program; debugging sessions are transient.


Logging은 프로그램의 속도(성능)을 느려지게할 수 있는 단점이 있다. logging이 지나치게 길 경우, 스크롤하는데 맹목적일 수 있다(blindness). 이런 우려로 인하여 log4j는 안정적, 신속, 확장 가능하도록 설계하였다. logging은 프로그램의 메인 요소가 아니기 때문에, log4j API는 쉽게 이해할 수 있고, 사용이 간단하다. 


Log4j 2

log4j 1.x는 많은프로그램에 널리 적용(채택)되어, 사용하고 있다. 그러나, 수 년의 개발 과정에서 느려졌다. 낮은 버전의 Java와 호환 여부와 어려운 유지 보수로 인하여 2015년 8월 업데이트가 끝났다. Its alternative, SLF4J/Logback made many needed improvements to the framework.(대안으로, SLF4J/Logback은 많은 개선들이 필요해졌다.) Log4j2에 집중할 이유

1. Log4j 2는 감사 로깅을 사용할 수 있도록 설계하였다. Log4j 1.x 및 Logback은 reconfiguring(재구성)하는 동안 이벤트가 소멸된다. Logback에서 Appenders의 Exception은 어플리케이션에서 볼 수 없지만, Log4j 2의 경우 Appenders 는 Exception 이 통과(퍼지도록)할 수 있도록 설정(허용)할 수 있다.

2. Log4j 2는 LMAX Disruptor[각주:3] 라이브러 기반으로 한 차세대 비동기 로거가 포함되어 있다. 멀티 스레드 시나리오에서 Asynchronous Loggers(동기화 로거)는 Log4j 1.x 및 Logback 보다 10배 빠르고 대기시간도 훨씬 짧다.

3. Log4j 2 is garbage free for stand-alone applications, and low garbage for web applications during 
steady state logging. (Log4j 2는 독립 실행형 어플리케이션에서는 가비지가 없고, 웹앱에서 정상 로깅 작업할 경우, 낮은 가비지 처리 기능을 제공한다. ) Garbage Collector의 사용 빈도를 줄이고, 더 나은 응답 시간을 제공한다. 

4. Log4j 2는 Appender Filters, Layouts, Lookups, Pattern Converters를 추가하여 매우 쉽게 확장할 수 있는 플러그인 시스템을 사용한다. 

5. 플러그인 시스템 구성으로 인해 더 간단하다. Configuration의 Entries들은 클래스 이름을 지정할 필요가 없다. 

6. Custom Log Level을 지원한다. 이것은 code 또는 config 파일에서 정의할 수 있다.

7. 람다식 제공. Java 8에서 실행중인 클라이언트 코드는 request된 log-level만 활성화된 경우에만, 로그 메시지를 사용하여 lazily하게 로그 메시지를 구성할 수 있다. 

8. Message objects 지원. Support for Message objects. Messages allow support for interesting and complex constructs to be passed through the logging system and be efficiently manipulated.( 구문 자체가 이해 안감)
사용자는 자신의 Message type을 만들고 사용자 정의 Layouts, Filters, Lookups을 정의하여 컨트롤할 수 있다.

9. Log4j 1.x의 Appenders는 Filters를 지원한다. Logback은 TurboFilters을 추가하여, Logger가 처리되기 전에, 이벤트를 필터링 할 수 있게한다. Log4j 2는 Logger가 이벤트를 처리하기 전에, 이벤트를 처리하도록 구성할 수 있는 Filter를 제공한다. 

10. 다양한 Logback Appenders는 Layout을 허용하지 않으며, 고정된 형식으로만 데이터를 보낸다. Log4j 2 Appenders는 레이아웃을 허용하며 원하는 형식으로 데이터를 전송할 수 있다.

11. Log4j 1.x와 Logback Layouts은 문자열을 리턴한다. 이로인해 Logback Encoder에서 문제가 발생한다. Log4j2는 Layouts이 항상 바이트 배열을 반환하는 간단한 접근 방식을 사용한다. OutputStream에 쓰는 것 뿐만 아니라, 모든 Appender에서 사용할 수 있는 장점이 있다. 

12. Syslog Appender는 TCP, UDP는 물론 BSD syslog, RFC 5424 형식을 지원한다. 

13. Log4j 2는 Java 5 동시성 지원을 활용하고 저 수준에서 잠금을 수행한다. Log4j 1.x에는 교착 상태 문제가 있다. 이 중 많은 부분이 Logback에서 수정되었지만, 많은 Logback 클래스는 여전히 높은 수준의 동기화가 필요하다. 



  1. (대부분, 다양한 오픈 소스 프로젝트( Mybatis, Spring KMIP4J 등)에는 logging을 사용한다. 거의 필수이며, 아파치 Log4j1.x는 De facto Standard(사실상 표준)라고 칭한다. ) [본문으로]
  2. BSD와 유사하며, 소스 코드 공개 여부:X 사용 여부에 대한 명시를 해야하며 명시에 대한 양식은 Apache 홈페이지에 있다. [본문으로]
  3. http://lmax-exchange.github.io/disruptor/ http://stackoverflow.com/questions/14630901/what-is-lmax-disruptor-design-pattern 어렵다..;; 나중에 따로보자 [본문으로]
저작자표시 비영리 (새창열림)

'개발관련 > (과거)메모' 카테고리의 다른 글

Integer.ParseInt() vs Integer.valueof()  (1) 2017.05.08
자바 Reflect Class, Method  (0) 2017.04.29
자바 웹 서버(톰켓, 카탈리나, 재스퍼)  (0) 2017.04.03
자바 제네릭 지우개  (0) 2017.03.31
자바 Keytool(셀프 사인) Server/Client 소켓 통신 방법  (0) 2017.03.31
Posted by 동팡

자바 제네릭 지우개

개발관련/(과거)메모 2017. 3. 31. 16:43

참고

저자 임백준, 한빛미디어, 폴리글랏 프로그래밍 Chapter1. 

http://stackoverflow.com/questions/11496705/instanceof-with-generic-collection

http://stackoverflow.com/questions/2320658/why-are-not-all-type-information-erased-in-java-at-runtime

http://www.artima.com/weblogs/viewpost.jsp?thread=208860

http://rgomes-info.blogspot.kr/2013/12/using-typetokens-to-retrieve-generic.html



코드

Apple.java

package tested;

public class Apple extends fruit{
	public Apple() {
	
	}
}

fruit.java

package tested;

public class fruit {
	
	public fruit() {
		
	}

}

test.java

package tested; import java.util.ArrayList; import java.util.List; public class test { public static void main(String args[]) { fruit apple = new Apple(); if (apple instanceof Apple) System.out.println("요미요미~"); if (apple instanceof List<Apple>) System.out.println("요미요미~"); } }


위의 코드를 실행 시 에러가 뜬다.

apple instanceof List<Apple>

Cannot perform instanceof check against parameterized type List<Apple>. Use the form List<?> instead since further generic type information will be erased at runtime


"런타임 시 type 정보가 지워지기 때문에,  List<?> 사용해라. 

List<?> 하는 순간 제네릭의 의미가 상실되지 않나... 생각한다..

List<?>는 List<? extends Object>라 이해하면 되겠다. Object는 모든 클래스 중 최상위 클래스다.

위의 ?는 공변성, 반공변성 또는 리플렉션 API에 대해 포스팅 할 때 다루겠다.

이런 현상으로 인하여 자바 제네릭은 여러 개발자에게 많은 욕을 잡수셨다.

 

C#의 경우, IL로 컴파일되어 => CLR 위에서 바이너리로 컴파일: 제네릭 타입은 여전히 상주한다.

즉 C#은 런타임 시 제네릭 정보를 지속적으로 보유하고 있다.

또한 제네릭 사용으로의 코드 간결화, 성능 개선의 이점을 확보한다.




여기까지 임백준 작가님의 책 내용이다.

(좀 더 풍부하지만, 깊은 내용과 관련된 내용은 독서하는 것을 추천한다.




추가적으로 알아보자 


예로 ArrayList<String>을 할 경우, 자바 런타임에서는 new ArrayList() 객체 생성한다.

그리고 컴파일을 할 때 타입을 잃어버린다.


자바는 설계상 바이트 코드로 컴파일 할 경우, 모든 타입 정보를 드랍시킨다. 그 이유는 자바 소스 코드와 바이트 코드의 버전 호환을 위해서다. 그래서 결국 제네릭도 똑같이 타입 정보를 드랍한다. 이러한 사항에서의 이점은 런타임 오버 헤드를 발생하지 않는다.


(C#의 경우, 이런 문제를 해결하기 위해 CLR 자체를 수정했지만, 자바는 하지 않았다.) 


그러나 모든 경우 타입 정보를 드랍하는 것은 아니다. 익명 클래스를 사용할 경우, 타입 정보를 보존할 수 있다. 


그 다음은 귀찮다. 담주나 주말에 게시글 수정하겠다.








ㅇㅇ

'개발관련 > (과거)메모' 카테고리의 다른 글

Log4j 2 Manual - Introduction  (0) 2017.04.29
자바 웹 서버(톰켓, 카탈리나, 재스퍼)  (0) 2017.04.03
자바 Keytool(셀프 사인) Server/Client 소켓 통신 방법  (0) 2017.03.31
로그 간단한 개념 및 라이브러리  (0) 2017.03.30
오픈소스 ERD툴: ERMaster  (0) 2017.03.30
Posted by 동팡

자바 Keytool(셀프 사인) Server/Client 소켓 통신 방법

개발관련/(과거)메모 2017. 3. 31. 16:12

참고

http://zero-gravity.tistory.com/199

https://docs.oracle.com/cd/E19159-01/820-4605/ablrb/index.html

http://m.blog.naver.com/onestone73/110106304935

http://stilius.net/java/java_ssl.php

https://www.sslshopper.com/article-how-to-create-a-self-signed-certificate-using-java-keytool.html

http://stackoverflow.com/questions/11617210/how-to-properly-import-a-selfsigned-certificate-into-java-keystore-that-is-avail

http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/keytool.html

http://stackoverflow.com/questions/3508050/how-can-i-get-a-list-of-trusted-root-certificates-in-java

http://stackoverflow.com/questions/6365209/java-and-ssl-java-security-nosuchalgorithmexception 


많은 참고: http://m.blog.naver.com/wndrlf2003/220649843082


좋은 글 감사합니다



Keytool

- 자바 키툴은 키, 인증서 생성,관리 유틸리티이다. 

- 공개/개인 키 쌍, 인증서를 생성한다.

- Keytool은 키스토어(keystore)에 키, 인증서를 저장하여 자바 keystore를 통해 구현할 수 있다.


시작 + R => Run : cmd  OR 작업 폴더 쉬프트 + 우 클릭 open command windows here(현재 창에서 명령프롬포트 실행) 


프롬포트에서 Keytool 입력시 에러 뜨면 패스 설정 안했음. 설정하거나 jdk 폴더에 keytool 파일 있는 경로가서 cmd 실행해야함.(전자 추천)






그리고 쓸 기능은 

- genkey: 키, 인증서 생성

- import: 인증서 추출

- export: 인증서 키 스토어에 저장





-help를 통해 명령어 옵션 확인 바람.


키쌍 생성


서버: keytool -genkey -v -keystore server.jks -alias server_private -keyalg RSA -sigalg MD5withRSA -keysize 1024 -validity 365

클라: keytool -genkey -v -keystore client.jks -alias client_private -keyalg RSA -sigalg MD5withRSA -keysize 1024 -validity 365 

- genkey의 옵션은 help 통해 참고.

- keystore 비밀번호, info 입력, key 비밀번호 입력

- 이미지



인증서 추출

서버: keytool -export -alias server_private -keystore server.jks -rfc -file trustServer.cer

클라: keytool -export -alias client_private -keystore client.jks -rfc -file trustClient.cer

- help 참고

- keystore 비밀번호 입력

- 여기까지 서버, 클라 같이 진행 서버만 진행하고 인증서 키 스토어 삽입하면 당연히 안됨

- 이미지는 위와 비슷할것이다.


인증서 키 스토어 삽입

서버: keytool -import -alias trustClient -file trustClient.cer -keystore server.jks

클라: keytool -import -alias trustServer -file trustServer.cer -keystore client.jks


- 서버는 클라이언트의 키스토어 비밀번호 입력

- 클라는 서버의 키스토어 비밀번호 입력

- 이미지




중간 정리

- server.jks 파일에 서버의 개인키 server_private, 클라의 공개키(인증서) 저장했다.

- client.jks 파일에 클라의 개인키 client_private, 서버의 공개키(인증서) 저장했다.

- 솔직히 나도 헷갈리다.

- http://m.blog.naver.com/wndrlf2003/220649843082 -> 이 친구가 위의 사항을 잘 정리 했다. 정말 고맙습니다.

(이 사이트의 그림 3을 보면 이해가 좀 더 쉬울것이다.)


일단 셀프 서명은 보안에 취약함.


이 방법 외 

1. java\jdk[version]\jre\lib\security 폴더 안에 cacerts에 저장하여 하는 방식도 있다.

 - 위의 경로에 들어가 cmd 창을 연다.

 - keytool -list -keystore cacerts 입력

 - default 비밀번호는 changeit(ㅋㅋㅋㅋ)

2. 또는 cacerts 파일을 따로 생성하여 관리하는 방식도 있다.

3. 또는 OpenSSL 툴을 사용하는 것도 있고, 다양하다.




이제 이 jks 파일을 통해 소켓 통신을 해보자.


TLSServerTestDrive.java

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocket;

public class TLSServerTestDrive {
	public static void main(String[] args) {
		try {
			System.setProperty("javax.net.ssl.keyStore", "D:\\tlstest\\keytool\\server.jks");
			System.setProperty("javax.net.ssl.keyStorePassword", "server123");
			System.setProperty("javax.net.debug", "ssl");
			
			SSLServerSocketFactory sslserversocketfactory =
					(SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
			SSLServerSocket sslserversocket =
					(SSLServerSocket) sslserversocketfactory.createServerSocket(5000);
			System.out.println(getTime() + "wait to request the client"); 
			SSLSocket sslsocket = (SSLSocket) sslserversocket.accept();

			InputStream inputstream = sslsocket.getInputStream();
			InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
			BufferedReader bufferedreader = new BufferedReader(inputstreamreader);

			String str = null;
			while((str = bufferedreader.readLine()) != null) {
				System.out.println(str);
				System.out.flush();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

		public static String getTime() {
			SimpleDateFormat f = new SimpleDateFormat("[hh:mm:ss]");
			return f.format(new Date());
		}
	}



TLSClientTestDrive.java

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;

import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

public class TLSClientTestDrive {
	public static void main(String[] args) {
		try {
			System.setProperty("javax.net.ssl.trustStore", "D:\\tlstest\\keytool\\client.jks");
			System.setProperty("javax.net.ssl.trustStorePassword", "client123");
			System.setProperty("javax.net.debug", "ssl");
			
			String serverIP = "본인 아이피(로컬호스트)";
			SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
			SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket(serverIP, 5000);
			
			InputStream inputstream = System.in;
			InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
			BufferedReader bufferedreader = new BufferedReader(inputstreamreader);
			
			OutputStream outputstream = sslsocket.getOutputStream();
			OutputStreamWriter outputstreamwriter = new OutputStreamWriter(outputstream);
			BufferedWriter bufferedwriter = new BufferedWriter(outputstreamwriter);
			
			String str = null;
			while((str = bufferedreader.readLine()) != null) {
				bufferedwriter.write(str + '\n');
				bufferedwriter.flush();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}



포인트

위 코드 싹 다 복붙 하면 안된다. 수정 좀 해야 한다.

서버

System.setProperty("javax.net.ssl.keyStore", "D:\\tlstest\\keytool\\server.jks");
			System.setProperty("javax.net.ssl.keyStorePassword", "server123");

클라

System.setProperty("javax.net.ssl.trustStore", "D:\\tlstest\\keytool\\client.jks");
			System.setProperty("javax.net.ssl.trustStorePassword", "client123");
String serverIP = "본인 아이피(로컬호스트)";

위의 사항은 본인의 환경에 맞게 수정하자.


그러면 컴파일을 해보자. 

해당 폴더에서 cmd 2개(Server, Client)를 킨다.


Java 파일을 컴파일 한다.(이것도 패스 설정 안하면 안됨)

Class 파일이 생성 됐으면, 실행한다.



실행 화면

위의 cmd는 server : 서버는 클라 접속 대기 하다, 클라가 접속(handshake: true(UDP가 아니라서)) 

아래 cmd는 client : 클라도 접속 클라는 타이핑해서 서버에게 메시지를 보낸다.

클라가 "한글도 보내진다 " 입력




서버는 한글도 보내진다를 입력 받는다.





이렇게 끝. 근데 현장, 제품을 저렇게 구성하면 진짜 ㅈ 된다.(불편, 보안 때문) ㅎㅎㅎㅎㅎㅎㅎ 이 예제는 그저 예제, 연습용이다.


한달 전에 한거 정리해서 올리는데 도중에 에러 떠서 (ㅆㅂ)

코드에 대한 리뷰는 상단 참고 링크 누르거나, 레퍼런스 구글링을 통해 알아보면 된다.

위의 코드를 응용하면 일단 1:1, 1:N SSL 적용, 간략한 채팅프로그램 구현할 수 있다.


다음에는 OpenSSL 키쌍 인증서를 만든 후 jks 포멧으로 변경 후 소켓 통신을 해보겠다.


'개발관련 > (과거)메모' 카테고리의 다른 글

자바 웹 서버(톰켓, 카탈리나, 재스퍼)  (0) 2017.04.03
자바 제네릭 지우개  (0) 2017.03.31
로그 간단한 개념 및 라이브러리  (0) 2017.03.30
오픈소스 ERD툴: ERMaster  (0) 2017.03.30
이클립스 메모리 할당 허용 값 설정  (0) 2017.03.30
Posted by 동팡

로그 간단한 개념 및 라이브러리

개발관련/(과거)메모 2017. 3. 30. 19:33

참고

아파치 log4j2 공식홈페이지: https://logging.apache.org/log4j/2.x/

Logging 라이브러리 벤치마킹: http://blog.takipi.com/the-logging-olympics-a-race-between-todays-top-5-logging-frameworks/

 로깅 관련 게시물 

http://m.blog.naver.com/writer0713/220733443205

http://stackoverflow.com/questions/30019585/log4j2-why-would-you-use-it-over-log4j


좋은 글 감사합니다.



로깅은 주로 서버(또는 백) 사이드 개발자가 활용하며, 프로그램의 상태를 지속, 실시간으로 출력하는 방식이다.

로깅은 일반적으로 디스크에 데이터를 쓰는 IO 작업이다.(그래서 성능 이슈, 비용 증가 발생)

로깅은 주로 프로그램 모니터링, 디버깅, 분석 및 통계  등을 위해 사용한다.


로깅 라이브러리는 log4j ver1/2, Logback, Java Util Logging 있는데,



도대체 slf4j 뭔지 이해가 안간다. 그래서 알아보려한다.


 log4j, java util logging, logback 등의 로깅 유틸리티를 똑같은 방법으로 사용할 수 있게 해준다. (어떤 이유로 인해 로깅 유틸리티를 바꿔도 실제 코드에 영향이 없도록 도와준다)

-> ?? 로깅 라이브러리를 동일한 방식으로 사용, 퍼서드 얘기가 나왔다. 디자인 패턴의 어뎁터 패턴이 떠오른다.

     즉 slf4j 퍼사드 패턴이다? 랄까?

퍼사드 패턴: 어떤 서브시스템의 일련의 인터페이스에 대한 통합된 인터페이스를 제공한다. 퍼사드에서 고수준 인터페이스를 정의하기 때문에 서브시스템을 더 쉽게 사용할 수 있다.(헤드퍼스트 디자인 페턴(KR) 302페이지)


결국 로깅 라이브러리와 slf4j 라이브러리를 함께 사용하여, 로깅 기능을 구현하고, 원하는 로깅 라이브러리(log4j, logback, JUL) 또는 상황에 맞게 라이브러리를 사용한다. slf4j는 구상체, 로깅라이브러리는 구현체로, 매핑 후, 로깅 요청 전달하는 역할이다.


이렇게 이해 했는데 맞는지 잘 모르겠다. 



다시 본론으로 돌아와서.

Log4j1.x는 2015년 8월 이후 Log4j1.x는 라이브러리 라이프 사이클이 끝이다.

또한 자바9에서 작동하지 않을 예정,(Log4j1.x는 관속으로 들어갈 예정이다)


알아볼 de facto standard가 된 Log4j ver2.x 로깅 라이브러리다.



- 커스텀 로깅(지정된 6단계 로깅 레벨 외 사용자 정의 로길 레벨 추가 기능)

- 람다 지원(lazy logging: lazy 기능을 통한 약간의 성능 이점 확보)

- low-garbage

- SLF4J, JUL, Log4j1.x 지원

- 동시성 프로그래밍 하는데 있어, 버그를 야기시키지 않는다.(log4j1.x 보다)

- XML, JSON, YAML을 통해 config 파일 구성








'개발관련 > (과거)메모' 카테고리의 다른 글

자바 웹 서버(톰켓, 카탈리나, 재스퍼)  (0) 2017.04.03
자바 제네릭 지우개  (0) 2017.03.31
자바 Keytool(셀프 사인) Server/Client 소켓 통신 방법  (0) 2017.03.31
오픈소스 ERD툴: ERMaster  (0) 2017.03.30
이클립스 메모리 할당 허용 값 설정  (0) 2017.03.30
Posted by 동팡
이전페이지 다음페이지
블로그 이미지

https://github.com/ehdvudee

by 동팡

공지사항

    최근...

  • 포스트
  • 댓글
  • 트랙백
  • 더 보기

태그

  • What is Vault
  • NBP
  • LoRaWA
  • 간단리뷰
  • vault
  • vault 개요
  • 하시콥 볼트
  • 개발자 준비
  • Secret Sharing 이론
  • Spring
  • 네이버 클라우드 이직
  • Hashicorp
  • java
  • 네이버 클라우드
  • 개발자 이직
  • 책리뷰
  • 이직 정보 공유
  • Secret Sharing
  • 개발자 글쓰기 책
  • 자바
  • vault tutorial
  • Shamir Secret Sharing
  • 글쓰기 가이드
  • 네이버 비즈니스 플랫폼
  • 개발자 책리뷰
  • 경력 채용
  • 이직 느낀점
  • 볼트란
  • 네이버 클라우드 개발자 면접
  • Thread-safe

글 보관함

«   2025/05   »
일 월 화 수 목 금 토
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31

링크

카테고리

메모장 (73)
개발관련 (71)
삽질 (26)
(과거)메모 (27)
강의 (0)
회고 (9)
책 리뷰 (9)
블로그 관리 글(비공개) (0)
일상 (2)
기타 (0)
책 리뷰 (1)
회고 (0)

카운터

Total
Today
Yesterday
방명록 : 관리자 : 글쓰기
동팡's Blog is powered by daumkakao
Skin info material T Mark3 by 뭐하라
favicon

메모장

https://github.com/ehdvudee

  • 태그
  • 링크 추가
  • 방명록

관리자 메뉴

  • 관리자 모드
  • 글쓰기
  • 메모장 (73)
    • 개발관련 (71)
      • 삽질 (26)
      • (과거)메모 (27)
      • 강의 (0)
      • 회고 (9)
      • 책 리뷰 (9)
    • 블로그 관리 글(비공개) (0)
    • 일상 (2)
      • 기타 (0)
      • 책 리뷰 (1)
      • 회고 (0)

카테고리

PC화면 보기 티스토리 Daum

티스토리툴바