Scalable GPS Data Backend: SpringBoot, Kafka, MongoDB & Redis Design

This guide outlines a complete backend architecture for high‑volume GPS data, detailing the overall system flow, technology stack choices, Maven dependencies, data models, Kafka producer/consumer configurations, SpringBoot controllers, asynchronous processing, Redis caching, health checks, Docker deployment, and performance tuning recommendations to ensure stability and scalability.

Ray's Galactic Tech
Ray's Galactic Tech
Ray's Galactic Tech
Scalable GPS Data Backend: SpringBoot, Kafka, MongoDB & Redis Design

1. System Architecture Design

The system processes vehicle terminal data through a pipeline: vehicle terminal → SpringBoot access layer → Kafka message queue → data processing layer → MongoDB storage + Redis cache.

1.1 Overall Architecture

Vehicle Terminal → SpringBoot Access Layer → Kafka Queue → Data Processing Layer → MongoDB Storage + Redis Cache

1.2 Technology Stack Rationale

SpringBoot : Provides a rich ecosystem and enables rapid development of the access layer.

Kafka : High‑throughput message queue that decouples system components and supports ordered messages.

Redis : In‑memory cache for fast queries of the latest vehicle positions.

MongoDB : Flexible document store suitable for massive GPS trajectory data.

2. Core Implementation

2.1 Maven Dependency Configuration

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.kafka</groupId>
        <artifactId>spring-kafka</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-mongodb</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>
</dependencies>

2.2 GPS Data Model Design

@Data
@Document(collection = "gps_data")
public class GpsData {
    @Id
    private String id;
    @Indexed
    private String deviceId;
    private Double latitude;
    private Double longitude;
    private Double speed;
    private Double direction;
    private Double altitude;
    @Indexed
    private LocalDateTime timestamp;
    private LocalDateTime receiveTime;
    private Integer dataQuality;
    private Map<String, Object> extraInfo;
}

@Data
public class VehicleLatestPosition {
    private String deviceId;
    private Double latitude;
    private Double longitude;
    private LocalDateTime timestamp;
    private Double speed;
}

2.3 Kafka Producer Configuration

@Configuration
@EnableKafka
public class KafkaConfig {
    @Value("${spring.kafka.bootstrap-servers}")
    private String bootstrapServers;

    @Bean
    public ProducerFactory<String, GpsData> gpsDataProducerFactory() {
        Map<String, Object> props = new HashMap<>();
        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class);
        props.put(ProducerConfig.ACKS_CONFIG, "all");
        props.put(ProducerConfig.RETRIES_CONFIG, 3);
        props.put(ProducerConfig.MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION, 1);
        props.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true);
        return new DefaultKafkaProducerFactory<>(props);
    }

    @Bean
    public KafkaTemplate<String, GpsData> gpsDataKafkaTemplate() {
        return new KafkaTemplate<>(gpsDataProducerFactory());
    }
}

@Service
@Slf4j
public class GpsDataProducer {
    @Autowired
    private KafkaTemplate<String, GpsData> kafkaTemplate;
    private static final String GPS_TOPIC = "gps-data-topic";

    public void sendGpsData(GpsData gpsData) {
        kafkaTemplate.send(GPS_TOPIC, gpsData.getDeviceId(), gpsData)
            .addCallback(
                success -> log.debug("Sent successfully: deviceId={}, offset={}", gpsData.getDeviceId(), success.getRecordMetadata().offset()),
                failure -> log.error("Send failed: deviceId={}", gpsData.getDeviceId(), failure)
            );
    }
}

2.4 Kafka Consumer Processing

@Service
@Slf4j
public class GpsDataConsumer {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private MongoTemplate mongoTemplate;
    private static final String LATEST_POSITION_KEY_PREFIX = "vehicle:position:";

    @KafkaListener(topics = "gps-data-topic", groupId = "gps-consumer-group")
    public void consumeGpsData(ConsumerRecord<String, GpsData> record) {
        GpsData gpsData = record.value();
        if (!validateGpsData(gpsData)) return;
        saveToMongoDB(gpsData);
        updateLatestPosition(gpsData);
        processBusinessLogic(gpsData);
        log.debug("Processing completed: deviceId={}", gpsData.getDeviceId());
    }

    private boolean validateGpsData(GpsData gpsData) {
        if (gpsData.getDeviceId() == null || gpsData.getLatitude() == null || gpsData.getLongitude() == null) {
            return false;
        }
        return Math.abs(gpsData.getLatitude()) <= 90 && Math.abs(gpsData.getLongitude()) <= 180;
    }

    @Async("taskExecutor")
    public void saveToMongoDB(GpsData gpsData) {
        gpsData.setReceiveTime(LocalDateTime.now());
        mongoTemplate.save(gpsData);
    }

    private void updateLatestPosition(GpsData gpsData) {
        VehicleLatestPosition position = new VehicleLatestPosition();
        position.setDeviceId(gpsData.getDeviceId());
        position.setLatitude(gpsData.getLatitude());
        position.setLongitude(gpsData.getLongitude());
        position.setTimestamp(gpsData.getTimestamp());
        position.setSpeed(gpsData.getSpeed());
        redisTemplate.opsForValue().set(
            LATEST_POSITION_KEY_PREFIX + gpsData.getDeviceId(),
            position,
            Duration.ofMinutes(30)
        );
    }

    private void processBusinessLogic(GpsData gpsData) {
        // e.g., geofence, overspeed alarm, trajectory analysis
    }
}

2.5 SpringBoot Controller Example

@RestController
@RequestMapping("/api/gps")
@Slf4j
public class GpsController {
    @Autowired
    private GpsDataProducer gpsDataProducer;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private MongoTemplate mongoTemplate;

    @PostMapping("/upload")
    public ResponseEntity<Map<String, Object>> upload(@RequestBody GpsData gpsData) {
        gpsData.setReceiveTime(LocalDateTime.now());
        gpsDataProducer.sendGpsData(gpsData);
        return ResponseEntity.ok(Map.of("success", true, "message", "Data received successfully"));
    }

    @GetMapping("/position/{deviceId}")
    public ResponseEntity<VehicleLatestPosition> getLatestPosition(@PathVariable String deviceId) {
        return Optional.ofNullable((VehicleLatestPosition) redisTemplate.opsForValue().get("vehicle:position:" + deviceId))
            .map(ResponseEntity::ok)
            .orElse(ResponseEntity.notFound().build());
    }

    @GetMapping("/track/{deviceId}")
    public ResponseEntity<List<GpsData>> getTrack(@PathVariable String deviceId,
                                                @RequestParam LocalDateTime startTime,
                                                @RequestParam LocalDateTime endTime) {
        Query query = new Query(Criteria.where("deviceId").is(deviceId)
            .and("timestamp").gte(startTime).lte(endTime));
        query.with(Sort.by(Sort.Direction.ASC, "timestamp"));
        return ResponseEntity.ok(mongoTemplate.find(query, GpsData.class));
    }
}

2.6 Asynchronous and Redis Configuration

@Configuration
@EnableAsync
public class AsyncConfig {
    @Bean("taskExecutor")
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(50);
        executor.setQueueCapacity(1000);
        executor.setThreadNamePrefix("gps-data-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(mapper);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);
        template.afterPropertiesSet();
        return template;
    }
}

3. High Stability Guarantees

Kafka : Enable idempotence, retry mechanisms, and proper partitioning.

MongoDB : Optimize indexes, use sharded clusters, and perform asynchronous writes.

Redis : Configure connection pools, set appropriate expiration times, and tune memory usage.

Application Health Checks :

@Component
public class SystemHealthChecker {
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @EventListener(ApplicationReadyEvent.class)
    public void check() {
        mongoTemplate.executeCommand("{ ping: 1 }");
        redisTemplate.opsForValue().get("health-check");
    }
}

4. Deployment and Optimization Recommendations

4.1 Docker Deployment

FROM openjdk:11-jre-slim
VOLUME /tmp
COPY target/gps-data-system-1.0.0.jar app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

4.2 Performance Tuning

Kafka: Enable compression, batch sending, and balanced partitioning.

MongoDB: Create appropriate indexes, use sharding, and archive historical data.

Redis: Optimize memory usage, configure connection pools, and set expiration policies.

This solution can reliably handle massive GPS streams, support high‑concurrency writes, provide fast location queries, and empower real‑time analytics for downstream business logic.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

DockerRedisKafkaSpringBootMongoDB
Ray's Galactic Tech
Written by

Ray's Galactic Tech

Practice together, never alone. We cover programming languages, development tools, learning methods, and pitfall notes. We simplify complex topics, guiding you from beginner to advanced. Weekly practical content—let's grow together!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.