분류없음2019.04.17 23:23
2017.07에 테스트 한것을 가져옴 kafka version 1.1

1. min.insync.replicas=2, replication-factor=1

생성) kafka-topics --create --zookeeper localhost:2181 --config min.insync.replicas=2 --topic top2 --partitions 1 --replication-factor 1
> 생성은 잘 된다. 
> 의견 : 생성 자체가 안되었으면 좋았을것 같다 (alter에서 수정하면 정상적으로 쓸 수 있기는 하다) - 생각해보면 min.insync.replicas가 relicas보다 많더라도, kafka-client에서 acks를 all로 하지 않는 다면 의미 없는 설정이다. 그렇기 때문에 '만들어 지는 것'자체에 validation check는 하지 않았던 것 아닐까?

상태)

Topic:top2 PartitionCount:1 ReplicationFactor:1 Configs:min.insync.replicas=2
Topic: top2 Partition: 0 Leader: 1 Replicas: 1 Isr: 1

메시지발행)

* acks=1  - 정상적으로 데이터가 적재된다.
* acks=all - 데이터가 적재되지 않는다
ㄴ   client 에서 Exception이 발생한다
  Caused by: org.apache.kafka.common.errors.NotEnoughReplicasException: Messages are rejected since there are fewer in-sync replicas than required
ㄴ broker : 리더인 1번 브로커에서 에러가 발생한다.

ERROR [ReplicaManager broker=1] Error processing append operation on partition top2-0 (kafka.server.ReplicaManager)
org.apache.kafka.common.errors.NotEnoughReplicasException: Number of insync replicas for partition top2-0 is [1], below required minimum [2]
INFO [GroupMetadataManager brokerId=1] Removed 0 expired offsets in 0 milliseconds. (kafka.coordinator.group.GroupMetadataManager)

 

2. min.insync.replicas=2, replication-factor=3

생성) kafka-topics --create --zookeeper localhost:2181 --config min.insync.replicas=2 --topic top2re2 --partitions 1 --replication-factor 1
상태)

Topic:top2re2 PartitionCount:1 ReplicationFactor:3 Configs:min.insync.replicas=2
Topic: top2re2 Partition: 0 Leader: 0 Replicas: 0,2,1 Isr: 0,2,1

메시지의발행)
* acks=1 : 데이터가 정상 적재된다.
* acks=all : 데이터가 정상 적재된다
이 상황에서 Broker2를 강제로 kill하여, cluster가 2EA로 동작하도록 함

상태)

Topic:top2re2 PartitionCount:1 ReplicationFactor:3 Configs:min.insync.replicas=2
Topic: top2re2 Partition: 0 Leader: 0 Replicas: 0,2,1 Isr: 0,1

cluster2EA에서 메시지의발행)
* acks=1 : 데이터가 정상 적재된다.
* acks=all : 데이터가 정상 적재된다
코멘트) 이미 토픽이 만들어 졌다면, replicas 설정보다 브로커 갯수가 적더라도 적재하는데 이슈는 안된다. 단! AdminUtils ( https://github.com/apache/kafka/blob/1.1.0/core/src/main/scala/kafka/admin/AdminUtils.scala ) 를 사용하는 kafka-topic bash 등을 이용 할 때에는 생성 시점에서 브로커 갯수와 replicas 갯수를 체크를 한다.

이 상황에서 다시한번 Broker1를 강제로 kill하여, cluster가 1EA로 동작하도록 함

Topic:top2re2 PartitionCount:1 ReplicationFactor:3 Configs:min.insync.replicas=2
Topic: top2re2 Partition: 0 Leader: 0 Replicas: 0,2,1 Isr: 0

메시지의발행)
* acks=1 : 데이터가 정상 적재된다.
* acks=all : 데이터가 적재되지 않는다 (에러메시지는 첫 번째 테스트와 동일하다)
* acks=all 하고 retries=1로 설정
> 동일하게 메시지는 적재되지 않고 Client Exception 도 동일, Server는 retry 설정 수만큼 메시지가 출력된다.

[2018-07-02 15:32:37,641] ERROR [ReplicaManager broker=0] Error processing append operation on partition top2re2-0 (kafka.server.ReplicaManager)
org.apache.kafka.common.errors.NotEnoughReplicasException: Number of insync replicas for partition top2re2-0 is [1], below required minimum [2]
[2018-07-02 15:32:37,746] ERROR [ReplicaManager broker=0] Error processing append operation on partition top2re2-0 (kafka.server.ReplicaManager)
org.apache.kafka.common.errors.NotEnoughReplicasException: Number of insync replicas for partition top2re2-0 is [1], below required minimum [2]

 

3. 요약 및 결론

1. ack=all에서 실재 중요한 것은 min.insync.replicas 만큼 브로커가 있어야 한다는 것이다.
ㄴ 공식 문서에서 min.insync.replicas 를 설명한대로다 When a producer sets acks to "all" ...
ㄴ NotEnoughReplicas 등의 에러가 발생하고
2. 토픽을 생성 할때 reclias 갯수보다 broker 갯수가 작으면 생성이 되지 않지만, 생성이 된 이후에는 메시지 발행/소비와는 무관하다
3. ack 0, 1 일때도 min.insync.replicas 갯수는 상관없다. (ack=1 로 셋팅하더라도 leader 가 반응을 해줄테니까)
4. min.insync.replicas=1도 큰 의미는 없다 (3번과 마찬가지로 leader가 반응해줄테니까)

 

4. 추가 테스트1 

브로커 3대, replicas 3인 토픽에 데이터를 발행중

  • 브로커 2번 죽음 (즉 브로커 0,1번에만 데이터가 들어가고 복제됨)
  • 브로커 2번을 zookeeper 주소만 다르게 해서 완전 독립적인 클러스터로 실행함
    • 데이터가 0,1번 브로커에 비해 1개 부족한 것을 확인
  • 브로커 2번의 zookeeper 원래대로 원복해서 실햄함. (즉 원래 클러스터 3으로 다시 띄움)
  • 다시 한번 2번 브로커를 죽이고 zookeeper 주소만 다르게 해서, 다시 실햄함
    • 원래 클러스터 (현재는 브로커가2개) 와 동일하게 데이터가 싱크됨
  • 이 데이터 복구는 토픽의 리더가 브로커가 살아 나는 것을 감지함으로써 시작된다.
    • 리더 브로커(1) 의 로그
      • [2018-07-02 15:14:51,800] INFO [Partition top2re2-0 broker=1] Expanding ISR from 1,0 to 1,0,2
    • 다시 살아난 브로커(2)의 로그
      • [2018-07-02 15:14:51,769] INFO [ReplicaFetcher replicaId=2, leaderId=1, fetcherId=0] Based on follower's leader epoch, leader replied with an offset 7 >= the follower's log end 
        [2018-07-02 15:14:51,789] INFO Updated PartitionLeaderEpoch. New: {epoch:3, offset:7}, Current: {epoch:1, offset:3} for Partition: top2re2-0. Cache now contains 2 entries. (kafka.server.epoch.LeaderEpochFileCache)

5. 추가 테스트2 (cluster에 사이즈에 따라 leader가 분배된다)

  • 여러개의 토픽을 만든 후, Broker 1개를 죽였을 경우, ISR의 변화
  •  
Topic:k1 PartitionCount:1 ReplicationFactor:3 Configs:min.insync.replicas=2
Topic: k1 Partition: 0 Leader: 0 Replicas: 0,2,1 Isr: 0,2,1
Topic:k2 PartitionCount:1 ReplicationFactor:3 Configs:min.insync.replicas=2
Topic: k2 Partition: 0 Leader: 2 Replicas: 2,0,1 Isr: 2,0,1
Topic:k3 PartitionCount:1 ReplicationFactor:3 Configs:min.insync.replicas=2
Topic: k3 Partition: 0 Leader: 2 Replicas: 2,0,1 Isr: 2,0,1
Topic:k4 PartitionCount:1 ReplicationFactor:3 Configs:min.insync.replicas=2
Topic: k4 Partition: 0 Leader: 1 Replicas: 1,2,0 Isr: 1,2,0
Topic:k5 PartitionCount:1 ReplicationFactor:3 Configs:min.insync.replicas=2
Topic: k5 Partition: 0 Leader: 1 Replicas: 1,2,0 Isr: 1,2,0

여기에서 broker2를 down 시키면 아래처럼 leader:2가 0,1으로 분산되어 동작한다.

Topic:k1 PartitionCount:1 ReplicationFactor:3 Configs:min.insync.replicas=2
Topic: k1 Partition: 0 Leader: 0 Replicas: 0,2,1 Isr: 0,1
Topic:k2 PartitionCount:1 ReplicationFactor:3 Configs:min.insync.replicas=2
Topic: k2 Partition: 0 Leader: 0 Replicas: 2,0,1 Isr: 0,1
Topic:k3 PartitionCount:1 ReplicationFactor:3 Configs:min.insync.replicas=2
Topic: k3 Partition: 0 Leader: 0 Replicas: 2,0,1 Isr: 0,1
Topic:k4 PartitionCount:1 ReplicationFactor:3 Configs:min.insync.replicas=2
Topic: k4 Partition: 0 Leader: 1 Replicas: 1,2,0 Isr: 1,0
Topic:k5 PartitionCount:1 ReplicationFactor:3 Configs:min.insync.replicas=2
Topic: k5 Partition: 0 Leader: 1 Replicas: 1,2,0 Isr: 1,0

여기서 다시 broker1을 down 시키면 모든 leader는 0이 된다.

Topic:k1 PartitionCount:1 ReplicationFactor:3 Configs:min.insync.replicas=2
Topic: k1 Partition: 0 Leader: 0 Replicas: 0,2,1 Isr: 0
Topic:k2 PartitionCount:1 ReplicationFactor:3 Configs:min.insync.replicas=2
Topic: k2 Partition: 0 Leader: 0 Replicas: 2,0,1 Isr: 0
Topic:k3 PartitionCount:1 ReplicationFactor:3 Configs:min.insync.replicas=2
Topic: k3 Partition: 0 Leader: 0 Replicas: 2,0,1 Isr: 0
Topic:k4 PartitionCount:1 ReplicationFactor:3 Configs:min.insync.replicas=2
Topic: k4 Partition: 0 Leader: 0 Replicas: 1,2,0 Isr: 0
Topic:k5 PartitionCount:1 ReplicationFactor:3 Configs:min.insync.replicas=2
Topic: k5 Partition: 0 Leader: 0 Replicas: 1,2,0 Isr: 0

여기에서 다시 broker1,2를 up 하면, 여전히 leader는 0에 있다. 이렇게 될 경우 모든 트래픽을 0번만 받지 않을까? 라는 의문이 들지만

Topic:k1 PartitionCount:1 ReplicationFactor:3 Configs:min.insync.replicas=2
Topic: k1 Partition: 0 Leader: 0 Replicas: 0,2,1 Isr: 0,1,2
Topic:k2 PartitionCount:1 ReplicationFactor:3 Configs:min.insync.replicas=2
Topic: k2 Partition: 0 Leader: 2 Replicas: 2,0,1 Isr: 0,1,2
Topic:k3 PartitionCount:1 ReplicationFactor:3 Configs:min.insync.replicas=2
Topic: k3 Partition: 0 Leader: 2 Replicas: 2,0,1 Isr: 0,1,2
Topic:k4 PartitionCount:1 ReplicationFactor:3 Configs:min.insync.replicas=2
Topic: k4 Partition: 0 Leader: 1 Replicas: 1,2,0 Isr: 0,1,2
Topic:k5 PartitionCount:1 ReplicationFactor:3 Configs:min.insync.replicas=2
Topic: k5 Partition: 0 Leader: 1 Replicas: 1,2,0 Isr: 0,1,2
Topic:top1 PartitionCount:1 ReplicationFactor:3 Configs:min.insync.replicas=1

시간이 지나면 leader는 재조정된다.
이 설정은 leader.imbalance.check.interval.seconds(default:5분)에 의해서 리서치 된다.
이 설정값은 auto-leader-rebalance-task 라는 스케줄러에 의해 재분배 된다.

Posted by dev.bistro
분류없음2019.04.14 00:27

스프링에서 가장 쉽게 프로퍼티를 읽어오는 방법은 @Value를 이용하는 것이다.

- @Value(value="${app.base.name}") 를 이용해서 application의 프로퍼티 하나를 읽어오거나
- @Value("#{systemProperties['os.name']}") 처럼 이용해서 시스템 값을 가져오거나
- @Value("${spring.profiles.active:default}") 를 이용해서 프로파일값을 가져 올 수 있지만,

expression 을 이용하거나, 몇몇 한정된 경우에만 쓰이고 실제로는 @ConfigurationProperties를 더 많이 이용한다.
(링크 : https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/context/properties/ConfigurationProperties.html )

ConfigurationProperties를 를 이용해서 외부 설정값을 application 내부에 사용하기 위해서는 spring bean으로 등록해야 한다.

첫번째로 명시적으로 @Bean 으로 명시적으로 생성 하기

@Configuration
public class AppConfigurare {
    @Bean
    public BistrosAppProperties bistrosAppProperties() {
        return new BistrosAppProperties();
    }
}

두번째로는 간단하기 해당 클래스에 바로 @Configuration을 붙여도 된다.

@Configuration
@ConfigurationProperties(prefix = "app.base")
public class BistrosAppProperties {
...
}

둘다 동작은 하지만, Intellij IDE 에서는  아래와 같은 화면을 보여준다. 

이 꼴보기 싫은 빨간줄을 없애려면 가이드대로 @EnableConfigratuinProperties를 사용해야 한다.
그래서 order-eda-gateway 는 4개의 @ConfigurationProperties 클래스가 존재하지만  @EnableConfigurationProperties 도 3개나 선언했다.  (패키지별로의 커플링을 위해서 모듈화 되어 있다)

왜 이럴까 갑자기 궁금해져서 찾아보니.... https://youtrack.jetbrains.com/issue/IDEA-204153

 

Spring: Incorrect error when combining @Configuration and @ConfigurationProperties annotations : IDEA-204153

What steps will reproduce the issue? 1. Start a Spring Boot project 2. Create a class with both the "@Configuration" and "@ConfigurationProperties" annotations (see screenshot) 3. See the red squiggle…

youtrack.jetbrains.com

@Component 는 사용하면 빨간줄이 안나오도록 수정되었고, @Service는 여전히 에러라고 표시된다.   
@Configuration 이나 @Service 어노테이션을 사용 할 경우에 에러라고 나오는 것은 문제이고 '경고'로 나와야 할것 같지만, 그건 좀 어려운것 같다. 정도로 이해했다. 그러니까... 그냥 이렇게 쓰자 '_';;

ps. 내 스프링 기억은 2.5에서 끝인데 끝도 없이 뭐가 생기네 https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html 를 보면 properties 의 적용 순서의 첫번째가 devtools global properties이다. 쓸일도 없고 써본적도 없는 devtools ....

Posted by dev.bistro
분류없음2019.04.11 15:59

Spring Security OAuth2  인증 순서.

1. https://localhost:8443/login  으로 로그인을 하려고 하면 security의 `anyRequest().authenticated()` 코드에 의해 인증이 필요한것으로 파악하여 302응답이 돌아온다.
    Status Code : 302
    Location: https://www.facebook.com/dialog/oauth?client_id=394182450695217&redirect_uri=https://localhost:8443/login&response_type=code&state=X6kWlA

해당 Server Log

...더보기

2019-04-11 15:17:37.327 DEBUG 52044 --- [nio-8443-exec-6] o.s.security.web.FilterChainProxy        : /login at position 2 of 12 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2019-04-11 15:17:37.327 DEBUG 52044 --- [nio-8443-exec-6] w.c.HttpSessionSecurityContextRepository : HttpSession returned null object for SPRING_SECURITY_CONTEXT
2019-04-11 15:17:37.327 DEBUG 52044 --- [nio-8443-exec-6] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade@16504e11. A new one will be created.

2019-04-11 15:17:37.327 DEBUG 52044 --- [nio-8443-exec-6] o.s.security.web.FilterChainProxy        : /login at position 6 of 12 in additional filter chain; firing Filter: 'OAuth2ClientAuthenticationProcessingFilter'
2019-04-11 15:17:37.327 DEBUG 52044 --- [nio-8443-exec-6] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/login'; against '/login'

2019-04-11 15:17:37.327 DEBUG 52044 --- [nio-8443-exec-6] uth2ClientAuthenticationProcessingFilter : Request is to process authentication

2019-04-11 15:17:37.329 DEBUG 52044 --- [nio-8443-exec-6] w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2019-04-11 15:17:37.329 DEBUG 52044 --- [nio-8443-exec-6] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
2019-04-11 15:17:37.329 DEBUG 52044 --- [nio-8443-exec-6] o.s.s.web.DefaultRedirectStrategy        : Redirecting to 'https://www.facebook.com/dialog/oauth?client_id=394182450695217&redirect_uri=https://localhost:8443/login&response_type=code&state=X6kWlA'

 

2.  해당 링크에서 다시 한번 페이스북 로그인을 요청하기 위해 302:redirect를 한다.

https://www.facebook.com/login.php?skip_api_login .. 이때 cancel_url 과 redirect_url 파라미터를 추가로 붙인다. 거기서 성공하면 다시 한번 https://www.facebook.com/dialog/oauth 를 호출하게 되고 302응답과 함께 code 값을 받아온다.

 

해당 값으로 access token을 얻기 위해 호출한다. ->  https://git.io/fjqur 에서 OAuth2RestTeamplte 를 이용해서 access token을 얻어온다는 https://git.io/fjquP 에서 context에 저장한다. 

Posted by dev.bistro