SpringBoot与ZooKeeper整合,实现智能停车计费体系

打印 上一主题 下一主题

主题 1025|帖子 1025|积分 3079

智能停车计费体系通太过布式锁技能确保在多台服务器环境下,同一辆车的计费操作不会发生冲突,从而包管计费的正确性和同等性。
关键点

1. 高并发处理



  • 需求: 停车场大概有大量的车辆同时进出,尤其是在高峰时段。
  • 办理方案: 使用分布式架构和高效的数据库管理来处理高并发哀求。
2. 数据同等性



  • 需求: 确保在多台服务器环境下,同一辆车的计费操作不会发生冲突。
  • 办理方案: 使用分布式锁(如ZooKeeper)来包管数据的同等性和正确性。
3. 实时性



  • 需求: 需要快速相应车辆的进失事件,并及时计算费用。
  • 办理方案: 优化算法和数据库查询,确保低耽误。
使用ZooKeeper的利益

1. 分布式锁



  • 包管数据同等性: 在多台服务器同时处理车辆进出记载的情况下,使用ZooKeeper的分布式锁可以确保同一辆车的计费操作在同一时间只能由一台服务器处理。这避免了并发问题,包管了计费的正确性和同等性。
  • 防止重复计费: 如果没有分布式锁,大概会出现同一辆车多次被不同服务器计费的情况,导致重复收费或计费错误。
2. 高可用性



  • 容错本领: ZooKeeper本身是一个高可用的服务,即使某一台ZooKeeper节点故障,其他节点仍然可以继续提供服务。这进步了整个体系的稳定性和可靠性。
  • 负载平衡: 分布式锁机制可以资助均匀分配任务到不同的服务器上,进步体系的团体性能和相应速度。
3. 易于扩展



  • 动态增长服务器: 随着业务的增长,可以通过简单地增长更多的服务器来处理更多的哀求。ZooKeeper可以轻松管理这些新增的服务器,并确保它们能够正确协同工作。
  • 机动性: 添加新的功能或调整现有逻辑时,ZooKeeper的分布式特性使得这些更改更轻易实现和部署。
4. 简化协调过程



  • 同一协调: ZooKeeper提供了一种集中式的协调机制,使得多个服务器之间的通讯和同步变得简单和高效。不再需要复杂的自定义协议或手动协调逻辑。
  • 轻量级: ZooKeeper的设计目标是轻量级且高性能,适合在各种规模的应用中使用。
5. 监控和日志



  • 实时监控: ZooKeeper提供了丰富的监控工具和API,可以实时监控集群的状态和健康状态。
  • 审计日志: 可以通过ZooKeeper的日志功能跟踪所有对共享资源的操作,便于审计和故障排查。
代码实操

  1.         <!-- Spring Boot Starter Web for RESTful services -->
  2.         <dependency>
  3.             <groupId>org.springframework.boot</groupId>
  4.             <artifactId>spring-boot-starter-web</artifactId>
  5.         </dependency>
  6.         <!-- Apache Curator for ZooKeeper interaction -->
  7.         <dependency>
  8.             <groupId>org.apache.curator</groupId>
  9.             <artifactId>curator-framework</artifactId>
  10.             <version>${curator.version}</version>
  11.         </dependency>
  12.         <dependency>
  13.             <groupId>org.apache.curator</groupId>
  14.             <artifactId>curator-recipes</artifactId>
  15.             <version>${curator.version}</version>
  16.         </dependency>
  17.         <!-- Lombok for reducing boilerplate code -->
  18.         <dependency>
  19.             <groupId>org.projectlombok</groupId>
  20.             <artifactId>lombok</artifactId>
  21.             <optional>true</optional>
  22.         </dependency>
复制代码
application.properties

  1. zookeeper.connect-string=localhost:2181
复制代码
ZookeeperConfig.java

  1. package com.example.parking.config;
  2. import org.apache.curator.framework.CuratorFramework;
  3. import org.apache.curator.framework.CuratorFrameworkFactory;
  4. import org.apache.curator.retry.ExponentialBackoffRetry;
  5. import org.springframework.beans.factory.annotation.Value;
  6. import org.springframework.context.annotation.Bean;
  7. import org.springframework.context.annotation.Configuration;
  8. /**
  9.  * 配置ZooKeeper连接。
  10.  */
  11. @Configuration
  12. publicclass ZookeeperConfig {
  13.     @Value("${zookeeper.connect-string}")
  14.     private String zkConnectString;
  15.     /**
  16.      * 创建并返回一个CuratorFramework实例。
  17.      * @return CuratorFramework实例
  18.      */
  19.     @Bean(initMethod = "start", destroyMethod = "close")
  20.     public CuratorFramework curatorFramework() {
  21.         return CuratorFrameworkFactory.newClient(zkConnectString, new ExponentialBackoffRetry(1000, 3));
  22.     }
  23. }
复制代码
ParkingController.java

  1. package com.example.parking.controller;
  2. import com.example.parking.model.ParkingRequest;
  3. import com.example.parking.service.ParkingService;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.http.ResponseEntity;
  6. import org.springframework.web.bind.annotation.*;
  7. /**
  8.  * 提供HTTP接口用于车辆进入和离开停车场的操作。
  9.  */
  10. @RestController
  11. @RequestMapping("/parking")
  12. publicclass ParkingController {
  13.     @Autowired
  14.     private ParkingService parkingService;
  15.     /**
  16.      * 车辆进入停车场。
  17.      * @param request 包含车辆ID的请求对象
  18.      * @return 成功或失败的消息
  19.      */
  20.     @PostMapping("/enter")
  21.     public ResponseEntity<String> enterParkingLot(@RequestBody ParkingRequest request) {
  22.         boolean success = parkingService.enterParkingLot(request.getCarId());
  23.         if (success) {
  24.             return ResponseEntity.ok("Vehicle entered parking lot.
  25. ");
  26.         } else {
  27.             return ResponseEntity.badRequest().body("Failed to enter parking lot.");
  28.         }
  29.     }
  30.     /**
  31.      * 车辆离开停车场。
  32.      * @param request 包含车辆ID和停留时间的请求对象
  33.      * @return 成功或失败的消息
  34.      */
  35.     @PostMapping("/leave")
  36.     public ResponseEntity<String> leaveParkingLot(@RequestBody ParkingRequest request) {
  37.         long durationInMinutes = request.getDurationInMinutes();
  38.         boolean success = parkingService.leaveParkingLot(request.getCarId(), durationInMinutes);
  39.         if (success) {
  40.             return ResponseEntity.ok("Vehicle left parking lot. Fee calculated and stored.
  41. ");
  42.         } else {
  43.             return ResponseEntity.badRequest().body("Failed to leave parking lot.");
  44.         }
  45.     }
  46. }
复制代码
ParkingDao.java

  1. package com.example.parking.dao;
  2. import com.example.parking.entity.ParkingRecord;
  3. import org.springframework.stereotype.Repository;
  4. import java.util.HashMap;
  5. import java.util.Map;
  6. /**
  7.  * 数据访问对象,负责存储和检索停车记录。
  8.  */
  9. @Repository
  10. publicclass ParkingDao {
  11.     privatefinal Map<String, ParkingRecord> records = new HashMap<>();
  12.     /**
  13.      * 记录车辆进入停车场的时间。
  14.      * @param carId 车辆ID
  15.      * @return 是否成功记录
  16.      */
  17.     public boolean saveEntry(String carId) {
  18.         if (!records.containsKey(carId)) {
  19.             records.put(carId, new ParkingRecord(carId));
  20.             returntrue;
  21.         }
  22.         returnfalse;
  23.     }
  24.     /**
  25.      * 获取指定车辆的停车记录。
  26.      * @param carId 车辆ID
  27.      * @return 停车记录
  28.      */
  29.     public ParkingRecord getRecord(String carId) {
  30.         return records.get(carId);
  31.     }
  32.     /**
  33.      * 移除指定车辆的停车记录。
  34.      * @param carId 车辆ID
  35.      * @return 是否成功移除
  36.      */
  37.     public boolean removeRecord(String carId) {
  38.         return records.remove(carId) != null;
  39.     }
  40. }
复制代码
ParkingRecord.java

  1. package com.example.parking.entity;
  2. import java.time.LocalDateTime;
  3. /**
  4.  * 实体类,表示一辆车的停车记录。
  5.  */
  6. publicclass ParkingRecord {
  7.     private String carId;
  8.     private LocalDateTime entryTime;
  9.     /**
  10.      * 构造函数,初始化停车记录。
  11.      * @param carId 车辆ID
  12.      */
  13.     public ParkingRecord(String carId) {
  14.         this.carId = carId;
  15.         this.entryTime = LocalDateTime.now();
  16.     }
  17.     /**
  18.      * 获取车辆ID。
  19.      * @return 车辆ID
  20.      */
  21.     public String getCarId() {
  22.         return carId;
  23.     }
  24.     /**
  25.      * 获取车辆进入停车场的时间。
  26.      * @return 进入时间
  27.      */
  28.     public LocalDateTime getEntryTime() {
  29.         return entryTime;
  30.     }
  31. }
复制代码
ParkingRequest.java

  1. package com.example.parking.model;
  2. /**
  3.  * 请求模型,包含车辆ID和停留时间。
  4.  */
  5. publicclass ParkingRequest {
  6.     private String carId;
  7.     privatelong durationInMinutes;
  8.     // Getters and Setters
  9.     /**
  10.      * 获取车辆ID。
  11.      * @return 车辆ID
  12.      */
  13.     public String getCarId() {
  14.         return carId;
  15.     }
  16.     /**
  17.      * 设置车辆ID。
  18.      * @param carId 车辆ID
  19.      */
  20.     public void setCarId(String carId) {
  21.         this.carId = carId;
  22.     }
  23.     /**
  24.      * 获取停留时间(分钟)。
  25.      * @return 停留时间
  26.      */
  27.     public long getDurationInMinutes() {
  28.         return durationInMinutes;
  29.     }
  30.     /**
  31.      * 设置停留时间(分钟)。
  32.      * @param durationInMinutes 停留时间
  33.      */
  34.     public void setDurationInMinutes(long durationInMinutes) {
  35.         this.durationInMinutes = durationInMinutes;
  36.     }
  37. }
复制代码
ParkingService.java

  1. package com.example.parking.service;
  2. /**
  3.  * 接口定义了进入和离开停车场的方法。
  4.  */
  5. public interface ParkingService {
  6.     /**
  7.      * 车辆进入停车场。
  8.      * @param carId 车辆ID
  9.      * @return 是否成功进入
  10.      */
  11.     boolean enterParkingLot(String carId);
  12.     /**
  13.      * 车辆离开停车场。
  14.      * @param carId 车辆ID
  15.      * @param durationInMinutes 停留时间(分钟)
  16.      * @return 是否成功离开
  17.      */
  18.     boolean leaveParkingLot(String carId, long durationInMinutes);
  19. }
复制代码
ParkingServiceImpl.java

  1. package com.example.parking.service.impl;
  2. import com.example.parking.dao.ParkingDao;
  3. import com.example.parking.entity.ParkingRecord;
  4. import com.example.parking.service.ParkingService;
  5. import org.apache.curator.framework.CuratorFramework;
  6. import org.apache.curator.framework.recipes.locks.InterProcessMutex;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.stereotype.Service;
  9. import java.util.concurrent.TimeUnit;
  10. /**
  11.  * 实现了ParkingService接口的具体逻辑,并使用ZooKeeper的互斥锁来保证同一辆车的计费操作不会被并发执行。
  12.  */
  13. @Service
  14. publicclass ParkingServiceImpl implements ParkingService {
  15.     privatefinal CuratorFramework client;
  16.     privatefinal ParkingDao parkingDao;
  17.     @Autowired
  18.     public ParkingServiceImpl(CuratorFramework client, ParkingDao parkingDao) {
  19.         this.client = client;
  20.         this.parkingDao = parkingDao;
  21.     }
  22.     /**
  23.      * 车辆进入停车场。
  24.      * @param carId 车辆ID
  25.      * @return 是否成功进入
  26.      */
  27.     @Override
  28.     public boolean enterParkingLot(String carId) {
  29.         return parkingDao.saveEntry(carId);
  30.     }
  31.     /**
  32.      * 车辆离开停车场。
  33.      * @param carId 车辆ID
  34.      * @param durationInMinutes 停留时间(分钟)
  35.      * @return 是否成功离开
  36.      */
  37.     @Override
  38.     public boolean leaveParkingLot(String carId, long durationInMinutes) {
  39.         InterProcessMutex lock = new InterProcessMutex(client, "/locks/" + carId);
  40.         try {
  41.             if (lock.acquire(10, TimeUnit.SECONDS)) {
  42.                 try {
  43.                     if (!enterParkingLot(carId)) {
  44.                         returnfalse;
  45.                     }
  46.                     ParkingRecord record = parkingDao.getRecord(carId);
  47.                     double fee = calculateFee(record, durationInMinutes);
  48.                     System.out.println("Calculated fee for " + carId + ": $" + fee);
  49.                     // Logic to store the fee in a database or other storage system
  50.                     parkingDao.removeRecord(carId);
  51.                     returntrue;
  52.                 } finally {
  53.                     lock.release();
  54.                 }
  55.             } else {
  56.                 System.err.println("Could not acquire lock for " + carId);
  57.                 returnfalse;
  58.             }
  59.         } catch (Exception e) {
  60.             e.printStackTrace();
  61.             returnfalse;
  62.         }
  63.     }
  64.     /**
  65.      * 计算停车费用。
  66.      * @param record 停车记录
  67.      * @param durationInMinutes 停留时间(分钟)
  68.      * @return 停车费用
  69.      */
  70.     private double calculateFee(ParkingRecord record, long durationInMinutes) {
  71.         double ratePerMinute = 0.5;
  72.         return durationInMinutes * ratePerMinute;
  73.     }
  74. }
复制代码
ParkingApplication.java

  1. package com.example.parking;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. /**
  5.  * Spring Boot应用的主类。
  6.  */
  7. @SpringBootApplication
  8. public class ParkingApplication {
  9.     public static void main(String[] args) {
  10.         SpringApplication.run(ParkingApplication.class, args);
  11.     }
  12. }
复制代码
测试结果

   “  确保ZooKeeper服务器正在运行。
  车辆进入停车场

  1. http://localhost:8080/parking/enter   
  2.      {
  3.        "carId": "A123"
  4.      }
复制代码


  • 相应:
    1. Vehicle entered parking lot.
    复制代码
车辆离开停车场

  1. http://localhost:8080/parking/leave
  2.      {
  3.        "carId": "A123",
  4.        "durationInMinutes": 60
  5.      }
复制代码


  • 相应:
    1. Vehicle left parking lot. Fee calculated and stored.
    复制代码
  • 控制台输出:
    1. Calculated fee for A123: $30.0
    复制代码

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

王國慶

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表