第十八章 1024电商平台-商品购物车微服务介绍和项目创建
第1集 1024电商平台-商品购物车微服务功能需求介绍
简介:商品-购物车-微服务功能需求介绍
-
电商里面的知识介绍
- 类目
- 一个树状结构的系统,根据业务可以分成4-5级。如手机->智能手机->国产手机 类目,在这里面,手机是一级类目,国产手机是三级类目,也是叶子类目
- SPU
- Standard Product Unit:标准化产品单元。是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。通俗点讲,属性值、特性相同的商品就可以称为一个SPU
- 比如 Iphone100 就是一个SPU
- SKU
- 一般指库存保有单位。库存保有单位即库存进出计量的单位, 可以是以件、盒、托盘等为单位。SKU是物理上不可分割的最小存货单元,在服装、鞋类商品中使用最多最普遍,买家购买、商家进货、供应商备货、工厂生产都是依据SKU进行的
- 比如Iphone100 128g 土豪金就是一个SKU
- 类目
-
1024电商平不做多类目SPU/SKU设计
-
否则这个就多了好几百集内容了,且不通用
-
大课训练营核心还是把多个技术知识点综合起来
-
有兴趣的同学可以看我们《小滴课堂的电商供应链平台设计专题视频》
-
-
商品微服务介绍
- 首页商品分页展示
- 商品详情展示
- 库存控制
-
购物车功能介绍
- 加入购物车
- 清空购物车
- 修改购物车数量
第2集 1024电商平台-商品购物车项目核心知识和数据库建立
简介:商品购物车项目核心知识和数据库建立
-
商品和购物车微服务核心知识:
- 商品微服务库存管理
- 分布式事务知识
- 电商平台购物车的实现方案
- 通用购物车数据结构设计
- 购物车价格统计业务逻辑梳理
- 重点知识下沉到订单微服务
- 商品库存锁定和回收
- 优惠券使用锁定和回收
- 订单验价
- 多通道支付设计等
- 商品服务数据库建立
CREATE TABLE `banner` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`img` varchar(524) DEFAULT NULL COMMENT '图片',
`url` varchar(524) DEFAULT NULL COMMENT '跳转地址',
`weight` int(11) DEFAULT NULL COMMENT '权重',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;
INSERT INTO `banner` (`id`, `img`, `url`, `weight`)
VALUES
(1, 'https://file.xdclass.net/video/2020/alibabacloud/zx-lbt.jpeg', 'https://m.xdclass.net/#/member', 1),
(2, 'https://file.xdclass.net/video/%E5%AE%98%E7%BD%91%E8%BD%AE%E6%92%AD%E5%9B%BE/20%E5%B9%B4%E5%8F%8C11%E9%98%BF%E9%87%8C%E4%BA%91/fc-lbt.jpeg', 'https://www.aliyun.com/1111/pintuan-share?ptCode=MTcwMTY3MzEyMjc5MDU2MHx8MTE0fDE%3D&userCode=r5saexap', 3),
(3, 'https://file.xdclass.net/video/%E5%AE%98%E7%BD%91%E8%BD%AE%E6%92%AD%E5%9B%BE/20%E5%B9%B4%E5%8F%8C11%E9%98%BF%E9%87%8C%E4%BA%91/FAN-lbu-vip.jpeg', 'https://file.xdclass.net/video/%E5%AE%98%E7%BD%91%E8%BD%AE%E6%92%AD%E5%9B%BE/Nginx.jpeg', 2);
CREATE TABLE `product` (
`id` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(128) DEFAULT NULL COMMENT '标题',
`cover_img` varchar(128) DEFAULT NULL COMMENT '封面图',
`detail` varchar(256) DEFAULT '' COMMENT '详情',
`old_price` decimal(16,2) DEFAULT NULL COMMENT '老价格',
`price` decimal(16,2) DEFAULT NULL COMMENT '新价格',
`stock` int(11) DEFAULT NULL COMMENT '库存',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`lock_stock` int(11) DEFAULT '0' COMMENT '锁定库存',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4;
INSERT INTO `product` (`id`, `title`, `cover_img`, `detail`, `old_price`, `price`, `stock`, `create_time`, `lock_stock`)
VALUES
(1, '小滴课堂抱枕', 'https://file.xdclass.net/video/2020/alibabacloud/zt-alibabacloud.png', 'https://file.xdclass.net/video/2021/60-MLS/summary.jpeg', 32.00, 213.00, 100, '2021-09-12 00:00:00', 31),
(2, '技术人的杯子Linux', 'https://file.xdclass.net/video/2020/alibabacloud/zt-alibabacloud.png', 'https://file.xdclass.net/video/2021/59-Postman/summary.jpeg', 432.00, 42.00, 20, '2021-03-12 00:00:00', 2),
(3, '技术人的杯子docker', 'https://file.xdclass.net/video/2020/alibabacloud/zt-alibabacloud.png', 'https://file.xdclass.net/video/2021/60-MLS/summary.jpeg', 35.00, 12.00, 20, '2022-09-22 00:00:00', 13),
(4, '技术人的杯子git', 'https://file.xdclass.net/video/2020/alibabacloud/zt-alibabacloud.png', 'https://file.xdclass.net/video/2021/60-MLS/summary.jpeg', 12.00, 14.00, 20, '2022-11-12 00:00:00', 2);
第3集 商品微服务+MybatisPlusGenerator代码自动生成工具
简介:Mybatis-plus-generator代码自动化生成微服务相关类
- 配置代码生成(标记TODO的记得修改)
public class MyBatisPlusGenerator {
public static void main(String[] args) {
//1. 全局配置
GlobalConfig config = new GlobalConfig();
// 作者
config.setAuthor("二当家小D")
// 生成路径,最好使用绝对路径,window路径是不一样的
//TODO TODO TODO TODO
.setOutputDir("/Users/xdclass/Desktop/demo/src/main/java")
// 文件覆盖
.setFileOverride(true)
// 主键策略
.setIdType(IdType.AUTO)
.setDateType(DateType.ONLY_DATE)
// 设置生成的service接口的名字的首字母是否为I,默认Service是以I开头的
.setServiceName("%sService")
//实体类结尾名称
.setEntityName("%sDO")
//生成基本的resultMap
.setBaseResultMap(true)
//不使用AR模式
.setActiveRecord(false)
//生成基本的SQL片段
.setBaseColumnList(true);
//2. 数据源配置
DataSourceConfig dsConfig = new DataSourceConfig();
// 设置数据库类型
dsConfig.setDbType(DbType.MYSQL)
.setDriverName("com.mysql.cj.jdbc.Driver")
//TODO TODO TODO TODO
.setUrl("jdbc:mysql://127.0.0.1:3306/xdclass_product?useSSL=false")
.setUsername("root")
.setPassword("xdclass.net");
//3. 策略配置globalConfiguration中
StrategyConfig stConfig = new StrategyConfig();
//全局大写命名
stConfig.setCapitalMode(true)
// 数据库表映射到实体的命名策略
.setNaming(NamingStrategy.underline_to_camel)
//使用lombok
.setEntityLombokModel(true)
//使用restcontroller注解
.setRestControllerStyle(true)
// 生成的表, 支持多表一起生成,以数组形式填写
//TODO TODO TODO TODO
.setInclude("banner","product");
//4. 包名策略配置
PackageConfig pkConfig = new PackageConfig();
pkConfig.setParent("net.xdclass")
.setMapper("mapper")
.setService("service")
.setController("controller")
.setEntity("model")
.setXml("mapper");
//5. 整合配置
AutoGenerator ag = new AutoGenerator();
ag.setGlobalConfig(config)
.setDataSource(dsConfig)
.setStrategy(stConfig)
.setPackageInfo(pkConfig);
//6. 执行操作
ag.execute();
System.out.println("======= 小滴课堂 Done 相关代码生成完毕 ========");
}
}
- 导入生成好的代码
- model (为啥不放common项目,如果是确定每个服务都用到的依赖或者类才放到common项目)
- mapper 类接口拷贝
- resource/mapper文件夹 xml脚本拷贝
- controller
- service 不拷贝
第4集 商品微服务项目基本配置整合+轮播图列表接口开发
简介:项目相关配置整合和轮播图接口开发
-
SpringBoot项目启动类和配置文件配置
-
轮播接口开发
@Service
public class BannerServiceImpl implements BannerService {
@Autowired
private BannerMapper bannerMapper;
@Override
public List<BannerVO> list() {
List<BannerDO> list = bannerMapper.selectList(new QueryWrapper<BannerDO>().
orderByAsc("weight"));
List<BannerVO> bannerVOS = list.stream().map(obj -> {
BannerVO vo = new BannerVO();
BeanUtils.copyProperties(obj, vo);
return vo;
}
).collect(Collectors.toList());
return bannerVOS;
}
}
第5集 商品微服务-商品列表分页接口开发
简介:商品首页分页列表接口开发
-
分页列表接口controller开发
-
分页列表接口service开发
第6集 商品微服务-商品详情接口开发和拦截器配置
简介:商品首页分页列表接口开发和拦截器配置
-
商品详情接口controller开发
-
商品详情接口service开发
-
拦截器配置
第十九章 1024电商平台-购物车数据结构设计+编码落地
第1集 电商平台-常见购物车功能实现介绍
简介:电商平台常见购物车 功能实现介绍
-
购物车里面的购物项常见字段
- user_id、product_id、buy_num
-
购物车常见实现方式
-
实现方式一:存储到数据库
- 性能存在瓶颈
-
实现方式二:前端本地存储-localstorage-sessionstorage
- localstorage在浏览器中存储 key/value 对,没有过期时间。
- sessionstorage在浏览器中存储 key/value 对,在关闭会话窗口后将会删除这些数据。
-
实现方式三:后端存储到缓存如redis
- 可以开启AOF持久化防止重启丢失(推荐)
-
实现方式四:后端存储到缓存如redis-并同步更新到数据库
- 大家可能会想到缓存和数据库的一致性,加了用户唯一标识后,没高并发操作同一数据的场景
-
第2集 1024电商平台-购物车和购物项VO类开发
简介:购物车和购物项VO类开发
-
一个购物车里面,存在多个购物项
-
CartVO 购物车
- 商品总件数
- 整个购物车总价
- 实际支付总价
-
CartItemVO 购物项
- 商品id
- 购买数量
- 商品标题(冗余)
- 商品图片(冗余)
- 商品单价
- 总价格 ( 单价*数量 )
-
CartVO 和 CartItemVO 编写
第3集 1024电商平台-购物车redis数据结构讲解
简介:购物车redis数据结构讲解
-
购物车数据结构介绍
- 一个购物车里面,存在多个购物项
- 所以 购物车结构是一个双层Map:
- Map<String,Map<String,String>>
- 第一层Map,Key是用户id
- 第二层Map,Key是购物车中商品id,值是购物车数据
-
对应redis里面的存储
- redis里面有多种数据结构,应该使用哪种?
- 答案是 hash结构
第4集 商品微服务-添加购物车接口开发和方法抽取
简介:添加购物车接口开发和方法抽取
- 拦截器配置(添加商品到购物车需要登录)
- 添加购物车接口
@Override
public void addToCart(CartItemRequest cartItemRequest) {
Long productId = cartItemRequest.getProductId();
int buyNum = cartItemRequest.getBuyNum();
//获取购物车
BoundHashOperations<String, Object, Object> myCart = getMyCartOps();
Object cacheObj = myCart.get(productId);
String result = "";
if (cacheObj != null) {
result = (String) cacheObj;
}
if (StringUtils.isBlank(result)) {
//不存在则新建一个购物项
CartItemVO cartItem = new CartItemVO();
ProductVO productVO = productService.findDetailById(productId);
cartItem.setAmount(productVO.getAmount());
cartItem.setBuyNum(buyNum);
cartItem.setProductId(productId);
cartItem.setProductImg(productVO.getCoverImg());
cartItem.setProductTitle(productVO.getTitle());
myCart.put(productId, JSON.toJSONString(cartItem));
} else {
//存在则新增数量
CartItemVO cartItem = JSON.parseObject(result, CartItemVO.class);
cartItem.setBuyNum(cartItem.getBuyNum() + buyNum);
myCart.put(productId, JSON.toJSONString(cartItem));
}
}
- 购物车方法抽取
/**
* 抽取我的购物车通用方法
*
* @return
*/
private BoundHashOperations<String, Object, Object> getMyCartOps() {
String cartKey = getCartKey();
return redisTemplate.boundHashOps(cartKey);
}
/**
* 获取购物车的key
*
* @return
*/
private String getCartKey() {
LoginUser loginUser = LoginInterceptor.threadLocal.get();
String cartKey = String.format(CacheKey.CART_KEY, loginUser.getId());
return cartKey;
}
- 注意:字段修改,统一使用 amount, 不使用price
第5集 购物车功能开发-redis乱码问题和清空购物车接口开发
简介:redis乱码问题和清空购物车接口开发
- redis存储乱码问题
- 默认使用JdkSerializationRedisSerializer进行序列化
- 修改key-value序列化方式,hash结构不修改
- 清空购物车接口开发
第6集 购物车功能开发-查看我的购物车接口
简介:查看我的购物车接口开发
-
查看我的购物车接口开发
-
抽取方法-需要支持查询商品最新价格
第7集 购物车功能开发-删除和修改购物项接口
简介:购物车-删除购物项和修改购物车数量接口开发
- 删除购物项接口开发
- 修改购物车商品数量接口开发
第二十章 1024电商平台-订单微服务开发
第1集 1024电商平台-订单微服务功能需求介绍
简介:订单微服务功能需求介绍
- 核心接口-下单
- 订单微服务涉及的功能知识点
- 创建订单和防重提交
- 多个微服务之间通讯-分布式事务选择
- 商品库存锁定和回收
- 优惠券使用锁定和回收
- 支付宝支付对接
- 优惠券+商品订单验价
- 多通道支付和设计整合
第2集 1024电商平台-订单微服务数据库表讲解
简介:订单微服务数据库表讲解
-
简单关系图(非ER图)
-
订单表
CREATE TABLE `product_order` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`out_trade_no` varchar(64) DEFAULT NULL COMMENT '订单唯一标识',
`state` varchar(11) DEFAULT NULL COMMENT 'NEW 未支付订单,PAY已经支付订单,CANCEL超时取消订单',
`create_time` datetime DEFAULT NULL COMMENT '订单生成时间',
`total_amount` decimal(16,2) DEFAULT NULL COMMENT '订单总金额',
`pay_amount` decimal(16,2) DEFAULT NULL COMMENT '订单实际支付价格',
`pay_type` varchar(64) DEFAULT NULL COMMENT '支付类型,微信-银行-支付宝',
`nickname` varchar(64) DEFAULT NULL COMMENT '昵称',
`head_img` varchar(524) DEFAULT NULL COMMENT '头像',
`user_id` int(11) DEFAULT NULL COMMENT '用户id',
`del` int(5) DEFAULT '0' COMMENT '0表示未删除,1表示已经删除',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`order_type` varchar(32) DEFAULT NULL COMMENT '订单类型 DAILY普通单,PROMOTION促销订单',
`receiver_address` varchar(1024) DEFAULT NULL COMMENT '收货地址 json存储',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2439 DEFAULT CHARSET=utf8mb4;
- 订单项表
CREATE TABLE `product_order_item` (
`id` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
`product_order_id` bigint(11) DEFAULT NULL COMMENT '订单号',
`out_trade_no` varchar(32) DEFAULT NULL,
`product_id` bigint(11) DEFAULT NULL COMMENT '产品id',
`product_name` varchar(128) DEFAULT NULL COMMENT '商品名称',
`product_img` varchar(524) DEFAULT NULL COMMENT '商品图片',
`buy_num` int(11) DEFAULT NULL COMMENT '购买数量',
`create_time` datetime DEFAULT NULL,
`total_amount` decimal(16,2) DEFAULT NULL COMMENT '购物项商品总价格',
`amount` decimal(16,0) DEFAULT NULL COMMENT '购物项商品单价',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=194 DEFAULT CHARSET=utf8mb4;
第3集 订单微服务+MybatisPlusGenerator代码自动生成工具
简介:Mybatis-plus-generator代码自动化生成微服务相关类
- 配置代码生成(标记TODO的记得修改)
public class MyBatisPlusGenerator {
public static void main(String[] args) {
//1. 全局配置
GlobalConfig config = new GlobalConfig();
// 作者
config.setAuthor("二当家小D")
// 生成路径,最好使用绝对路径,window路径是不一样的
//TODO TODO TODO TODO
.setOutputDir("/Users/xdclass/Desktop/demo/src/main/java")
// 文件覆盖
.setFileOverride(true)
// 主键策略
.setIdType(IdType.AUTO)
.setDateType(DateType.ONLY_DATE)
// 设置生成的service接口的名字的首字母是否为I,默认Service是以I开头的
.setServiceName("%sService")
//实体类结尾名称
.setEntityName("%sDO")
//生成基本的resultMap
.setBaseResultMap(true)
//不使用AR模式
.setActiveRecord(false)
//生成基本的SQL片段
.setBaseColumnList(true);
//2. 数据源配置
DataSourceConfig dsConfig = new DataSourceConfig();
// 设置数据库类型
dsConfig.setDbType(DbType.MYSQL)
.setDriverName("com.mysql.cj.jdbc.Driver")
//TODO TODO TODO TODO
.setUrl("jdbc:mysql://127.0.0.1:3306/xdclass_order?useSSL=false")
.setUsername("root")
.setPassword("xdclass.net");
//3. 策略配置globalConfiguration中
StrategyConfig stConfig = new StrategyConfig();
//全局大写命名
stConfig.setCapitalMode(true)
// 数据库表映射到实体的命名策略
.setNaming(NamingStrategy.underline_to_camel)
//使用lombok
.setEntityLombokModel(true)
//使用restcontroller注解
.setRestControllerStyle(true)
// 生成的表, 支持多表一起生成,以数组形式填写
//TODO TODO TODO TODO
.setInclude("product_order","product_order_item");
//4. 包名策略配置
PackageConfig pkConfig = new PackageConfig();
pkConfig.setParent("net.xdclass")
.setMapper("mapper")
.setService("service")
.setController("controller")
.setEntity("model")
.setXml("mapper");
//5. 整合配置
AutoGenerator ag = new AutoGenerator();
ag.setGlobalConfig(config)
.setDataSource(dsConfig)
.setStrategy(stConfig)
.setPackageInfo(pkConfig);
//6. 执行操作
ag.execute();
System.out.println("======= 小滴课堂 Done 相关代码生成完毕 ========");
}
}
- 导入生成好的代码
- model (为啥不放common项目,如果是确定每个服务都用到的依赖或者类才放到common项目)
- mapper 类接口拷贝
- resource/mapper文件夹 xml脚本拷贝
- controller
- service 不拷贝
第4集 订单微服务项目基本配置整合-拦截器-枚举类配置
简介:项目相关配置整合和拦截器配置
-
SpringBoot项目启动类和配置文件配置
-
拦截器配置
-
枚举类配置
/**
* 客户端枚举类
*/
public enum ClientType {
/**
* 原生应用
*/
APP,
/**
* 电脑端
*/
PC,
/**
* 网页
*/
H5
}
public enum ProductOrderPayTypeEnum {
/**
* 微信支付
*/
WECHAT,
/**
* 支付支付
*/
ALIPAY,
/**
* 银行卡支付
*/
BANK;
}
public enum ProductOrderStateEnum {
/**
* 未支付订单
*/
NEW,
/**
* 已经支付订单
*/
PAY,
/**
* 超时取消订单
*/
CANCEL;
}
public enum ProductOrderTypeEnum {
/**
* 普通订单
*/
DAILY,
/**
* 促销订单
*/
PROMOTION;
}
第5集 订单微服务-创建订单接口介绍-request类
简介:订单微服务-创建订单接口介绍和request类型编写
- 下单逻辑
- 二步:创建订单-》调用支付(京东)
- 一步:创建订单并调用支付(手淘)
- 创建订单
- controller编写
第6集 订单微服务-创建订单service伪代码编写
简介:订单微服务-创建订单伪代码编写
- service编写伪代码
- 防重提交
- 用户微服务-确认收货地址
- 商品微服务-获取最新购物项和价格
- 订单验价
- 优惠券微服务-获取优惠券
- 验证价格
- 锁定优惠券
- 锁定商品库存
- 创建订单对象
- 创建子订单对象
- 发送延迟消息-用于自动关单
- 创建支付信息-对接三方支付
第二十一章 微服务注册中心Nacos和feign远程调用开发
第1集 微服务注册中心Docker容器化部署Nacos
简介:注册中心Docker容器化部署Nacos
- docker拉取镜像
docker pull nacos/nacos-server
- 查看镜像
docker images
- 启动Nacos
docker run --env MODE=standalone --name xdclass-nacos -d -p 8848:8848 nacos/nacos-server
//查看日志
docker logs -f
- 访问Nacos(记得开放阿里云的网络安全组)
http://公网ip:8848/nacos
# 登录密码默认nacos/nacos
第2集 微服务Nacos注册中心配置Mysql持久化
简介:Nacos注册中心配置mysql持久化
-
nacos数据库脚本
- 导入数据库脚本(本章本集资料里面)
- 新增一个用户 nacos/nacos
INSERT INTO `users` (`username`, `password`, `enabled`) VALUES ('nacos', '$2a$10$XXXXXXXxxxxxxxx', 1);
-
mysql最好使用5.7版本(业界用的比较多,稳定)
-
启动命令
docker run -d \
-e MODE=standalone \
-e SPRING_DATASOURCE_PLATFORM=mysql \
-e MYSQL_SERVICE_HOST=192.168.0.104 \
-e MYSQL_SERVICE_PORT=3306 \
-e MYSQL_SERVICE_USER=root \
-e MYSQL_SERVICE_PASSWORD=xdclass.net \
-e MYSQL_SERVICE_DB_NAME=xdclass_nacos \
-p 8848:8848 \
--restart=always \
--name nacos \
nacos/nacos-server
- 访问(大家改成自己的ip)
- http://192.168.0.104:8848/nacos
第3集 多个微服务服务引入Nacos注册中心和Feign依赖
简介:微服务引入Nacos注册中心和Feign远程调用
- common项目添加依赖
<!--添加nacos客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--Feign远程调用-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 用户和优惠券微服务配置注册中心
cloud:
#注册中心地址
nacos:
discovery:
server-addr: 112.74.55.160:8848
- 用户和优惠券微服务启动类添加
@EnableFeignClients
@EnableDiscoveryClient
- 订单和商品微服务配置注册中心
cloud:
#注册中心地址
nacos:
discovery:
server-addr: 112.74.55.160:8848
- 订单和商品微服务启动类添加
@EnableFeignClients
@EnableDiscoveryClient
第4集 拉新业务-用户微服务和优惠券微服务之间的通讯
简介:拉新业务,用户微服务和优惠券微服务之间的通讯
- 用户微服务调用优惠券微服务开发
@FeignClient(name = "xdclass-coupon-service")
public interface CouponFeignService {
/**
* 新用户注册发放优惠券
* @param newUserCouponRequest
* @return
*/
@PostMapping("/api/coupon/v1/new_user_coupon")
JsonData addNewUserCoupon(@RequestBody NewUserCouponRequest newUserCouponRequest);
}
第5集 大课训练营里面的小结和思考建议
简介:大课训练营的小结和思考建议
-
大课里面的小结
-
代码编写不少,有些小bug或者优化点,大家可以细心发现,群里讨论
-
比如分布式事务、分布式锁、业务逻辑等,多思考下
-
第二十二章 微服务核心知识之分布式事务的那些事情
第1集 关于分布式事务的由来你知识多少
简介:分布式事务介绍和产生原因
-
什么是分布式事务
- 事务
事务指的就是一个操作单元,在这个操作单元中的所有操作最终要保持一致的行为,要么所有操作都成功,要么所有的操作都被撤销 分两种: 一个是本地事务:本地事物其实可以认为是数据库提供的事务机 一个是分布式事务
- 分布式事务
指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。 简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用 分布式事务需要保证这些小操作要么全部成功,要么全部失败。 本质上来说,分布式事务就是为了保证不同数据库的数据一致性
-
产生的原因
- 业务发展,数据库的拆分-分库分表
- SOA和微服务架构的使用
- 多个微服务之间调用异常
- 网络异常、请求超时、数据库异常、程序宕机等
-
为啥说是核心知识呢,基本是每个微服务架构项目都离不开的难题
第2集 分布式事务下数据最终一致性-CAP的权衡结果 BASE理论
简介:分布式事务下数据最终一致性-BASE理论介绍
- 什么是Base理论
CAP 中的一致性和可用性进行一个权衡的结果,核心思想就是:我们无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性, 来自 ebay 的架构师提出
-
Basically Available(基本可用)
- 假设系统,出现了不可预知的故障,但还是能用, 可能会有性能或者功能上的影响,比如RT是10ms,变成50ms
-
Soft state(软状态)
- 允许系统中的数据存在中间状态,并认为该状态不影响系统的整体可用性,即允许系统在多个不同节点的数据副本存在数据延时
-
Eventually consistent(最终一致性)
- 系统能够保证在没有其他新的更新操作的情况下,数据最终一定能够达到一致的状态,因此所有客户端对系统的数据访问最终都能够获取到最新的值
- 关于数据一致性
- 强一致:操作后的能立马一致且可以访问
- 弱一致:容忍部分或者全部访问不到
- 最终一致:弱一致性经过多一段时间后,都一致且正常
第3集 分布式事务的常见解决方案概览
简介:讲解分布式事务常见解决方案概览
-
常见分布式事务解决方案
- 2PC 和 3PC
- 两阶段提交, 基于XA协议
- TCC
- Try、Confirm、Cancel
- 事务消息
- 最大努力通知型
- 2PC 和 3PC
-
分布式事务分类
- 刚性事务:遵循ACID
- 柔性事务:遵循BASE理论
-
分布式事务框架
- TX-LCN:支持2PC、TCC等多种模式
- https://github.com/codingapi/tx-lcn
- 更新慢(个人感觉处于停滞状态)
- Seata:支持 AT、TCC、SAGA 和 XA 多种模式
- https://github.com/seata/seata
- 背靠阿里,专门团队推广
- 阿里云商业化产品GTS
- https://www.aliyun.com/aliware/txc
- RocketMq:自带事务消息解决分布式事务
- https://github.com/apache/rocketmq
- TX-LCN:支持2PC、TCC等多种模式
第二十三章 架构师系列-玩转分布式事务N种解决方案和利弊选择
第1集 分布式事务的常见核心概念知识讲解
简介:讲解分布式事务常见核心概讲解
-
前置知识
- X/OpenDTP 事务模型
是X/Open 这个组织定义的一套分布式事务的标准,也就是定义了规范和 API 接口,由各个厂商进行具体的实现 DTP 是分布式事物处理(Distributed Transaction Processing)的简称
- XA协议
XA是由X/Open组织提出的分布式事务规范。 XA规范主要定义了(全局)事务管理器(TM)和(局 部)资源管理器(RM)之间的接口 主流的数据库产品都实现了XA接口,是一个双向的系统接口,在事务管理器以及多个资源管理器之间作为通信桥梁
- JTA
Java Transaction API,java根据XA规范提供的事务处理标准
- AP
application, 应用程序也就是业务层,微服务等
- RM
Resource Manager,资源管理器。一般是数据库,也可以是其他资源管理器,比如消息队列,文件系统
- TM
Transaction Manager ,事务管理器、事务协调者,负责接收来自用户程序(AP)发起的 XA 事务指令,并调度和协调参与事务的所有 RM(数据库),确保事务正确完成
-
事务模型
在分布式系统中,每一个机器节点能够明确知道自己在进行事务操作过程中的 结果是成功还是失败,但无法直接获取到其他分布式节点的操作结果
当一个事务操作跨越多个分布式节点的时候,为了保持事务处理的 ACID 特性,
需要引入一个“协调者”(TM)来统一调度所有分布式节点的执行逻辑,这些被调度的分布式节点被称为 AP。
TM 负责调度 AP 的行为,并最终决定这些 AP 是否要把事务真正进行提交到(RM)
第2集 XA实现分布式事务的原理-两阶段提交2PC流程解析
简介:讲解XA实现分布式事务的原理
-
XA协议规范-实现分布式事务的原理如下
-
一般习惯称为 两阶段提交协议(The two-phase commit protocol,2PC)
-
是XA用于在全局事务中协调多个资源的机制,MySql5.5以上开始支持
-
准备阶段:
事务管理器给每个参与者都发送Prepared消息,每个数据库参与者在本地执行事务,并写本地的Undo/Redo日志,此时事务没有提交。
- Undo日志是记录修改前的数据,用于数据库回滚
- Redo日志是记录修改后的数据,用于提交事务后写入数据
-
提交阶段:
- 如果事务管理器收到了参与者的执行失败或者超时消息时,直接给每个参与者发送回滚(Rollback)消息,否则发送提交(Commit)消息;
- 参与者根据事务管理器的指令执行【提交】或者【回滚】操作,并释放事务处理过程中使用的锁资源
- 注意:必须在最后阶段释放锁资源。
-
- 总结
- XA协议简单,数据库支持XA协议,开发使用成本比较低
- 对业务侵⼊很小,最⼤的优势就是对使⽤⽅透明
- 用户可以像使⽤本地事务⼀样使⽤基于 XA 协议的分布式事务,能够严格保障事务 ACID 特性
- 事务执⾏过程中需要将所需资源全部锁定,也就是俗称的刚性事务
- 刚性事务:遵循ACID
- 柔性事务:遵循BASE理论
- 性能不理想,占用锁资源比较多,高并发常见下无法满足
- 商业付费数据库支持好,mysql目前支持不是很完善
- 基于 XA 协议的 除了2PC,还有 3PC等
- 三段提交(3PC)是二阶段提交(2PC)的一种改进版本 ,为解决两阶段提交协议的阻塞问题
- 采用超时机制,解决TM故障后RM的阻塞问题,但与此同时却多了一次网络通信,性能上也不理想
- 2PC和3PC目前使用不是很多,只做简单了解即可
第3集 分布式事务的解决方案之一-柔性事务-TCC介绍
简介:讲解TCC柔性事务的解决方案
- 什么是TCC柔性事务
- 刚性事务:遵循ACID
- 柔性事务:遵循BASE理论
- TCC:
- 将事务提交分为
- Try:完成所有业务检查( 一致性 ) ,预留必须业务资源( 准隔离性 )
- Confirm :对业务系统做确认提交,默认 Confirm阶段不会出错的 即只要Try成功,Confirm一定成功
- Cancel : 业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放, 进行补偿性
- TCC 事务和 2PC 的类似,Try为第一阶段,Confirm - Cancel为第二阶段,它对事务的提交/回滚是通过执行一段 confirm/cancel 业务逻辑来实现,并且也并没有全局事务来把控整个事务逻辑
- 将事务提交分为
含义 | 操作方法 |
---|---|
预留业务资源/数据效验 | Try |
确认执行业务操作,提交数据,不做任何业务检查,try成功,confirm必定成功,需保证幂等 | Confirm |
取消执行业务操作,回滚数据,需保证幂等,也是常说的补偿性事务 | Cancel |
- 看下TCC的交互图(图片来源网上)
- 优点:
- 它把事务运行过程分成 Try、Confirm/Cancel 两个阶段
- 每个阶段由业务代码控制,这样事务的锁力度可以完全自由控制
- 不存在资源阻塞的问题,每个方法都直接进行事务的提交
- 缺点
- 在业务层编写代码实现的两阶段提交,原本一个方法,现在却需要三个方法来支持
- 对业务的侵入性很强,不能很好的复用
- 注意:使用TCC时要注意Try - Confirm - Cancel 3个操作的幂等控制,由于网络原因或者重试操作都有可能导致这几个操作的重复执行
第4集 分布式事务的解决方案之一事务消息
简介:讲解分布式事务的解决方案之一事务消息
-
事务消息
- 消息队列提供类似Open XA的分布式事务功能,通过消息队列事务消息能达到分布式事务的最终一致
-
半事务消息
- 暂不能投递的消息,发送方已经成功地将消息发送到了消息队列服务端,但是服务端未收到生产者对该消息的二次确认,此时该消息被标记成“暂不能投递”状态,处于该种状态下的消息即半事务消息。
-
消息回查
- 由于网络闪断、生产者应用重启等原因,导致某条事务消息的二次确认丢失,消息队列服务端通过扫描发现某条消息长期处于“半事务消息”时,需要主动向消息生产者询问该消息的最终状态(Commit或是Rollback),该询问过程即消息回查
-
交互图(来源rocketmq官方文档)
-
目前较为主流的MQ,比如ActiveMQ、RabbitMQ、Kafka、RocketMQ等,只有RocketMQ支持事务消息
- 如果其他队列需要事务消息,可以开发个消息服务,自行实现半消息和回查功能
-
好处
- 事务消息不仅可以实现应用之间的解耦,又能保证数据的最终一致性
- 同时将传统的大事务可以被拆分为小事务,能提升效率
- 不会因为某一个关联应用的不可用导致整体回滚,从而最大限度保证核心系统的可用性
-
缺点
- 不能实时保证数据一致性
- 极端情况下需要人工补偿,比如 假如生产者成功处理本地业务,消费者始终消费不成功
第5集 最终一致性的体现-第三方支付平台和微服务之间的通讯
简介:讲解第三方支付平台和微服务之间的交互
-
支付业务
- 支付宝支付
- 微信支付
- 其他支付
-
多个服务之间通信,怎么保证分布式事务呢?
-
利用最终一致性思想,也叫柔性事务解决方案
第二十四 阿里开源分布式事务框架-Seata 新版最佳实践
第1集 阿里巴巴开源分布式事务框架Seata介绍
简介:讲解分布式事务框架Seata介绍
-
分布式事务框架
- TX-LCN:支持2PC、TCC等多种模式
- https://github.com/codingapi/tx-lcn
- 更新慢(个人感觉处于停滞状态)
- Seata:支持 AT、TCC、SAGA 和 XA 多种模式
- https://github.com/seata/seata
- 背靠阿里,专门团队推广
- TX-LCN:支持2PC、TCC等多种模式
-
为啥选择Seata呢
- git地址
- https://github.com/seata/seata
- 背靠阿里,专门团队推广和技术团队
- 已经线上大规模使用且没出现重大漏洞
- 和主流微服务框架轻松整合
- 社区活跃度高、文档齐全、功能强大
- git地址
- 有专门的阿里云商业化产品GTS,可以无缝迁移
-
什么是Seata
- 一个开源的分布式事务框架, 由阿里中间件团队发起的开源项目Fescar,后更名为Seata
- 中文文档地址
- http://seata.io/zh-cn/
- 详细介绍
是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。 在 Seata 开源之前,Seata 对应的内部版本在阿里经济体内部一直扮演着分布式一致性中间件的角色,帮助经济体平稳的度过历年的双11,对各BU业务进行了有力的支撑。 经过多年沉淀与积累,商业化产品先后在阿里云、金融云进行售卖。 2019.1 为了打造更加完善的技术生态和普惠技术成果,Seata 正式宣布对外开源,未来 Seata 将以社区共建的形式帮助其技术更加可靠与完备
- 文档比较详细,如果大家把上一章分布式事务的核心知识:
- XA协议的2PC、TCC、事务消息掌握,那学起来会很轻松
第2集 分布式事务框架Seata核心组件和术语介绍
简介:讲解分布式事务框架Seata核心组件和术语
-
Seata主要由三个重要组件组成:
-
TC:Transaction Coordinator 事务协调器,管理全局的分支事务的状态,用于全局性事务的提交和回滚。
-
TM:Transaction Manager 事务管理器,用于开启、提交或者回滚【全局事务】。
-
RM:Resource Manager 资源管理器,用于分支事务上的资源管理,向TC注册分支事务,上报分支事务的状态,接受TC的命令来提交或者回滚分支事务
- 传统XA协议实现2PC方案的 RM 是在数据库层,RM本质上就是数据库自身;
- Seata的RM是以jar包的形式嵌入在应用程序里面
-
架构:TC 为单独部署的 Server 服务端,TM 和 RM 为嵌入到应用中的 Client 客户端
- 对比X/OpenDTP事务模型
-
其它术语
- XID
- TM 请求 TC 开启一个全局事务, TC 会生成一个 XID 作为该全局事务的编号XID, XID会在微服务的调用链路中传播,保证将多个微服务的子事务关联在一起
- XID
第3集 Seata框架里面分布式事务的生命周期过程描述
简介:讲解分布式事务框架Seata事务处理过程描述
- 一个典型的事务过程包括:
- A服务的TM 向 TC 申请开启(Begin)一个全局事务,全局事务创建成功并生成一个全局唯一的 XID。
- A服务的RM向TC注册分支事务
- A服务执行分支事务,对数据库做操作
- A服务开始远程调用B服务,并把XID 在微服务调用链路的上下文中传播。
- B服务的RM向TC注册分支事务,并将其纳入XID对应的全局事务的管辖
- B服务执行分支事务,向数据库做操作
- 全局事务调用链处理完毕,TM 根据有无异常向 TC 发起针对 XID 的全局提交(Commit)或回滚(Rollback)决议。
- TC 调度 XID 下管辖的全部分支事务完成提交(Commit)或回滚(Rollback)请求。
- 好看点的图
- Seata 实现分布式事务,关键角色UNDO_LOG(回滚日志记录表)
在每个应用需要分布式事务的业务库中创建这张表,这个表的核心作用是将业务数据在更新前后的数据镜像组织成回滚日志,保存在UNDO_LOG表中,以便业务异常能随时回滚
第4集 分布式事务框架Seata的AT模式流程介绍
简介:介绍Seata的AT模式流程
-
Seata
有四种模式: (简单了解即可,深入的话看分布式事务专题)-
AT
-
AT模式可以应对大多数的业务场景,并且基本可以做到无业务入侵、开发者无感知
-
用户只需关心自己的 业务SQL. AT 模式分为两个阶段,可以认为是2PC
- 一阶段:执行用户SQL
Seata 会拦截“业务 SQL”,找到“业务 SQL”要更新的业务数据,在业务数据被更新前,将其保存成“before image”,然后执行“业务 SQL”更新业务数据 在业务数据更新之后,再将其保存成“after image”,最后生成行锁 以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性
- 二阶段:
Seata
框架自动生成提交或者回滚
二阶段提交: 因为“业务 SQL”在一阶段已经提交至数据库, 所以 Seata 框架只需将阶段一保存的快照数据和行锁删掉,完成数据清理即可。 二阶段回滚: 还原业务数据, 回滚方式便是用“before image”还原业务数据; 但在还原前要首先要校验脏写,对比“数据库当前业务数据”和 “after image” 如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转人工处理
-
-
TCC
-
Sage
-
XA
-
第二十五章 SpringCloudAlibaba微服务整合分布式事务Seata实战
第1集 新版本-分布式事务框架Seata 服务端部署安装最佳实践
简介:讲解新版本Seata分布式事务的部署和安装
-
基于AT模式
- 创建undo_log表, 每个库都需要
- http://seata.io/zh-cn/docs/dev/mode/at-mode.html
-- 注意此处0.7.0+ 增加字段 context CREATE TABLE `undo_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `branch_id` bigint(20) NOT NULL, `xid` varchar(100) NOT NULL, `context` varchar(128) NOT NULL, `rollback_info` longblob NOT NULL, `log_status` int(11) NOT NULL, `log_created` datetime NOT NULL, `log_modified` datetime NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
-
下载部署Seata的TC服务端
- http://seata.io/zh-cn/blog/download.html
- 我们下载1.3就行,不要下载最新的(除非对新的很有把握不出问题)
-
Linux/Mac/Windows服务器安装
- 解压
- 修改jvm内存(默认是2g,防止内存不够)
- ./seata-server.sh 启动,默认是8091端口(记得防火墙开放端口,也可以nohup守护进程启动)
-
TC需要存储全局事务和分支事务的记录,支持三种存储模式
- file模式 (默认):性能高, 适合单机模式,在内存中读写,并持久化到本地文件中
- 在 bin/sessionStore/root.data文件
- db模式 :性能较差,适合tc集群模式
- redis模式:性能教高,适合tc集群模式
- file模式 (默认):性能高, 适合单机模式,在内存中读写,并持久化到本地文件中
-
问题:
- seata 在 JDK11下运行报错
- 解决: 下载下来的seata 默认没有存放日志文件的目录, 手动创建seata/logs/seata_gc.log 目录和文件
第2集 SpringCloudAlibaba微服务整合Seata分布式事务框架《上》
简介:SpringCloudAlibaba整合分布式事务seata框架
- common项目添加依赖
- 出现的问题:no available service ‘null’ found, please make sure registry config correct
- 安装的服务端版本必须要和你客户端的版本保持一样
<!--alibaba微服务整合分布式事务,上面的方式不行 mvn 包冲突-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
<!--alibaba微服务整合分布式事务,这个方式才行-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
- 各个微服务配置文件修改
#seata配置
seata:
tx-service-group: ${spring.application.name}-group
service:
grouplist:
xdclass: 127.0.0.1:8091
vgroup-mapping:
xdclass-user-service-group: xdclass
第3集 SpringCloudAlibaba微服务整合Seata分布式事务框架《下》
简介:SpringCloudAlibaba整合分布式事务seata框架《下》
- 注释掉全局异常
- 开发测试接口
- 测试分布式事务
- TM入口service的方法增加 @GlobalTransactional 注解
第4集 Seata分布式事务undo_log表-分析AT模式执行机制回顾
简介:分布式事务AT模式执行机制回顾梳理
-
AT
-
AT模式可以应对大多数的业务场景,并且基本可以做到无业务入侵、开发者无感知
-
用户只需关心自己的 业务SQL. AT 模式分为两个阶段,可以认为是2PC
- 一阶段:执行用户SQL
Seata 会拦截“业务 SQL”,找到“业务 SQL”要更新的业务数据,在业务数据被更新前,将其保存成“before image”,然后执行“业务 SQL”更新业务数据 在业务数据更新之后,再将其保存成“after image”,最后生成行锁 以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性
- 二阶段:
Seata
框架自动生成提交或者回滚
二阶段提交: 因为“业务 SQL”在一阶段已经提交至数据库, 所以 Seata 框架只需将阶段一保存的快照数据和行锁删掉,完成数据清理即可。 二阶段回滚: 还原业务数据, 回滚方式便是用“before image”还原业务数据; 但在还原前要首先要校验脏写,对比“数据库当前业务数据”和 “after image” 如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转人工处理
-
-
undo_log表的rollback_info字段
{"@class":"io.seata.rm.datasource.undo.BranchUndoLog","xid":"192.168.0.115:8091:107926818928988160","branchId":107926819939815424,"sqlUndoLogs":["java.util.ArrayList",[{"@class":"io.seata.rm.datasource.undo.SQLUndoLog","sqlType":"INSERT","tableName":"user","beforeImage":{"@class":"io.seata.rm.datasource.sql.struct.TableRecords$EmptyTableRecords","tableName":"user","rows":["java.util.ArrayList",[]]},"afterImage":{"@class":"io.seata.rm.datasource.sql.struct.TableRecords","tableName":"user","rows":["java.util.ArrayList",[{"@class":"io.seata.rm.datasource.sql.struct.Row","fields":["java.util.ArrayList",[{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"id","keyType":"PRIMARY_KEY","type":-5,"value":["java.math.BigInteger",26]},{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"name","keyType":"NULL","type":12,"value":null},{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"pwd","keyType":"NULL","type":12,"value":"34234234"},{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"head_img","keyType":"NULL","type":12,"value":null},{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"slogan","keyType":"NULL","type":12,"value":null},{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"sex","keyType":"NULL","type":-6,"value":1},{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"points","keyType":"NULL","type":4,"value":0},{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"create_time","keyType":"NULL","type":93,"value":["java.sql.Timestamp",[1614166959000,0]]},{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"mail","keyType":"NULL","type":12,"value":"2343223@qq.com"},{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"secret","keyType":"NULL","type":12,"value":"111"}]]}]]}}]]}
第5集 全局异常下微服务整合Seata分布式事务失效解决方案
简介:自定义全局异常下分布式事务失效解决方案
- 问题: 微服务场景下,配置了统一全局异常处理,导致seata在AT模式下无法正常回滚问题
- 如果使用Feign 配置了容错类(fallback)或者容错工厂(fallbackFactory),也是一样的问题
- 原因:服务A调用服务B, 服务B发生异常,由于全局异常处理的存在(@ControllerAdvice), seata 无法拦截到B服务的异常,从而导致分布式事务未生效
- 解决思路
配置了全局异常处理,所以rpc一定会有返回值, 所以在每个全局事务方法最后, 需要判断rpc是否发生异常
发生异常则抛出 RuntimeException或者子类
- 方式一:RPC接口不配置全局异常
- 方式二:利用AOP切面解决
- 方式三:程序代码各自判断RPC响应码是否正常,再抛出异常
第6集 架构必备-高并发下分布式事务下的总结和思考
简介:高并发业务下分布式事务的选择和思考
- 分布式事务解决方案很多,XA的2PC、TCC、MQ事务消息等
- 框架也有Seata, 同时支持多种方式模式
- 重点
不管选哪一种方案,在项目中应用都要谨慎再思考,
除特定的数据强一致性场景外,能不用尽量就不要用
因为无论它们性能如何优越,一旦项目链路加入分布式事务
整体效率会几倍的下降,在高并发情况下弊端尤为明显
- 任何多链路的操作,换个方案或者换个思路,可以避免使用分布式事务(接下去大课就是)
- 下单商品库存锁定
- 下单优惠券记录锁定
- 总之
- 分布式事务和分布式锁一样,能不用就不用
- 实在要用,使用优先是 柔性事务,实在无法满足再考虑 刚性事务
- 分布式锁也是,尽量降低锁的粒度
第二十六章 高性能的数据一致性方案-扣减库存和优惠券记录锁定设计
第1集 分布式事务的另一种解决方案-下单商品库存扣减
简介:分布式事务事务的另一种解决方案,下单商品库存的扣减
-
下单伪代码
* * 防重提交 * * 用户微服务-确认收货地址 * * 商品微服务-获取最新购物项和价格 * * 订单验价 * * 优惠券微服务-获取优惠券 * * 验证价格 * * 锁定优惠券 * * 锁定商品库存 * * 创建订单对象 * * 创建子订单对象 * * 发送延迟消息-用于自动关单 * * 创建支付信息-对接三方支付
-
下单里面的分布式事务
-
核心逻辑
- 用下单:锁库存和优惠券记录
- 下单异常或者超时未支付:释放库存和优惠券记录
-
初体验
第2集 【重点】深入架构流程图-下单商品库存扣减多场景异常处理
简介:深入扣减下单商品库存的扣减多场景异常处理
-
核心逻辑
- 用下单:锁库存和优惠券记录
- 下单异常或者超时未支付:释放库存和优惠券记录
-
锁定记录状态
lock_state
‘锁定状态锁定LOCK-完成 FINISH-取消CANCEL’,
第3集 抽取架构业务模型-优惠券记录扣减-释放流程解析
简介:触类旁通-抽取架构业务模型-转移到优惠券记录扣减回收
-
核心逻辑
- 用下单:锁库存和优惠券记录
- 下单异常或者超时未支付:释放库存和优惠券记录
-
锁定记录状态
lock_state
‘锁定状态锁定LOCK-完成 FINISH-取消CANCEL’,
第4集 商品库存锁定和优惠券记录锁定任务表设计
简介:商品库存锁定和优惠券记录锁定任务表设计
- 下单锁库存任务表设计
CREATE TABLE `product_task` (
`id` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
`product_id` bigint(11) DEFAULT NULL COMMENT '商品id',
`buy_num` int(11) DEFAULT NULL COMMENT '购买数量',
`product_name` varchar(128) DEFAULT NULL COMMENT '商品标题',
`lock_state` varchar(32) DEFAULT NULL COMMENT '锁定状态锁定LOCK 完成FINISH-取消CANCEL',
`out_trade_no` varchar(32) DEFAULT NULL,
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
- 优惠券记录锁定任务表设计
CREATE TABLE `coupon_task` (
`id` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
`coupon_record_id` bigint(11) DEFAULT NULL COMMENT '优惠券记录id',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`out_trade_no` varchar(64) DEFAULT NULL COMMENT '订单号',
`lock_state` varchar(32) DEFAULT NULL COMMENT '锁定状态 锁定LOCK-完成FINISH 取消CANCEL',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
第5集 商品库存锁定和优惠券记录锁定任务表代码生成
简介:商品库存锁定和优惠券记录锁定任务表代码生成
- 代码生成
第二十七章 新版消息队列RabbitMQ回顾和容器化安装部署
第1集 基于Linux服务器安装RabbitMQ容器化部署
简介:Docker安装RabbitMQ消息队列
- 阿里云安装RabbitMQ
- 阿里云折扣地址 https://www.aliyun.com/minisite/goods?userCode=r5saexap&share_source=copy_link
- 最少 2核4g或者推荐 2核8g(用家人账号购买,接近1折,初次买1年或者3年)
- 登录个人的Linux服务器
- ssh root@8.129.113.233
- Docker安装RabbitMQ
- 地址:https://hub.docker.com/_/rabbitmq/
#拉取镜像
docker pull rabbitmq:3.8.12-management-alpine
docker run -d --hostname rabbit_host1 --name xd_rabbit -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=password -p 15672:15672 -p 5672:5672 rabbitmq:3.8.12-management-alpine
#介绍
-d 以守护进程方式在后台运行
-p 15672:15672 management 界面管理访问端口
-p 5672:5672 amqp 访问端口
--name:指定容器名
--hostname:设定容器的主机名,它会被写到容器内的 /etc/hostname 和 /etc/hosts,作为容器主机IP的别名,并且将显示在容器的bash中
-e 参数
RABBITMQ_DEFAULT_USER 用户名
RABBITMQ_DEFAULT_PASS 密码
- 主要端口介绍
4369 erlang 发现口
5672 client 端通信口
15672 管理界面 ui 端口
25672 server 间内部通信口
-
访问管理界面
- ip:15672
-
注意事项!!!!
- Linux服务器检查防火墙是否关闭
- 云服务器检查网络安全组是否开放端口
CentOS 7 以上默认使用的是firewall作为防火墙 查看防火墙状态 firewall-cmd --state 停止firewall systemctl stop firewalld.service 禁止firewall开机启动 systemctl disable firewalld.service
第2集 延迟队列核心知识之RabbitMQ死信队列 + TTL回顾
简介:讲解RabbitMQ的的死信队列+ TTL
-
什么是TTL
- time to live 消息存活时间
- 如果消息在存活时间内未被消费,则会别清除
- RabbitMQ支持两种ttl设置
- 单独消息进行配置ttl
- 整个队列进行配置ttl(居多)
-
什么是rabbitmq的死信队列
- 没有被及时消费的消息存放的队列
-
什么是rabbitmq的死信交换机
- Dead Letter Exchange(死信交换机,缩写:DLX)当消息成为死信后,会被重新发送到另一个交换机,这个交换机就是DLX死信交换机。
-
消息有哪几种情况成为死信
- 消费者拒收消息**(basic.reject/ basic.nack)**,并且没有重新入队 requeue=false
- 消息在队列中未被消费,且超过队列或者消息本身的过期时间TTL(time-to-live)
- 队列的消息长度达到极限
- 结果:消息成为死信后,如果该队列绑定了死信交换机,则消息会被死信交换机重新路由到死信队列
第3集 RabbitMQ 延迟队列介绍和应用场景回顾
简介:讲解RabbitMQ的延迟队列和应用场景
-
什么是延迟队列
- 一种带有延迟功能的消息队列,Producer 将消息发送到消息队列 服务端,但并不期望这条消息立马投递,而是推迟到在当前时间点之后的某一个时间投递到 Consumer 进行消费,该消息即定时消息
-
使用场景
- 通过消息触发一些定时任务,比如在某一固定时间点向用户发送提醒消息
- 用户登录之后5分钟给用户做分类推送、用户多少天未登录给用户做召回推送;
- 消息生产和消费有时间窗口要求:比如在天猫电商交易中超时未支付关闭订单的场景,在订单创建时会发送一条 延时消息。这条消息将会在 30 分钟以后投递给消费者,消费者收到此消息后需要判断对应的订单是否已完成支付。 如支付未完成,则关闭订单。如已完成支付则忽略
-
Cloud微服务大课训练营里面的应用
- 优惠券回收
- 商品库存回收
-
业界的一些实现方式
- 定时任务高精度轮训
- 采用RocketMQ自带延迟消息功能
- RabbitMQ本身是不支持延迟队列的,怎么办?
- 结合死信队列的特性,就可以做到延迟消息
- 操作
- 消息生产
- 投递到普通的topic交换机
- 消息过期,进入死信交换机
- 消息消费
- 消费者监听死信交换机的队列
- 消息生产
第4集 SpringCloudAlibaba微服务整合RabbitMQ依赖和配置
简介:项目整合RabbitMQ依赖和配置
- 什么是Spring-AMQP
- 官网:https://spring.io/projects/spring-amqp
- Spring 框架的AMQP消息解决方案,提供模板化的发送和接收消息的抽象层,提供基于消息驱动的 POJO的消息监听等
- 提供不依赖于任何特定的AMQP代理实现或客户端库通用的抽象,最终用户代码将很容易实现更易替换、添加和删除AMQP,因为它可以只针对抽象层来开发
- 总之就是提高我们的框架整合消息队列的效率,SpringBoot为更方便开发RabbitMQ推出了starter,
- 我们使用 spring-boot-starter-amqp 进行开发
- common项目添加依赖
<!--引入AMQP-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
- 各个微服务添加配置
#消息队列
rabbitmq:
host: 8.129.113.233
port: 5672
virtual-host: /
password: password
username: admin
#开启手动确认消息
listener:
simple:
acknowledge-mode: manual
- 其他自定义配置后续添加
第二十八章 下单-优惠券记录锁定和释放功能设计开发
第1集 商品下单锁定优惠券记录模块开发
简介:商品下单锁定优惠券记录模块开发
- 协议设计
/**
* 记录id
*/
private List<Long> lockCouponRecordIds;
/**
* 订单号
*/
private String orderOutTradeNo;
- controlle层接口开发
- service层开发
第2集 优惠券微服务RabbitMQ延迟消息交换机和队列配置
简介:优惠券微服务RabbitMQ延迟消息交换机和队列配置
- 配置文件
#自定义消息队列配置,发送锁定库存消息-》延迟exchange-》lock.queue-》死信exchange-》release.queue
mqconfig:
#延迟队列,不能被监听消费
coupon_release_delay_queue: coupon.release.delay.queue
#延迟队列的消息过期后转发的队列
coupon_release_queue: coupon.release.queue
#交换机
coupon_event_exchange: coupon.event.exchange
#进入延迟队列的路由key
coupon_release_delay_routing_key: coupon.release.delay.routing.key
#消息过期,进入释放死信队列的key
coupon_release_routing_key: coupon.release.routing.key
#消息过期时间,毫秒,测试改为15秒
ttl: 15000
- 配置类
/**
* 交换机
*/
@Value("${mqconfig.coupon_event_exchange}")
private String eventExchange;
/**
* 第一个队列延迟队列,
*/
@Value("${mqconfig.coupon_release_delay_queue}")
private String couponReleaseDelayQueue;
/**
* 第一个队列的路由key
* 进入队列的路由key
*/
@Value("${mqconfig.coupon_release_delay_routing_key}")
private String couponReleaseDelayRoutingKey;
/**
* 第二个队列,被监听恢复库存的队列
*/
@Value("${mqconfig.coupon_release_queue}")
private String couponReleaseQueue;
/**
* 第二个队列的路由key
*
* 即进入死信队列的路由key
*/
@Value("${mqconfig.coupon_release_routing_key}")
private String couponReleaseRoutingKey;
/**
* 过期时间
*/
@Value("${mqconfig.ttl}")
private Integer ttl;
第3集 延迟消息功能测试开发和注意事项
简介:延迟消息测试和注意事项
- 测试代码编写测试
@RunWith(SpringRunner.class)
@SpringBootTest(classes = CouponApplication.class)
@Slf4j
public class DemoApplicationTests {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void send(){
rabbitTemplate.convertAndSend("coupon.event.exchange","coupon.release.delay.routing.key","5qeqweqw");
}
}
- 注意
- 有些版本会自动创建队列和交换机@bean,有些是要初次请求才会(如果有同学知道原因可以分享下)
- 如果MQ已经存在对应的队列,则不会重新创建
- 修改配置后,需要删除队列重新建立生效
- 如果队列和交换机已经存在,重新启动项目会有错误警告,可以忽略
第4集 优惠券锁定-发送延迟消息功能开发
简介:优惠券微服务发送延迟消息开发
- 消息协议介绍
@Data
public class CouponRecordMessage {
/**
* 消息队列id
*/
private Long messageId;
/**
* 订单号
*/
private String outTradeNo;
/**
* 库存锁定工作单id
*/
private Long taskId;
}
- 发送功能开发
for(CouponTaskDO couponTaskDO : couponTaskDOList){
CouponRecordMessage couponRecordMessage = new CouponRecordMessage();
couponRecordMessage.setOutTradeNo(orderOutTradeNo);
couponRecordMessage.setTaskId(couponTaskDO.getId());
rabbitTemplate.convertAndSend(rabbitMQConfig.getEventExchange(),rabbitMQConfig.getCouponReleaseDelayRoutingKey(),couponRecordMessage);
log.info("优惠券锁定消息发送成功:{}",couponRecordMessage.toString());
}
第5集 优惠券微服务延迟消息消费-释放优惠券功能开发《上》
简介:优惠券回收-延迟消息消费回收功能开发《上》
-
优惠券消费MQ监听器开发
-
流程梳理
优惠券解锁记录场景
1、超时未支付,比如30分钟则订单失效关闭
2、下单成功,创建订单业务失败,订单回滚
库存解锁防止继续支付:
1、30分支付超时则无法支付订单
2、订单31分延迟消息(比订单超时大几分钟)
->查询订单状态-向第三方支付查询订单状态,只有未支付状态,且本地订单状态是NEW,才修改本地订单状态为取消CANCEL,其他业务才可以解锁对应的库存库存
3、商品、优惠券库存32分延迟消息(比订单超时大几分钟)
->查询订单状态-订单不存在,解锁库存
->查询订单状态
1)订单状态为取消CANCEL的情况,才可以解锁库存,确认消息接收;
2)订单状态为未支付NEW的情况,则不解锁库存,不修改订单状态,重新投递消息或者拒收;
(避免网络延迟到 导致订单关单消息,比库存解锁还慢,没更新订单状态)
3)如果是订单已经支付则修改库存task工作单状态,确认消息接收;
注意:延迟队列一定要开启手动的ack机制,防止解锁失败,消息丢失,也要防止多次解锁
解锁库存的时候:修改状态和修改对应库存task工作单状态应该是同个事务,防止其中一个失败
第6集 订单微服务-查询订单支付状态接口开发
简介:订单微服务-查询订单支付状态接口开发
- 查询订单支付状态接口开发(MQ里面没token,不需要登录)
第7集 数据一致性多场景用例测试-延迟消息消费释放优惠券功能开发《下》
简介:优惠券回收-延迟消息消费回收功能开发《下》
-
流程测试
- 写单元测试,发送消息
- 订单支付完成用例测试
- 订单超时未支付用例测试
- 订单异常不存在用例测试
- 消息延迟,监听消息处理
- 记录更新情况
- bug修复:useState变量名称
- 写单元测试,发送消息
-
数据准备
INSERT INTO `product_order` (`id`, `out_trade_no`, `state`, `create_time`, `total_amount`, `pay_amount`, `pay_type`, `nickname`, `head_img`, `user_id`, `del`, `update_time`, `order_type`, `receiver_address`)
VALUES
(1, '123456abc', 'PAY', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL);
INSERT INTO `coupon_task` (`id`, `coupon_record_id`, `create_time`, `out_trade_no`, `lock_state`)
VALUES
(1, 1, '2021-02-27 16:05:11', '123456abc', 'LOCK');
- 功能流程完善
- 业务逻辑优化
- 如何保证消息不会重复消费-幂等处理
- 多个消息并发情况下是否需要加锁
- 是否还有其他问题,大家可以思考下,然后给出解决方案
第二十九章 高并发下商品库存扣减和释放解决方案设计和编码
第1集 商品下单-商品库存锁定模块设计和开发
简介:商品下单-锁定商品库存模块开发
- 协议设计
@Data
public class LockProductRequest {
@JsonProperty("order_out_trade_no")
private String orderOutTradeNo;
@JsonProperty("order_item_list")
private List<OrderItemRequest> orderItemList;
}
@Data
public class OrderItemRequest{
@JsonProperty("product_id")
private long productId;
@JsonProperty("buy_num")
private int buyNum;
}
- controlle层接口开发
- service层开发
- 登录拦截器配置
第2集 商品微服务RabbitMQ延迟消息交换机和队列配置
简介:商品微服务RabbitMQ延迟消息交换机和队列配置
- 配置文件
#自定义消息队列配置,发送锁定库存消息-》延迟exchange-》lock.queue-》死信exchange-》release.queue
mqconfig:
#延迟队列,不能被监听消费
stock_release_delay_queue: stock.release.delay.queue
#延迟队列的消息过期后转发的队列
stock_release_queue: stock.release.queue
#交换机
stock_event_exchange: stock.event.exchange
#进入延迟队列的路由key
stock_release_delay_routing_key: stock.release.delay.routing.key
#消息过期,进入释放队列的key
stock_release_routing_key: stock.release.routing.key
#消息过期时间,毫秒,测试改为15秒
ttl: 15000
- 配置类
/**
* 交换机
*/
@Value("${mqconfig.stock_event_exchange}")
private String eventExchange;
/**
* 第一个队列延迟队列,
*/
@Value("${mqconfig.stock_release_delay_queue}")
private String stockReleaseDelayQueue;
/**
* 第一个队列的路由key
* 进入队列的路由key
*/
@Value("${mqconfig.stock_release_delay_routing_key}")
private String stockReleaseDelayRoutingKey;
/**
* 第二个队列,被监听恢复库存的队列
*/
@Value("${mqconfig.stock_release_queue}")
private String stockReleaseQueue;
/**
* 第二个队列的路由key
*
* 即进入死信队列的路由key
*/
@Value("${mqconfig.stock_release_routing_key}")
private String stockReleaseRoutingKey;
/**
* 过期时间
*/
@Value("${mqconfig.ttl}")
private Integer ttl;
第3集 商品锁定-延迟消息功能测试开发和注意事项
简介:延迟消息测试和注意事项
- 测试代码编写测试
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ProductApplication.class)
@Slf4j
public class DemoApplicationTests {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void send(){
rabbitTemplate.convertAndSend("stock.event.exchange","stock.release.delay.routing.key","23342342");
}
}
- 注意
- 只有微服务监听mq,才会创建对应的队列和交换机,所以编写测试方法或者写监听器就行
- IOC容器存在不行,RabbitMQ默认是懒加载模式
- 如果MQ已经存在对应的队列,则不会重新创建
- 修改配置后,需要删除队列重新建立生效
- 如果队列和交换机已经存在,重新启动项目会有错误警告,可以忽略
第4集 商品库存锁定-发送延迟消息功能开发
简介:商品微服务发送延迟消息开发
- 消息协议介绍
@Data
public class ProductMessage {
/**
* 消息队列id
*/
private Long messageId;
/**
* 订单号
*/
private String outTradeNo;
/**
* 库存锁定工作单id
*/
private Long taskId;
}
- 发送功能开发
//发送MQ延迟消息 ,解锁商品库存 taskId TODO
ProductMessage productMessage = new ProductMessage();
productMessage.setTaskId(productTaskDO.getId());
productMessage.setOutTradeNo(orderOutTradeNo);
rabbitTemplate.convertAndSend(rabbitConfig.getEventExchange(),rabbitConfig.getStockReleaseDelayRoutingKey(),productMessage);
log.info("商品库存锁定信息发送成功:{}",productMessage);
第5集 商品微服务延迟消息消费-释放商品库存功能开发《上》
简介:商品库存释放回收功能开发《上》
-
商品微服务消费MQ监听器开发
-
流程梳理
商品库存解锁记录场景
1、超时未支付,比如30分钟则订单失效关闭
2、下单成功,创建订单业务失败,订单回滚
库存解锁防止继续支付:
1、30分支付超时则无法支付订单
2、订单31分延迟消息(比订单超时大几分钟)
->查询订单状态-向第三方支付查询订单状态,只有未支付状态,且本地订单状态是NEW,才修改本地订单状态为取消CANCEL,其他业务才可以解锁对应的库存库存
3、商品、优惠券库存32分延迟消息(比订单超时大几分钟)
->查询订单状态-订单不存在,解锁库存
->查询订单状态
1)订单状态为取消CANCEL的情况,才可以解锁库存,确认消息接收;
2)订单状态为未支付NEW的情况,则不解锁库存,不修改订单状态,重新投递消息或者拒收;
(避免网络延迟到 导致订单关单消息,比库存解锁还慢,没更新订单状态)
3)如果是订单已经支付则修改库存task工作单状态,确认消息接收;
注意:延迟队列一定要开启手动的ack机制,防止解锁失败,消息丢失,也要防止多次解锁
解锁库存的时候:修改状态和修改对应库存task工作单状态应该是同个事务,防止其中一个失败
第6集 数据一致性多场景用例测试-延迟消息消费释放商品库存开发《下》
简介: 商品库存回收-延迟消息消费回收功能开发《下》
- 流程测试
- 写单元测试,发送消息
- 订单支付完成用例测试
- 订单超时未支付用例测试
- 订单异常不存在用例测试
- 消息延迟,监听消息处理
- 记录更新情况
- 数据准备
- 写单元测试,发送消息
INSERT INTO `product_order` (`id`, `out_trade_no`, `state`, `create_time`, `total_amount`, `pay_amount`, `pay_type`, `nickname`, `head_img`, `user_id`, `del`, `update_time`, `order_type`, `receiver_address`)
VALUES
(1, '123456abc', 'PAY', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL);
INSERT INTO `product_task` (`id`, `product_id`, `buy_num`, `product_name`, `lock_state`, `out_trade_no`, `create_time`)
VALUES
(1, 1, 2, '杯子-小滴课堂', 'LOCK', '123456abc', '2021-02-28 16:01:52');
- 功能流程完善
- 业务逻辑优化
- 如何保证消息不会重复消费-幂等处理
- 多个消息并发情况下是否需要加锁
- 是否还有其他问题,大家可以思考下,然后给出解决方案
第三十章 1024电商平台-订单微服务下单和验价设计和开发
第1集 订单微服务下单逻辑之确认收货地址模开发
简介: 订单微服务-确认收货地址模块开发
-
订单号生产
-
获取收货地址详情
- 方案一:前端一并传递收货地址详情过来
- 少了一次网络交互
- 前后端通信包更大
- 方案二:后端根据收货地址id调用服务查询地址详情
- 多了一次网络交换
- 前后端通信包更小
- 方案一:前端一并传递收货地址详情过来
-
功能测试
- token传递失败
第2集 Feign底层源码-token令牌丢失原因分析和解决方案
简介: token令牌丢失原因分析和解决方案
- token传递失败原因分析
- 解决方案-加入到common公共类上
@Bean("requestInterceptor")
public RequestInterceptor requestInterceptor(){
return template -> {
ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
if(attributes!=null){
HttpServletRequest request = attributes.getRequest();
if (null == request){
return;
}
log.info(request.getHeaderNames().toString());
template.header("token", request.getHeader("token"));
}else {
log.warn("requestInterceptor获取Header空指针异常");
}
};
}
第3集 订单微服务-下单获取商品最新价格开发
简介:订单微服务下单获取最新价格开发
- 获取最新商品价格开发
/**
* 用于订单服务,确认订单,获取全部购物项
*
* 会清空购物车对应的商品
*
* @return
*/
@ApiOperation("下单清空购物项")
@RequestMapping("confirm_order_cart_items")
public JsonData confirmOrderCartItem(@ApiParam("商品id列表") @RequestBody List<Long> productIdList ){
List<CartItemVO> cartItemVOList = cartService.confirmOrderCartItem(productIdList);
return JsonData.buildSuccess(cartItemVOList);
}
- 下单协议
{
"coupon_record_id":-1,
"product_ids":[1,2],
"pay_type":"ALIPAY",
"client_type":"H5",
"address_id":45,
"total_amount":10,
"real_pay_amount":10,
"token":"SbD5D4FLpUzemiuwSEytwGM9LLFGISDQ"
}
第4集 多种解决方案设计-购物车清空下单商品的设计思路你知道多少
简介:下单-购物车清空已经下单商品逻辑作业设计
-
需求作业
- 用户创建订单,购物车的商品什么时候进行清除
- 假如下单流程异常后,怎么回滚
-
清空购物车逻辑设计方案一
- 想加分布式事务Seata????
-
【推荐】清空购物车逻辑设计方案二
- 直接调用清空-MQ延迟消息(假如订单创建失败则购物车会丢失数据)
- 解决方案:类似库存解锁和优惠券释放一样的思路(购物车这边做)
- 延迟消息可以1分钟或者5分钟
- 直接查询订单是否存在即可(协议增加一个outTradeNo)
- 解决方案:类似库存解锁和优惠券释放一样的思路(购物车这边做)
- 直接调用清空-MQ延迟消息(假如订单创建失败则购物车会丢失数据)
-
这是一个作业,大家要自己尝试哈
第5集 订单微服务-商品验价和优惠券的抵扣功能开发《上》
简介:商品验证价格和优惠券抵扣功能开发
-
统计商品价格
-
远程调用获取优惠券
-
当前购物车是否满足优惠券使用条件
-
验证价格
第6集 订单微服务-下单锁定优惠券和商品库存逻辑开发《下》
简介:下单锁定优惠券和商品库存逻辑开发
- 锁定优惠券
- 锁定商品库存
第7集 订单微服务-创建商品订单和订单项模块开发
简介:下单创建商品订单和订单项模块开发
-
下单创建商品订单
-
下单创建商品订单项
-
bug
- 数据库 product_order 表的 user_id 改为bigint类型,java类型是long
第三十一章 订单超时未支付-定时关单功能设计和开发
第1集 订单超时未支付-定时关单功能流程设计
简介:订单超时未支付-定时关单功能设计
- 业务流程梳理
- 消息消费-关单流程设计
第2集 定时关单RabbitMQ延迟消息交换机和队列配置
简介:定时关单RabbitMQ延迟消息交换机和队列配置
- 配置文件
#自定义消息队列配置,发送锁定库存消息-》延迟exchange-》lock.queue-》死信exchange-》release.queue
mqconfig:
#延迟队列,不能被监听消费
order_close_delay_queue: order.close.delay.queue
#延迟队列的消息过期后转发的队列
order_close_queue: order.close.queue
#交换机
order_event_exchange: order.event.exchange
#进入延迟队列的路由key
order_close_delay_routing_key: order.close.delay.routing.key
#消息过期,进入释放队列的key,进入死信队列的key
order_close_routing_key: order.close.routing.key
#消息过期时间,毫秒,测试改为15秒
ttl: 15000
- 配置类
/**
* 交换机
*/
@Value("${mqconfig.order_event_exchange}")
private String eventExchange;
/**
* 延迟队列
*/
@Value("${mqconfig.order_close_delay_queue}")
private String orderCloseDelayQueue;
/**
* 关单队列
*/
@Value("${mqconfig.order_close_queue}")
private String orderCloseQueue;
/**
* 进入延迟队列的路由key
*/
@Value("${mqconfig.order_close_delay_routing_key}")
private String orderCloseDelayRoutingKey;
/**
* 进入死信队列的路由key
*/
@Value("${mqconfig.order_close_routing_key}")
private String orderCloseRoutingKey;
/**
* 过期时间
*/
@Value("${mqconfig.ttl}")
private Integer ttl;
第3集 订单微服务下单-延迟消息功能测试开发和注意事项
简介:延迟消息测试和注意事项
- 测试代码编写测试
@RunWith(SpringRunner.class)
@SpringBootTest(classes = OrderApplication.class)
@Slf4j
public class DemoApplicationTests {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void send(){
rabbitTemplate.convertAndSend("order.event.exchange","order.close.delay.routing.key","23342342");
}
}
- 注意
- IOC容器存在不行,RabbitMQ默认是懒加载模式
- 只有微服务监听mq,才会创建对应的队列和交换机,所以编写测试方法或者写监听器就行
- 如果MQ已经存在对应的队列,则不会重新创建
- 修改配置后,需要删除队列重新建立生效
- 如果队列和交换机已经存在,重新启动项目会有错误警告,可以忽略
第4集 订单超时未支付定时关单-发送消息功能开发
简介:用户下单发送定时关单消息开发
- 消息协议介绍
@Data
public class OrderMessage {
/**
* 消息队列id
*/
private Long messageId;
/**
* 订单号
*/
private String outTradeNo;
}
- 发送功能开发
//MQ发送延迟队列 TODO,用于自动关单
OrderMessage orderMessage = new OrderMessage();
orderMessage.setOutTradeNo(orderOutTradeNo);
rabbitTemplate.convertAndSend(rabbitConfig.getEventExchange(), rabbitConfig.getOrderCloseDelayRoutingKey(), orderMessage);
第5集 订单微服务-订单超时未支付自动关单-消息监听处理
简介:订单微服务-订单超时未支付关单消息监听处理
-
订单微服务消费MQ监听器开发
-
流程梳理
第三十二章 第三方支付平台对接-蚂蚁金服开放平台接入
第1集 常用的第三方支付和聚合支付平台介绍
简介:介绍常用的第三方支付和聚合支付
-
什么是第三方支付
-
第三方支付是指具备一定实力和信誉保障的独立机构,采用与各大银行签约的方式,通过与银行支付结算系统接口对接而促成交易双方进行交易的网络支付模式
-
通俗的例子:
- 支付宝,微信支付,百度钱包,PayPal(主要是欧美国家)
- 拉卡拉(中国最大线下便民金融服务提供商)
-
优点
- 支付平台降低了政府、企业、事业单位直连银行的成本,满足了企业专注发展在线业务的收付要求。
- 使用方便。对支付者而言,他所面对的是友好的界面,不必考虑背后复杂的技术操作过程
-
缺点
- 风险问题,在电子支付流程中,资金都会在第三方支付服务商处滞留即出现所谓的资金沉淀,如缺乏有效的流动性管理,则可能存在资金安全和支付的风险
- 电子支付经营资格的认知、保护和发展问题
-
-
什么是聚合支付也叫第四方支付
- 聚合支付是相对之前的第三方支付而言的,作为对第三方支付平台服务的拓展,第三方支付是介于银行和商户之间的,而聚合支付是介于第三方支付和商户之间
- 出现的场景
- 一堆第三方支付出现,并通过大量的钱补贴线上商家使用它们的支付,导致商户收银台堆满各种,POS机器,扫码设备,商户还需要去各家支付公司申请账号,结算等
- 聚合支付产品,其实聚合的是一种支付能力(支付宝支付、微信支付、百度钱包、ApplePay……),将这些收款能力聚合在一起,统一打包提供给电商网站或一些线下商家
- 解决的问题
- 聚合支付公司提供的二维码,支付多种方式支付,不再是一种,各个公司的竞争,就是支付渠道和方式的支持
第2集 蚂蚁金服开放平台介绍和支付宝支付应用申请
简介:蚂蚁金服开放平台介绍和支付宝申请
-
蚂蚁金服开放平台
- 地址:https://openhome.alipay.com/docCenter/docCenter.htm
- 介绍:https://opendocs.alipay.com/open/200/105304
- 支付宝扫码登录即可
-
网页移动应用开发指南
- 地址:https://opendocs.alipay.com/open/200
- 申请应用:https://openhome.alipay.com/platform/developerIndex.htm
- 核心是获取APPID
第3集 支付宝沙箱环境介绍和应用权限申请
简介:支付宝沙箱环境介绍和应用权限申请
- 支付宝沙箱环境介绍
蚂蚁沙箱环境 (Beta) 是协助开发者进行接口功能开发及主要功能联调的辅助环境
在开发者应用上线审核前,开发者可以根据自身需求,先在沙箱环境中了解、组合和调试各种开放接口,进行开发调试工作,从而帮助开发者在应用上线审核完成后,能更快速、更顺利的完成线上调试和验收
-
文档地址:https://opendocs.alipay.com/open/200/105311
-
沙箱地址:https://openhome.alipay.com/platform/appDaily.htm?tab=info
-
支付接入:有时不稳定,或者一直报错等等,一般就是支付宝沙箱环境问题
Beta 测试阶段每周日中午 12 点至每周一中午 12 点为维护时间,在此时间内沙箱环境部分功能可能不可用,敬请谅解。
-
APPID: xxxxxxxxxxxxxxxxxxxxxxxxxxxx
-
沙箱支付宝网关:https://openapi.alipaydev.com/gateway.do
-
买家信息
买家账号xxxxxxxxxxxxxxx
登录密码111111
支付密码111111
用户名称沙箱环境
证件类型身份证(IDENTITY_CARD)
证件号码xxxxxxxxxxxxxxxxx
- 商家信息
商家账号bqnism8671@sandbox.com
商户UID2088102176491744
登录密码111111
第4集 密码学的那些事情-非对称加密和对称加密介绍
简介:介绍密码学-对称加密和非对称加密
- 对称加密
优点:操作比较简单,加密速度快,秘钥简单
缺点:秘钥上面,一旦被窃取,信息会暴露,安全性不高
场景:消息发送方需要加密大量数据时使用
常见的算法:
DES: 全称:Data Encryption Standard,现已被破解
3DES:全称: Triple Data Encryption Algorithm, 暂时未被破解
解释: 3DES 是在 DES 基础算法上的改良,该算法可向下兼容 DES 加密算法,但计算性能不高,暂时还未被破解
AES: 全称:Advanced Encryption Standard,暂未被破解
- 非对称加密
注意:非对称加密具有双向性,即公钥和私钥中的任一个均可用作加密,此时另一个则用作解密
解释:加密与解密的过程不是对称的,不是用的同一个秘钥,一把是公钥,一把是私钥,在加密的时候,用公钥去加密,接收方再用对应的私钥去解密
优点:安全性更高,公钥是公开的,秘钥是自己保存的,不需要将私钥给别人。
缺点:加密和解密花费时间长、速度慢,只适合对少量数据进行加密
场景: 数字签名与验证
常见的算法:RSA,DSA,ECC等,ECC也是比特币底层用的比较多的算法
第5集 应用对接支付宝里面的非对称加密流程梳理
简介:支付宝开里面的非对称加密通讯流程梳理
- 应用对接支付宝加密流程
-
手机网站支付文档地址:
- https://opendocs.alipay.com/apis/api_1/alipay.trade.wap.pay?scene=API002020081300013628
- 参数介绍
- 流程介绍
-
项目依赖包添加和样例代码
-
https://opendocs.alipay.com/open/54/cyz7do
-
<!-- https://mvnrepository.com/artifact/com.alipay.sdk/alipay-sdk-java --> <dependency> <groupId>com.alipay.sdk</groupId> <artifactId>alipay-sdk-java</artifactId> <version>4.10.218.ALL</version> </dependency>
-
第6集 支付宝开发助手简介和非对称加密钥生成工具下载
简介:支付宝开发助手简介和秘钥生成工具下载
-
支付宝开发助手简介
- 支付宝开放平台开发助手即密钥生成工具,用于对应用的客户端服务端之间的交互进行加密保护。
- 工具主要功能有生成密钥、签名、验签、格式转换、密钥匹配、智能反馈、开放社区
- https://opendocs.alipay.com/open/291/introduce
-
秘钥生成工具下载
-
代码新建配置类
第7集 手机网站支付宝支付样例代码+单例设计模式应用
简介:手机网站支付宝支付样例代码编写测试
- 编写样例代码
- 测试参数配置使用
//商户订单号,64个字符以内、可包含字母、数字、下划线;需保证在商户端不重复
String no = UUID.randomUUID().toString();
log.info("订单号:{}",no);
content.put("out_trade_no", no);
content.put("product_code", "FAST_INSTANT_TRADE_PAY");
//订单总金额,单位为元,精确到小数点后两位
content.put("total_amount", String.valueOf("111.99"));
//商品标题/交易标题/订单标题/订单关键字等。 注意:不可使用特殊字符,如 /,=,& 等。
content.put("subject", "杯子");
//商品描述,可空
content.put("body", "好的杯子");
// 该笔订单允许的最晚付款时间,逾期将关闭交易。取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)。 该参数数值不接受小数点, 如 1.5h,可转换为 90m。
content.put("timeout_express", "5m");
第8集 沙箱环境常见的坑和手机网站支付宝支付样例问题修复
简介:手机网站支付宝支付样例代码问题修改
-
Bug修改
-
沙箱环境的坑
-
如果支付页面出现 “支付存在钓鱼风险” ,清空浏览器缓存,只开一个支付宝支付窗口
-
每周日中午12点至每周一中午12点沙箱环境进行维护,期间可能出现不可用
-
第三十三章 设计模式在多渠道支付里面的设计+编码实战
第1集 软件架构设计-设计模式的六大原则你知道多少
简介:讲解设计模式的六大设计原则
-
设计模式是站在设计原则的基础之上的,软件架构也一样,有必要对这些设计原则先做一下了解
-
软件设计开发原则
- 为了让的代码更好重用性,可读性,可靠性,可维护性
- 诞生出了很多软件设计的原则,这6大设计原则是我们要掌握的
- 将六大原则的英文首字母拼在一起就是SOLID(稳定的),所以也称之为SOLID原则
-
单一职责原则
- 一个类只负责一个功能领域中的相应职责,就一个类而言,应该只有一个引起它变化的原因
- 是实现高内聚、低耦合的指导方针
- 解释:
- 高内聚
- 尽可能类的每个成员方法只完成一件事(最大限度的聚合)
- 模块内部的代码, 相互之间的联系越强,内聚就越高, 模块的独立性就越好
- 低耦合: 减少类内部,一个成员方法调用另一个成员方法, 不要有牵一发动全身
- 高内聚
-
开闭原则
- 对扩展开放,对修改关闭,在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果
-
里氏替换原则LSP
- 任何基类可以出现的地方,子类一定可以出现
- 在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象
- controller->service->dao
-
依赖倒转原则
- 是开闭原则的基础,针对接口编程,依赖于抽象而不依赖于具体
- 高层模块不应该依赖低层模块,二者都应该依赖其抽象
-
接口隔离原则
- 客户端不应该依赖那些它不需要的接口
- 使用多个隔离的接口,比使用单个接口要好,降低类之间的耦合度
-
迪米特法则
- 最少知道原则,一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立
- 类之间的耦合度越低,就越有利于复用,一个处在松耦合中的类一旦被修改,不会对关联的类造成太大波及
- 通过引入一个合理的第三者来降低现有对象之间的耦合度
第2集 设计模式最佳实践-第三方支付对接-工厂模式回顾
简介:设计模式知识回顾-工厂模式
-
工厂模式介绍:
- 它提供了一种创建对象的最佳方式,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象
-
工厂模式有 3 种不同的实现方式
- 简单工厂模式:通过传入相关的类型来返回相应的类,这种方式比较单 一 , 可扩展性相对较差;
- 工厂方法模式:通过实现类实现相应的方法来决定相应的返回结果,这种方式的可扩展性比较强;
- 抽象工厂模式:基于上述两种模式的拓展,且支持细化产品
-
例子:
- 需要购买一辆车,不用管车辆如何组装,且可以购买不同类型的比如轿车、SUV、跑车,直接去4s店购买就行(4s店就是工厂)
- 工厂生产电脑,除了A品牌、还可以生产B、C、D品牌电脑
- 业务开发中,支付很常见,里面有统一下单和支付接口,具体的支付实现可以微信、支付宝、银行卡等
-
简单工厂模式
- 又称静态工厂方法, 可以根据参数的不同返回不同类的实例,专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类
- 由于工厂方法是静态方法,可通过类名直接调用,而且只需要传入简单的参数即可
-
优点:
- 将对象的创建和对象本身业务处理分离可以降低系统的耦合度,使得两者修改起来都相对容易。
-
缺点
- 工厂类的职责相对过重,增加新的产品需要修改工厂类的判断逻辑,这一点与开闭原则是相违背
- 即开闭原则(Open Close Principle)对扩展开放,对修改关闭,程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果
- 将会增加系统中类的个数,在一定程度上增加了系统的复杂度和理解难度,不利于系统的扩展和维护,创建简单对象就不用模式
- 项目里面的应用
第3集 设计模式最佳实践-第三方支付对接-策略模式回顾
简介:设计模式知识回顾-策略模式
-
策略模式(Strategy Pattern)
- 定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换
- 淘宝天猫双十一,正在搞活动有打折的、有满减的、有返利的等等,这些算法只是一种策略,并且是随时都可能互相替换的, 我们就可以定义一组算法,将每个算法都封装起来,并且使它们之间可以互换
-
应用场景
- 老王计划外出旅游,选择骑自行车、坐汽车、飞机等,每一种旅行方式都是一个策略
- Java AWT中的LayoutManager,即布局管理器
- 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么可以使用策略模式
- 不希望暴露复杂的、与算法有关的数据结构,那么可以使用策略模式来封装算法
- 对接第三方支付里面,微信支付、支付宝支付等都可以是一种策略
-
角色
- Context上下文:屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化
- Strategy策略角色:抽象策略角色,是对策略、算法家族的抽象,定义每个策略或算法必须具有的方法和属性
- ConcreteStrategy具体策略角色:用于实现抽象策略中的操作,即实现具体的算法
第4集 多渠道支付对接-策略模式+工厂模式编码实战
简介:多渠道支付对接-策略模式+工厂模式编码实战
- PayInfoVO类开发
- 策略接口开发 PayStrategy
- 策略上下文 PayStrategyContext开发
- 具体支付策略开发 AlipayStrategy、WechatPayStrategy
- 简单工厂类开发
第5集 策略设计模式-支付宝支付下单策略编码实战
简介:支付宝支付策略编码实战
- 下单策略编码实战
- 二次支付订单超时设计考虑
第6集 策略设计模式-支付宝订单状态查询编码实战
简介:支付宝订单状态查询策略编码实战
-
订单状态查询策略编码实战
-
接口文档:https://opendocs.alipay.com/apis/api_1/alipay.trade.query
第三十四章 内网穿透工具和支付结果通知回调模块开发
第1集 内网穿透映射工具介绍和使用
简介:内网穿透映射工具介绍和使用
- 什么是内网穿透
支付成功需要配置回调通知应用服务器订单支付成功,需要配置对应的域名
在本地电脑开发,支付宝没法回调,所以需要配置个地址映射,就是外部服务器
可以通过这个地址访问当前开发电脑的地址
-
工具
-
ngrock https://ngrok.com/
-
花生壳 https://hsk.oray.com/
-
natapp https://natapp.cn/
-
小米球 http://ngrok.ciqiuwl.cn/
-
-
账号申请
#配置token和子域名
token: xxxxxxxxxxxxxx
#增加权限
chmod 777 ./*
#启动
./ngrok -log=ngrok.log -config ngrok.conf start httptun httpstun
-
地址配置
- 回调地址:外网可以访问
- http://xdclass.ngrok2.xiaomiqiu.cn/api/callback/order/v1/alipay
- 支付成功配置:外网可以访问
- 回调地址:外网可以访问
- 访问测试
第2集 支付宝支付结果通知回调地址配置和接口开发
简介:支付结果通知回调地址配置和接口开发
-
补充支付宝手机支付文档:https://opendocs.alipay.com/open/203/105286
-
支付宝沙箱环境配置支付结果通知回调地址
-
配置文件配置支付结果通知回调地址
-
controller接口开发
-
工具转换
/**
* 将request中的参数转换成Map
* @param request
* @return
*/
private static Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {
Map<String, String> paramsMap = new HashMap<>(16);
Set<Map.Entry<String, String[]>> entrySet = request.getParameterMap().entrySet();
for (Map.Entry<String, String[]> entry : entrySet) {
String name = entry.getKey();
String[] values = entry.getValue();
int size = values.length;
if (size == 1) {
paramsMap.put(name, values[0]);
} else {
paramsMap.put(name, "");
}
}
System.out.println(paramsMap);
return paramsMap;
}
第3集 支付宝支付结果通知回调验证签和更新订单状态开发
简介:支付结果通知回调验证签和更新订单状态开发
- 文档:https://opensupport.alipay.com/support/helpcenter/193/201602472200?ant_source=antsupport
- 回调业务逻辑开发
- 更新订单状态
- 如何保证幂等性: 可以不做幂等性处理,本身不影响
第三十五章 订单微服务下单链路完善和支付整合测试
第1集 下单支付链路和超时未支付定时关单功能开发完善
简介:下单支付链路和超时未支付定时关单功能开发完善
- 下单支付功能开发
- 消费者功能完善
- 查询订单状态
第2集 订单微服务下单支付全链路多场景测试准备工作
简介:订单微服务下单支付宝支付全链路多场景测试工作工作
-
下单支付全链路测试-支付-超时未支付
-
登录-加入购物车-使用优惠券-下单-支付
-
登录-加入购物车-使用优惠券-下单-不支付
-
测试准备工作
-
修改多个微服务的死信队列
- 订单5分钟内支付未支付则关单
- 延迟消息6分钟
- 修改了延迟队列的属性,记得先删除下全部交换机和队列
-
检查优惠券记录和商品库存
-
-
注意:
-
bug修改:saveProductOrder方法
-
初次启动微服务记得先调用下,防止超时
-
第3集 订单微服务下单支付全链路多场景测试
简介:订单微服务下单支付宝支付全链路多场景测试
- 登录-加入购物车-使用优惠券-下单-支付
- 代码本身有问题-比如真的少了参数
- 代码bug修改下单协议:total_amount、timeout_express
- 支付沙箱环境抽风(偶尔出现)
- 代码本身有问题-比如真的少了参数
- 下单支付全链路测试-支付-超时未支付(优惠券记录释放正常、商品库存释放正常、订单关闭正常)
- 登录-加入购物车-使用优惠券-下单-不支付(数据正常)
{
"coupon_record_id":50,
"product_ids":[1,2],
"pay_type":"ALIPAY",
"client_type":"H5",
"address_id":45,
"total_amount":510,
"real_pay_amount":505,
"token":"SbD5D4FLpUzemiuwSEytwGM9LLFGISDQ"
}
第4集 订单微服务订单列表和订单项功能开发
简介:订单微服务分页查询个人订单列表功能开发
- 分页个人查询订单功能开发
第5集 未支付订单二次支付业务逻辑设计和编码实战
简介:未支付订单二次支付业务逻辑设计和编码实战
- controller开发
- service开发
第6集 未支付订单二次支付全链路测试
简介:未支付订单二次支付业务逻全链路测试
- 全链路测试
- 加入购物车
- 下单不支付
- 我的订单列表
- 二次支付
- 备注
- 测试的时候可以快速下两笔订单,3分钟内可以支付,3分钟后就不行
- 订单支付超时,可以往前推,也可以往后推1分钟
第7集 订单微服务-避免重复下单token令牌机制+lua脚本原子操作
简介:订单微服务-避免重复下单tokne令牌机制处理
-
问题
- 前端下单按钮重复点击导致订单创建多次
- 前端有限制,后端也需要有限制
- 任何提交表单的时候,都可以采用token令牌机制避免重复点击
-
token令牌机制开发
- 下单前先获取令牌-存储redis
- 下单时一并把token提交并检验和删除-lua脚本
String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
实战**
-
订单状态查询策略编码实战
-
接口文档:https://opendocs.alipay.com/apis/api_1/alipay.trade.query
第三十四章 内网穿透工具和支付结果通知回调模块开发
第1集 内网穿透映射工具介绍和使用
简介:内网穿透映射工具介绍和使用
- 什么是内网穿透
支付成功需要配置回调通知应用服务器订单支付成功,需要配置对应的域名
在本地电脑开发,支付宝没法回调,所以需要配置个地址映射,就是外部服务器
可以通过这个地址访问当前开发电脑的地址
-
工具
-
ngrock https://ngrok.com/
-
花生壳 https://hsk.oray.com/
-
natapp https://natapp.cn/
-
小米球 http://ngrok.ciqiuwl.cn/
-
-
账号申请
#配置token和子域名
token: A3dc8765c57f4e6e8Ac84276deA889c4
#增加权限
chmod 777 ./*
#启动
./ngrok -log=ngrok.log -config ngrok.conf start httptun httpstun
-
地址配置
- 回调地址:外网可以访问
- http://xdclass.ngrok2.xiaomiqiu.cn/api/callback/order/v1/alipay
- 支付成功配置:外网可以访问
- 回调地址:外网可以访问
-
访问测试
第2集 支付宝支付结果通知回调地址配置和接口开发
简介:支付结果通知回调地址配置和接口开发
-
补充支付宝手机支付文档:https://opendocs.alipay.com/open/203/105286
-
支付宝沙箱环境配置支付结果通知回调地址
-
配置文件配置支付结果通知回调地址
-
controller接口开发
-
工具转换
/**
* 将request中的参数转换成Map
* @param request
* @return
*/
private static Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {
Map<String, String> paramsMap = new HashMap<>(16);
Set<Map.Entry<String, String[]>> entrySet = request.getParameterMap().entrySet();
for (Map.Entry<String, String[]> entry : entrySet) {
String name = entry.getKey();
String[] values = entry.getValue();
int size = values.length;
if (size == 1) {
paramsMap.put(name, values[0]);
} else {
paramsMap.put(name, "");
}
}
System.out.println(paramsMap);
return paramsMap;
}
第3集 支付宝支付结果通知回调验证签和更新订单状态开发
简介:支付结果通知回调验证签和更新订单状态开发
- 文档:https://opensupport.alipay.com/support/helpcenter/193/201602472200?ant_source=antsupport
- 回调业务逻辑开发
- 更新订单状态
- 如何保证幂等性: 可以不做幂等性处理,本身不影响
第三十五章 订单微服务下单链路完善和支付整合测试
第1集 下单支付链路和超时未支付定时关单功能开发完善
简介:下单支付链路和超时未支付定时关单功能开发完善
- 下单支付功能开发
- 消费者功能完善
- 查询订单状态
第2集 订单微服务下单支付全链路多场景测试准备工作
简介:订单微服务下单支付宝支付全链路多场景测试工作工作
-
下单支付全链路测试-支付-超时未支付
-
登录-加入购物车-使用优惠券-下单-支付
-
登录-加入购物车-使用优惠券-下单-不支付
-
测试准备工作
-
修改多个微服务的死信队列
- 订单5分钟内支付未支付则关单
- 延迟消息6分钟
- 修改了延迟队列的属性,记得先删除下全部交换机和队列
-
检查优惠券记录和商品库存
-
-
注意:
-
bug修改:saveProductOrder方法
-
初次启动微服务记得先调用下,防止超时
-
第3集 订单微服务下单支付全链路多场景测试
简介:订单微服务下单支付宝支付全链路多场景测试
- 登录-加入购物车-使用优惠券-下单-支付
- 代码本身有问题-比如真的少了参数
- 代码bug修改下单协议:total_amount、timeout_express
- 支付沙箱环境抽风(偶尔出现)
- 代码本身有问题-比如真的少了参数
- 下单支付全链路测试-支付-超时未支付(优惠券记录释放正常、商品库存释放正常、订单关闭正常)
- 登录-加入购物车-使用优惠券-下单-不支付(数据正常)
{
"coupon_record_id":50,
"product_ids":[1,2],
"pay_type":"ALIPAY",
"client_type":"H5",
"address_id":45,
"total_amount":510,
"real_pay_amount":505,
"token":"SbD5D4FLpUzemiuwSEytwGM9LLFGISDQ"
}
第4集 订单微服务订单列表和订单项功能开发
简介:订单微服务分页查询个人订单列表功能开发
- 分页个人查询订单功能开发
第5集 未支付订单二次支付业务逻辑设计和编码实战
简介:未支付订单二次支付业务逻辑设计和编码实战
- controller开发
- service开发
第6集 未支付订单二次支付全链路测试
简介:未支付订单二次支付业务逻全链路测试
- 全链路测试
- 加入购物车
- 下单不支付
- 我的订单列表
- 二次支付
- 备注
- 测试的时候可以快速下两笔订单,3分钟内可以支付,3分钟后就不行
- 订单支付超时,可以往前推,也可以往后推1分钟
第7集 订单微服务-避免重复下单token令牌机制+lua脚本原子操作
简介:订单微服务-避免重复下单tokne令牌机制处理
-
问题
- 前端下单按钮重复点击导致订单创建多次
- 前端有限制,后端也需要有限制
- 任何提交表单的时候,都可以采用token令牌机制避免重复点击
-
token令牌机制开发
- 下单前先获取令牌-存储redis
- 下单时一并把token提交并检验和删除-lua脚本
String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";