Improve MonitoringTimerService logic
This commit is contained in:
parent
8071209098
commit
e65f002672
5 changed files with 26 additions and 60 deletions
|
|
@ -11,7 +11,7 @@ import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
|
||||||
import org.springframework.kafka.core.*;
|
import org.springframework.kafka.core.*;
|
||||||
import org.springframework.kafka.listener.ContainerProperties;
|
import org.springframework.kafka.listener.ContainerProperties;
|
||||||
import org.springframework.kafka.support.serializer.JsonDeserializer;
|
import org.springframework.kafka.support.serializer.JsonDeserializer;
|
||||||
import ru.nbch.credit_tracker.service.mode.monitoring.TestData;
|
import ru.nbch.credit_tracker.service.mode.monitoring.PackageMessage;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
@ -21,8 +21,8 @@ import java.util.Map;
|
||||||
public class KafkaConfig {
|
public class KafkaConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public ConcurrentKafkaListenerContainerFactory<String, TestData> kafkaListenerContainerFactory(ConsumerFactory<String, TestData> consumerFactory) {
|
public ConcurrentKafkaListenerContainerFactory<String, PackageMessage> kafkaListenerContainerFactory(ConsumerFactory<String, PackageMessage> consumerFactory) {
|
||||||
ConcurrentKafkaListenerContainerFactory<String, TestData> factory =
|
ConcurrentKafkaListenerContainerFactory<String, PackageMessage> factory =
|
||||||
new ConcurrentKafkaListenerContainerFactory<>();
|
new ConcurrentKafkaListenerContainerFactory<>();
|
||||||
factory.setConsumerFactory(consumerFactory);
|
factory.setConsumerFactory(consumerFactory);
|
||||||
factory.setBatchListener(true);
|
factory.setBatchListener(true);
|
||||||
|
|
@ -32,14 +32,14 @@ public class KafkaConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public ConsumerFactory<String, TestData> consumerFactory() {
|
public ConsumerFactory<String, PackageMessage> consumerFactory() {
|
||||||
Map<String, Object> props = new HashMap<>();
|
Map<String, Object> props = new HashMap<>();
|
||||||
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:29092");
|
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:29092");
|
||||||
props.put(ConsumerConfig.GROUP_ID_CONFIG, "credit-tracker");
|
props.put(ConsumerConfig.GROUP_ID_CONFIG, "credit-tracker");
|
||||||
props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
|
props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
|
||||||
props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 20);
|
props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 20);
|
||||||
|
|
||||||
JsonDeserializer<TestData> jsonDeserializer = new JsonDeserializer<>(TestData.class);
|
JsonDeserializer<PackageMessage> jsonDeserializer = new JsonDeserializer<>(PackageMessage.class);
|
||||||
jsonDeserializer.addTrustedPackages("*");
|
jsonDeserializer.addTrustedPackages("*");
|
||||||
jsonDeserializer.setUseTypeHeaders(false);
|
jsonDeserializer.setUseTypeHeaders(false);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,6 @@ public class CTMonitoringApplication {
|
||||||
// MonitoringService monitoringService = ctx.getBean(MonitoringService.class);
|
// MonitoringService monitoringService = ctx.getBean(MonitoringService.class);
|
||||||
// monitoringService.run();
|
// monitoringService.run();
|
||||||
|
|
||||||
// ctx.getBean(KafkaMonitoringService.class).generateMessages();
|
ctx.getBean(KafkaMonitoringService.class).generateMessages();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
|
@ -64,21 +65,26 @@ public class KafkaMonitoringService {
|
||||||
monitoringTimerService.registerFile(msg.packageId(), filePath);
|
monitoringTimerService.registerFile(msg.packageId(), filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO send PackageMessage
|
|
||||||
public void generateMessages() throws JsonProcessingException {
|
public void generateMessages() throws JsonProcessingException {
|
||||||
ObjectMapper mapper = JsonMapper.builder()
|
ObjectMapper mapper = JsonMapper.builder()
|
||||||
.build();
|
.build();
|
||||||
|
Random rand = new Random();
|
||||||
|
int max = 10;
|
||||||
|
int min = 1;
|
||||||
for (int i = 0; i < 500; i++) {
|
for (int i = 0; i < 500; i++) {
|
||||||
TestData data = new TestData();
|
PackageMessage data = new PackageMessage(
|
||||||
data.setId(UUID.randomUUID());
|
rand.nextInt((max - min) + 1) + min,
|
||||||
data.setMessage("Message " + i);
|
"Message " + i,
|
||||||
send(data.getId().toString(), mapper.writeValueAsString(data));
|
UUID.randomUUID()
|
||||||
|
);
|
||||||
|
|
||||||
|
send(data.uuid().toString(), mapper.writeValueAsString(data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void send(String id, String data) {
|
private void send(String id, String data) {
|
||||||
CompletableFuture<SendResult<String, String>> future =
|
CompletableFuture<SendResult<String, String>> future =
|
||||||
kafkaTemplate.send("test-topic", id, data);
|
kafkaTemplate.send("my-topic", id, data);
|
||||||
|
|
||||||
future.whenComplete((result, exception) -> {
|
future.whenComplete((result, exception) -> {
|
||||||
if (exception == null) {
|
if (exception == null) {
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ public class MonitoringTimerService {
|
||||||
|
|
||||||
public void registerFile(Integer packageId, Path filePath) {
|
public void registerFile(Integer packageId, Path filePath) {
|
||||||
bundles.compute(packageId, (id, existing) -> {
|
bundles.compute(packageId, (id, existing) -> {
|
||||||
if (existing == null || existing.isIdle()) {
|
if (existing == null) {
|
||||||
PackageBundle bundle = new PackageBundle();
|
PackageBundle bundle = new PackageBundle();
|
||||||
bundle.addFile(filePath);
|
bundle.addFile(filePath);
|
||||||
scheduleFlush(packageId, bundle);
|
scheduleFlush(packageId, bundle);
|
||||||
|
|
@ -40,19 +40,21 @@ public class MonitoringTimerService {
|
||||||
return bundle;
|
return bundle;
|
||||||
}
|
}
|
||||||
existing.addFile(filePath);
|
existing.addFile(filePath);
|
||||||
log.debug("Queued file for packageId={} state={}", packageId, existing.getState());
|
|
||||||
return existing;
|
return existing;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scheduleFlush(Integer packageId, PackageBundle bundle) {
|
private void scheduleFlush(Integer packageId, PackageBundle bundle) {
|
||||||
bundle.setState(BundleState.SCHEDULED);
|
|
||||||
scheduler.schedule(() -> flush(packageId, bundle), DELAY_MINUTES, TimeUnit.MINUTES);
|
scheduler.schedule(() -> flush(packageId, bundle), DELAY_MINUTES, TimeUnit.MINUTES);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void flush(Integer packageId, PackageBundle bundle) {
|
private void flush(Integer packageId, PackageBundle bundle) {
|
||||||
bundle.setState(BundleState.MERGING);
|
// Атомарно забираем snapshot и удаляем из мапы
|
||||||
List<Path> snapshot = bundle.drainFiles();
|
List<Path> snapshot = new ArrayList<>();
|
||||||
|
bundles.compute(packageId, (id, current) -> {
|
||||||
|
snapshot.addAll(bundle.drainFiles());
|
||||||
|
return null; // удаляем этот пакет
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!snapshot.isEmpty()) {
|
if (!snapshot.isEmpty()) {
|
||||||
|
|
@ -74,19 +76,8 @@ public class MonitoringTimerService {
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("Flush failed for packageId={}", packageId, e);
|
log.error("Flush failed for packageId={}", packageId, e);
|
||||||
} finally {
|
} finally {
|
||||||
bundle.setState(BundleState.IDLE);
|
|
||||||
bundles.remove(packageId);
|
|
||||||
|
|
||||||
// Файлы, добавленные во время мержа — забираем и стартуем новый цикл
|
// Файлы, добавленные во время мержа — забираем и стартуем новый цикл
|
||||||
List<Path> leftover = bundle.drainFiles();
|
bundle.drainFiles().forEach(leftover -> registerFile(packageId, leftover));
|
||||||
if (!leftover.isEmpty()) {
|
|
||||||
log.info("Files arrived during merge, restarting timer for packageId={}, count={}",
|
|
||||||
packageId, leftover.size());
|
|
||||||
PackageBundle next = new PackageBundle();
|
|
||||||
leftover.forEach(next::addFile);
|
|
||||||
bundles.put(packageId, next);
|
|
||||||
scheduleFlush(packageId, next);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,11 +96,9 @@ public class MonitoringTimerService {
|
||||||
log.info("Merged {} files → {} for packageId={}", files.size(), merged, packageId);
|
log.info("Merged {} files → {} for packageId={}", files.size(), merged, packageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum BundleState {SCHEDULED, MERGING, IDLE}
|
|
||||||
|
|
||||||
private static class PackageBundle {
|
private static class PackageBundle {
|
||||||
private final Queue<Path> files = new ConcurrentLinkedQueue<>();
|
private final Queue<Path> files = new ConcurrentLinkedQueue<>();
|
||||||
private volatile BundleState state = BundleState.IDLE;
|
|
||||||
|
|
||||||
void addFile(Path path) {
|
void addFile(Path path) {
|
||||||
files.add(path);
|
files.add(path);
|
||||||
|
|
@ -121,17 +110,5 @@ public class MonitoringTimerService {
|
||||||
while ((p = files.poll()) != null) result.add(p);
|
while ((p = files.poll()) != null) result.add(p);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
BundleState getState() {
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setState(BundleState s) {
|
|
||||||
this.state = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isIdle() {
|
|
||||||
return state == BundleState.IDLE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
package ru.nbch.credit_tracker.service.mode.monitoring;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
import lombok.Setter;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
@AllArgsConstructor
|
|
||||||
@NoArgsConstructor
|
|
||||||
public class TestData {
|
|
||||||
private UUID id;
|
|
||||||
private String message;
|
|
||||||
}
|
|
||||||
Loading…
Add table
Reference in a new issue