项目总结

美多商城项目总结

面试官您好,我叫李少辉,我上次做的项目为美多商城,它是一个电商购物平台,该项目前端开发使用 vue.is 框架,后端使用 python 语言的 Django 框架,数据库方面采用MySQLRedis,并且使用Celery任务队列实现异步任务。

具体来说,美多商城包含了商品列表展示、购物车、订单管理、收货地址管理等多个功能,可以实现用户浏览商品、选择商品加入购物车、提交订单、管理订单等多个购物流程。

下边我来介绍一下项目的大概功能,本项目可分为四个模块:用户模块、商品模块两大部分,商品模块下又分为购物车模块与订单模块两个小的模块。

一、用户模块

(一)、注册登录

1、注册模块

其中有一项为手机号验证,其中为防止手机号在传输的时候被人拦截,进行恶意操作,设置了一项图片验证码验证的验证,验证通过才能进行发送短信的操作:使用 captcha 生成图片验证码时,为了使图片可以刷新,用 uuid 拼接进获取图片的路由中,同时把以uuidkey captcha中的textvalue ,并设置过期时间存入reids中。

短信验证成功后:我们调用的是容联云,调用容联云发送短信验证码,以手机号为key,验证码为value,设置过期时间,对用户进行验证,如果验证成功,删除图片验证码的reids,以防他人拦截进行恶意操作,把该用户信息存入用户信息表中。

2、登录模块

登录模块可分为账号密码登录、手机号登录与三方登录。

1)账号密码登录:输入注册时候的账号密码进行验证登录即可。

2)手机号登录:与手机号注册验证大致相同,消息添加入信息库改为去信息库进行验证即可。

3)三方登录:这里我就用微博登录举例:调用三方登录接口并构造url,调用三方登录接口登录,三方应用并获取相应的授权,登录成功后,在回调接口中根据返回的code授权码向三方应用的token接口发起请求获取token,根据token向三方应用的用户信息接口请求登录用户的详细信息,如手机号、uid等,根据uid到三方登录表中查询是否已经登录过,如果没有登录过,则使用手机号注册一个用户写入用户表,写入三方登录表并关联用户,最后登录成功。

在用户使用以上三种方式任意一种登录成功之后,都需要依据已有的用户信息,用该用户的唯一标识数据与其他信息构造存入jwt,生成token返回。

4jwtJwtJson Web Token(jwt), 用于接口开发中的安全认证,相对于以往的cookie/session/token来说,优势在于实现客户端自认证,服务端不需要存储,节约了服务器资源,适用于分布式应用下的单点登录。

session的工作原理是客户端向服务端发起请求,服务端进行验证,生成一个session文件,文件名为sessionid,将sessionid返回客户端存入cookie,每次请求携带sessionid,服务端查找sessionid对应的文件,如果存在取出用户信息,不存在显示没有权限操作。分布式应用下有多台服务器,在A服务器登录时session文件存放在A服务器,当某次请求负载到B服务器时会显示没有权限。可以将session持久化到MysqlRedis数据库中。服务端生成一个token,保存在数据库中并返回给客户端,下次请求携带token,在数据库中查询。

Jwt有三部分构成,头部(描述token类型为jwt,并指定加密方式,默认为HS256);有效载荷(携带用户信息和过期时间,base64编码);签名。解析时,用'.'分割token字符串,对前两部分用服务端盐和加密方式进行加密生成新的签名,和第三部分签名对比,如果相同代表没有被修改。

 

5)权限认证:因涉及到接口较多,所以主要使用中间件进行权限的认证,首先设定一个白名单、该白名单里是不需要权限就可以访问的接口,其余不在白名单里的接口,需要获取到用户登录后的token才可进行访问操作、未登录则直接跳转至登录界面让用户进行登录。同时为了应对少部分特殊接口,比如只有管理员才可以进行的操作,我们是用装饰器的方式进行验证,把验证过程写入装饰器,在需要的这些需求的接口直接使用该装饰器即可。

 

(二)、收获地址

美多商城更是一个电商平台,收获地址也是必不可少的一块,用户可以根据自己的需要自行添加修改自己的收获地址及收件人信息。

(三)、个人信息的修改

当用户对系统默认分配的默认信息不满意时、可以对自己的信息进行一些修改操作。

二、商品模块

(一)、首页及详情页商品的展示

1、使用redis把首页及详情页商品的信息存入

 因首页与详情页的商品信息过多,为了防止用户量增加后,同一时间收到大量的请求,而去mysql数据库中查询,给数据库带来一些不良的影响,使数据库性能降低,我们采用了把商品信息存入redis,减少mysql的负担。

1)mysqlredis的区别:

mysqlmysql是关系型数据库,项目中主要用于基础数据的存储,支持外键支持索引支持事务,适合结构化数据处理,支持分步式事务。

redis:redis是内存型数据库,也可以通过配制持久化到磁盘中,通过 key-value 的方式进行访问,读写速度非常快,读每秒11万次,写每秒8万次,数据结构丰富,可以方便地处理复杂的数据类型,Redis 是单节点的,不支持分布式事务。

同时我们使用redis还因为以下几个原因:

redis是纯内存操作,避免了大量访问数据库,减少了直接读取磁盘数据,将数据储存在内存里面,读写数据的时候都不会受到硬盘I/O速度的限制,所以速度快。

redis采用单线程操作,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗。

redis采用了非阻塞I/O多路复用机制,多路复用原理允许一个单独的线程处理多个连接。

redis内部使用一个redisObject对象来表示所有的keyvalue,采用这种方式实现了灵活多样的数据结构。

redis的数据都存放在内存中,如果没有配置持久化,redis重启后数据就全丢失了,于是需要开启redis的持久化功能,将数据保存到磁盘上,当redis重启后,可以从磁盘中恢复数据。

redis的单线程模型在处理速度上是非常高效的。

2redis缓存雪崩与击穿

为了防止redis中的数据大面积的同时过期失效时,用户请求访问缓存,而缓存不存在,造成数据库短时间内承受大量请求而崩掉。我们采用了setnx分布式锁来解决这个问题 。

3setnx分布式锁:

当发生雪崩与击穿现象时,我们设置了分布式锁,在缓存失效后,会由带锁的人员访问数据库,并把商品信息重新加载入redis中,同时更新redis中的商品缓存。

分布式锁是分布式系统中的关键概念,用于解决多个节点同时访问共享资源可能引发的并发问题。以下是分布式锁的一些基本概念:

锁(Lock):锁是一种同步机制,用于确保在任意时刻只有一个节点(进程或线程)可以访问共享资源。锁可以防止竞态条件和数据不一致问题。

共享资源(Shared Resource):共享资源是多个节点需要访问或修改的数据、文件、服务等。在分布式系统中,多个节点可能同时尝试访问这些共享资源,从而引发问题。

锁的状态:锁通常有两种状态,即锁定状态和解锁状态。在锁定状态下,只有持有锁的节点可以访问共享资源,其他节点被阻塞。在解锁状态下,任何节点都可以尝试获取锁。

竞态条件(Race Condition):竞态条件指的是多个节点在执行顺序上产生了不确定性,导致程序的行为变得不可预测。在分布式系统中,竞态条件可能导致多个节点同时访问共享资源,破坏了系统的一致性。

数据不一致(Data Inconsistency):数据不一致是指多个节点对同一个数据进行修改,但由于缺乏同步机制,数据可能处于不一致的状态。这可能导致应用程序出现错误或异常行为。

死锁(Deadlock):死锁是多个节点因相互等待对方释放资源而陷入无限等待的状态。在分布式系统中,多个节点可能同时竞争资源,如果没有良好的协调机制,就可能出现死锁情况。

4Mysqlredis信息一致性:

为了确保在商品信息进行了修改之后,两个数据库之间的信息的一致性。

MySQL作为主数据源,Redis作为缓存:在这种模式下,MySQL是数据的主要存储位置,所有的写操作都直接在MySQL中进行。当数据发生改变时,通过某种方式(如触发器、定时任务等)同步到Redis中。这种方式下,Redis主要是作为一个缓存系统,提供快速的数据读取,而数据的一致性主要依赖于MySQL的更新操作。

数据同步策略:需要根据业务需求和系统性能要求,选择合适的同步策略。例如,可以选择实时同步、定时同步、批量同步等。

数据冲突解决:当MySQLRedis中的数据发生冲突时,需要有一种机制来解决这种冲突。例如,可以根据数据的更新时间戳、版本号等信息进行冲突解决。

数据一致性检查:需要定期或不定期地对MySQLRedis中的数据进行一致性检查,以确保数据的一致性。

(二)、购物车

1、加入购物车

在用户把商品加入购物车时,我们把获取到的用户信息与用户加入购物车中商品用redis中的hash数据类型重新构造加入 购物车。

2redis的五种数据类型:

1 String(字符串)

常用命令: set,get,incr 等。

String数据结构是简单的key-value类型,value其实不仅可以是String,也可以是数字。 常规key-value缓存应用;

应用场景 常规计数:微博数,粉丝数等。

2Hash(哈希(字典))

常用命令: hget,hset,hgetall 等。

Hash 是一个 string 类型的 field value 的映射表,hash 特别适合用于存储对象,后续操作的时候,你可以直接仅仅修改这个对象中的某个字段的值。 比如我们可以Hash数据结构来存储用户信息,商品信息。应用场景购物车

3List(列表)

常用命令: lpush,rpush,lpop,rpop,lrange

list 就是链表,Redis list 的应用场景非常多,也是Redis最重要的数据结构之一,比如微博的关注列表,粉丝列表,消息列表等功能都可以用Redislist 结构来实现。

4Set(集合)

常用命令: sadd,spop,smembers

set 对外提供的功能与list类似是一个列表的功能,特殊之处在于 set 是可以自动排重的。

5Sorted Set(有序集合)

常用命令: zadd,zrange,zrem,zcard

set相比,sorted set增加了一个权重参数score,使得集合中的元素能够按score进行有序排列。

3redis的封装(单例模式)

因为加入购物车,修改购买数量之类的需要频繁使用redis中的hash的增删改查方法,所以把这些方法封装起来,直接实例化一个redis对象,当我们想使用某种方法时,直接调用该对象进行操作即可。

这种模式的主要应用场合包括需要频繁实例化然后销毁的对象、创建对象时耗时过多或者耗资源过多,但又经常用到的对象、有状态的工具类对象以及频繁访问数据库或文件的对象。

(三)、订单生成及支付

1、订单的生成

当用户对购物车中商品的数量及其他商品信息核实无误后,提交去购买,我们会生成响应的订单,及订单详情。

在生成订单时,为了防止用户因为网络波动及其他问题,对提交按钮多次点击而造成生成多个相同内容的订单,我们采用了幂等性操作来防止这一现象的发生。

同时为了防止超卖现象的发生,我们可以用悲观锁与乐观锁来进行判断。乐观锁和悲观锁是两种思想,用于解决并发场景下的数据竞争问题。

确认无误后生成订单号,把订单信息存入订单表与订单详情表中。

同时,为了解决一些生成订单占用商品库存的用户不支付,而使其他用户可能因为库存问题而无法生成订单,我们将这些未支付的订单存入redis队列当中,并用celery设置定时任务,将用户半小时内未支付的订单删除。首先先获取到当前时间戳,再减去半个小时的时间节点,然后查询从0开始到半个小时前的所有订单进行循环遍历,获取到订单详情,再把该订单详情加入取消订单表与里,同时把商品表里锁定的订单库存给解放,方便其他用户下单,同时把订单表中及订单详情表中的相关信息删除,最后删除队列中的订单信息。

本项目还会不定期的发送一些优惠券,在用户结算订单时会默认使用优惠力度最大的优惠券进行结算。

1)幂等性

我们生成一个全局唯一的变量orderkey”并存入redis中,当用户提交订单时需要携带这个“orderkey”过去,我们获取到了这个“orderkey”时,让用户正常生成订单即可,成功之后删除“orderkey”即可,如没有该“orderkey”,则提醒重复操作。

所谓的幂等性,是分布式环境下的一个常见问题,一般是指我们在进行多次操作时,所得到的结果是一样的,即多次运算结果是一致的。也就是说,用户对于同一操作,无论是发起一次请求还是多次请求,最终的执行结果是一致的,不会因为多次点击而产生副作用,而幂等性的核心思想,其实就是保证这个接口的执行结果只影响一次,后续即便再次调用,也不能对数据产生影响。

2)悲观锁

悲观锁认为别人会同时修改数据。因此操作数据时直接把数据锁住,直到操作完成后才会释放锁,上锁期间其他人不能修改数据。mysql使用select for update对数据加行级锁。悲观锁保证数据的一致性和完整性,适用于数据竞争激烈(或数据修改频繁)的场景。

3)乐观锁

乐观锁认为别人不会同时修改数据。因此乐观锁不会上锁,只是在执行更新的时候判断一下在此期间别人是否修改了数据,如果别人修改了数据则放弃操作,否则执行操作。以购物车为例,购买时购物车查询库存,生成订单再查询库存,如果两次相同中间没被操作继续执行,如果两次不相同已经被修改,根据情况做下一步操作。

4)Celery

Celery 使用消息代理(Message Broker)来存储和传递任务消息。常见的消息代理包括 RabbitMQRedisAmazon SQS 等。当一个任务被提交到 Celery 时,它会被序列化成消息,并发送到消息代理中。Celery 使用一个统一的调度器来调度任务。调度器负责监视注册的任务,并在需要执行它们时将任务发送到工作节点中。工作节点是真正执行任务的地方。它们从消息代理中获取任务消息,并执行相应的任务。Celery 支持在多台机器上部署工作节点,从而实现分布式的任务执行。Celery 提供了结果存储功能,可以将任务的执行结果保存在持久化存储中,比如数据库或缓存中。这样提交任务的客户端就可以查询任务的执行结果。

 

2、支付订单

在生成订单,获取订单号、订单金额等信息,生成支付请求url并调用支付接口,支付宝系统验证和校验,在支付页面进行支付操作,支付完成后,在回调接口中获取回调参数并验签,验签通过更新订单状态,更新商品库存。

在用户对商品不满意需要退款时,根据流水号查询到相关表的订单详情,根据表中的商品金额与流水号生成url,调用支付宝的退款接口,后续操作类似于支付时的类似。

posted @ 2023-12-15 16:39  灰灰灰灰辉  阅读(23)  评论(0)    收藏  举报