Open jjy0918 opened 2 years ago
https://en.wikipedia.org/wiki/Page_cache 운영체제는 메인 메모리(RAM)의 사용하지 않는 부분을 디스크 캐시로 사용한다.
https://kafka.apache.org/documentation/#persistence https://medium.com/@sunny_81705/what-makes-apache-kafka-so-fast-71b477dcbf0 카프카는 메세지를 보관하고 캐싱하는데 파일시스템에 많이 의존
자세하게는 모르겠지만, SSD는 전기를 HDD는 자기를 이용함
https://www.baeldung.com/java-management-extensions JMX(Java Management eXtension)
Instrumentation : 컴퓨터 프로그래밍에서 인스트루먼테이션은 오류를 진단하거나 추적 정보를 쓰기 위해 제품의 성능 정도를 모니터하거나 측정하는 기능을 가리킨다. 프로그래머들은 시스템에서 특정한 구성 요소를 모니터링하는 코드 명령어 형태로 인스트루먼테이션을 구현할 수 있다.
ack = 0일 경우
ack = 1일 경우
ack = -1일 경우
min.insync.replicas
옵션에 따라 동작이 달라짐min.insync.replicas
프로듀서가 acks = -1의 브로커에게 메세지를 보낼 때, write를 성공하기 위한 최소 복제본의 수
kafka가 poll을 시작하면, heartbeat 스레드를 시작한다.
public boolean poll(Timer timer) {
maybeUpdateSubscriptionMetadata();
invokeCompletedOffsetCommitCallbacks();
if (subscriptions.hasAutoAssignedPartitions()) {
...
if (!ensureActiveGroup(timer)) {
return false;
}
}
...
}
...
boolean ensureActiveGroup(final Timer timer) { // always ensure that the coordinator is ready because we may have been disconnected // when sending heartbeats and does not necessarily require us to rejoin the group. if (!ensureCoordinatorReady(timer)) { return false; }
startHeartbeatThreadIfNeeded();
return joinGroupIfNeeded(timer);
}
...
private synchronized void startHeartbeatThreadIfNeeded() { if (heartbeatThread == null) { heartbeatThread = new HeartbeatThread(); heartbeatThread.start(); } }
2. heartbeat 스레드는 컨슈머 그룹에서 레코드를 정상적으로 받아 올 수 있는 상태인지 판단한다.
```java
if (state != MemberState.STABLE) {
// the group is not stable (perhaps because we left the group or because the coordinator
// kicked us out), so disable heartbeats and wait for the main thread to rejoin.
disable();
continue;
}
브로커에 연결될 수 있는 상태인지 판단한다.
브로커에 연결되었다면, 세션 타임 아웃과 poll 타임 아웃, 설정한 heartbeatTImeout을 체크한다.
heartbeat를 전송이 성공하는 경우 타이머를 업데이트 한다.
heartbeat를 전송이 실패하는 경우 리벨런싱 중인지 판단하고, 그렇지 않다면 인터벌 타임을 조절하여 hearbeat를 전송한다.
https://gunju-ko.github.io/kafka/kafka-stream/2018/05/24/KTable-GlobalKTable.html https://m.blog.naver.com/syam2000/222158302362
핵심 : 레코드의 모음(특정 토픽)을 레코드의 스트림(시간순 중요)으로 추상화해서 데이터를 조회하는 것 ex) Test 토픽의 데이터를 KStream으로 조회한다.
import org.apache.kafka.common.serialization.Serdes;
import org.apache.kafka.streams.StreamsBuilder;
import org.apache.kafka.streams.kstream.KStream;
StreamsBuilder builder = new StreamsBuilder();
KStream<String, Long> wordCounts = builder.stream(
"word-counts-input-topic", /* input topic */
Consumed.with(
Serdes.String(), /* key serde */
Serdes.Long() /* value serde */
)
);
KStream
KTable
GlobalKTable
Kstream & KTable
KStream, KTable & GlobalKTable
val props = Properties()
props[StreamsConfig.APPLICATION_ID_CONFIG] = APPLICATION_NAME
props[StreamsConfig.BOOTSTRAP_SERVERS_CONFIG] = BOOTSTRAP_SERVERS
props[StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG] = Serdes.String().javaClass.name
props[StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG] = Serdes.String().javaClass.name
...
val streams = KafkaStreams(builder.build(), props)
streams.start()
KafkaStreams 객체를 생성할 때 Properties를 매개변수로 지정한다.
KafkaStreams 생성자 안에서는 매개변수 Properties를 바탕으로 StreamsConfig를 생성한다.
https://www.youtube.com/watch?v=vKxhPUUEDmM
실시간으로 데이터를 처리하는 방식은 크게 2가지
출처 : https://www.instaclustr.com/blog/kafka-mirrormaker-2-theory/
https://hyperconnect.github.io/2021/01/11/cdc-platform.html
운영 환경이므로 분산 모드 커넥트만 생각한다. 카프카 커넥트의 아키텍쳐는 크게 3가지 모델로 나뉜다.
MySQL은 Create, Drop과 같은 DDL / Insert, Update, Delete와 같은 DML문으로 데이터를 변경할 때 해당 이벤트를 로그 파일로 기록
https://engineering.linecorp.com/ko/blog/line-shopping-platform-kafka-mongodb-kubernetes/ 널리 사용되는 대부분의 DBMS는 데이터 수준의 로그를 남긴다 (MongoDB도 있다고 함)
Kafka 커넥터를 제작할 때 JDBC와 같은 통일된 규격이 존재하지 않음
https://www.baeldung.com/java-executor-service-tutorial
ForkJoinPool을 지원하는 fork/join framework와 다른 것!
ExecutorService - 각 스레드가 각자 다른 일을 담당할 경우 fork/join - 각 스레드가 하나의 일을 분할(fork)해서 정복(join)할 경우
ExecutorService 타입에 인스턴스를 할당해야 한다.
방법1) Executors 클래스의 팩토리 메소드 사용
ExecutorService executor = Executors.newFixedThreadPool(10);
방법2) 직접 ExecutorService 생성
ExecutorService는 인터페이스이므로, 어떤 구현체든 사용할 수 있다. java.util.concurrent
패키지에 정의된 구현체를 사용해도 되고, 직접 정의해도 된다.
// 사실 newSingleThreadExecutor() 팩토리 메소드의 소스 코드와 유사하다.
// 대부분의 케이스에서 팩토리 메소드로 해결 가능
ExecutorService executorService =
new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()
);
ExecutorService는 Runnable
/ Callable
타입의 태스크를 실행한다.
(책에서 Runnable 인터페이스를 구현한 커스텀 Worker 클래스를 만듬)
executorService.execute(runnableTask);
Future<String> future = executorService.submit(callableTask);
String result = executorService.invokeAny(callableTasks);
List<Future<String>> futures = executorService.invokeAll(callableTasks);
일반적으로 태스크가 없어도 해당 객체는 자동 파괴되지 않음 (생성한 스레드 풀이 잔재)
Oracle이 추천하는 방법. 일정 시간 동안 작업 수행을 기다린 뒤, 이후에도 끝나지 않으면 즉시 종료.
executorService.shutdown();
try {
if (!executorService.awaitTermination(800, TimeUnit.MILLISECONDS)) {
executorService.shutdownNow();
}
} catch (InterruptedException e) {
executorService.shutdownNow();
}
submit()과 invokeAll()의 반환 객체. 비동기 작업의 수행 결과를 담는 인터페이스.
Q. 생성된 Pool은 몇 개의 스레드를 갖고 있는 걸까요? A. 스레드 풀의 종류마다 다르다
엘라스틱서치
키바나
class MetricHelper:
def __init__(self):
self.host = '엘라스틱 서치 url'
self.port = 9200
self.es = Elasticsearch([{'host': self.host, 'port': self.port}, ], timeout=10)
self._index = "item_count-{:%Y.%m}".format(datetime.now(timezone('Asia/Seoul')))
이 경우 월별로 다른 인덱스에 데이터를 저장하게 될 것이다.
이 때 키바나에서 모든 item_count 인덱스들의 데이터를 시각화하고 싶다면, 인덱스 패턴을 item_count-*
로 설정하게 된다.
https://www.koyeb.com/blog/blue-green-rolling-and-canary-continuous-deployments-explained
@ConfigurationProperties("my.service")
public class MyProperties {
private boolean enabled;
private InetAddress remoteAddress;
private final Security security = new Security();
// getters / setters...
public static class Security {
private String username;
private String password;
private List<String> roles = new ArrayList<>(Collections.singleton("USER"));
// getters / setters...
}
}
Future
는 비동기 작업의 결과를 담는 역할
비동기 작업의 결과를 콜백 함수로 처리하기 위해선 다양한 방법이 있다.
CompletionHandler
인터페이스 구현
기존 Future처럼 사용하되, 객체 생성 후 callback을 나중에 등록
할 수 있음
@Async
메소드의 반환 파라미터로 받을 수 있음
ListenableFuture<SendResult<String, String>> future = customKafkaTemplate.send(TOPIC_NAME, "test");
future.addCallback(new KafkaSendCallback<String, String>() {
// onSucess 메소드 오버라이딩
// onFailure 메소드 오버라이딩
});
작업이 완료될 것을 가정하고 fluent
한 함수형 프로그래밍 스타일로 코드를 작성할 수 있는 future
최초의 비동기 작업 후, 다음 작업 구성 시 다양한 작업 가능 (동기 / 비동기 / 결과조합 등)
public class completablefuture {
@Test
public void completableFuture() throws Exception {
CompletableFuture<String> completableFuture = CompletableFuture
.supplyAsync(() -> "Hello")
.thenCompose(str -> CompletableFuture.supplyAsnyc(() -> s + " World"))
.exceptionally(throwable -> {
System.out.println("exception occurred!!");
return null;
});
assertThat(completableFuture.get()).isEqaulTo("Hello World");
}
}
분산처리 시스템을 제공하는 아파치 재단의 오픈 소스 프레임워크
Amazon S3, 네이버에서 우리가 사용하는 스토리지 머더라 그것도 분산 파일 시스템
장점
단점
카프카 속도 향상 전략
카프카 캐시 전략
Records are evicted using a simple LRU scheme after the cache size is reached.
DefaultPartitioner
RoundRobinPartitioner
은 파티션들을 순회하며 저장한다.UniformStickyPartitioner
은 배치 사이즈나 시간을 바탕으로 하나의 파티션에 저장 후 다시 할당하여 저장하는 방식을 반복한다.UniformStickyPartitioner
가 더 효율적일까?파티션의 개수가 많아지면 더 효율적이다.