আমি এই প্রশ্নটি খুব আকর্ষণীয় এবং এটির চেষ্টা করে দেখেছি found
আমি আরও মূল্যায়ন হিসাবে, আপনার প্রচেষ্টা নিজেই ভাল, নিম্নলিখিত ব্যতীত:
অক্ষাংশের 5-6 প্রথম সংখ্যা দ্বারা বিভাজনটি দ্রাঘিমাংশের 5-6 প্রথম অঙ্কগুলিতে সংক্ষিপ্ত হয়ে যায়
অক্ষাংশ এবং দ্রাঘিমাংশের উপর ভিত্তি করে আপনার যদি ইতিমধ্যে রাস্তা বিভাগের আইডি / নাম পাওয়ার জন্য কোনও পদ্ধতি থাকে তবে প্রথমে সেই পদ্ধতিটি কল করে প্রথমে ডেটা ভাগ করার জন্য রোড বিভাগের আইডি / নাম ব্যবহার করবেন না কেন?
এবং তারপরে, সবকিছু বেশ সহজ, তাই টপোলজি হবে
Merge all four streams ->
Select key as the road section id/name ->
Group the stream by Key ->
Use time windowed aggregation for the given time ->
Materialize it to a store.
(আরও বিস্তারিত ব্যাখ্যা নীচের কোডে মন্তব্যে পাওয়া যাবে anything কিছু অস্পষ্ট কিনা তা জিজ্ঞাসা করুন)
আমি এই উত্তরটির শেষে কোডটি যুক্ত করেছি, দয়া করে নোট করুন যে গড়ের পরিবর্তে আমি যোগফলটি প্রদর্শন করা সহজ হিসাবে ব্যবহার করেছি। কিছু অতিরিক্ত ডেটা সঞ্চয় করে গড় করা সম্ভব।
আমি মন্তব্যে উত্তর বিস্তারিত করেছি। নীচে কোড থেকে উত্পন্ন টপোলজি চিত্রটি দেওয়া হয়েছে ( https://zz85.github.io/kafka-streams-viz/ কে ধন্যবাদ )
টোপোলজি:
import org.apache.kafka.common.serialization.Serdes;
import org.apache.kafka.streams.KafkaStreams;
import org.apache.kafka.streams.StreamsBuilder;
import org.apache.kafka.streams.StreamsConfig;
import org.apache.kafka.streams.Topology;
import org.apache.kafka.streams.kstream.KStream;
import org.apache.kafka.streams.kstream.Materialized;
import org.apache.kafka.streams.kstream.TimeWindows;
import org.apache.kafka.streams.state.Stores;
import org.apache.kafka.streams.state.WindowBytesStoreSupplier;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
public class VehicleStream {
// 5 minutes aggregation window
private static final long AGGREGATION_WINDOW = 5 * 50 * 1000L;
public static void main(String[] args) throws Exception {
Properties properties = new Properties();
// Setting configs, change accordingly
properties.put(StreamsConfig.APPLICATION_ID_CONFIG, "vehicle.stream.app");
properties.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092,kafka2:19092");
properties.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass());
properties.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass());
// initializing a streambuilder for building topology.
final StreamsBuilder builder = new StreamsBuilder();
// Our initial 4 streams.
List<String> streamInputTopics = Arrays.asList(
"vehicle.stream1", "vehicle.stream2",
"vehicle.stream3", "vehicle.stream4"
);
/*
* Since there is no connection between a specific stream
* to a specific road or vehicle or anything else,
* we can take all four streams as a single stream
*/
KStream<String, String> source = builder.stream(streamInputTopics);
/*
* The initial key is unimportant (which can be ignored),
* Instead, we will be using the section name/id as key.
* Data will contain comma separated values in following format.
* VehicleId,Speed,Latitude,Longitude
*/
WindowBytesStoreSupplier windowSpeedStore = Stores.persistentWindowStore(
"windowSpeedStore",
AGGREGATION_WINDOW,
2, 10, true
);
source
.peek((k, v) -> printValues("Initial", k, v))
// First, we rekey the stream based on the road section.
.selectKey(VehicleStream::selectKeyAsRoadSection)
.peek((k, v) -> printValues("After rekey", k, v))
.groupByKey()
.windowedBy(TimeWindows.of(AGGREGATION_WINDOW))
.aggregate(
() -> "0.0", // Initialize
/*
* I'm using summing here for the aggregation as that's easier.
* It can be converted to average by storing extra details on number of records, etc..
*/
(k, v, previousSpeed) -> // Aggregator (summing speed)
String.valueOf(
Double.parseDouble(previousSpeed) +
VehicleSpeed.getVehicleSpeed(v).speed
),
Materialized.as(windowSpeedStore)
);
// generating the topology
final Topology topology = builder.build();
System.out.print(topology.describe());
// constructing a streams client with the properties and topology
final KafkaStreams streams = new KafkaStreams(topology, properties);
final CountDownLatch latch = new CountDownLatch(1);
// attaching shutdown handler
Runtime.getRuntime().addShutdownHook(new Thread("streams-shutdown-hook") {
@Override
public void run() {
streams.close();
latch.countDown();
}
});
try {
streams.start();
latch.await();
} catch (Throwable e) {
System.exit(1);
}
System.exit(0);
}
private static void printValues(String message, String key, Object value) {
System.out.printf("===%s=== key: %s value: %s%n", message, key, value.toString());
}
private static String selectKeyAsRoadSection(String key, String speedValue) {
// Would make more sense when it's the section id, rather than a name.
return coordinateToRoadSection(
VehicleSpeed.getVehicleSpeed(speedValue).latitude,
VehicleSpeed.getVehicleSpeed(speedValue).longitude
);
}
private static String coordinateToRoadSection(String latitude, String longitude) {
// Dummy function
return "Area 51";
}
public static class VehicleSpeed {
public String vehicleId;
public double speed;
public String latitude;
public String longitude;
public static VehicleSpeed getVehicleSpeed(String data) {
return new VehicleSpeed(data);
}
public VehicleSpeed(String data) {
String[] dataArray = data.split(",");
this.vehicleId = dataArray[0];
this.speed = Double.parseDouble(dataArray[1]);
this.latitude = dataArray[2];
this.longitude = dataArray[3];
}
@Override
public String toString() {
return String.format("veh: %s, speed: %f, latlong : %s,%s", vehicleId, speed, latitude, longitude);
}
}
}