> 文档中心 > Java电商平台之订单功能和支付功能实现

Java电商平台之订单功能和支付功能实现

下单支付流程
1.选择商品添加至购物车。
2.点击支付,创建订单
3.将订单写入数据库
4.同时扣减库存,将订单数据同时同步到redis,并且设置过期时间。
5.取消订单(超时未支付),退回商品库存。
6.定时清理未支付异常订单,退回商品库存。
订单中遇到的问题:
1.重复下单,恶习重复下单,
采取的接口的幂等性+token验证机制,自定义token注解,实现token的创建和删除功能,创建拦截器继承处理器拦截适配器,重写prehandle方法,在下单之后请求处理钱完成拦截处理,下单生成自定义token将其存储在redis缓冲中的session中,并且将其返回响应给客户端,客户端下次请求会携带token作为请求头发送请求,请求进入到拦截器进行前端请求获取token,如果token不存在则进行创建token,同时成功创建订单,保存在redis缓冲中等待库存的扣减和订单超时管理删除订单,如果存在,则处理拦截适配器进行拦截,返回不允许重复下单。
2.如何安全扣减库存,不超卖不少卖
采用springboot操作redisbution实现对库存字段分布式锁机制,利用redission框架,set key value nx方法设置延时时间,当有很
多请求发过来时,会一个一个请求来执行,因为redis是单线程架构,系统宕机完成自动释放库存锁。解决了由于系统宕机锁未释放,还有释放锁异常采用try cath final。释放锁和删除锁。

@RestControllerpublic class IndexController {    @Autowired    private Redisson redisson;    @Autowired    private StringRedisTemplate stringRedisTemplate;    @RequestMapping("/deduct_stock")    public String deductStock(){ String lockKey="lockKey"; RLock redisId=redisson.getLock(lockKey);//拿到锁对象try {  //问题一    redisId.lock();//j加锁    int stock=Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));//jedis.get("stock");    if(stock>0){ int realStock=stock-1; stringRedisTemplate.opsForValue().set("stock",realStock+"");//jedis.set(key,value)    }else{ System.out.println("扣减失败,库存不足");    }}finally { redisId.unlock();   }    return "end";}}

序列化和反序列化
多线程问题
spring中的事务控制
事务管理器transactionmanager
订单支付中重复支付问题解决(幂等性分布式锁)
订单支付中支付失败订单处理
订单支付中支付成功但是订单无变化
订单提交时由于本地bug或者意外故障导致用户钱支付了但是订单不成功,采用对账方式来解决

个服务器之间的执行流程

1.mybatis在项目中的使用

持久化框架封装了jdbc数据库链接,ssm框架整合mybatis,首先配置全局文件applicationcontext.xml文件,整合mybatis,完成数据源source配置,编写mapper映射文件,加载全局配置文件,生成sqlsessionfactory对象,builde方法创建sqlsession对象调用mapper映射文件中的sql语句,完成对数据库的操作。

2.mysql数据库主从集群的配置,实现高可用

配置修改主节点数据库服务器的etc/my.cnf文件,设置号server-id和二进制日志文件启名字 重启服务器让配置生效从节点服务器也去配置etc/my.cnf文件在mysqld范围里内配置server-id保证和主节点服务器不一样。从节点删除uuid文件,在配置主节点的ip,port,username,password等信息,执行挂载命令完成主从高可用服务器配置。

3.nginx实现动静分离和负载均衡

启动nginx 设置配置ningx.conf文件中的https server 中的端口号 location server_name location 实现动静分离()负载均衡 利用ip_hash算法实现

4.redis实现session共享和热点数据缓存

redis-cluster实现session共享验证用户+热点数据缓存+防止雪崩(大量热点数据过期+用户高并发访问直接到达数据库服务器压力打死)+
穿透(有人故意访问redis中不从在数据+数据库也不存在+大量请求导致数据库打死)+击穿(热点数据过期+高并发访问)

5.mycat中间件在项目中的使用

mycat是目前最流行的分布式数据库中间件,是一个开源的分布式数据库系统,解决系统中数据库大量存储问题,解决了表的容量问题,解决了查询表的性能问题,解决了mysql服务器宕机问题。可以实现读写分离。mycat读写分离,schema.xml文件编写。
<!DOCTYPE mycat:schema SYSTEM "schema.dtd"><mycat:schema xmlns:mycat="http://org.opencloudb/" ><schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100"><table name="tb_user" dataNode="dn1" primaryKey="id"/><table name="t_product" primaryKey="product_id" dataNode="easymall_dn1"/></schema><dataNode name="dn1" dataHost="localhost1" database="mstest"/><dataNode name="easymall_dn1" dataHost="localhost1" database="easydb"/><dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100"><heartbeat>select now()</heartbeat><writeHost host="hostM2" url="10.9.182.139:3306" user="root" password="tarena2017Up;"/><writeHost host="hostM1" url="10.42.17.191:3306" user="root" password="tarena2017Up;"/></dataHost></mycat:schema>
schema中的name TESTDB 没变

table中的是逻辑表的名称,所以和物理表同名’t_user’
dataNode中的database表示的是物理数据库名称demo1
balance的值要设置为1否则查询会读取不到从库的数据
writeHost中的url表示写库的地址
readHost表示配置读库的信息
user属性对应的应该是server.xml中配置的账号

  1. 为什么选用MyCat作为数据库的中间件来实现读写分离?什么是读写分离?读写分离的效果?如果利用MyCat实现了数据库的高可用
    例如:读写分离的目的是为了降低数据库的访问压力,后边回答如何降低访问压力的
    其他的数据库中间件还有360 altas cobar不能解决假死现象 TDDL 过时了
    mycat实现分片分表
    读写分离的目的是因为我们项目中搭建的双击主从数据库服务器,如果从节点服务器只做数据库备份等待主节点服务器宕机来使用,会浪费较大资源,同时如果全部的数据读写都在主节点服务器的话,压力也会过,所以采用读写分离,将所有写操作放在主节点,读操作放在从节点,这样可以提高数据库处理效率,降低数据库的访问压力。在服务器中搭建mycat中间件,通过在mycat conf文件夹下的配置文件server.xml 和配置文件 schema.xml文件配置读写分离,table datanode writtehost readhost 完成
  2. 什么是分布式锁?常见的分布式锁机制有哪些?为什么选用redission?redission在订单扣减库存业务上是如何做的?
    分布式锁,分布式锁即是在分布式集群服务器中,保证互斥性,不死锁,可重入的特点。从而减少分布式系统的数据库资源的原子性问题。常见的分布式锁有数据库的悲观锁和乐观锁,乐观锁是通过更新数据库时的版本号更新实现,悲观锁则是for update实现,缓冲中的redis set命令 set key value nx ex 实现,设置键值,生成不同客户端对于的value,nx在key不存在下执行,还有redlock,设置了expire锁释放时间,zookeeper是通过创建临时顺序节点来完成分布式锁,监听阻塞保证资源不发生抢占。
    redission解决了redis的 set key value nx ex命令存在的锁超时和锁误删的问题,是一个基于java开发的开源的redis框架客户端。
    采用springboot操作redisbution实现对库存字段分布式锁机制,利用redission框架,对商品库存字段值进行redission加锁,之后进行锁对象判断其库存是否大于0,是则进行减库存操作更新redis缓存数据,完成锁释放,不是的话则要进行输出提示库存不足,当有很
    多请求发过来时,会一个一个请求来执行,因为redis是单线程架构,系统宕机完成自动释放库存锁。解决了由于系统宕机锁未释放,还有释放锁异常采用try cath final。释放锁和删除锁。
@RestControllerpublic class IndexController {    @Autowired    private Redisson redisson;    @Autowired    private StringRedisTemplate stringRedisTemplate;    @RequestMapping("/deduct_stock")    public String deductStock(){ String lockKey="lockKey"; RLock redisId=redisson.getLock(lockKey);//拿到锁对象try {  //问题一    redisId.lock();//j加锁    int stock=Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));//jedis.get("stock");    if(stock>0){ int realStock=stock-1; stringRedisTemplate.opsForValue().set("stock",realStock+"");//jedis.set(key,value)    }else{ System.out.println("扣减失败,库存不足");    }}finally { redisId.unlock();   }    return "end";}}3. 延时双删策略是什么?在订单中,Redis的数据一致性体现在什么方面?为什么要将订单库存信息放置在redis缓冲当中,因为订单存在超时未支付异常问题,所以订单生成表单先存储在redis缓冲当中,支付成功之后进行redis缓冲同步到数据库,使用延时双删策略,先淘汰缓冲,再写数据库,休眠1s左右,再次淘汰缓冲,这样做就是延时双删策略可以保证数据库和redis的数据一致,完成对订单的实时校验,设置号订单超时未支付的记录删除。4. 同步通知干了什么?异步通知包含了什么?为什么选用同步通知来实现xxx,为什么不能是异步通知?同步通知是从支付宝页面返回支付状态,返回get参数到自己的系统完成订单状态的改变和通知,异步通知是为了解决可能出现的支付延迟,客户端浏览器卡死和服务器异常,实现支付结果订单的通知,notify_url进行传参到系统订单状态改变服务,在订单状态改变后进行向支付宝平台发送success参数完成异步回调通知。采用分布式锁实现同步回调和异步回调的串行化执行,先同步回调做支付状态的校验,后进行异步回调对订单支付状态进行更改。6. 订单号的规则是什么?为什么要对下单实现幂等性?采取的接口的幂等性+token验证机制,自定义token注解,实现token的创建和删除功能,创建拦截器继承处理器拦截适配器,重写prehandle方法,在下单之后请求处理钱完成拦截处理,下单生成自定义token将其存储在redis缓冲中的session中,并且将其返回响应给客户端,客户端下次请求会携带token作为请求头发送请求,请求进入到拦截器进行前端请求获取token,如果token不存在则进行创建token,同时成功创建订单,保存在redis缓冲中等待库存的扣减和订单超时管理删除订单,如果存在,则处理拦截适配器进行拦截,返回不允许重复下单。7. 权限的粒度是怎么划分的?8. 报销单表设计中字段怎么涉及的?是否有空余字段或者备用字段?如果有,为什么?

WIFI共享精灵