- Java
- 제네릭 - 데이터 타입을 일반화, 컴파일 시간에 데이터 타입을 검사, 데이터타입에의한 에러를 줄일수있고 타입별로 구현을 따로 만들지 않아도 됨
- Java8 변경사항
- 2014년 발표
- 람다 표현식
- Optional, 스트림 도입
- 스트림은 단 한번만 사용할 수 있음 ( 재사용 불가)
- 원본데이터를 변경하지 않는다
- java.time 패키지 추가 -> LocalDate, LocalDateTime
- immutable함 (수정불가)
- ※ 기존 Calendar, Date객체 문제점 -> 수정가능, 월 인덱스 0부터 시작..
- Static / Default Method
- Interface 에서 method 구현가능 (field 사용못함)
- static / default 키워드 붙여주면됨
- default쓰면 다중상속이 가능..
- Functional Interface
- method가 하나만 있는 인터페이스를 특정하여 구현체 자체를 람다식으로 표현할 수 있게함
- annotation 안달아도 되지만 달면 method를 하나로 제한시켜 확실하게 할 수 있음.
- Deep copy / Shallow copy
- Shallow copy : 인스턴스는 하나 reference가 늘어나는거
- Deep copy : 내용까지 copy (clone)
- 람다식이란 - 익명 메소드
- 실행될 때 마다 새로 생성되지 않는다. 자바 힙에 Permanent 영역에 저장되고 재사용됨
- Generic
- class : public class Test<T> {}
- method : public <T> Test<T> makeTest(T t){}
- 와일드 카드 타입 <?> :파라메터(주로), 필드, 지역변수, 반환타입등에 쓰임
- <?> : 제한없음, 모든 타입 가능
- <? extends T> T를 상속받은 타입만 가능
- <? super T> T의 부모타입만 가능
- JVM 동작
- 자바코드를 컴파일한 바이트코드를 동작시켜주는 주체
- 메모리 구조
- Method area : 클래스 정보, 메소드 정보, type정보, 상수 pool, static변수, final class 변수
- Heap : new 로 생성한 객체와 배열
- Stack : 지역변수 파라메터, 리턴값, 임시값 (원시타입은 값 자체가 들어가고, new 한 객체는 힙에 들어가고 변수가 스택에)
- PC register : 스레드 생성될 때마다 생성되는 영역, 스레드 실행되는 부분의 주소와 명령 저장 -> 이걸 이용해서 스레드가 돌아가면서 수행됨
- Native method stack : 자바 외 언어로 작성된 네이티브 코드를 위한 영역 (JNI)
- GC
- 영역
- Young
- Eden
- Survivor 1,2 (2개)
- Old
- Young
- Minor GC -> Young generation, Major GC(Full GC) -> Old generation
- New 하면 Eden에 저장 -> Minor GC -> Survivor 영역으로 이동 ( Eden -> Survivor, Survivor1,2에서 왔다갔다) -> 그래도 살아남으면 Old generation
- Old까지 가는 기준 : Minor GC발생할 때 마다 age bit 1씩 증가 -> MaxTenuringThreshold값 초과하면 Old이동, 초과 전이라도 Survivor영역 메모리가 부족하면 이동
- GC 변화
- Serial(스레드1개) -> Parallel(스레드 여러개) -> Parallel Old -> CMS -> G1GC (java7 에 등장)
- STW (Stop The World)를 최소화 하는 방향으로
- CMS (Concurrent Mark Sweep)
- Application thread와 GC thread가 동시에 실행되어 STW최소화
- Old 영역의 Object tree를 search해서 gc대상을 찾아 mark 하고 mark된 object들을 해제하는 방식
백그라운드에서 gc thread가 계속 수행 - STW가 짧다, 대신 cpu를 많이 쓴다
- Compaction을 안한다
- Parallel GC는 FullGC때마다 Compaction수행->시간많이 소요->이후 메모리 연속지정 가능
- CMS는 Compaction안해서 빠름 -> 나중에 메모리 단편화로인해 Concurrent mode failure발생하면 그때 수행 -> 더 오래걸림
- Parallel GC보다 더 많은 Heap사용, 백그라운드에 항상 GC스레드 실행
- G1GC
- 요약
- 큰 메모리를 region단위로 나눠서 관리. 할당, 제거 하기땜에 메모리 할당할때의 파편화 문제가 적다
- 리전중에 쓰레기가 많은 리전을 먼저 정리하기때문에 Garbage First GC
- 정리하고 남은건 다른 리전으로 옮기면서 Compaction 수행
- 멀티 스레드로 리전을 나눠 작업하고, region들의 live object 비율을 계산하면서 비율 낮은것부터 수행하기때문에 STW가 짧고 예측 가능해짐
- 큰 힙 메모리(최소 4G) 에서 짧은 GC시간을 위해, STW 시간 예측 가능
- 각 영역이 고정된 크기가 아니고 전체 힙메모리를 Region으로 나눠서 역할 부여(2048개로나눌수있음
- region : Eden, Survivor, Old, Humongous, Available/Unused
- Humongous : Region크기의 50%를 초과하는 큰 객체를 저장하기 위한 공간, 이 region에서는 GC동작이 최적으로 동작하지 않음, 그렇게 큰 객체가 있는건 어플리케이션 문제 아닌지..
- region : Eden, Survivor, Old, Humongous, Available/Unused
- heap메모리 회수할 때 최대한 살아있는 객체가 적게 들어있는 region을 수집하는 것
- 살아있는게 적다 -> 쓰레기가 많다 -> Garbage First
- Java7에 등장, Java9부터 default (java 8은 parallelGC)
- 동작
- region들을 free region list로 관리, 메모리공간이 필요해지면 free region list에서 young/old 영역으로 할당함
- Young GC : 각 region중 GC대상 객체가 가장 많은 region(Eden, Survivor)에서 수행하고 살아남은건 다른 region(Suvivor)로 옮기고, 비워진건 Available로 변경, STW 발생
- Full GC
- Initial Mark (STW) : Young GC와 같이 수행됨, STW, Old Generation객체가 참조하는 Survivor region 찾아 mark
- Root Region Scan : Initial Mark에서 찾은 survivor region에서 GC대상 객체 스캔
- Concurrent Mark : 빈 region을 찾아 표기, region별 live object 비율 계산해둔다
- Remark (STW): 최종적으로 살아남을 객체 식별
- Cleanup (STW) : live object비율이 낮은 region 들 선택 미사용 객체 제거 수행, empty region은 free list에 추가
- Copy : 완전히 비워지지 않은 region의 살아남은 객체를 새로운 region에 복사 -> Compaction
- YoungGC때에는 Eden, Survivor 에서 사용되지 않는 객체에대해 수행하고 살아남은건 다른 리전으로 옮긴다
Full GC때에는 Old 영역까지 포함해서 Root region 스캔한 후 GC수행하고 남은건 새로운 리전에 복사해서 컴팩션까지 수행
- 요약
- 언어비교
- C, C++ : 동적할당하면 코드에서 임의로 해제해야함
- Python : gc모듈 존재, reference counting 해서 메모리 해제, generational gc 동작도 한다
- 힙덤프
- jmap 으로 힙덤프
- jcmd 로 강제로 full gc실행시키고 남은 객체 히스토그램 표시
- 영역
- Reference type
- 일반적인 참조 : strong reference
- java.lang.ref -> SoftReference, WeakReference, PhantomReference
- 객체를 wrapping
- reference object(감싸는 reference 객체) -> referent(실제 객체)
- ex) new WeakReference(new SampleObject())
- GC 대상을 선정하는 reachability 계산에 영향
- Reachability
※ root set : Stack(지역변수, 파라메터), Native Stack(JNI), Method영역(static 객체) 로 부터 시작하는 참조 사슬 (heap 내의 객체끼리의 참조시작은 제외라는 것)- strong reachable : 일반적 참조상태, reference object가 끼어있지 않음, GC회수 안됨
- softly reachable : GC회수 안됨. 자주 사용될수록 오래 살아남는다.-> 오래 안쓰면(설정값) 회수된다. heap이 부족하면 모두 회수됨
- weakly reachable : GC 동작할때 회수됨. 참조는 가능하지만 항상 유효하지 않을 수 있다는 것
LRU cache만들때 주로 사용(쓰다가 없어져도 괜찮을때, SoftReference로 하면 메모리 회수 가능성이 낮아서 힙을 많이 쓰고 GC도 자주 일어나기 때문에 WeakReference 사용) - phantomly reachable : 객체의 finalize 이후에 처리해야할게 필요하면 이용
- 어떤 객체를 만들고 PhantomReference에 넣어둠
- 그 객체가 strong reachable을 잃으면, GC에 의해 finalize 실행됨
- 근데 그 객체가 phantom reachable 이면 ReferenceQueue에 넣어놓고 추가 처리 할 수 있게 해주면서 메모리 회수는 지연시킴
-> finalize 된 이후 메모리 회수 전에 사용자 코드를 수행할 수 있게 해줌 - unreachable : root set으로부터 시작되는 참조 사슬로 참조되지 않은 상태, GC 회수
- GC동작 : 대상 찾기 -> 대상 객체 처리(finalize) -> 메모리 회수
- SoftReference, WeakReference에 ReferenceQueue를 같이 넣어주면 GC동작에서 Reference객체에서 referent가 null로 설정될 때 reference 객체를 Queue에 넣는다
-> GC에 의해서 사용중에 갑자기 회수될 수 있지만, 회수된 Reference객체를 queue에 넣어줌으로써 회수된 이후 처리를 할 수 있다. - 객체 처리, 메모리 회수 동작 순서 : soft reference 판별, 대상이면 null설정 후 enqueue-> weak reference 판별, 대상이면 null설정 후 enqueue -> finalize -> phantom reference 판별, 대상이면 enqueue, 메모리 회수 지연, 후처리 후 명시적으로 clear호출해야함-> 메모리 회수(phantom reachable제외)
- SoftReference, WeakReference에 ReferenceQueue를 같이 넣어주면 GC동작에서 Reference객체에서 referent가 null로 설정될 때 reference 객체를 Queue에 넣는다
- 캐시는 거의 WeakReference 사용해서 구현, -> WeakHashMap 을 사용
- WeakHashMap : key를 WeakReference에 wrapping해서 보관. key에 대한 strong reference가 다 끊기면 key, value 쌍으로 gc됨. string은 JVM에서 관리되기때문에 따로 회수되지 않으므로 key로 쓰면 안됨, Primitive Boxing object도 마찬가지(Integer...) new를 해서 쓰면 된다..
- SoftReference는 가끔 쓸 수도 있음(Heap이 여유있을때.. 캐싱될 가지수가 많지 않을 때)
PhantomReference는 거의 안씀. 관리하기 어려움 - Spring
- IoC 와 AOP를 지원하는 경량의 컨테이너 프레임워크
- Inversion of Control
- 의존관계 역전
- 이전엔 자기가 필요한 의존객체를 자기가 직접 만들어서 썼다.
- 필요한 의존객체를 만들지않고 외부로부터 받아 쓴다
- 의존관계가 역전됨
- DI (Dependency Injection)
- 의존관계 역전에서 그 외부로부터 받는걸 스프링이 대신 넣어준다
- 의존성을 주입해줌 (Autowired)
- 스프링 컨테이너가 객체를 대신 생성해주고 알아서 객체를 주입해준다
- -> IoC원칙을 DI라는 개념으로 구현한 것
- 컨테이너가 객체를 관리
- BeanFactory를 상속받은 ApplicationContext가 컨테이너 구동 시점에 Bean class들을 스캔하여 객체화한다.
- Spring Bean Scope
- singleton : default
- prototype : getBean()할때마다 생성
- request : 요청 단위
- session : 세션 단위
- application: 스레드 단위
- AOP
- 관점지향프로그래밍
- 어떤 로직을 핵심적인 관점과 부가적인 관점으로 나누어서 보고 그 관점을 기준으로 각각 모듈화
- 부가적인 관점에서 횡적으로 공통 부분을 모듈화 (로깅, 트랜잭션처리 등)
- 개념
- Aspect : 관심사를 모듈화 한 것
- Target : Aspect를 적용하는 곳
- Advice : 실질적인 구현체
- JointPoint : Advice가 적용될 위치, 시점
- PointCut : JointPoint의 상세 스펙을 정의한 것
- Weaving : Advice를 적용하는 행위
- Spring AOP
- 프록시 패턴을 사용하여 AOP효과를 낸다
- 어떤 클래스가 Spring AOP 대상이라면, 그 빈이 만들어질 때 Spring AOP가 프록시객체를 자동으로 만들고 원본 객체 대신에 프록시객체를 빈으로 등록해 사용되게 한다.
- 스프링이 객체의 생성을 관리해주는 컨테이너니까 가능한것
- Spring은 Method에만 JoinPoint지원, 그 이상은 AspectJ같은 모듈 사용해야함
- 예) @Transactional
- Dynamic Proxy
- JDK Dynamic Proxy 기능 사용 (1.3부터 있었음) -> Interface기반으로 프록시 생성
- Java에서 Reflection을 활용한 Proxy 사용, Proxy.newProxyInstance(classloader, interface, InvocationHandler) (reflection 사용해서 좀 느림)
- InvocationHandler통해서 invoke 호출받을 수 있음
- 인터페이스를 통해서만 Proxy객체 생성 가능
- CGLib
- 외부 라이브러리
- 동적으로 프록시 생성해주는건 똑같으나, 처음 메소드 호출때 동적으로 bytecode를 생성하고 이후엔 재사용 (RTW - Run Time Weaving)
- TargetClass를 상속받아 생성, Interface 필요없음
- 상속이라, final, private등 상속안되는것에는 aspect적용 불가
- 실제 바이트 코드를 조작하여 DynamicProxy보다 빠름
- Spring 3.2부터는 CGLib가 기본으로 포함되어 따로 추가하지 않아도 된다.
- AspectJ
- CTW (Compile Time Weaving), LTW (Load Time Weaving) 지원
- 컴파일부터 바이트 코드 수정 -> 성능 젤 빠르다, 어렵다(IDE설정부터 바꿔야함)
- BCI활용 (Byte Code Instrumentation)
- Spring AOP는 기본적으로 JDK Dynamic Procy가 우선적
- Interface가 없을경우 CGLib으로 동작
- SpringBoot부터는 디폴트로 CGLib
- Dynamic Proxy
- 주의점
- 스프링이 프록시를 임의로 감싸서 실행한것이기때문에 내부적으로 자기 호출을 하면 프록시를 안거쳐서 동작 안한다. self-invocation 하면..
- Filter 와 Interceptor 차이
- 둘다 비슷
- Filter
- J2EE 표준스펙에 정의
- DispatcherServlet 앞단에서 처리, Spring과 무관
- doFilter에서 각 필터 chaining됨
- Interceptor
- SpringFramework에서 자체적으로 제공하는 기능
- Dispatcher -> Controller로 가기전에 처리
- 큰 차이는 없지만, 주로 모든 요청의 선/후 가공이 필요하면 Filter 사용(인코딩, 압축)
uri기반으로 별도 처리를 해야하면 Interceptor로 (filter로도 할 수 있음) - 큰 차이점은 바로 예외처리
- Filter에서 예외발생하면 바로 Web Application으로 전달 (Tomcat...) -> 정의된 Error Page로 가는등
더이상 코드에서 예외 받을 수 없음, 필터 내에서 try-catch해야함 - Interceptor는 아직 Spring의 ServletDispatcher내에 있다. 즉 @ControllerAdvice에서 @ExceptionHandler 에서 받을 수 있다.
- Filter에서 예외발생하면 바로 Web Application으로 전달 (Tomcat...) -> 정의된 Error Page로 가는등
- Spring MVC
- MVC : 모델과 뷰를 분리하고 컨트롤러가 중간에 비즈니스로직처리
- DispatcherServlet -> HandlerMapping에 컨트롤러 물어봄 -> Controller에 처리요청 -> ModelAndView리턴 -> DispatcherServlet에서 ViewResolver로 결과 View 요청 -> View받아서 응답생성 출력 요청 -> Response
- DTO / VO
- DTO : 계층간 데이터 교환을 위한 자바 빈, VO는 주로 entity성격
- MSA
- MSA에서의 트랜잭션
- Two-Phase Commit : 2단계 커밋으로 각 서버로 prepare시키고 다 되면 commit 시키고 그러다 실패하면 rollback 시킨다.
DB의존적 , DB가 분산트랜잭션을 지원해야함..(동일제품군.), NoSql은 분산트랜잭션 지원 안해서 적용어려움 - SAGA 패턴
- Eventual Consistency 개념, 이벤트 기반으로 맞춰주자, 롤백을 받을경우를 위해 이미 끝난 처리에 대해 보상 트랜잭션을 구현해야함.
- Choreography-based Saga -각 모듈이 Event 스트림을 이용해서 전파, 실패하면 보상이벤트 거꾸로 발생, 스트림으로 이동해서 트랜잭션 상태를 알기 어렵다.
- Orchestration-Based Saga - 트랜잭션을 위한 Saga인스턴스가 별도로 존재, 중앙에서 트랜잭션 관리(호출해줌), 모든 트랜잭션을 한곳에서 관리해서복잡함. 추가서비스 생성해야함
- Two-Phase Commit : 2단계 커밋으로 각 서버로 prepare시키고 다 되면 commit 시키고 그러다 실패하면 rollback 시킨다.
- MSA에서의 트랜잭션
- DB
- Transaction
- 쪼개질 수 없는 업무 단위 보장
- 속성
- 원자성 : 모두 성공 / 모두 실패
- 일관성 : 같은 트랜잭션에서 여러번 읽어도 같은 값(Repeatable read)
트랜잭션 도중 DB가 변경되더라도 반영되는게 아니고 시작할때의 값으로 일관되게 진행 - 격리성 : 둘 이상의 트랜잭션이 실행중이면 다른트랜잭션에 영향을 끼칠 수 없다
- 지속성 : 트랜잭션이 완료되면 결과는 영구적으로 반영되어야한다
- 트랜잭션 문제 유형
- Dirty Read : 트랜젝션에서 바꾸고 아직 커밋 안했는데 다른트랜젝션에서 변경한 값이 읽히는 경우
- Non-Repeatable Read : 같은 걸 두번 읽을 건데, 처음읽었을때랑 두번째 읽었을때가 다른경우
(데이터불일치) - Phantom Read : 특정 범위를 두번 읽는데, 처음읽을때 없던게 그사이 다른 트랜잭션이 데이터를 추가해서 두번째 읽을때 나오는경우
- InnoDB의 Lock
- row-level lock
- Shared lock (SELECT ... FOR SHARE) : 읽고있는 lock, 이때 X lock 못건다.
읽고있는 row를 수정하거나 삭제할 수 없다 - Exclusive lock (SELECT ... FOR UPDATE) : 쓰기위한 lock, 이때 S lock, X lock 다 못건다.
수정중인 row를 읽고/수정할 수 없다.
- Shared lock (SELECT ... FOR SHARE) : 읽고있는 lock, 이때 X lock 못건다.
- record lock : index record에 걸리는 락
- Gap lock
- db index record의 gap에 걸리는 락
- 범위를 지정해서 아직 없는 row에 대해서도 생성을 막을 수 있다.
- row-level lock
- 격리 수준 (Isolation)
- DEFAULT : db 기본 값을 따른다
- READ_UNCOMMITTED (lv 0) : 커밋되지 않은 데이터 읽기 허용 -> Dirty Read발생
- READ_COMMITTED (lv 1) : 커밋된 데이터만 읽기 -> Dirty Read 방지,
select할때마다 스냅샷 뜬다 -> 스냅샷뜰때 다른데서 커밋된게 반영됨. record lock만 걸고 gap lock은 사용안함 - REPEATABLE_READ (lv 2)-->MySqlInnodb기본
- 트랜잭션 완료될 때 까지 Select문장이 사용하는 모든 데이터에 shared lock
- 그 영역에 해당되는 데이터에 대한 수정이 불가능
- Non-Repeatable Read 방지
- Phantom Read 발생 가능
(MySql InnoDB에서는 발생 안함 -> Serializable사용할 필요 없음-> gap lock 걸려서.)
- SERIALIZABLE (lv 3)
- 기본적으로 REPEATABLE_READ 와 같지만 모든 select가 SELECT ... FOR SHARE 처리됨( S lock)
- shared lock이 걸려 수정 및 입력도 불가능
- Phantom Read 방지
- Propagation
- REQUIRED : 디폴트, 부모있으면 부모에, 없으면 새로만들어
- SUPPORTS : 부모있으면 부모에, 없으면 트랜잭션 없이
- REQUIRES_NEW : 부모 무시 무조건 새로 만듦
- MANDATORY : 이미 시작된게 있으면 참여, 없으면 예외 발생, 혼자서는 트랜잭션 못쓰게
- NOT_SUPPORTED : 트랜잭션 사용하지 않기, 진행중인게 있으면 보류
- NEVER : 트랜잭션 사용하지 않기, 이미 진행중인게 있으면 예외발생
- NESTED : 이미 진행중인게 있으면 중첩으로 만듦, REQUIRES_NEW와는 다르게 SAVEPOINT지정하면 거기까지 롤백 가능
- 정규화
- db설계할 때 데이터의 중복을 최소화 하는 작업
- OLTP, OLAP
- OLTP : Online Transaction Processing
- OLAP : Online Analytical Processing
- Statement, Prepared Statement
- DBMS에서 모든 쿼리는 파싱 -> Optimization(실행계획) -> 컴파일 -> 실행 단계를 거친다
- Prepared Statement는 template 쿼리를 parse하고 캐싱하고, 캐싱된걸 이용하고 바인딩을 진행
- Prepared Statement쓰면 파라메터는 바인딩 변수로써 대입을 하기때문에 문법적 기능을 가질 수 없어 sql injection 못함
- Transaction
- ORM
- Object Relation Mapping
- JPA
- JPA란
- Java ORM 기술에 대한 표준 명세 Java API (스프링 아님) 인터페이스임
- 구현체는 별도로
- Spring-Data-JPA
- JPA를 Repository 형태로 한번더 wrapping
- 성능향상
- 버퍼링 - 모았다가 커밋할때 한번에 쓴다
- 캐싱 - 읽은거 캐싱
- 캐시
- 1차 캐시 : 영속성 컨택스트 내부의 캐시, 트랜잭션 단위로 캐싱, 끝나면 지워짐
단 엔티티 형태의 데이터만 캐싱됨 (jpql쿼리로 스칼라 값을 직접 조회한건 캐싱안됨)
OSIV(Open Session In View) - 1차 캐시를 트랜잭션 단위를 넘어 View까지 확장 (Requst scope), SpringBoot부터는 기본 -> view에서까지 지연로딩을 할 수 있다.
Spring OSIV : 트랜잭션 밖에선 영속상태이나 수정은 불가능하도록 함
단점 : db 커넥션을 api가 끝날때까지 물고있다. -> 모자랄 수 있음 - 2차 캐시 : 어플리케이션 레벨에서 캐싱 -> db접근을 획기적으로 줄일 수 있음
락거는 대신 복사본 반환, 1차 캐시 -> 2차 캐시 -> db
@Cacheable 어노테이션 달면 적용
- 1차 캐시 : 영속성 컨택스트 내부의 캐시, 트랜잭션 단위로 캐싱, 끝나면 지워짐
- 트랜잭션
- 1차 캐시를 활용하면, db가 READ COMMITTED여도 REPEATABLE READ효과를 낼 수 있다.
- 낙관적 락 / 비관적 락
- 낙관적 락
트랜잭션간에 거의 충돌이 안난다고 보고, db의 락기능 사용안하고 jpa자체 버전관리로 구현 - 비관적 락
트랜잭션간에 충돌이 발생한다고 가정하고 일단 db 락을 걸고 보는것(select for update)
- 낙관적 락
- N+1 문제
- 1번의 쿼리로 N개의 데이터를 가져왔는데, 추가적으로 N번의 쿼리를 또 한다
연관 객체 로딩하기위해
-> fetch join 쓴다 (쿼리 추가해야함), OneToMany면 distinct도 해줘야함
queryDsl로 fetch join한다 (테이블이 아닌 참조 필드로 left join )
-> BatchSize 지정 -N번은 아니고 모아서 쿼리, Eager로 바꿔야함
- 1번의 쿼리로 N개의 데이터를 가져왔는데, 추가적으로 N번의 쿼리를 또 한다
- JPA란
- Utils
- Map
- merge와 compute 차이
- init value 계산 시점 차이
- merge
init value를 미리 정함, init value null이면 에러, 있든 없든 계산됨
key가 없으면 init value바로 들어가고(BiFunction실행안됨)
BiFunction 리턴이 null이면 삭제
BiFunction의 인자 (old, init)
BiFunction에서 old가 null이 아님, null체크 안해도 됨 - compute
init value를 넣을 시점에 계산할 수 있음, BiFunction 리턴이 null 이면 삭제
BiFunction의 인자 (key, old)
BiFunction에서 old가 null이면 init을 리턴하게 짜야함
- merge
- compute 가 더 자유도 좋은데, init value가 상수값으로 정해져있으면(카운팅같이) merge가 더 간단
- init value 계산 시점 차이
- HashMap은 key, value에 모두 null 허용
- HashMap은 순서 x,
LinkedHashMap 은 들어간 순서대로
TreeMap 은 정렬하며 삽입 - Equals, hashcode 재정의
- HashTable에서 찾는 방법이, key의 hashcode 로 해싱한값을 먼저 찾고, 찾은걸 equals로 한번 더 비교(해시충돌때문) -> 따라서 둘다 같게 재정의 해야지 올바르게 찾을 수 있다.
- merge와 compute 차이
- Map
- POJO
- Plain Old Java Object
- 특정 기술(프레임워크나 라이브러리)에 종속적이지 않는 순수한 자바 오브젝트
- 환경에 종속적이지않고 필요에 따라 재사용할 수 있는 방식으로 설계
- cf) EJB 같은 무거운 프레임워크를 쓸땐 뭘 상속받아야 하는등의 종속이 많음
그 코드를 그대로 다른데선 쓰기 힘듦
spring은 상속대신 annotation으로 사용함으로써 코드에서 프레임웤 의존성을 낮춤 - POJO를 단순히 Java Beans나 객체로 정의하는것이 아닌 POJO style을 지향한다는게 맞는 표현일듯
- REST API
- 자원을 URI로 표현하고, 해당 자원의 상태를 주고받는것
- 자원 이름과, 행위는 HTTP Method로 표현
- Stateless
- NoSQL
- SQL을 쓰지 않는 DBMS (non sql), 비 관계형 DBMS
- 크게 두가지 방향성
- Schemaless : 유연함, 빠른개발
- 확장성 : 확장에 용이, 대규모 데이터 사용가능
- 분류
- Wired column store : Hbase, Cassnadra
- Document : Mongodb, Couchbase
- Key-Value : Dynamo, Redis
- Graph : Neo4J
- MongoDB vs Cassandra
- 약간 성능과 확장성/일관성 의 Tradeoff 같은 관계
- Database 엔진 동작 방식부터 다르다
- Mongodb 의 WiredTiger rdb처럼 btree 알고리즘 사용 -> 읽기 빠르고 쓰기 느림
- Cassandra 는 LSM-Tree(Log Structured Merge Tree)라서 Append only, 일단 쓰고 머지하는 방식
쓰기 빠르고 읽기 느리다 -> 대용량, 쓰기 많은 곳에 적합
- Mongodb
- 쓰기할때 메모리에 먼저 쓰고 1분단위로 디스크 Flush -> 성능 빠름
- 여러 인덱스로 다양한 검색이 용이
- 간단하게 유연하게 사용이 편의, 실제로 NoSQL입문으로 많이 쓰이는것 같음
- 샤딩하려면 라우터(퍼사드)와 컨피그 서버(데이터 어딨는지 확인) 필요
- 클러스터 구성은 투표를 위해 최소 3대이상 있어야함 (3대 어려우면 두대 + 아비터로 구성)
- Cassandra
- Memtable, SSTable, CommitLog 사용
- 클러스터가 기본(최소노드 3개)
- Replication Factor로 1개 이상의 노드에서 읽고 쓰기때문에 일관성이 좋음, 확장성 좋음
- 모든 노드가 동등한 조건, 요청 받은 노드가 코디네이터 노드 -> 해싱해서 실제 노드에 요청
- write : commit log쓰고 memtable에 입력 -> 어느정도 차면 SSTable에 flush (SSTable은 immutable - append only)
- Read : Consistency Level만큼 노드들에 질의, 젤 가까운 한곳에선 데이터를 받고 나머지에서는 digest를 받아서 비교, 틀리면 맞는데서 읽어오고 나머지 동기화
Memtable보고, SSTable 보고 확인 (bloom filter 먼저보고 sstable확인 - bloom filter : FalsePositive -> 없다고 하면 확실히 없는거, 확률로 검색시간 줄여줌) - Delete : Tombstone 으로 마킹 주기적으로 컴팩션
- Update : Delete -> Insert
- Batch -> Atomic 보장해주기 위해 -> Logged batch
- 어느정도 규모 이하면 MongoDB가 편리할 듯(Schemaless, 인덱스 검색)
- 대규모일수록 Cassandra 가 좋을것 같음 (일관성, 확장성 좋음)
- OOP
- OOP란
- 데이터를 객체로 취급하여 설계, 순차적으로 동작하는것과 다르게 객체간의 상호작용을 통해 프로그램이 동작하도록 하는 방식
- 객체간 상호작용을 어떻게 잘 설계하느냐가 관건
- 상속, 캡슐화, 다형성을 이용해 코드 재사용성을 증가시키고, 유지보수를 용이하게
- 상속 : 부모 자식, 자식의 공통부분을 부모에 두고 상속함으로써 코드 재사용성 높힘
- 캡슐화 : 객체의 필드와 메소드를 클래스로 묶고, 내부 구현내용은 숨김, 보호
- 다형성 : 파생이 여러가지가 있더라도 부모타입으로 관리/실행 가능
형이 많다는것, 부모 타입으로 여러 자식타입을 받을 수 있다는거
- SOLID
- 목표 : 항상 코드는 유연하고, 확장할 수 있고, 유지보수가 용이하고, 재사용할 수 있어야 한다
- Single Responsibility Principle (단일 책임 원칙)
- 객체는 하나의 책임만 지녀야 한다
- 응집도는 높히고, 결합도는 낮추고
- Open/Closed Principle
- 확장에는 개방, 수정에는 폐쇄
- 확장은 유연하게, 수정은 영향도가 최소한으로
- 추상화를 잘 하면(인터페이스를 잘 뽑으면), 인터페이스 수정없이(client도 수정없이) 새로운 기능을 확장할 수 있다.
- Liskov's Substitution Principle (리스코프 치환 원칙)
- 자식 클래스는 언제든 부모 클래스로 치환할 수 있다.
- 부모 인터페이스를 자식이 잘 구현해야 한다.
- 부모로 치환하더라도, 온전히 잘 동작해야 한다.
- 부모클래스의 인터페이스중에 자식한테 안맞는게 있는데 어거지로 상속하면 위반.
- Interface Segregation Principle (인터페이스 분리 원칙)
- 자기와 상관없는 인터페이스는 분리하여 구현하지 말라
- 인터페이스를 성격에 맞게 잘게 쪼갠다.
- Dependency Inversion Principle (의존성 역전 원칙)
- 자신보다 변하기 쉬운것에 의존하지 마라
- 자주 변경되는 구체 클래스에 의존하지 마라
- 인터페이스로 의존, 구체클래스가 바껴도 영향받지 않게.
- 구현체를 바꾸면 동작이 바뀐다.
- 내가 구현체를 직접 의존하는게 아니고
나는 인터페이스만 보고, 설정되는 구현체에 의해 동작이 바뀌므로
의존 관계가 역전된다고 함 - IoC 와 같음, DI로 구현
- OOP란
- 디자인패턴
- Singleton 구현방법
- DCL (Double Checked Locking) - 두번체크, volatile 키워드 넣어야함
- ※ volatile - java 메모리 구조가 Main Memory - Working Memory 로 나뉘어져 있어서.
- 스레드마다 working memory가지고 있고, 두 메모리간에 데이터 이동과정이 있음
- 두 메모리간 동기화 되는 시간에 빈틈이 있긴함.
- volatile 을 쓰면 해당 변수의 값을 메인메모리에서 직접 가져오고, 쓸때도 메인메모리에 즉시 플러시
- ※ volatile - java 메모리 구조가 Main Memory - Working Memory 로 나뉘어져 있어서.
- Enum - 괜찮은 방법. Eager Initialization - class 로딩만 돼도 생성됨
- LazyHolder - 좋음, synchronized도 필요없고, 성능도 뛰어남
inner class로 private static class LazyHoder 에 인스턴스 변수 만들어 참조 -> 본 클래스 로딩돼도 생성안됨.
getInstance를 호출해서 LazyHolder에 변수를 참조할때 생성.(Lazy initialization)
class를 로딩하고 초기화 하는시점은 thread-safe를 보장하기 때문
- DCL (Double Checked Locking) - 두번체크, volatile 키워드 넣어야함
- Decorator 패턴
- 객체에 동적으로 책임을 추가 (기본 기능이 있고 거기에 추가할 기능이 여러가지가 있을 때)
- 새로운 책임을 상속으로 구현하는것이 아니고 감싸서 구현
- 예 ) InputStream : 여러 구현체를 생성자에 넣어서 감싸서 추가 기능을 구현
- Adapter, Proxy, Decorator, Facade 패턴 차이
- Adapter : 다른 인터페이스를 연결하기 위해 인터페이스를 전환할 때, 구조만 이어줌, 기능을 변경하진 않는다.
- Proxy : 실제 오브젝트를 숨기고 자기가 진짜인척 해야할 때 (인터페이스가 같다)
- Facade : 어떤 복잡한 서브 모듈 등을 단순하게 접근할 수 있도록 노출시킬때 (새로운 인터페이스)
- Decorator : 런타임으로 기존 오브젝트에 새로운 기능을 추가할 때, 동일한 인터페이스면서 동작을 변경
- GOF 패턴
- 생성, 구조, 행위 패턴
- Abstract Factory, Factory Method
- Factory Method는 여러 구현체중 하나를 생성해주는 팩토리
-> 한 메소드에서 여러 구현체 생성 - Abstract Factory는 Factory를 추상화 해서 어떤 구현체 군을 조합해 줌
서로 연관되거나 의존적인 객체들의 조합을 만든다
-> Factory자체를 인터페이스화, 한 Factory구현 클래스에서 한 구현체 생성 - 첨에 Factory Method패턴으로 시작해서 점점 복잡해지면 Abstract Factory패턴으로 간다
- Factory Method는 여러 구현체중 하나를 생성해주는 팩토리
- 구조 패턴 : 정적 구조에 관한 패턴, 더 큰 구조를 형성하기 위해서 어떻게 클래스와 객체를 합성하는지. Composite, Decorator, Adapter
- Composite : 필요한 디펜던시 모음에서 유사한 객체들은 구체 클래스말고 인터페이스로 통일해서 관리, 객체 그룹을 동일하게 취급하는것이 목적 --> 비슷한 객체 그룹을 인터페이스로 관리 (파일,디렉토리 / html엘리먼트)
- Decorator : 런타임 시 객체에 새로운 능력을 추가하거나 행위를 변화시키는것, 상속대신 합성이용
- 행위 패턴 : 동적 구조패턴, 런타임시에 객체간의 상호작용을 다룬다. 행위 수행의 방법, 한 객체가 혼자 할 수 없는 작업을 여러개의 객체로 어떻게 분배할지. Observer, State, Strategy, Iterator
- Singleton 구현방법
- DevOps
- Blue-Green 배포
- 새로운 인프라셋(Blue)를 만들어서 배포하고 앞단 LB에서 변경
- 이상있으면 기존 인프라셋(Green)으로 다시 돌림 (Rollback)
- Canary
- LB에서 일부만 새로배포된곳으로 돌려본다
- Blue-Green 배포
- HTTPS
- HTTP 에 SSL 더해 보안을 강화한 것 ( Secure Socket Layer ), 이젠 TLS라고 하나 아직도 SSL이라고 많이 씀
- 사용하는 암호화 방식
- 공개키 방식
- 키 두개로 서로 암호화 하면 복호화 할 수 있음
- 하나를 공개키 하나를 개인키로 해서 공개키를 공개하고 비공개키는 자기가 가지고 있다가 복호화함
- 대칭키 방식
- 한 키로 암호화 복호화 다 함
- 매번 새로 생성해서 안전 ( 단 대칭키를 안전하게 공유하는게 중요)
- 공개키 방식
- 순서
- 사이트가 CA에 사이트정보, 사이트 공개키를 전달
- CA는 CA개인키로 사이트 정보, 공개키를 암호화해서 사이트에 인증서로 전달
- CA는 발급된 사이트 인증서와 CA 공개키를 브라우저에 공유함
- 사용자가 사이트에 접속하면
- 사이트는 브라우저로 발급받은 자기 인증서 내려보냄
- 브라우저는 공개된 CA공개키로 인증서 복호화해서 사이트 확인 후 사이트 공개키 추출
- 대칭키 생성해서 얻은 사이트 공개키로 암호화
- 사이트는 받은 대칭키를 사이트 개인키로 복호화
- 이제 사용자(브라우저)와 사이트가 모두 대칭키를 알고있으므로, 통신 시작
- HTTP2
- HTTP는 Application protocol
- HTTP 1 - 요청마다 커넥션 새로 연결
- HTTP 1.1 - 커넥션을 재사용 (일정시간동안 열어놓음 keep-alive), 좀더 빨라짐
, 파이프라이닝 - 요청을 여러개 일단 보내고, 순차적으로 응답을 받는다,
Head of Line Blocking - 첫번째 요청이 오래걸리면 뒤에께 오래기다림 - HTTP 2
- 표준 대체가 아닌 확장
- 바이너리 프레이밍 - 바이너리로 전송 (파싱, 전송속도 올라감)
- 데이터 스트림으로 보내고 받음
- 헤더 압축
- 서버푸시 - html보고 필요한 리소스를 알아서 먼저 보내줌
- spring boot에서 설정만 해주면 사용가능
- HTTP3는 UDP 사용해 빠르다
- Auth
- Authentication (인증) : 그 사람이 맞는지 확인
- Authorization (인가) : 해당 작업을 할 권한이 있는지 확인
- OAuth2
- 다른서비스의 기능을 사용하기 위해 하는 인증절차를 오픈 프로토콜로 정의
- Authorization Server, Resource Server가 따로 있고
Authorization Server에서 인증/인가 받아 AccessToken 받고 그걸로 Resource Server이용
- OS(Linux)
- Page Fault
- 가상메모리 : 실제메모리 공간을 넘어서 하드디스크등을 이용해 가상메모리 공간을 만들어서 쓴다
- OS는 CPU에서 당장 실행해야할 부분만 메모리에 올려놓고 그렇지 않은 부분은 디스크 스왑영역에 놨다가 필요할때 메모리에 올리는 방식
- 어플리케이션 입장에서는 다 그냥 메모리로 인식(가상메모리)
- (Page : 메모리를 사용하는 최소 크기 단위)
- 프로그램이 자신의 주소공간(가상메모리공간)에는 존재하지만 RAM에는 없는 데이터에 접근하였을 때 Page Fault발생 -> 그 데이터를 메모리로 가져와서 계속 동작시켜줌
- File Open제한 이유
- 운영체제에서는 열려있는 파일들을 관리하기 위해 메모리가 필요하고, 메모리는 제한된 리소스이기 때문에 파일 오픈을 제한
- 스레드
- 프로세스 내에서 실행되는 흐름의 단위
- 자체적인 스택(JVM Stack, Native Method Stack)을 할당받음, Heap/Method Area는 공유
- 커널레벨스레드
- 커널이 스레드의 생성 및 스케쥴링 관리
- 커널이 관리해서 CPU효율적으로 스레드 배정
- 많이 생성하면 리소스 제약(Max User Process limit), 컨텍스트 스위칭 비쌈
- 유저 레벨 스레드
- 사용자 레벨의 라이브러리를 통해 구현, OS는 모름
- 컨텍스트 스위칭 빠르나, 하나라도 OS IO블락걸리면 모두 중단
- 스레드 덤프
- 스레드 상태
NEW -> RUNNABLE ( BLOCKED, WAITING, TIMED_WAITING) -> TERMINATED - 추출 : jstack [pid]
- BLOCKED - 모니터를 획득하기 위해 다른 스레드가 락을 해제하길 기다림, waiting to lock <thread-id>확인
WAITING - wait, join, park 로 대기중
TIMED_WAITING - waiting 과 같지만 시간지나면 해제될 수 있음 (sleep, parkNanos, parkUntil)
-> DB slow query에서 많이 나타남 - fastthread.io 에서 분석가능
- 자바의 객체는 하나의 모니터를 가지고, 하나의 모니터는 하나의 스레드만 가질 수 있다
- 스레드 상태
- Threa별 CPU 점유율 확인
- ps -mo pcpu,lwp -p [pid]
- 힙덤프
- jcmd
- 분석 툴 - MAT
- java option : -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log
- Page Fault
- 네트워크
- OSI 7 layer
- Physical - 전기 <->전파
- Datalink - 같은 네트워크에서 구분하기위해 (프레이밍)
- Network - ip 붙여서
- Transport - 포트 붙여서
- Session
- Presentation
- Application - TCP/IP기반 프로토콜 정의해서 기능 덧붙여
- L4 스위치 - ip/port 기반 스위칭
L7 스위치 - Application layer 기반- http header나 path등을 가지고 스위칭 가능 - TCP/IP 상태 전이
- 3 way handshaking : SYN -> SYN+ACK -> ACK
-
- 끊을때 4 way handshake: FIN -> ACK (application close) FIN-> ACK
- client가 FIN보내고 server가 받으면 close_wait으로 상태바꾸고 ack보내고, application에 close요청
- application에서 close완료되면, 서버가 FIN을 보내고 LASK_ACK 상태로 전환
- 먼저 FIN을 보내는 기준으로 Active Close / Passive Close 정함
- Close_Wait 쌓인다 (Passive쪽)
- close 당하는쪽에서 FIN받고 CLOSE_WAIT으로 상태 바꾸고,
application에 close요청 -> 응답을 못받음. FIN 못보냄 (application 이 먹통인 상태)
-> 다른 io를 기다리고 있어서 close 처리를 못할 때 (db slow query)
application이 먹통 ( OOM 등 처리 불능)
close_wait이 많이 쌓이면 그거땜에 또 먹통됨 (커넥션이 계속 늘어나서 FD 초과) - close 요청한쪽은 FIN_WAIT2상태로 대기 -> 시간지나면 TIME_WAIT으로 대기 -> 시간지나면(이 값은 고정) CLOSED
- close 당하는쪽에서 FIN받고 CLOSE_WAIT으로 상태 바꾸고,
- Time_Wait : 정상적인 연결 해제를 위해 필요 (Active 쪽)
- 늦게 들어온 패킷도 받아주려고
- Client의 마지막 ack가 유실되었을 경우, 서버는 LAST_ACK상태로 남음, 서버가 재시도로 다시 FIN을 보내면 그걸 받아주기 위해 좀 더 기다려봄), 많다고 크게 문제안됨
- OSI 7 layer
- NginX, Apache
- Apache : 요청당 스레드 또는 프로세스가 처리
- NginX : 이벤트 드리븐 처리구조, 스레드를 많이 사용하지 않음, 비동기로 많은 요청 처리 가능
- Git
- merge / rebase
- merge는 병합, rebase는 베이스 커밋을 바꾸는거
- merge는 커밋해시 안변하고, rebase는 커밋해시 변함 -> 다음 머지 시 충돌..
- merge는 브랜치 그래프 남아있고, rebase는 브랜치 그래프까지 정리됨
- merge는 merge커밋 생김
- 커밋 합칠때 squash하기위해 rebase함
- git flow 정책에서 feature -> dev 브랜치는 squash, dev->master는 merge
- 여러사람이 한 feature 브랜치 개발 같이하면, 서로 계속 merge하면 커밋 그래프가 너무 복잡해짐
-> feature branch pull 할 때 rebase해서 땡김
- reset
- default (--mixed) : Staging area 비움, commit 명령도 되돌림, 수정사항은 남아있음
- --hard : 워킹디렉토리까지 초기화(수정사항 날라감)
- merge / rebase
- GOF, SOLID, DB튜닝, Spring 편리한점
- DevOps
- 개발과 운영의 합성어로서, 개발과 운영의 협업 및 통합을 강조하는 개발 환경이나 문화
- 개발조직과 운영조직간의 서로 협업해 빠른시간에 서비스를 개발 및 배포하는 것을 목적으로 하는 일련의 기술들
- MOM (Message Oriented Middleware)
- Kafka, Rabbit MQ, Active MQ -> 비동기 메시지 전달에 사용
- 느슨하게 결합시킬 수 있음 -> 어플리케이션의 의존성을 제거할 수 있다.
- 책
- 이펙티브 자바
- 시간잴땐 nanoTime, 복사본을 많이 써라 (Immutable)
- 이펙티브 자바
- 이벤트소싱
- CQRS (Command and Query Responsibility Segregation)
- 시스템단위분산
- CQRS (Command and Query Responsibility Segregation)
- 멱등성
- 여러번 수행해도 결과가 같은 성질
'IT' 카테고리의 다른 글
이진탐색트리 - Java (0) | 2020.11.07 |
---|---|
[Java] QuickSort (0) | 2020.10.12 |
그냥 손전등 개인정보처리방침 (0) | 2017.02.03 |
Mac에 anaconda, tensorflow 설치 (0) | 2017.01.04 |
Mac에서 Python 삭제 (0) | 2017.01.04 |