基础面试题
主要内容包括:HTML,CSS,JavaScript,浏览器,性能优化等等
架构设计
-
老板发红包,此时缓存初始化红包个数,红包金额(单位分),并异步入库。
-
抢红包,判断缓存剩余红包金额,剩余金额大于零则抢到红包,否则手慢了,红包派完了
-
拆红包,根据
redPacketId获取分布式锁,如果获取到锁,红包个数减一,如果剩余红包个数大于零抢红包成功、否则失败。成功则计算红包金额,缓存总红包金额减去抢到的红包金额,异步入库、异步到账。 -
若获取分布式锁失败,使用
Redis的decr命令对红包个数加一。
数据库设计
- 红包信息表
CREATE TABLE red_racket (
id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
red_packet_id bigint(20) NOT NULL COMMENT '红包唯一ID',
total_amount int(11) NOT NULL COMMENT '红包金额单位分',
total_packet int(11) NOT NULL COMMENT '红包个数',
type int(11) NOT NULL COMMENT '红包类型',
create_time datetime DEFAULT NULL COMMENT '创建时间',
version int(11) NOT NULL COMMENT '版本号',
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 CHECKSUM=1 DELAY_KEY_WRITE=1 ROW_FORMAT=DYNAMIC COMMENT='红包信息表'
- 抢红包记录表
CREATE TABLE red_packet_record (
id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
amount int(11) NOT NULL COMMENT '抢到红包的金额',
red_packet_id bigint(20) NOT NULL COMMENT '红包ID',
uid int(11) NOT NULL COMMENT '抢到红包用户的用户标识',
create_time datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8 CHECKSUM=1 DELAY_KEY_WRITE=1 ROW_FORMAT=DYNAMIC COMMENT='抢红包记录表'
代码案例
老板发了10个红包一共200人民币,100个人同时抢红包,伪代码分别为拆红包和抢红包相关业务逻辑。模拟抢红包伪代码:
/**
-
抢红包 拆红包 抢到不一定能拆到
-
@param redPacketId
-
@return
*/
@ApiOperation(value="抢红包二",nickname="爪哇笔记")
@PostMapping("/startTwo")
public Result startTwo(long redPacketId){
int skillNum = 100;
final CountDownLatch latch = new CountDownLatch(skillNum);//N个抢红包
/**
- 初始化红包数据,抢红包拦截
*/
redisUtil.cacheValue(redPacketId+"-num",10);
/**
- 初始化红包金额,单位为分
*/
redisUtil.cacheValue(redPacketId+"-money",20000);
/**
- 模拟100个用户抢10个红包
*/
for(int i=1;i<=skillNum;i++){
int userId = i;
Runnable task = () -> {
/**
- 抢红包 判断剩余金额
*/
Integer money = (Integer) redisUtil.getValue(redPacketId+"-money");
if(money>0){
/**
-
虽然能抢到 但是不一定能拆到
-
类似于微信的 点击红包显示抢的按钮
*/
Result result = redPacketService.startTwoSeckil(redPacketId,userId);
if(result.get("code").toString().equals("500")){
LOGGER.info("用户{}手慢了,红包派完了",userId);
}else{
Double amount = DoubleUtil.divide(Double.parseDouble(result.get("msg").toString()), (double) 100);
LOGGER.info("用户{}抢红包成功,金额:{}", userId,amount);
}
}else{
/**
- 直接显示手慢了,红包派完了
*/
//LOGGER.info("用户{}手慢了,红包派完了",userId);
}
latch.countDown();
};
executor.execute(task);
}
try {
latch.await();
Integer restMoney = Integer.parseInt(redisUtil.getValue(redPacketId+"-money").toString());
LOGGER.info("剩余金额:{}",restMoney);
} catch (InterruptedException e) {
e.printStackTrace();
}
return Result.ok();
}
业务层拆红包:
@Override
@Transactional
public Result startTwoSeckil(long redPacketId, int userId) {
Integer money = 0;
boolean res=false;
try {
/**
- 获取锁 保证红包数量和计算红白金额的原子性操作
*/
res = RedissLockUtil.tryLock(redPacketId+"", TimeUnit.SECONDS, 3, 10);
if(res){
long restPeople = redisUtil.decr(redPacketId+"-num",1);
if(restPeople>0){
/**
- 如果是最后一人
*/
if(restPeople==1){
money = Integer.parseInt(redisUtil.getValue(redPacketId+"-money").toString());
}else{
Integer restMoney = Integer.parseInt(redisUtil.getValue(redPacketId+"-money").toString());
Random random = new Random();
总结
=============================================================
从转行到现在,差不多两年的时间,虽不能和大佬相比,但也是学了很多东西。我个人在学习的过程中,习惯简单做做笔记,方便自己复习的时候能够快速理解,现在将自己的笔记分享出来,和大家共同学习。
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
个人将这段时间所学的知识,分为三个阶段:
第一阶段:HTML&CSS&JavaScript基础
第二阶段:移动端开发技术
第三阶段:前端常用框架
-
推荐学习方式:针对某个知识点,可以先简单过一下我的笔记,如果理解,那是最好,可以帮助快速解决问题;如果因为我的笔记太过简陋不理解,可以关注我以后我还会继续分享。
-
大厂的面试难在,针对一个基础知识点,比如JS的事件循环机制,不会上来就问概念,而是换个角度,从题目入手,看你是否真正掌握。所以对于概念的理解真的很重要。