반응형

 Datasource의 pool 관련 기본적인 아래의 설정들을 하게 된 후 실제 서비스에 적용하게 되면 몰려드는 요청 및 처리 시간 때문에 예상치 못한 문제들을 확인하게 됩니다.

<기본설정 예제>

datasource.driverClassName=com.mysql.jdbc.Driver

datasource.url=

datasource.username=

datasource.password=


 보통 요청에 대한 pool 을 관리하기 위해 개수를 조정하게 됩니다.

datasource.initialSize=4    # 커넥션 초기값

datasource.minIdle=4       # 유휴상태로 있을 수 있는 커넥션 최소값

datasource.maxActive=16 # 커넥션 최대값

datasource.maxIdle=16


 이러고 나면, DB 와의 연결 관련 문제들이 발생하게 된다. Validation-Query 설정이나 autoReconnect=true (url의 param 값으로들 많이 붙임) 설정을 하게 된다. 하지만 실제 지속적으로 트래픽이 있는 실환경과 사용과 연결이 거의 없는 테스트 환경은 각기 다르니 유의하기를 바라고 autoReconnect 옵션은 false 해 두기를 바랍니다.(라고 어디서 봄) 

datasource.validation-query=select 1 # DB마다 다름.

datasource.test-on-borrow=true        # 커넥션 사용 시 validation 쿼리를 호출하여 커넥션을 확인하는 기능을 사용할지 말지 여부

datasource.test-while-idle=true


 위와같이 했는데도 'Communications link failure' 같은 메시지를 확인하게 된다면, 사용하는 DB 의 timout 들을 확인하기 바란다.(주로 wait_timeout)

확인을 하고 evictor thread 설정을 해야 에러 메시지를 그만 보게 될 것입니다. DB  wait_timeout 보다 당연히 짧아야 할 것 입니다.

datasource.timeBetweenEvictionrunsMillis=60000 # 1000이 1초, thread 실행주기

datasource.minEvictableIdleTimeMillis=60000

datasouce.numTestsPerEvictionRun=4                      # thread 실행 시 처리될 커넥션 개수


※ Tip 자신의 서비스의 master / slave 마다 설정이 다를 수 있고 권한 때문에 DBA만 조회 가능한 환경이 있을 수 있으니 유의 바랍니다.

-- mysql 의 wait_timeout 확인 방법

SHOW GLOBAL VARIABLES LIKE '%timeout%';


-- mysql 에서 host 에서 접속한 연결 개수 확인 방법

SELECT *

  FROM INFOMATION_SCHEMA.PROCESSLIST

WHERE host = '' #

     AND db = ''

;


그 외에 일반적으로 같이 추가하는 설정들 입니다.

datasource.maxWait=5000 # 애플리케이션 레벨에서 DBCP pool의 커넥션을 획득하기 위해 기다리는 시간이니 주의하기 바람.


대략 이렇게 설정하고 서버의 하드웨어 및 서비스에 따라 적절히 값을 조정하면 될 것 같습니다.


※ 사용하는 dbcp1 / dbcp2 / tomcat-dbcp 에 따라 상이할 수 있으니 유의바랍니다.

반응형
반응형

DDD START 책의 핵심 내용에 대한 정리


1. 도메인 모델 시작.


도메인이란? 소프트웨어로 해결하고자하는 문제 영역으로 다수의 하위 도메인을 가질 수 있다.


[표 1.1] 아키텍처 구성

계층(Layer)

 설명 

 사용자인터페이스(UI) 또는 표현(Presentation) 

 사용자의 요청을 처리하고 사용자에게 정보를 보여준다. 여기서 사용자는 소프트웨어를 사용하는 사람뿐만 아니라 외부 시스템도 사용자가 될 수 있다. 

 응용(Application) 

 사용자가 요청한 기능을 실행한다. 업무 로직을 직접 구현하지 않으며 도메인 계층을 조합해서 기능을 실행한다. 

 도메인  

 시스템이 제공할 도메인 규칙을 구현한다. 

 인프라스트럭처(Infrastructure) 

 데이터베이스나 메시징 시스템과 같은 외부 시스템과 연동을 처리한다. 


엔터티와 벨류

기획서, 유즈케이스, 유저스토리 등의 분석을 통해 도출된 모델은 Entity와 Value로 구분할 수 있다.

Entity는 식별자를 갖는 모델, Value는 개념적으로 완전한 하나의 데이터를 표현할 때 사용한다.




2. 아케텍처 개요.


네 개의 영역

...

DIP 주의사항

 DIP의 핵심을 고수준 모듈이 저수준 모듈에 의존하지 않도록 하기 위함, 하위 기능을 추상화한 인터페이스는 고수준 모듈 관점에서 도출하기 때문에 고수준 모듈에 같이 위치해야 한다.


도메인 영역의 주요 구성요소

[표2.1] 도메인 영역의 주요 구성요소

 요소

 설명 

 Entity 

 고유의 식별자를 갖는 객체로 자신의 라이프사이클을 갖는다. 주문(Order), 회원(Member), 상품(Product)과 같이 도메인의 고유한 개념을 표현한다. 도메인 모델의 데이터를 포함하며 해당 데이터와 관련된 기능을 함께 제공한다. 

 Value

 고유의 식별자를 갖지 않는 객체로 주로 개념적으로 하나인 도메인 객체의 속성을 표현할 때 사용된다. 배송지 주소를 표현하기 위한 주소(Address)나 구매 금액을 위한 금액(Money)과 같은 타입이 밸류 타입이다. 엔터티의 속성으로 사용될 뿐만 아니라 다른 밸류 타입의 속성으로도 사용될 수 있다. 

 Aggregate (애그리거트) 

 애그리거트는 관련된 엔터티와 밸류 객체를 개념적으로 하나로 묶은 것이다. 예를 들어, 주문과 관련된 Order 엔터티, OrderLine 밸류, Orderer밸류 객체를 '주문' 애그리거트로 묶을 수 있다. 

 Repository 

 도메인 모델의 영속서을 처리한다. 예를 들어, DBMS 테이블에서 엔터티 객체를 로딩하거나 저장하는 기능을 제공한다. 

 Domain Service 

 특정 엔티티에 속하지 않은 도메인 로직을 제공한다 '할인 금액 계산'은 상품, 쿠폰, 회원 등급, 구매 금애 등 다양한 조건을 이용해서 구현하게 되는데, 이렇게 도메인 로직이 여러 엔티티와 밸류를 필요한 경우 도메인 서비스에서 로직을 구현한다. 




3. 애그리거트


 복잡한 도메인을 이해하고 관리하기 쉬운 단위로 만들려면 상위 수준에서 모델을 조망할 수 있는 방법이 필요한데, 그 방법이 바로 애그리거트이다. 애그리거트는 관련된 모델을 하나로 모은 것이기 때문에 한 애그리거트에 속한 객체는 유사하거나 동일한 라이프사이클을 갖는다.


4. 리포지터리와 모델 구현(JPA 중심)


엔티티와 밸류 기본 매핑 구현

  • 애그리거트 루트는 엔티티이므로 @Entity로 매핑 설정한다.
  • 한 테이블에 엔티티와 밸류 데이터가 같이 있다면,
    • 밸류는 @Embeddable로 매핑 설정한다.
    • 밸류 타입 프로퍼티는 @Embedded로 매핑 설정한다.
  • AttributeConverter를 이용한 밸류 매칭 처리.(115p)
  • 밸류 컬랙션을 별도 테이블로 매핑할 때
    • @ElementCollection과 @CollectionTable을 함께 사용한다. (120p)

※ JPA에서 식별자 타입은 Serializable 타입이어야 하므로 식별자로 사용될 밸류 타입은 Serializable 인터페이스를 상속받아야 한다.




5. 리포지터리의 조회 기능(JPA 중심)


 기본적으로 아래와 같은 스펙(Specification) 인터페이스를 구현하여, 다양한 조합을 만들 수 있다.

public interface Specification<T> {

public boolean isSatisfiedBy(T agg);

}

(143 page)


JPA를 위한 스펙(Specification)은 CriteriaBuilder와 Predicate를 이용해서 검색 조건을 구현해야 하는데 다음의 인터페이스를 구현해야 한다.

public interface Specification<T> {

public boolean toPredicate(Root<T> root, CriteriaBuilder cb);

}

(147 page)


위와 같은 스펙(Specification) 인터페이스를 구현한 스펙들을 사용할 수 있도록 리포지터리(Repository)를 구현해야 하며, 관련 인터페이스는 다음과 같다.

public interface OrderRepository {

public List<Order> findAll(Specification<Order> spec);

}

(153 page)



조회 전용 기능 구현

 

리포지터리는 애그리거트의 저장소를 표현하는 것으로서 다음 용도로 리포지터리를 사용하는 것은 적합하지 않다.

  • 여러 애그리거트를 조합해서 한 화면에 보여주는 데이터 제공
  • 각종 통계 데이터 제공

JPA와 하이버네이트를 사용하면 동적 인스턴스 생성, 하이버네이트의 @Subselect 확장 기능, 네이티브 쿼리를 이용해서 조회 전용 기능을 구현할 수 있다.

  • 동적 인스턴스 생성 : TypedQuery 사용 시 query 내의 문자열에 new 키워드와 생성할 인스턴스의 완전한 클래스 이름을 지정하고 사용함
  • @Subselect : 쿼리 결과를 @Entity로 매핑할 수 있는 유용한 기능으로 쿼리 실행 결과를 매핑할 테이블처럼 사용한다.



6. 응용 서비스와 표현 영역

사용자에게 기능을 제공하려면 도메인과 사용자를 연결해 줄 표현 영역과 응용 영역이 필요하다.

  • 표현영역은 사용자의 요청을 해석한다.(URL, 요청 파라미터, 쿠키, 헤더 등)
  • 응용서비스는 기능을 실행하는데 필요한 입력값을 메서드 파라미터로 전달받고 실행 결과를 반환한다.


응용서비스의 역할

  • 도메인 객체간의 흐름을 제어한다.
  • 도메인의 상태 변경을 트랜잭션으로 처리해야 한다.

한 응용 서비스 클래스의 역할이 많아지고 크기가 커지는 것 보다 클래스가 많아지더라도 가능한 구분되는 기능별로 구분하는 것이 낫다.(178-179 page) 그리고 인터페이스가 명확하게 필요하기 전까지는 응용서비스에 대한 인터페이스를 작성하는 것이 좋은 설계는 아니다.(181page)



표현영역

  • 사용자가 시스템을 사용할 수 있는 (화면)흐름을 제공하고 제어한다.
  • 사용자의 요청을 알맞은 응용 서비스에 전달하고 결과를 사용자에게 제공한다.
  • 사용자의 세션을 관리한다.

값 검증, 권한검사, ...

조회전용기능은 11장 CQRS에서...




7. 도메인 서비스


 도메인 영역의 코드를 작성하다 보면 한 애그리거트로 기능을 구현할 수 없을 때가 있다. 억지로 한 애그리거트에 넣기 보다는 도메인 서비스를 활용해서 도메인 개념을 명시적으로 드러내면 된다. 도메인 서비스를 구현하는 데 필요한 상태는 애그리거트나 다른 방법으로 전달받는다. 트랜잭션 처리와 같은 로직은 응용 로직이므로 도메인 서비스가 아닌 응용 서비스에서 처리해야 한다. 일반적으로 도메인 서비스의 패키지 위치는 다른 도메인 요소와 같이 위치하고, 외부 의존적인 경우에 따라 인터페이스로 추상화해야 한다.




8. 애그리거트 트랜잭션 관리


  두 스레드가 같은 DBMS를 사용하는 하나의 애그리거트를 사용할 경우 먼저 조회 및 변경을하는 스레드가 있을 때 다른 스레드의 접근을 막거나 다른 스레드의 변경이 있을 경우 다시 조회 후 수정이 이루어지도록 해야한다. 이런 상황은 트랜잭션과 관련이 있는데 Pessimistic-Lock(선점잠금)과 Optimistic-Lock(비선점잠금)을 이해해하고 잘 쓸 수 있어야 한다.


  • Pessimistic-Lock(선점잠금)
    • 공유데이터에 대한 동기화라고 보면 된다.
  • Optimistic-Lock(비선점잠금)
    • version 속성을 활용하여 데이터에 대한 변경을 제어한다.



9. 도메인 모델과 BOUNDED CONTEXT


 모델은 특정한 컨텍스트(문맥)하에서 완전한 의미를 갖는다. 같은 제품이라도 카탈로그 컨텍스트와 재고 컨텍스트에서 의미가 다르다. 이렇게 구분되는 경계를 갖는 컨텍스트를 DDD에서는 BOUNDED CONTEXT라고 부른다. BOUNDED CONTEXT는 모델의 경계를 결정하며 한 개의 BOUNDED CONTEXT는 논리적으로 한 개의 모델을 갖는다. BOUNDED CONTEXT는 용어를 기준으로 구분한다.


 BOUNDED CONTEXT는 도메인 모델뿐만 아니라 도메인 기능을 사용자에게 제공하는데 필요한 표현영역, 응용서비스, 인프라영역 등을 모두 포함한다. 단순한 서비스-DAO로 구성된 CRUD 방식을 사용하거나 CQRS패턴과 같은 형태로 구현할 수도 있다.


 BOUNDED CONTEXT는 하나의 마이크로서비스가 될 수 있으며, 이러한 BOUNDED CONTEXT 사이의 연결은 REST-API나 메시지큐를 통해서 처리할 수 있다.




10. 이벤트


 도메인 객체에 서로 다른 도메인 로직이 섞이면 트랜잭션이나 성능 그리고 설계에 문제를 일으킬 수 있다. 서로 다른 도메인 로직을 이벤트 처리를 통해 DeCoupling하여 한 기능을 하는 로직의 cohesion을 높이고 성능이 향상되도록 한다. 


이벤트 처리 지원 방법

  • 이벤트, 핸들러, 디스패처로 방식과 스프링 AOP를 통해서 구현할 수 있다.
  • REST-API로 처리되도록 구현한다.
  • Scheduler처럼 Forwarder를 구현한다.



11. CQRS (Command and Query Responsibility Segregation)

 단일 모델에서의 조회문제로 인하여 상태를 변경하는 명령(Command)을 위한 모델과 상태를 제공하는 조회(Query)를 위한 모델을 분리하는 패턴이다. 서비스 상황에 맞게 조회의 성능을 위해  MyBatis를 사용하거나 메모리 기반의 NoSQL을 사용할 수도 있다.







※ 인터넷 DDD 자료들

- DDD 관련 지디넷 게시글들





반응형
반응형

Java Web Services API 개념 되새기기


 최근 몇 년 사이에 웹서비스 개발과 관련되어 인터넷의 URI 를 나타내는데 REST-API 라는 architechural 구조를 가진 개념이 많이 사용되고 있다. Java-Spring 기반의 사용자들은 Spring-MVC 를 사용하거나 Spring with jersey 의 조합으로 REST-API 서비스들을 만들어 오고 있었다. 이렇게 개발만하다 보니 왜 spring 과 jersey 를 사용하게 되었지도 잊어버리고 (ㅡ,.ㅡ ... ) 그래서 정리를 해 보려고 한다. 


 Java Web Service API 의 주요한 2가지 개념이 있다. JAX-RS 와 JAX-WS 이다. 자바 웹 서비스 애플리케이션 코드를 작성하는 두가지 방법이 있는데 바로 잘 아는 Restful 과 SOAP 이다. 이 두개의 주요 API들은 JavaEE6 부터 웹서비스 애플리케이션 개발을 위해 자바 표준으로 정의 되어 있다.


JAX-RS 는 RESTful 웹서비스를 위한 것이며, JAX-RS 애플리케이션을 생성하기 위해 현재 사용중인 주요한 2가지 구현으로는 Jersey 와 RESTeasy 가 있다. (Apache CFX, Restlet 도 있다.)

JAX-WS 는 SOAP 웹서비스를 위한 것이며, JAX-WS를 코드로 작성하는 두가지 방법으로 RPC 와 Document 형태가 있다.


 REST 와 SOAP 의 차이를 표를 통해서 나타내면 다음과 같다.

REST 

SOAP

  Architecural style

 Protocol

 REST stands for REpresentational State Transfer

 Simple Object Access Protocol

 REST can use SOAP web services because it is a concept 

 and can use any protocol like HTTP, SOAP.

 SOAP can't use REST because it is a protocol.

 REST use URI to expose business logic.

 SOAP use service interfaces to expose the business logic. 

 REST does not define too much standards like SOAP.

 SOAP defines standards to be strictly followed. 

 REST permits different data format such as Plain text, HTML, 

 XML JSON etc...  

 SOAP permits XML format data only.


 이러한 차이와 과거로부터 현재까지 SOAP 에서 REST로 변화?하게된 흐름을 이해한다면 더 나은 REST-API 개발을 이끌어 낼 수 있을 것이다.


ref) https://www.javatpoint.com/web-services-tutorial

반응형
반응형

 스프링에서는 포괄적인 캐시 추상 인터페이스를 이용하여, ehcache를 구현할 수 있습니다. (3.1부터? 2.5부터?)

그래서 메소드에 어노테이션으로 @Cacheable 이나 @CacheEvict 와 같이 코드에 추가하여 개발자로 하여금 쉽게 사용할 수 있게 하고 있습니다.


하지만 당연하게도 어노테이션만으로 구현되지는 않으나 기본적인 추가내용이 어렵지는 않습니다.

아래의 단계를 거치면 기본적인 cache 구현을 사용할 수 있습니다.



1. dependency 추가

A. gradle 

compile('net.sf.ehcache:ehcache')


B. maven

<dependency>

<groupId>net.sf.ehcache</groupId>

<artifactId> ehcache </artifactId>

<version>2.10.4</version>

</dependency>


2. ehcache.xml 생성

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:noNamespaceSchemaLocatioin="ehcache.xsd"

updateCheck="true"

dynamicConfig="true"

maxBytesLocalHeap="4M">


<cache name="testName"

     maxBytesLocalHeap="2M"

     memoryStoreEvictionPolicy="LRU" // LFU, FIFO 

     timeToLiveSeconds="3600">

</cache>

</ehcache>


3. CacheManager config 생성

@EnableCaching

@Configuration

public class MyCacheConfig {

@Bean

public CacheManager cacheManager() {

return new EhCacheCacheManager(ehCacheManager().getObject());

}


@Bean

public EhCacheManagerFactoryBean ehCacheManager() {

EhCacheManagerFactoryBean bean = new EhCacheManagerFactoryBean();

bean.setConfigLocation(new ClassPathResource("ehcache.xml"));

bean.setShared(true);

}

}


4. 메소드에 @Cacheable 어노테이션으로 사용

/**

 * 기본적으로 key-value 형태이며, 캐시이름으로 값이 없으면 만들고 조건을 줄 수 있다.

 */

@Cacheable("testName") 

@Cacheable(cacheNames = "testName", key="#{keyId}") 

@Cacheable(value = "testName", key="#{keyId}")


@CachePut(cacheNames = "testName") // 캐시를 갱신한다.


@CacheEvict(cacheNames = "testName", allEntries = true) // 캐시를 삭제한다.

※ 주의사항으로 같은 클래스 내의 호출(self-invocation)은 안 되니 유의해야한다.


5. 간단한 CacheManager TEST-CASE.

@RunWith(SpringRunner.class)

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)

@Import(MyCacheConfig.class)

public class MyCacheConfigTest {

@Autowired CacheManager cacheManager;


@Test public void testSample() {

Cache cache = cacheManager.getCache("testName");

cache.put("testKey", "testValue");


System.out.println("- cache=" + cache.get("testKey").get());

}

}



ref) http://www.ehcache.org/

ref) https://spring.io/guides/gs/caching/

ref) https://memorynotfound.com/spring-boot-ehcache-2-caching-example-configuration/


반응형
반응형

Spring-boot-cli 설치 및 Repository 적용


Spring-CLI 로 프로젝트를 생성하여 Git Repository를 처음에 올리는 방법을 기술하도록 하겠습니다.


A. 먼저 spring init 명령어를 통해서 spring-cli 를 수행하려면 설치를 해야하는데요. 아래의 방법들이 있습니다.

1. spring-boot-cli 라이브러리를 직접 설치 및 환경설정을 한다.

2. SDKMAN(http://sdkman.io) 를 설치하여 spring-boot-cli 를 설치한다.(bashrc 가 수정되는 바람에 나중에 삭제할 때 헤맬 수 있다.)

3. Homebrew 로 설치한다.(https://brew.sh)

4. 맥포트로 설치한다.(https://www.macports.org/install.php)


B. 저는 spring-boot-cli 라이브러리를 직접 설치 및 환경설정을 하려고 합니다.

1. 적당한 위치에 파일을 받고 푼다.

2. .bash_profile 에 SPRIING_HOME, PATH, CLASSPATH 를 설정하고 적용한다. (source .bash_profile)

3. 커멘드 창에 spring --version 또는 spring --help 를 타이핑하여 잘 적용되었는지 확인한다.


C. Spring-Initializer(start.spring.io)를 통해서 새로운 프로젝트를 생성합니다.

1. 아주 간단한 예

  spring init

2. -d 옵션을 사용하여 원하는 모듈을 추가한다.--build 옵션을 사용해 gradle 또는 maven 을 지정할 수 있다.

- spring init -dweb -g=groupId -a=artifactId -n="name" --package-name=pa --description=de --build maven myproject

- spring init -dweb --groudId=g --artifactId=a --name=n --package-name=p --description=d --build maven myproject

3. 존재하는 파일이나 디렉토리에 덮어쓰려면 -f, --force 를 사용하면 됩니다.


D. 옵션 목록 상세 입니다. spring help init을 통해 볼 수 있고, spring init --list 명령어를 수행하면 자세한? 정보들도 볼 수 있어요.

1. -a / --artifactId

2. -b / --boot-version

3. --build

4. -d / dependencies

5. --description

6. -f / force

7. --format

8. -g / --groupId

9. -j / --java-version

10. -l / --language

11. -n / --name

12. -p / --packaging

13. --package-name

14. -t / --type

15. -- target

16. -v / --version

17. x / --extract


E. 설치된 프로젝트를 실행해 본다.

1. 같이 설치된 mvnw 로 실행해 본다.

- ./mvnw spring-boot:run

2. 이미 설치된 maven 으로 실행해 본다.

- mvn spring-boot:run


1.  같이 설치된 gradlew 로 실행해 본다.

- ./gradlew bootRun

2. 이미 설치된 gradle 로 실행해 본다.

- gradle bootRun


F. Git 정보 조회 및 Repository 반영.

1. 프로젝트의 git 정보를 조회한다.

> git config --list

2. 수정할 정보들을 처리한다.

> git config user.name "jooChangYoo"

> git config remote.origin.url " https://github...."

3. 설치된 소스들을 올린다.

> git add -A

> git commit -m ""

> git push origin master (또는 git remote add origin https://github... 후 처리함.)


관련글) http://joochang.tistory.com/84

반응형
반응형

JAVA 기반의 웹서비스 개발을 하는 사람이라면 Spring을 떼어낼 수 없습니다. 그리고 누구나 @Autowired를 최소한 한 번쯤은 사용하고 있습니다. 하지만 보통 크게 관심을 갖지 않으면 “IoC 개념을 구현한 Spring-Container의 Dependency-Injection을 사용했다.” 라는 정도만 숙지하고 사용하게 됩니다. 바로 아래의 가장 초보적인 예제로 말이죠.


ex) 가장 초보적인 @Autowired 사용예제.

public class MyTest {

@Autowired MyService1 myService1;

}


조금 익숙해 지게 되면 다음과 같이 constructor 또는 setter 를 사용하여 확장하게 됩니다. @Autowired 는 기본적으로 byType으로 bean을 찾고 못 찾으면 byName으로 찾습니다.  그리고 필수적인 속성들은 생성자로 그렇지않은 것은 메서드로 생성하기도 합니다.


ex) constructor 또는 setter 를 사용한 @Autowired 사용예제.

public class MyTest {

MyService2 myService2;

MyService3 myService3;


@Autowired

public MyTest(MyService2 myService2) {

this.myService2 = myService2;

}


@Autowired(required=false)

public void setMyService3(MyService3 myService3) {

this.myService3 = myService3;

}

}


우리가 이와 같이 사용하는 Spring의 @Autowired 와 관련있는 DI 의 히스토리와 개념에 대해 짚고 넘어 가도록 하겠습니다. IoC(Inversion of Control)의 한 형태인  DI(Dependency-Injection)는 2004년부터 자바개발의 중요한 프로그래밍 패러다임이 되었습니다. Spring 뿐만 아니라 Guice 프레임워크 등에서 DI를 구현해 왔는데요. 다양한 프레임워크에서 각자의 방향으로 발전하다가  SpringSource의 Rod-Johnson 과 Guice 를 발표한 Bob-Lee가 2009년5월 함께 표준-DI 인터페이스 JSR-330을 작업하기로 발표한 후 몇 달만에 final을 내놓게 되었습니다..(링크)


 Martin-Fowler 의 말을 빌리면 IoC 는 Concept 이고 DI는 Pattern 이라는 설명을 하기도 했고, IoC 를 실제 구현한 것들이 DI를 포함하여 많은 분들께 익숙한 Factory-Pattern과 Service-Locator-Pattern 등이 있습니다.


... ING~~


PrototypeBean의 Dependency-Lookup 의 전략들..

1. ObjectFactory

2. ServiceLocatorFactoryBean

3. Provider<T>

4. @Lookup




reference) https://martinfowler.com/articles/injection.html  

반응형
반응형

Spring boot Tips


  • spring-boot 에서 spring-boot-starter-data-* 관련 dependency 들을 추가하면 'mysql-connector-java' 같은 connector를 추가해야 합니다.
  • no profiles are currently active 가 뜨면 spring.profiles.active=@activeProfile@ 설정을 해야 합니다.
  • gradle 로 구동 시 -Dspring.profiles.active= 를 하더라도 되지 않는 경우가 있는데 아래와 같이 설정해 주어야 합니다.
bootRun {
systemProperties System.properties
}


테스트 케이스에서 ConfigurationProperties나 lombok으로 인해 gradle build 실패

  • spring boot configuration annotation processor not found in classpath 
    • Spring-Boot2에서 ConfigurationProperties 관련 에러가 발생
    • Spring-Doc 문서 참고 - spring-boot-configuration-processor 관련 설정 추가
    • Gradle 버전마다 다를 수 있으니 유의

annotationPorcessor('org.springframework.boot:spring-boot-configuration-processor')
compile('org.springframework.boot:spring-boot-configuration-processor')

annotationPorcessor('org.projectlombok:lombok')
compile('org.projectlombok:lombok)




반응형
반응형

Class 파일은 실행 시 Link를 할 수 있도록 Symbolic Reference 만을 가지고 있다.

Runtime 시점에 실제 물리적인 주소로 대체되는 작업인 Dynamic Linking이 일어나게 된다.



Class-File-Format은 Network-Byte-Order 를 사용하기 때문에 Big-Endian 방식을 사용하게 된다.


메모리 주소값을 할당하는 방식을 Network-Byte-Order 를 사용하는데 Big-Endian을 사용하기로 약속되어있고, 서로 다른 계열의 CPU끼리 데이터를 전송받을 때의 문제점을 해결하기 위해 정해진 일종의 약속이다.


  • Gabage-Collector
    • Gabage-Collection을 통해 Heap이나 Method-Area의 사용되지 않는 Object를 Memory에서 삭제한다.
    • HotspotJVM은 Heap을 Young-Generation과 Old-Generation으로 나누어 사용한다.
    • Young-Generation은 Eden영역과 Survivor영역이 존재한다. 객체가 Eden영역으로 처음 Allocation 되며 자주 사용되던 객체가 Old영역으로 가기 전에 Survivor 영역으로 이동하게 된다.
    • Old-Generation은 성숙된 객체들이 Promotion 되는 장소이다.
    • Minor-GC는 Young-Generation에서 발생, Promotion이 발생...
    • Major-GC는 Old-Generation에서 발생, Minor-GC를 통해서 발생할 수도 있음...
    • Full-GC는 너무많은 클래스 로딩으로 Permanent-Area가 부족하게 되어 발생할 수 있음...
    • 잘못하면 suspend-time이 길어져 Stop-The-World 방식의 Compaction으로 회귀할 수 밖에 없음.
  • Gabage-First-Collector
    • Generation 구분을 없애고 Region 으로 재편하였다. 논리적인 구분?
    • 물리적인 Young/Old 영역에서의 Allocation/Promotion 대신 Gabage로만 꽉 차 있는 Region부터 Collection을 시작한다고해서 붙여진 이름이다.
    • G1 Collector는 철저하게 Region 단위로 Gabaga-Collection 이 발생한다. 따라서 Suspend 현상도 Region을 사용하는 Thread에 국한된다.
  • Calss-Loader
    • JVM 안으로 Class를 Load 하고 Link를 통해 적절히 배치하는 일련의 작업을 수행하는 모듈.
    • *.class 파일들을 Runtime-Data-Area 에 적재한다.
    • Bootstrap-Class-Loader(jre/lib/rt.jar), Extention-Class-Loader(jre/lib/ext), Application-Class-Loader(System-Class-Loader, User-Defined-Class-Loader)
    • java5부터 Class-Sharing 제공...
    • Load-Time-Dynamic-Loading과 Runtime-Dynamic-Loading 이 있다...
    • Class-Loader-Work 는 Loading, Linking, Initialization의 세가지 과정이 있다. 클래스파일이 검증을 거쳐 symbolic-reference 가 direct-reference 로 변경되는 과정이다.
  • Executor-Engine
    • Load된 Class의 Bytecode를 실행하는 Runtime-Module이다.
    • 모든 ByteCodes를 Interpreter 방식으로 처리하여 초기의 JVM은 실행속도에 있어서 약점을 가지고 있었는데,  JIT(Just-In-Time) Compiler 방식이 등장하여 문제점을 보완하였다.
    • JIT(Just-In-Time) Compiler 는 Bytecodes 를 NativeCode로 Compile하여 실행시간을 빠르게 하였다. 보통 두 방식을 섞어서 쓴다(Lazy Fashion).
    • loop 와 다차원배열 처리에 성능적으로 약함...


  • Runtime-Data-Area
    • Process로서의 JVM을 수행하기 위해 OS로부터 할당받는 메모리 영역.
    • PC Register, Java Virtual Machine Stacks, Method Area, Heap, Native Method Stacks.
    • PC Register
      • 각 Thread마다 하나씩 존재하며 Thread가 시작할 때 생성된다. Stack-Base로 작동하며 현재 수행중인 JVM 명령어를 가지게 된다.
    • Java Virtual Machine Stacks
      • 각 Thread마다 하나씩 존재하며 Thread가 시작할 때 생성된다. 여러 Stack-Frame들이 있고 현재 수행하고 있는 Method의 정보를 저장하는 것을 Current-Frame 이라고 한다.
      • Stack-Frame은 Local-Variable-Section, Operand-Stack, Frame-Data 세 부분으로 되어 있다.
    • Native Method Stacks
      • 각 Thread마다 하나씩 존재하며 Thread가 시작할 때 생성된다. Native-Code로 호출한 메소드에 대한 Stack 영역.
    • Method Area
      • 모든 Thread들이 공유하는 메모리 영역이다.  JVM이 기동할 때 생성이 되며, Garbage-Collection의 대상이 된다. ClassLoader에게 넘겨받은 ClassFile에서 Type 관련 정보를 추출하여 저장하게 된다. Type-Information, Constant-Pool, Field-Information, Method-Information, Class-Variable, ReferenceToClass-ClassLoader, ReferenceToClass-Class, Method-Table.
    • Heap
      • 모든 Thread들에 의해 공유된다. 메모리 해제는 Gabage-Collection을 통해서 이루어지고, Instance(Object)와 Array객체 두 가지 종류만 저장되는 공간일 뿐이다. 실제 구현에 대해서는 각 벤더에게 위임하였다. HotspotJVM이나 IBM-JVM의 Object-Layout과 Heap 구조를 통해 JVM의 Heap에 대한 전반적인 이해를 하는 것이 의미있을 것이다.

Class와 Instance의 관계

Java 관련 서적을 보면 Class와 Instance를 붕어빵틀과 붕어빵으로 표현하는데, 이것은 좋은 비유라고 생각한다. Class가 Loading 되면 Method-Area 에는 Java로 작성한 Code에서 Class의 속성들을 추철하여 정보화되어 기록된다. 다시 말해 하나의 틀이 만들어지는 셈이다. 이를 테면 이 Class는 이러이러한 Method가 있고 이 내용은 어떠하여 변수는 어떤 것을 가진다는 정보가 MethodArea에서 생성된다.

 만약 누군가가 이 Class에 대한 Instance를 하나 생성하려고 한다면 Method Area의 Class 정보를 틀로 하여 Heap에 Object를 하나 찍어낸다. 이것이 Instance이고 여기에 실제 변수에 대한 실제 메모리 상의 주소 등을 포함하여 사용하게 되는 것이다.

 Member Variable(Instance Variable)의 경우는 Heap에 생성된 Instance에 그 값이 저장되고 Class Variable은 이 Class 정보가 있는 Method Area, 더 정확하게는 Class Variable에 저장된다.


  • Thread...
    • Native Thread 를 White-Thread로 부르고, JVM의 Thread를 Green-Thread라고 부르던 때가 있었다...(JAVA 1.1)
    • JAVA1.2부터 ... 둘이 혼용된 Thread-Model ...
    • JAVA1.3부터 결국엔 Native 가 Default-Thread-Model...
    • GreenThread는 MultiProcessSystem에서 이점을 가질 수 없었다...

Synchronization이 필요한 지역을 Critical-Section이라 하고, Thread가 Critical-Section에 들어가면 반드시 Synchronization 작업을 수행하게 된다. 바꾸어 말하면 Thread 가 Critical-Section에 들어가기 위해서는 Lock을 요청하는 것이고 이 Critical-Section은 Synchronization으로 보호되고 있다고 할 수 있다.


- Object Header에 lock-counter를 통해서 알 수 있다...





반응형
반응형
  • JAVA1
    • Collection이 없어서 Vector, Stack, Hashtable, Properties 가 컬렉션의 명명법을 따르지 않음.
    • Vector, HashTable 클래스들은 동기화처리됨.

  • JAVA1.1
    • Reflection
    • Calendar 

  • JAVA1.2
    • Collection 프레임워크 등장
    • ArrayList, HashMap 클래스들은 동기화처리하지 않고 필요한 경우에만 java.util.Collections 클래스의 동기화 메소드를 이용.

  • JAVA1.3
    • Hotspot-Compiler 내장...(불필요한 컴파일을 회피하고 HotCode에 Optimization을 집중..)
    • Math 의 OS 의존성 때문에 (CPU명령어처리가 다르니까..) StrictMath 가 탄생했다.

  • JAVA1.4
    • CharSequence 가 추가되어 String, StringBuffer 등의 클래스가 구현하였다.

  • JAVA1.5
    • enhanced for-looop
    • Generics 가 추가됨.
    • Iterable 인터페이스 추가.(Collection 인터페이스가 상속받도록 결정)
    • enum(열거형)이 추가됨.
    • Autoboxing 이 추가됨. 기본형과 참조형 사이의 연산이 가능해짐. 
    • Covariant ReturnType(공변반환타입)이 추가되어 Override하는 함수의 반환타입을 SubClass 타입으로 쓸 수 있게 함.
    • 메소드의 매개변수에 동적으로 할당 할 수 있게 가변인자(variable arguments)가 추가되었음.
      • 가변인자는 매개변수 중 제일 마직막에 선언, 내부적으로 배열을 생성하므로 성능을 고민해야 함
    • java.util.concurrent.locks 와 java.util.concurrent.atomic 패키지 추가.
    • String의 contains(CharSequence s), replace(CharSequence old, CharSequence nw)가 추가되었다.
  • JAVA1.6
    • 상수를 선언과 동시에 초기화하지 않아도 되며, 사용하기 전에만 초기화하면 되도록 바뀌었다.

  • JAVA1.7
    • 정수형 리터럴 중간에 '_' under-score를 넣을 수 있게 되었다.
    • 2진 리터럴이 추가되었다. 
    • switch 문 조건식에 문자열이 추가되었다
    • '|' 기호를 이용한 multi-catch 블럭.
    • try-with-resources 가 추가되었음.
      • 보통 입출력관련 클래스들을 사용할 때 close()를 호출하지 않아도 try 블럭을 벗어나는 순간 자동적으로 호출이 된다.
      • AutoClosable 인터페이스를 구현한 클래스여야만 한다.
    • Generics 객체 생성 시 추정가능한 경우 타입을 생략할 수 있다.
      • List<String> list = new ArrayList<>();
    • @SafeVarargs Generics 가변인자에 사용.
    • fork & join 프레임워크가 추가됨.
    • '+'가 포함된 문자열이 parseInt()로 변환가능하게 된 것은 JDK1.7부터이다.

  • JAVA1.8
    • 람다와 스트림.
    • 인터페이스에 default-method와 static-method 가 추가되었다.
    • (지역클래스에서 접근하는 지역변수...)Inner-Class에서 외부클래스의 지역변수는 final을 붙여야 했으나, JAVA8부터 final을 생략할 수 있게 바뀌었다.(대신 컴파일러가 자동으로 붙여준다.)
    • java.time 패키지로 개선된 날짜/시간 제공.
    • java.util.Object 의 hash() 함수의 추가로 Override한 hashCode 구현 지원.
    • @FunctionalInterface 함수형인터페이스 선언.
    • @Native native 메소드에서 참조되는 상수 앞에 붙인다.
    • @Repeatable 어노테이션을 반복해서 적용할 수 있게 한다.

  • ETC
    • JVM이 모든 배열의 길이를 별도로 관리하며 arrayName.length ...
    • array vs arrayList
    • Comparable은 기본적으로 오름차순이기 때문에, Comparator를 구현하여 정렬기준을 바꿀 수 있다.
    • Generics 는 컴파일 시 형변환되어 *.class 파일에는 없는데 주된 이유는 이전 버전 호환을 위해서이다.


반응형
반응형

Let's take a lookt at spring-boot!


한 줄로만 요약할 수는 없겠지만 굳이 이야기 한다면 '좀 더 쉽게 스프링을 관리하여 스프링을 빠르게 개발하는데 도움을 주는 도구' 이지 않을까 합니다.


A. spring-boot 를 사용하는데 있어서 핵심요소들이 있습니다. 

1. @SpringBootApplication 을 통한 기본적인 Auto-Configuration 을 활성화 할 수 있습니다. 

2. spring-boot 의 starter 들을 통해 이전에 비해 spring 의 설정들을 간소화 및 관련된 의존성들을 쉽게 관리할 수 있게 됬습니다.

3. spring-boot CLI(Commnand Line Interface) 를 통해서 간결한 스프링 개발 방식을 제공합니다.

4. Actuator 를 통해서 애플리케이션의 내부 작동을 살펴볼 수 있습니다.


B. 다음으로 spring-boot 애플리케이션 프로젝트를 생성하는 방법들을 확인해 보겠습니다.

1. Maven 으로 프로젝트를 생성할 수 있습니다.

mvn archetype:generate -B\

  -DarchetypeGroupId=am.ik.archetype\

  -DarchetypeArtifactId=spring-boot-blank-archetype\

  -DarchetypeVersion=1.0.6\

  -DgroupId=com.example\

  -DartifactId=hajiboot\

  -Dversion=1.0.0-SNAPSHOT

2. Sping Initializer의 웹 인터페이스를 사용합니다.

- http://start.spring.io

3. 개발도구들을 활용해서 프로젝트를 생성합니다.

- IntelliJ, SpringToolSuite

4. Spring Boot CLI 에서 Initializer 를 사용 합니다.

- spring init

- spring init -dweb,jpa,security --build gradle -p jar -x (현재디렉토리에 풀기를 원하면 --extract 나 -x 사용)

spring init -d=web,thymeleaf,data-jpa,h2 --groupId=com.example  --artifactId=mylotto --name="My Lotto Service" --package-name=mylotto --description="My Lotto Service" --build maven mylotto


참고) spring-boot 의 실행.

1. mvn spring-boot:run

2. mvn package 실행 후 java -jar *.jar


참고) spring-boot-cli 설치.

1. spring-boot-cli 라이브러리를 직접 설치 및 환경설정을 한다.

2. SDKMAN(http://sdkman.io) 를 설치하여 spring-boot-cli 를 설치한다.(bashrc 가 수정되는 바람에 나중에 삭제할 때 헤맬 수 있다.)

3. Homebrew 로 설치한다.(https://brew.sh)

4. 맥포트로 설치한다.(https://www.macports.org/install.php)


C. DB

1. JDBC 연결.

a) pom.xml 에 spring-boot-starter-data-jdbc 또는 spring-boot-starter-data-jpa(jdbc를 포함) 그리고 mysql-connector 를 추가한다.

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-data-jdbc</artifactId>

</dependency>

<!-- jpa 안에 jdbc 포함 -->

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-data-jpa</artifactId>

</dependency>

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

</dependency>


b) application.properties 에 설정값들을 추가한다.

spring.datasource.dirver-class-name=com.mysql.jdbc.Driver

spring.datasource.url=jdbc:mysql://*******:3306/my_lotto

spring.datasource.connectionProperties=useSSL=false;characterEncoding=UTF-8;zeroDateTimeBehavior=convertToNull;useAffectedRows=true

spring.datasource.username=

spring.datasource.password=

spring.datasource.initSQL=SELECT 1

spring.datasource.tomcat.max-wait=3000

spring.datasource.tomcat.mac-active=2

spring.datasource.tomcat.test-on-borrow=true


c) JdbcTemplate 으로 CRUD 를 한다.

@Autowired JdbcTemplate jdbcTemplate;


public List<MyLotto> selectList() {

String query = "SELECT * FROM mylotto ORDER BY seq desc LIMIT 10";

return jdbcTemplate.query(query, new BeanPropertyRowMapper<MyLottoVO>(MyLottoVO.class));

}


2. MyBatis 연동.

a) mybatis-spring-boot-starter 를 추가한다.(1.2.0 이 있지만... parent version 1.5.2에선 안 되는 듯)

<dependency>

<groupId>org.mybatis.spring.boot</groupId>

<artifactId>mybatis-spring-boot-starter</artifactId>

<version>1.1.1</version>

</dependency>


b) using simple mapper 

- dependency 를 추가하고, datasource에 대한 application.properties 설정이 되어 있단면 아래의 interface Mapper 만 있으면 동작한다.

@Mapper

public interface MyLottotMapper {

@Select("SELECT * FROM mylotto WHERE key = #{key}")

MyLotto selectOne(@Param("key") Integer key);


@Select("SELECT NOW() FROM DUAL")

String selectCurrentDateTime();

}


c) using SqlSession

- Dao, Repository 역할을 가지는 클래스를 만든다.

@Repository

public class MyLottoRepository {

private final SqlSession sqlsession;


public MyLottoRepository(SqlSession sqlsession) {

this. sqlsession = sqlsession;

}


public MyLotto selectOne() {

return (MyLotto) this.sqlSession.selectOne("com.joochang.lotto.selectOne"); 

}

}

- mapper.xml 을 만든다.

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.joochang.lotto">

<resultMap id="myLotto" type="com.joochang.lotto.MyLotto">

...

</resultMap>

<select id="selectOne" resultMap="myLotto">

<![CDATA[

SELECT * FROM mylotto LIMIT 1

]]>

</select>

</mapper>


3. JPA 연동.

a) JPA 를 MySql과 함께 사용하려면 먼저 아래와 같이 pom.xml 에 dependency 를 추가해 줍니다.

<!-- jpa 안에 jdbc 포함 -->

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-data-jpa</artifactId>

</dependency>

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

</dependency>

b) application.properties 에 JPA와 관련된 설정을 추가합니다.

spring.jpa.databas=MYSQL

spring.jpa.show-sql=true

# 테스트할 때 유용할 것 같은 기능. #실환경에서 돌린다면 지옥을 경험한다..

# spring.jpa.hibernate.ddl-auto=create # create or create-drop

c) Entity 클래스를 생성합니다.

@Entity

public class MyLotto {

@Id

@GeneratedValue(strategy = GenerationType.AUTO)

private int id;

private String name;


(생략)...

}

d) Dao, Repository 역할을 가지는 interface 를 만들어야 합니다.

public interface MyLottoJPA extends CrudRepository<MyLotto, Integer> {

MyLotto findById(Integer id);

}

또는

public interface MyLottoJPA extends JpaRepository<MyLotto, Integer> {

MyLotto findById(Integer id);

}


D. Logger

spring-boot는 모든 내부 로깅에 대해서 Commons-Loggins-API 를 사용합니다. 기본 구성은 Java-Util-Logging, Log4j2logback 입니다. 기본적으로 spring-boot 'Starters' 를 사용하면 logback 이 사용될 것인데, Java-Util-Logging, Commons-Logging, Log4j 또는 SLF4J 를 사용하는 의존적인 라이브러리들이 모두 올바르게 동작하도록 적절한 logback 라우팅이 포함됩니다.


a) spring-boot는 콘솔에 INFO 레벨 로그를 표시하려고 로그백(http://logback.qos.ch)으로 로깅을 설정합니다. 로깅 구성을 완전히 제어하려면 클래스패스 루트(src/main/resources)에 logback.xml 파일을 생성해야 합니다. logback.xml 이 존재하면 우선적으로 파일의 내용이 적용됩니다. 다르게 하려면 logback.xml 의 이름을 변경하고 (logback-spring.xml) application.properties 에 로그설정들을 추가하면 됩니다.

참고) applcation.properties

logging.config=classpath:logback-spring.xml

logging.exception-conversion-word=

logging.file=

logging.level=

logging.path=

logging.pattern.console=


b) 만약 logback 대신 log4j2 를 사용하고 싶다면 다음과 같이 해야 합니다.

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter</artifactId>

<exclusions>

<exclusion>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-logging</artifactId>

</exclusion>

</exclusions>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-log4j2</artifactId>

</dependency>


references of loggers)

https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-logging.html

https://docs.spring.io/spring-boot/docs/current/reference/html/howto-logging.html


E. Profile

 스프링 프레임워크는 스프링 3.1부터 Profile-Based Configuration을 지원했습니다. 이것은 애플리케이션의 환경설정을 분리하는 방법이면서 특정한 환경을 구성하기도 합니다. @Component나 @Configuration 은 @Profile 과 함께 쓰여 애플리케이션의 로딩을 제한할 수 있습니다.

 spring-boot를 빌드 및 실행 시 "no profiles are currently active" 라는 메시지를 마주칠 수 있는데요. 에러는 spring.profiles.active 값을 설정하면 해결할 수 있습니다. pom.xml 과 src/main/resrouce 의 application.properties 에 대한 설정을 다음과 같이 합니다.


참고) pom.xml

<properties>

<activeProfile>local</activeProfile>

</properties>

<profiles>

<profile>

<id>local</id>

<activation>

<activationByDefault>true</activationByDefault>

</activation>

<properties>

<activeProfile>local</activeProfile>

</properties>

</profile>

<profile>

<id>release</id>

<properties>

<activeProfile>release</activeProfile>

</properties>

</profile>

</profiles>


참고) application.properties

spring.profiles.active=@activeProfiles@

위와 같이 설정 후 application-{profile}.properties 형태의 입맛에 맞는 파일들을 생성하여 사용할 수 있으며, 실행은 아래와 같이 합니다.

java -jar -Dspring.profiles.active=local target/lotto-0.0.1-SNAPSHOT.jar

또는 각 서버에서 환경변수 설정을 통해 사용할 수 있습니다.

export SPRING_PROFILES_ACTIVE=release


references of profiles)

https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html

- https://docs.spring.io/spring-boot/docs/current/reference/html/howto-properties-and-configuration.html

https://groups.google.com/forum/#!topic/ksug/BBaO3y8hSaU



Spring-boot-profiles

https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html


Common-Application-Properties

http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html


Etc...

http://www.mybatis.org/spring-boot-starter/

https://github.com/spring-projects/spring-boot

https://www.slideshare.net/madvirus/spring-boot-42817314

https://www.slideshare.net/whiteship/ss-47273947



반응형
반응형

Python 으로 간단하게 만들어본 테스트 푸시 발송 기능.


#!/usr/bin/env python

# -*- coding: utf-8 -*-

 

import socket, ssl, json, struct, time

 

 

if __name__ == '__main__':

    # host

    apnsHost = 'gateway.push.apple.com'

    # port

    apnsPort = 2195

    # dictionary type

    apnsHostPort = (apnsHost, apnsPort)

 

    # certifated file

    certFile = 'apns_production.pem'

 

    # device Token

    apnsToken = 'abcedfg0123456789'

 

    # notification payload

    apnsPayload = {

        'aps':{

            'alert': '테스트푸시 123 abc'.decode('UTF-8'),

            'sound': 'pushSound.caf',

            'content-available': 1,

        },

        'extra':{

            'param1':0,'param2':'test'

        },

        'data':{

            'title':'a',

              'body':'b'

        }

    }

 

    apnsPayload = json.dumps(apnsPayload, ensure_ascii=False, separators=(',',':')).encode('UTF-8')

    # apnsPayload = json.dumps(apnsPayload)

 

    apnsToken = apnsToken.decode('hex') # python 2

    # apnsToken = apnsToken.replace(' ','')

    # apnsToken = apnsToken.decode('hex') # python 3

 

    format = '!BLLH32sH%ds' % len(apnsPayload)

    # format = '!BH32sH%ds' % len(apnsPayload)

 

    expiry = int(time.time()) + 60*60*24 # item['expire_date']

    packet = struct.pack(format, 1, 0, expiry, 32, apnsToken, len(apnsPayload), apnsPayload)

    # packet = struct.pack(format, 0, 32, apnsToken, len(apnsPayload), apnsPayload)

 

    print "-- result %s " % packet

    # sslSock = ssl.wrap_socket(socket.socket(socket.AF_INET, socket.SOCK_STREAM), certfile = config.get('ios', 'cert'))

    sslSock = ssl.wrap_socket(socket.socket(socket.AF_INET, socket.SOCK_STREAM), certfile = certFile)

    sslSock.connect(apnsHostPort)

 

    sslSock.write(packet)

    sslSock.close()


반응형
반응형

PHP 간단하게 만들어 봤었던 테스트 푸시 발송 기능.


<?php

    $title = 'this is title';

    $message = 'message!!!';

    $title = preg_replace("/\"/", "'", $title);

    $message = preg_replace("/\"/", "'", $message);

 

    $result = array();

   

    $apnsHost  = 'gateway.push.apple.com';

    $apnsCert  = '.../apns_production.pem';   // iOS Developer 를 통한 생성.

    $apnsPort  = 2195;

    $apnsToken = 'abcdefg012345'; // app token

   

    $payload = array();

    // 현재 발송 메시지 형태

    $payload['aps'] = array(

        "alert" => $message,

        "badge" => 1,

        "sound" => "pushSound.caf",

        "content-available" => 1);

    $payload['extra'] = array("param1" => 0, "param2" => "");


    $push = json_encode($payload);

    $streamContext = stream_context_create();

    stream_context_set_option($streamContext, 'ssl', 'local_cert', $apnsCert);

    $apns = stream_socket_client('ssl://'.$apnsHost.':'.$apnsPort, $error, $errorString, 10, STREAM_CLIENT_CONNECT, $streamContext);

    if($apns) {

        $apnsMessage = chr(0).chr(0).chr(32).pack('H*', str_replace(' ', '', $apnsToken)).chr(0).chr(strlen($push)).$push;

        var_dump($apnsMessage);

        fwrite($apns, $apnsMessage);

        @fclose($apns);

    }

?>



반응형
반응형

Gradle 간단히 사용하기.


Maven 은 사용한지 오래되기도 했고 익숙하지만 Gradle 아직 회사에서 사용을 안 하여;; ㅠㅠ ;;잘 잊어버리곤해서 간단한 Gradle 설치와 실행을 확인해 보고자 한다.


1. Gradle 설치 및 간단한 실행.

  • 먼저 Gradle 사이트에 접속한다.(https://gradle.org/gradle-download/)
  • sdkman 을 사용하던지, 개별적으로 download 하여 $PATH 와 $CLASSPATH 를 설정한다.(나는 후자)
  • 간단한 java project 를 생성 및 Helloworld.java 를 만들고, 프로젝트 폴더 안에 build.gradle 파일을 생성한다.
  • 생성한 build.gradle 에 apply plugin: 'java' 를 한 줄 넣는다.
  • gradle tasks 명령어를 실행해서 콘솔에 뜨는 목록들을 확인해 본다.
  •  gradle build 명령어를 사용하면 test, compile 후 jar 파일이 만들어 지는 것을 볼 수 있다.

실행한 java 파일은 다음과 같습니다.

package main.java.hello;


import org.joda.time.LocalTime;


public class HelloWorld {

public static void main(String[] args) {

LocalTime currentTime = new LocalTime();

System.out.println("The current local time is: " + currentTime);

Greeter greeter = new Greeter();

System.out.println(greeter.sayHello());

}

}


class Greeter {

public String sayHello() {

return "Hello world!";

}

}



2. Gradle 의존 파일들 추가하기.

 

Maven Central 을 추가했듯이 다음과 같이 build.gradle 파일에 추가한다.

repositories {

    mavenCentral()

}


필요한 dependencis 도 추가한다.

sourceCompatibility = 1.8

targetCompatibility = 1.8


dependencies {

    compile "joda-time:joda-time:2.2"

    testCompile "junit:junit:4.12"

}


그리고 JAR Artifact 의 이름도 정의한다.

jar {

    baseName = 'java-test'

    version = '0.1.0'

}


다시 gradle build 를 수행하면 정상적으로 수행된다. (dependency 된 import 는 주석처리)



3. 빌드를 쉽게 도와주는 Gradle Wrapper.


다음의 스크립트를 사용하면 시스템에 Gradle을 설치하지 않고도 Gradle 빌드를 수행할 수 있습니다.

$ gradle wrapper --gradle-version 3.3


생성된 명령어로 빌드를 할 수 있습니다.

./gradlew build


생성된 jar 를 실행해 봅니다. 

$ jar tvf build/libs/java-test-0.1.0.jar

0 Thu Jan 05 01:42:22 KST 2017 META-INF/

25 Thu Jan 05 01:01:30 KST 2017 META-INF/MANIFEST.MF

0 Thu Jan 05 01:01:30 KST 2017 main/

0 Thu Jan 05 01:01:30 KST 2017 main/java/

0 Thu Jan 05 01:42:22 KST 2017 main/java/hello

389 Thu Jan 05 01:42:22 KST 2017 main/java/hello/Greeter.class

1028 Thu Jan 05 01:42:22 KST 2017 main/java/hello/HelloWorld.class


작업한 코드를 실행할 수 있게 하려면 gradle 의 applicaiton 플러그인을 추가해야 합니다. 

build.gradle 파일에 다음과 같이 추가하세요.

apply plugin: 'application'

mainClassName = 'hello.HelloWorld'


자 이제 다시 실행해 봅시다.

$ ./gradlew run


지금은 jar 로 실행했는데요. 

war 로 실행되기를 원하면, 플러그인을 추가하면 됩니다. (https://docs.gradle.org/current/userguide/war_plugin.html)


다 만들어진 build.gradle 파일은 아래처럼 될 것 같습니당.


apply plugin: 'java'

apply plugin: 'application'

mainClassName = 'main.java.hello.HelloWorld'


// For starters, you need to add a source for 3rd party libraries.

// tag::repositories[]

repositories {

    mavenCentral()

}

// end::repositories[]


// tag::jar[]

jar {

    baseName = 'java-test'

    version =  '0.1.0'

}

// end::jar[]


// tag::dependencies[]

sourceCompatibility = 1.8

targetCompatibility = 1.8


dependencies {

    compile "joda-time:joda-time:2.2"

    testCompile "junit:junit:4.12"

}

// end::dependencies[]





반응형
반응형

Spring4 + myBatis 환경에서 트랜잭션 설정하기.

트랜잭션 위주로 정리해 봅니다. mybatis 는 뭐 정보가 많으니 패스하겠습니다.


1. 기본 설정.

transaction 범위와 관련하여, dispatch-servlet 에서와  applicaitonContext.xml 에서의  component-scan 설정을 수정해 줘야 합니다.

mvc-config.xml

<context:component-scan base-package="com.slowstarter" use-default-filters="false">

<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />

</context:component-scan>


applicationContext.xml

<context:component-scan base-package="com. slowstarter">

<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />

</context:component-scan>


poll(dbcp2, c3po), mybatis 등 필요한 dependency 설정들 추가하기. 

<dependency>

<groupId>org.mybatis</groupId>

<artifactId>mybatis</artifactId>

<version>3.4.0</version>

</dependency>


<dependency>

<groupId>org.aspectj</groupId>

<artifactId>aspectjweaver</artifactId>

<version>1.8.1</version>

</dependency>


2. aop 를 통한 설정.


<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

<property name="dataSource" ref="pushDataSource" />

</bean>


<tx:annotation-driven transaction-manager="transactionManager" mode="proxy"/>


<tx:advice id="txAdvice"  transaction-manager="transactionManager">

<tx:attributes>

<tx:method name="createTest"

    isolation="REPEATABLE_READ"

    propagation="REQUIRED"

    rollback-for"RuntimeException" />

</tx:attributes>

</tx:advice>


<aop:config>

<aop:pointcut id="servicePublicMethod"

                expression="execution(* com.joochang.service.*Service.*(..))" />

<aop:adviser id="transactionAdvisor"

               pointcut-ref="servicePubicMethod" advice-ref="txAdvice" />

</aop:config>


- 스프링AOP 대신 AspectJ-AOP를 사용하는 방법도 있다. AspectJ는 프록시 대신 클래스 바이트코드를 직접 변경해서 부가기능을 추가하기 때문에 타깃 오브젝트의 자기 호출 중에도 트랜잭션 부가기능이 잘 적용된다. 굳이 자신의 메소드를 호출하는 경우에도 트랜잭션 속성이 정확히 적용되기를 원한다면 AspectJ를 사용하는편이 좋다. <tx:annotation-driven mode="aspectj" /> 처럼 설정하면 된다. (Spring AOP 는 기본적으로 JDK프록시이고, AspectJ는 클래스기반 프록시)


3. annotation 을 통한 설정. 

 MySql 은 기본 isolation 이 repeatable_read  인데, spring  의 default 가 알아서 맞춰주는지 알아볼 필요도 있고 명확하게 해당 타입을 지정해 주는게 더 와 닿는 것 같습니다.


@Transactional(propagation = Propagarion.REQUIRED,

                  isolation = Isolation.REPEATABLE_READ,

                  rollbackFor = RuntimeException.class)

public MyTestResult createTest() throws RuntimeException {

...

}


 선언적 트랜잭션에서는 런타입 예외가 발생하면 롤백합니다. 반면에 예외가 전혀 발생하지 않거나 checked-exception이 발생하면 commit 합니다.  스프링에서 data-access-exception은 Runtime-Exception으로 전환돼서 던져지므로 Runtime-Exception만 롤백대상이 됩니다. 하지만 롤백대상으로 삼아야 한다면 rollbackFor 를 사용해서 등록합니다. 반대로 기본적인 롤백대상인 Runtime-Exception을 트랜잭션 커밋 대상으로 해 주려면 noRollbackFor 를 사용하면 됩니다.


 그리고 rollback 시 createTest() 에서 만약 try catch 쓸 경우 statement 안에서 예외를 잡더라도 throw new RuntimeException  하지 않으면 롤백되지 않습니다. 또한 finally 로 뭔가 동작을 하길 바래서 사용한다면 ㅠㅠ rollback 이 되지 않습니다.


4. 기타 사항. 

 스프링의 Proxy 는 2가지 타입이 있습니다.  JDK Reflection API 를 사용한 JDK다이내믹프록시 와 CBLIB(Code Generator Library) 인데요. JDK프록시는 인터페이스를 구현한 클래스에 적용이되고, CBLIB은 spring 3.2부터 기본적용이 되어있어서 인터페이스를 사용하지 않은 클래스에 대해 적용이 됩니다.

 스프링AOP는 기본적으로 JDK Reflection API를 사용하여 Spring에 의해 생성 된 다이내믹 프록시 기법을 이용해 동작합니다. 다이내믹 프록시를 적용하려면 인터페이스가 있어야 하지만 스프링 내부의 ProxyFactoryBean 클래스나 CGLIB 라이브러리가 제공해주는 클래스 레벨의 프록시도 사용할 수 있습니다. AspectJ를 사용해 트랜잭션을 적용할 때는 앞에서 설명한 것처럼 @Transactional을 클래스레벨과 클래스 메소드 레벨에 부여해야 한다는 점을 주의해야합니다. AspectJ 모드를 사용하면서 인터페이스에만 @Transactional을 부여하면 트랜잭션이 적용되지 않습니다. 그래서 클래스프록시 모드에서는 Service 코드에 인터페이스를 Implements 해야한다는니 이런 거 필요없습니다.


명확하게 CGLIB을 사용하려면 proxy-target-class=true 옵션을 아래의 설정에 추가 해야만 합니다.

<tx:annotation-driven proxy-target-class="true">

또는 <aop:config proxy-targer-class="true"> </aop:config>

또는 <aop:aspectj-autoproxy proxy-target-class="true"> //  aspectj는 기본이 클래스모드

또는 @EnableAspectJAutoProxy(proxyTargetClass=true)

또는 spring.aop.proxy-target-class=true // application.properties 




반응형
반응형

OIO(Old Input/Output) 와 NIO(Non-blocking Input/Output or New Input/Output) 


 몇 년 사이 두드러진 netty(http://netty.io/) 나 vert.x(http://vertx.io/) 등과 같은 네트워크 라이브러리를 잘 활용하기 위해서는 비동기에 대한 못지 않게 OIO(Old Input Output) 과 NIO(Non-blocking Input Output or New Input Output) 에 대한 개념에 대해 알고 있어야 합니다.


 Java 에서 NIO 는 JDK1.4 에서부터 지원을 하기 시작했는데요. 기존의 Java I/O 는 socket 을 accept() 할 때와, data 를 read() 할 때 해당 Thread 가 Block 이 되는데요. 이런 구조? 때문에 Multi-Thread 를 사용하게 되었으나, 컴퓨터가 처리할 수 없을 정도의 요청이 들어오게 되면 Stack 에 메모리를 할당해야 하는데, 운영체제에 따라 다르지만 보통 64KB 에서 1MB 까지 차지할 수 있다고 합니다. Java는 JVM 을 통해서 많은 수를 처리할 수 있다고 하지만 그럴 경우에는 Context-Switching 에 따른 OverHead 가 크다고 합니다.


Java IO: A classic IO server design - one connection handled by one thread.



 하여튼 Multi-Thread 만으로는 부족하여, polling 과 thread-pool 을 같이 사용하기도 하지만 이또한 완벽한 비동기를 구현하기는 어렵습니다. 그래서 JDK1.4에서는 Non-blocking I/O lib 를 통해서 운영체제 레벨에서 지원되던 것을 Java를 통해서 사용할 수 있도록 했습니다.


Java NIO: A single thread managing multiple connections.


 Java NIO 에 대한 사용을 간단하게 설명하면, Selector 를 생성하여 ServerSocketChannel 에 등록하고 대기상태 없이 Selector의 SelectedSet 에서 ServerSocketChannel 로 들어온 이벤트와 관련된 SelectionKeySet 을 잃어와 이로부터 channel을 구하여 데이터를 처리하게 됩니다. 이 과정에서 Selector 는 Multi-channel 의 작업을 Single-thread 에서 처리할 수 있도록 되어 있는데 이 동작을 Multiplexing 이라 할 수 있습니다. Thread 를 사용하지 않으면서 Thread를 사용하는 효과를 볼 수 있습니다.


Java NIO: Reading data from a channel until all needed datas is in buffer.



 이를 통해, 다수의 클라이언트를 하나 또는 적은 수의 Thread로 처리하게 됨으로써, 메모리 관리와 Context-Switching에 따른 Overhead 가 감소하게 됩니다. 입출력을 처리하지 않을 때는 Thread 를 다른 작업에 활용할 수도 있습니다.




ref) http://tutorials.jenkov.com/java-nio/index.html

ref) http://javacan.tistory.com/entry/87

ref) https://devlearnings.wordpress.com/2010/09/21/tip-use-scheduledexecutor-instead-of-thread-for-polling-or-infinite-loops/

ref) http://www.ibm.com/support/knowledgecenter/SSCRJU_4.0.0/com.ibm.streams.dev.doc/doc/pollingsource.html


반응형

+ Recent posts