day10-物流信息微服务

课程安排

  • 物流信息的需求分析
  • 技术实现分析
  • 基于MongoDB的功能实现
  • 多级缓存的解决方案
  • Redis缓存存在的问题分析并解决

1、背景说明

让收发件人清晰的了解到包裹的“实时”状态,就是“快递到哪了”

在电商大促期间,快件数量非常庞大,也就意味着查询人的量也是很大的

使用缓存技术解决并形成通用的高并发查询的解决方案

2、需求分析

用户寄件后,是需要查看运单的运输详情,也就是需要查看整个转运节点,类似这样:

产品的需求描述如下(在快递员端的产品文档中):

可以看出,物流信息中有状态、时间、具体信息、快递员姓名、快递员联系方式等信息。

在快递员取件时使用mq发消息进行保存

3、实现分析

3.1、MySQL实现

如果采用MySQL的存储,一般是这样存储的,首先设计表结构:

CREATE TABLE `sl_transport_info` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `transport_order_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '运单号',
  `status` varchar(10) DEFAULT NULL COMMENT '状态,例如:运输中',
  `info` varchar(500) DEFAULT NULL COMMENT '详细信息,例如:您的快件已到达【北京通州分拣中心】',
  `created` datetime DEFAULT NULL COMMENT '创建时间',
  `updated` datetime DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

插入测试数据:

INSERT INTO `sl_transport_info`(`id`, `transport_order_id`, `status`, `info`, `created`, `updated`) VALUES (1, 'SL920733749248', '已取件', '神领快递员已取件, 取件人【快递员,电话 18810966207}】', '2022-09-25 10:48:30', '2022-09-25 10:48:33');
INSERT INTO `sl_transport_info`(`id`, `transport_order_id`, `status`, `info`, `created`, `updated`) VALUES (2, 'SL920733749262', '已取件', '神领快递员已取件, 取件人【快递员,电话 18810966207}】', '2022-09-25 10:51:11', '2022-09-25 10:51:14');
INSERT INTO `sl_transport_info`(`id`, `transport_order_id`, `status`, `info`, `created`, `updated`) VALUES (3, 'SL920733749248', '运输中', '您的快件已到达【昌平区转运中心】', '2022-09-25 11:14:33', '2022-09-25 11:14:36');
INSERT INTO `sl_transport_info`(`id`, `transport_order_id`, `status`, `info`, `created`, `updated`) VALUES (4, 'SL920733749248', '运输中', '您的快件已到达【北京市转运中心】', '2022-09-25 11:14:54', '2022-09-25 11:14:57');
INSERT INTO `sl_transport_info`(`id`, `transport_order_id`, `status`, `info`, `created`, `updated`) VALUES (5, 'SL920733749262', '运输中', '您的快件已到达【昌平区转运中心】', '2022-09-25 11:15:17', '2022-09-25 11:15:19');
INSERT INTO `sl_transport_info`(`id`, `transport_order_id`, `status`, `info`, `created`, `updated`) VALUES (6, 'SL920733749262', '运输中', '您的快件已到达【江苏省南京市玄武区长江路】', '2022-09-25 11:15:44', '2022-09-25 11:15:47');
INSERT INTO `sl_transport_info`(`id`, `transport_order_id`, `status`, `info`, `created`, `updated`) VALUES (7, 'SL920733749248', '已签收', '您的快递已签收,如有疑问请联系快递员【快递员},电话18810966207】,感谢您使用神领快递,期待再次为您服务', '2022-09-25 11:16:16', '2022-09-25 11:16:19');

查询运单号【SL920733749248】的物流信息:

SELECT
	* 
FROM
	sl_transport_info 
WHERE
	transport_order_id = 'SL920733749248' 
ORDER BY
	created ASC

结果:

3.2、优化(MongoDB实现)

分析

物流信息功能的特点:

  • 数据量大
  • 查询频率高(签收后查询频率低)

显然,在存储大数据方面非关系型数据库更合适一些。并且物流信息相对用户信息,物品信息不是那么重要,不需要mysql的ACID功能,故使用MongoDB数据库。

MySQL存储在一张表中,每条物流信息就是一条行数据,数据条数将是运单数量的数倍,查询时需要通过运单id作为条件,按照时间正序排序得到所有的结果。

其次MongoDB数据库可以使用嵌套文档存储数据,一条物流信息只需存储一条数据即可。查询时指需要根据运单id进行一次查询。相较于mysql的多条数据存储物流信息,有较大优势

基于MongoDB的实现,可以充分利用MongoDB数据结构的特点,可以这样存储:

{
    "_id": ObjectId("62c6c679a1222549d64ba01e"),
    "transportOrderId": "SL1000000000585",
    "infoList": [
        {
            "created": NumberLong("1657192271195"),
            "info": "神领快递员已取件, 取件人【快递员,电话 18810966207}】",
            "status": "已取件"
        },
        {
            "created": NumberLong("1657192328518"),
            "info": "神领快递员已取件, 取件人【快递员,电话 18810966207}】",
            "status": "已取件"
        }
    ],
    "created": NumberLong("1657194104987"),
    "updated": NumberLong("1657194105064"),
    "_class": "com.sl.transport.info.entity.TransportInfoEntity"
}

如果有新的信息加入的话,只需要向【infoList】中插入元素即可,查询的话按照【transportOrderId】条件查询。

db.sl_transport_info.find({"transportOrderId":"SL1000000000585"})

4、功能实现

4.1、Service实现

在TransportInfoService中定义了2个方法,一个是新增或更新数据,另一个是根据运单号查询物流信息。

4.2.1、saveOrUpdate

逻辑

根据运单id和infoDetail查询数据库(infoDetail是infoList中的一条信息)

1根据运单id查询TransportInfoEntity

        2TransportInfoEntity为空,新建对象,并设置属性,设置更新时间,保存数据库

        3非空,将infoDetail加入infoList中。设置更新时间,保存数据库

代码
mongoTemplate.findOne(Query.query(Criteria.where("transportOrderId").is(transportOrderId)),TransportInfoEntity.class);

查询,此处传入TransportInfoEntity.class,即可识别该类对应表,因为在类中有@Document注释

@Override
    public TransportInfoEntity saveOrUpdate(String transportOrderId, TransportInfoDetail infoDetail) {
        TransportInfoEntity transportInfoEntity = mongoTemplate.findOne(Query.query(Criteria.where("transportOrderId").is(transportOrderId)),TransportInfoEntity.class);
        if(ObjectUtil.isEmpty(transportInfoEntity))
        {
            TransportInfoEntity entity = new TransportInfoEntity();
            entity.setTransportOrderId(transportOrderId);
            entity.setInfoList(ListUtil.toList(infoDetail));
            entity.setCreated(System.currentTimeMillis());
            entity.setUpdated(System.currentTimeMillis());
            return mongoTemplate.save(entity);
        }
        else{
            transportInfoEntity.getInfoList().add(infoDetail);
            transportInfoEntity.setUpdated(System.currentTimeMillis());
            return mongoTemplate.save(transportInfoEntity);
        }
    }

注意: MongoDB也有自动填写id的功能,不设置也可以

entity.setId(new ObjectId());

4.2.2、查询

根据运单号查询物流信息。

@Override
    public TransportInfoEntity queryByTransportOrderId(String transportOrderId) {
        //根据运单id查询
        Query query = Query.query(Criteria.where("transportOrderId").is(transportOrderId)); //构造查询条件
        TransportInfoEntity transportInfoEntity = this.mongoTemplate.findOne(query, TransportInfoEntity.class);
        if (ObjectUtil.isNotEmpty(transportInfoEntity)) {
            return transportInfoEntity;
        }
        throw new SLException(ExceptionEnum.NOT_FOUND);
    }

4.2、记录物流信息

4.2.1、分析

如何时记录物流信息?

一种是微服务直接调用,另一种是通过消息的方式调用,也就是同步和异步的方式。

选择通过消息的方式,主要原因有两个:

  • 物流信息数据的更新的实时性并不高,例如,运单到达某个转运中心,晚几分种记录信息也是可以的。
  • 更新数据时,并发量比较大,例如,一辆车装了几千或几万个包裹,到达某个转运中心后,司机入库时,需要一下记录几千或几万个运单的物流数据,在这一时刻并发量是比较大的,通过消息(异步)的方式,可以进行对流量削峰,从而保障系统的稳定性

4.2.2、消息结构

消息的结构如下:

{
    "info": "您的快件已到达【$organId】",
    "status": "运输中",
    "organId": 1012479939628238305,
    "transportOrderId": "SL920733749248",
    "created": 1653133234913
}

通过$organId占位符表示机构,也就是,需要通过传入的organId查询机构名称替换到info中,当然了,如果没有机构,无需替换。

练习1

难度系数:★★★★☆

描述:在work微服务中完成发送【物流信息】的消息的逻辑,这样的话,work微服务就和transport-info微服务联系起来了。

提示,一共有4处代码需要完善:

  • com.sl.ms.work.mq.CourierMQListener#list
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值