hm_mall项目介绍
翰墨商城是一个基于互联网电商平台的后台项目 主要包括 商品分类,品牌管理,文件上传下载,商品搜索服务,商品页面服务,结合RSA的授权中心
使用到的技术
该项目是基于springboot,sping,mybatis的通用mapper框架进行开发; 通过Elasticsearch 实现商品搜索服务;基于rabbitmq消息队列实现商品服务与搜索服务和静态页面的同步管理; 通过fastDFS实现文件的上传下载;使用redis实现存储用户的临时验证码; `基于JWT + RSA非对称加密 实现用户的认证授权;
1. 商品的品牌管理和查询
商品分类表设计注意点:商品分类是分层级的,如何区分儿子和爹的关系,就是在表中添加一个parentID字段 通用mapper会把对象中的非空属性作为查询条件 问题整体解决思路: 通过前端请求,分析请求方式(post,get..),请求路径(http://api.leyou.com/api/item/category/list?pid=0)请求参数(pid),返回类型(是个json数组) 进而来设计和构造controller和业务层 利用cors解决跨域问题 具体使用:在网关中加入GlobalCorsConfig,添加跨域信息,定义请求方式,头信息等
2. 商品分类表设计注意事项
数据库设计的时候注意,tb_category_brand用于商品分类和品牌之间的关联,同时这表里没有外键,为什么不加外键呢?为了提高电商系统的性能,因为外键关联会影响到商品的删除 也会影响数据库的读写性能 根据返回数据的特点:有个用brand对象包装的list还有个总条数,所以选择用对象来包装返回结果 具体逻辑处理时:包括1 分页,2.过滤 3.排序 4.查询 5.解析分页结果 商品新增功能
3. 商品的文件上传
注意一点:由于文件上传在经过zuul网关时,再高并发时可能会导致网络阻塞,zuul网关不可用,所以我们在做文件上传时最好绕过请求的缓存(也会经过zuul网关,只是不会在缓存请求) 解决方法:通过nginx的指令对地址进行重写,修改到以/zuul为前缀
4. 文件上传下载的升级
通过fastDFS进行小文件的上传下载 将代码中的字符串,及变量配置到配置文件中去 具体操作步骤: 1 在application.yml文件中配置 hm.upload.baseUrl 和 hm.upload.allowTypes属性 2 定义 UploadProperties属性类 3 在UploadService中注入 UploadProperties属性类 并使用属性替代字符串
5. spu与sku的关系
spu:标准产品单位 sku:库存量单位 spu是一个抽象的商品集概念,作用:为了方便后台管理, sku因具体特征不同而区分出的商品,sku是具体要销售的商品,用户购买的是sku 每一个分类都有统一的规格参数模板,但不同商品其参数值可能不同 商品分类与规格模板是一对一关系 商品分类与商品spu是一对多关系 规模模板与商品spu是一对多关系 商品spu保存有规格参数的具体值 因为sku的特有属性是商品规格参数的一部分, 所以可以将规格参数中的属性划分为 SKU 通用规格与SKU特有规格 所以 spu与sku是一对多关系 spu保存着所有sku共享的规格属性 sku中保存每个sku特有的规格属性
6. 将库存表和sku表分开处理的目的?
因为库存字段写频率较高,而sku的其他字段以读为主,因此我们将两张表分离,读写不会干扰 sku表中的indexes字段是一个特有规格属性字段,它表示spu属性模板中的对应下标组合字段 因为在spu表中其实已经对特有规格参数及可选项作了保存,我们在sku表中将不同的角标串联起来作为spu下不同 sku的标识,这就是index字段;;;、
当前规格参数的显示应该是以能够查询到的所有商品为准,而不是以数据库中的所有查询区间 解决思路是查询数据库中的商品尺寸规格,然后处理成段,再覆盖原来的值
进行搜索时,存的其实是spu,以spu为单位,主要包括,图片,价格,标题,副标题;暗藏的数据:spu的id,sku的id 首先要编写分类和品牌查询的相关服务 当实现将数据库中的数据同步到索引库时 需要用到Feign(使用feign之后,我们调用eureka 注册的其他服务,在代码中就像各个service之间相互调用那么简单。) 同时要想再serach-service中获取品牌信息和分类等信息,如果直接在serach-service中定义接口这种做法不好,因为调用方无法知道被调用方的接口信息和参数, 这样也将两者绑定死了 如何来操作? 在被调用方的item-interface中定义接口信息,在serach-service中引入hm-interface的包,然后继承interface中的接口 @FeignClient(value = "item-service") public interface GoodsClient extends GoodsApi { }
7. 解决搜索服务与商品页面服务同步的问题
1. 两种传统路线,
- 每一次对商品做增删改查时,同时修改索引库数据及页面数据
- 搜索服务与商品页面服务对外提供接口,后台在商品增删改后调用接口 但是这两种路线有缺陷,代码耦合度较高,违背了微服务的独立原则
2. 新的解决路线
通过消息队列 消息队列的优势:消息队列分为生产者和消费者,生产者负责消息的发送,消费者负责消息的接收,两者是异步的,而且也只关注消息的接收与发送, 没有业务逻辑的侵入,对代码的耦合度较低
3. 消息队列中的问题:
- 消息丢失如何处理?(如果消费者领取消息后,还没执行操作就挂掉了呢?或者抛出了异常?消息消费失败,这时消息就丢失了) 采用rabbitmq的ack确认机制 而ack机制分为两种。自动ack与手动ack, 两者区别:自动ack是消息一旦被接收,那么消费者就会自动发送ack; 而手动ack是消息接收后不会自动发送ack,需要手动来调用 而如何来去选择 就要看消息的重要性
- 如果消息不太重要,丢失也没有影响,那么自动ACK会比较方便
- 如果消息非常重要,不容丢失。那么最好在消费完成后手动ACK,否则接收消息后就自动ACK,RabbitMQ就会把消息从队列中删除。 如果此时消费者宕机,那么消息就丢失了。 如何实现 channel.basicConsume(QUEUE_NAME, false, consumer); 当boolean类型值为true为自动ack, 反之为false同时当为手动ack时要注意如果设置为手动ack,一定要有手动ack代码 channel.basicAck(envelope.getDeliveryTag(), false); 引申:如果消息在消费者还未来得及消费时就丢失了?该如何处理? 通过将消息持久化,但是消息持久化的前提是:队列、Exchange都持久化,消息的持久化是在发送消息时设置devilerMode为2 对于生产者方出现消息丢失时可以采用生产者确认机制来保证,就是MQ向生产方发送消息 而对于除MQ以外的其他消息工具不带生产者确认时,应该怎么做?先将消息持久化到数据库,并记录消息状态(可靠消息服务)
如何避免消息堆积? 什么情况会消息堆积?比如第三方的发短信,转账业务,或者大量订单 1)采用workqueue,多个消费者监听同一队列 2)接收到消息后,通过线程池,异步消费
如何避免消息的重复执行? 要保证消息的幂等性(同一接口被重复执行,其结果一致)
消息队列的几种类型? simple(基本消息模型),--------》一个生产者,一个消费者,一个队列 worker(work消息模型),----》一个生产者,多个消费者,一个队列 工作队列模型 默认是队列把消息每一轮平均分�