1. 同城配送系统架构设计
同城配送系统的核心在于处理实时订单分配、路径优化和状态追踪。采用微服务架构能够有效解耦这些功能模块,提升系统的可扩展性和容错能力。以下是典型的服务划分:
- 订单服务:处理下单、支付、退款等业务流程
- 配送服务:负责骑手调度和路线规划
- 位置服务:实时追踪骑手和订单位置
- 通知服务:向用户和骑手推送状态变更
- 计费服务:计算配送费用和骑手薪酬
1.1 技术选型考量
对于Java技术栈,推荐组合:
- Spring Boot:快速构建微服务
- Spring Cloud:服务发现与治理
- Redis:缓存和实时位置存储
- RabbitMQ:异步消息处理
- MongoDB:存储轨迹等非结构化数据
- MySQL:核心业务数据存储
重要提示:生产环境务必配置读写分离,订单表建议按城市分片
2. 核心功能实现详解
2.1 订单创建接口实现
java复制@RestController
@RequestMapping("/orders")
public class OrderController {
@PostMapping
public ResponseEntity<Order> createOrder(
@RequestBody OrderRequest request) {
// 参数校验
if (!validateOrderRequest(request)) {
throw new InvalidParameterException("请求参数不合法");
}
// 计算配送费
DeliveryFee fee = feeService.calculateFee(
request.getPickupAddress(),
request.getDeliveryAddress());
// 创建订单实体
Order order = new Order();
order.setUserId(request.getUserId());
order.setStatus(OrderStatus.PENDING);
order.setFeeAmount(fee.getAmount());
// 保存到数据库
Order savedOrder = orderRepository.save(order);
// 发送到消息队列进行后续处理
rabbitTemplate.convertAndSend(
"order.created",
savedOrder.getId());
return ResponseEntity.ok(savedOrder);
}
private boolean validateOrderRequest(OrderRequest request) {
// 实现校验逻辑
}
}
2.2 骑手调度算法
采用加权评分策略选择最优骑手:
java复制public Rider selectBestRider(Order order, List<Rider> candidates) {
return candidates.stream()
.max(Comparator.comparingDouble(rider -> {
double score = 0;
// 距离权重40%
double distance = locationService.getDistance(
rider.getPosition(),
order.getPickupAddress());
score += 0.4 * (1 - normalize(distance));
// 骑手评分30%
score += 0.3 * rider.getRating();
// 当前负载20%
score += 0.2 * (1 - rider.getCurrentLoad());
// 接单速度10%
score += 0.1 * rider.getAcceptSpeed();
return score;
}))
.orElseThrow(() -> new NoAvailableRiderException());
}
private double normalize(double value) {
// 将距离归一化到0-1区间
}
3. 关键问题解决方案
3.1 订单状态同步
采用事件驱动架构保证状态一致性:
java复制@Service
public class OrderStatusService {
@Transactional
public void updateStatus(Long orderId, OrderStatus newStatus) {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new OrderNotFoundException());
if (!order.getStatus().canTransitionTo(newStatus)) {
throw new IllegalStatusTransitionException();
}
order.setStatus(newStatus);
orderRepository.save(order);
// 发布状态变更事件
eventPublisher.publishEvent(
new OrderStatusChangedEvent(orderId, newStatus));
}
}
3.2 实时位置追踪
使用Redis GEO存储位置信息:
java复制@Service
public class LocationService {
private final RedisTemplate<String, String> redisTemplate;
public void updatePosition(String userId, double lng, double lat) {
redisTemplate.opsForGeo().add(
"user:locations",
new Point(lng, lat),
userId);
}
public List<String> findNearbyRiders(double lng, double lat, double radius) {
Circle within = new Circle(new Point(lng, lat),
new Distance(radius, Metrics.KILOMETERS));
return redisTemplate.opsForGeo()
.radius("user:locations", within)
.getContent()
.stream()
.map(GeoResult::getContent)
.map(GeoLocation::getName)
.collect(Collectors.toList());
}
}
4. 性能优化实践
4.1 数据库优化方案
-
索引策略:
- 订单表:
(city_code, status, create_time)联合索引 - 骑手表:
position空间索引
- 订单表:
-
查询优化:
java复制@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
@Query("SELECT o FROM Order o WHERE " +
"o.cityCode = :cityCode AND " +
"o.status = :status AND " +
"o.createTime >= :startTime " +
"ORDER BY o.createTime DESC")
List<Order> findRecentOrders(
@Param("cityCode") String cityCode,
@Param("status") OrderStatus status,
@Param("startTime") Instant startTime,
Pageable pageable);
}
4.2 缓存策略设计
采用多级缓存架构:
- 本地缓存:高频访问的配置信息
- Redis缓存:
- 订单详情:5分钟TTL
- 骑手信息:30分钟TTL
- 缓存穿透防护:
java复制public Order getOrderWithCache(Long orderId) {
// 缓存key设计
String cacheKey = "order:" + orderId;
// 先查缓存
Order order = redisTemplate.opsForValue().get(cacheKey);
if (order != null) {
return order;
}
// 查数据库
order = orderRepository.findById(orderId).orElse(null);
// 空值也缓存防止穿透
redisTemplate.opsForValue().set(
cacheKey,
order != null ? order : new NullOrder(),
order != null ? 5 : 1, // 空值缓存1分钟
TimeUnit.MINUTES);
return order;
}
5. 生产环境注意事项
- 分布式事务处理:
java复制@Service
public class OrderCompletionService {
@Transactional
public void completeOrder(Long orderId) {
// 1. 更新订单状态
orderService.updateStatus(orderId, OrderStatus.COMPLETED);
// 2. 计算骑手报酬
billingService.calculateRiderPayment(orderId);
// 3. 发送完成通知
notificationService.sendCompletionNotice(orderId);
}
@Retryable(maxAttempts = 3, backoff = @Backoff(delay = 1000))
public void retryCompleteOrder(Long orderId) {
try {
completeOrder(orderId);
} catch (Exception e) {
log.error("订单完成处理失败", e);
throw e;
}
}
}
- 监控指标配置:
- 关键指标:
- 订单创建QPS
- 平均配送时长
- 骑手接单响应时间
- Prometheus配置示例:
yaml复制metrics:
delivery:
enabled: true
duration:
buckets: 100ms, 500ms, 1s, 5s
- 日志排查技巧:
java复制@Slf4j
@Service
public class OrderService {
public void processOrder(Long orderId) {
MDC.put("orderId", orderId.toString());
try {
log.info("开始处理订单");
// 业务逻辑
} finally {
MDC.clear();
}
}
}
在日志配置中增加traceId:
properties复制logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} [%X{traceId},%X{orderId}] - %msg%n
