介绍项目
黑马商城
为什么使用rabbitmq
用于异步更新订单
扣减余额后异步更新订单失败怎么办
分布式事务回滚
什么是接口幂等性,怎么实现
接口幂等性是指同一个接口在短时间点击多次都之后返回只执行一次的结果。
- Token机制:
- 生成Token:在客户端发起请求前,服务器生成一个唯一的Token,并将该Token与请求绑定。
- 携带Token:客户端在发起请求时,将Token携带在请求头或请求体中。
- 校验Token:服务器在接收到请求后,首先校验Token的有效性。如果Token有效且未使用过,则处理请求并标记Token为已使用;如果Token无效或已使用,则直接返回错误或重复处理的提示。
- 删除Token:处理完请求后,服务器删除或更新Token的状态,以避免重复处理。
- 唯一索引或主键约束:
- 在数据库中,为需要保证幂等性的数据表设置唯一索引或主键约束。这样,在插入数据时,如果数据已存在,则数据库会拒绝插入,从而保证了幂等性。
- 乐观锁:
- 乐观锁是一种在数据库层面实现幂等性的方法。通过在数据表中增加一个版本号(version)字段,每次更新数据时都会检查版本号是否一致。如果版本号一致,则进行更新并将版本号加1;如果版本号不一致,则说明数据已被其他事务修改过,当前事务则拒绝更新。
- 悲观锁:
- 悲观锁通过在读取数据时加锁来防止其他事务修改数据。使用悲观锁时,一般伴随事务一起使用,确保在事务处理期间数据不会被其他事务修改。但需要注意的是,悲观锁可能会导致性能问题,特别是在高并发的系统中。
- 分布式锁:
- 在分布式系统中,可以使用分布式锁来保证同一时间只有一个实例处理特定消息或请求。分布式锁可以通过Redis、Zookeeper等中间件实现。
- 幂等性校验:
- 在业务逻辑层进行幂等性校验。例如,在支付场景中,可以通过检查订单状态或支付流水号来确保不会重复扣款。
下单时候扣减金额失败,订单生成但是待支付的状态,这时候用户再次发起购买,流程是什么。
先生成订单,预扣减库存,清除购物车,消息队列发送延时订单,然后支付时判断订单是否是未支付,是则扣减金额,再异步更新订单,如果扣减金额失败,直接返回错误回滚即可。再次发起购买则继续正常支付订单。
seata分布式事务有哪几种实现方式
XA模式:强一致性分阶段事务模式,牺牲了一定的可用性,无业务侵入。
TCC模式:最终一致的分阶段事务模式,有业务侵入。
AT模式:最终一致的分阶段事务模式,无业务侵入,也是Seata的默认模式。
SAGA模式:长事务模式,有业务侵入。
项目里怎么使用分布式事务,结合业务描述流程
在支付和生成订单模块使用了分布式事务,生成订单包括查询商品个数,预扣减库存、清除购物车、发送延时订单。用户点击支付后,查询余额并扣减,更新支付单状态,再异步更新订单状态。
限流都有哪些实现的方式
计数器法、令牌桶法、漏桶法、滑动窗口法
java中有哪些锁,有什么区别
读写锁、乐观锁悲观锁、synchronized、偏向锁、CAS、volatile、atomic、reetrantlock
synchronized锁升级过程
1、不加锁
2、有线程获取偏向锁
3、其他线程竞争偏向锁失败后升级为轻量级锁
4、轻量级锁失败升级为重量级锁
CMS和G1的区别
CMS用于老年代,是以最短停顿时间为目的的垃圾回收器,并发标记和最终清除都是并行的。
G1是将老年代和青年代都划分为一个个独立区域,优先回收价值最大的区域。只有并发标记不用STW。
怎么判断垃圾对象
1、计数器法:每个对象被引用则计数器加一,引用失效计数器减一,不能解决循环依赖问题。
2、可达性分析:从gc root出发,向下搜索,不能通过gc root到达的对象认为是垃圾对象。
gc root有哪些
- 虚拟机栈(Java Stack)中引用的对象:
- 当一个方法正在执行时,它的局部变量和参数都在虚拟机栈的栈帧中。这些局部变量和参数可能引用了一些对象,这些对象就是GC Roots。换句话说,当一个线程执行Java方法时,它的局部变量和参数引用的对象都是GC Roots。
- 方法区(Method Area)中静态(Static)属性引用的对象:
- 类的静态变量(包括静态字段和静态方法中的局部变量)都存储在方法区中。这些静态变量可能引用了一些对象,这些对象也是GC Roots。例如,类的静态字段引用的对象,以及字符串常量池(String Constant Pool)中的字符串对象,它们都被视为GC Roots。
- 本地方法栈(Native Method Stack)中JNI(Java Native Interface)引用的对象:
- 当Java代码调用本地方法(如C或C++编写的代码)时,本地方法栈用于存储这些本地方法的局部变量和参数。这些本地方法可能通过JNI引用Java对象,这些被引用的Java对象也是GC Roots。
mysql的sql语句执行过程
1、客户端与mysql连接器建立TCP连接,进行身份验证及权限验证。
2、mysql8.0之前会查询缓存,存在则将sql结果返回。
3、分析器对sql语句进行词法分析、语法分析。
4、优化器可能重写sql、选择合适索引等。
5、执行器调用引擎接口,返回结果。
事务的隔离级别
读未提交:不加任何锁、可能读到事务未提交的脏数据,即脏读。
读已提交:MVCC每次读都生成readview,避免脏读,会出现不可重复读。
可重复读:MVCC保证一个事务只有第一次快照读生成readview,保证可重复读,可能幻读。
串行化:使用synchronized保证事务串行执行,避免幻读。
介绍RPC框架,问客户端怎么找到服务端的
RPC框架通常包含以下几个核心组件:
- 客户端(Client):服务调用方,发起RPC调用的应用程序部分。
- 客户端存根(Client Stub):存放服务端地址信息,将客户端的请求参数数据信息打包成网络消息,再通过网络传输发送给服务端。
- 网络传输模块:用于在客户端和服务端之间传输数据的通信机制,通常使用TCP/IP或HTTP等协议。
- 服务端存根(Server Stub):接收客户端发送过来的请求消息并进行解包,然后再调用本地服务进行处理。
- 服务端(Server):服务的真正提供者,执行客户端请求的方法或过程。
RPC框架有Google的gRPC、Facebook的Thrift、Alibaba的Dubbo等。
- 服务注册与发现:
- 服务端在启动时,会将其提供的服务名称和服务地址注册到服务注册中心(如Zookeeper、Nacos等)。
- 客户端在调用服务之前,会先向服务注册中心查询所需服务的地址信息。
- 建立网络连接:
- 客户端根据从服务注册中心获取的服务地址信息,建立与服务端之间的网络连接。这通常通过创建一个套接字(Socket)来实现,套接字是一种能够在网络上进行数据传输的工具。
- 发送请求:
- 客户端通过网络连接向服务端发送请求,请求中包含了需要调用的方法名、参数等信息。
- 接收响应:
- 服务端接收到请求后,执行相应的方法或过程,并将执行结果返回给客户端。
- 客户端接收到响应后,进行反序列化得到返回值,并继续后续的处理流程。
redis的zset的数据结构怎么实现的
跳表,采用多级链表实现。
具体的,每个跳表节点维护一个数组,数组长度为跳表高度,数组元素为该节点在i层的下一个节点。
查询时,找到下一个节点大于目标值时,向下搜索。
删除时,查询并记录每层最后节点,找到后向上删除目标值。
增加时,也是记录,然后向上添加。
期望时间复杂度O(nlogn),最坏O(n^2)
期望空间复杂度O(n),最坏(nlogn)
TCP和UDP区别
1、TCP面向连接,提供可靠传输、UDP不面向连接,提供不可靠输出
2、TCP面向字节流,无边界、UDP面向报文,有边界
3、TCP支持一对一、UDP支持多对多
4、TCP有拥塞控制和流量控制、UDP无
5、TCP有序但速度慢、UDP无序但速度快
6、TCP首部20~60字节、UDP首部8字节
TCP粘包和拆包问题
粘包原因:发送方等到缓冲区满了才发送,接收端不及时接收。
拆包原因:TCP报文长度大于MSS(最大报文长度),TCP报文大于缓冲区剩余大小。
解决办法:固定长度、设置首部并包含数据长度、设置边界。
递归,对当前k个反转,然后向后递归。
当时忘记main函数是static不能直接调用成员函数,要先生成对象或设为静态方法。