腾讯经典面试题-如何做一个迷你版的微信抢红包呢?

2023-12-29 22:04

本文主要是介绍腾讯经典面试题-如何做一个迷你版的微信抢红包呢?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  • 👏作者简介:大家好,我是爱吃芝士的土豆倪,24届校招生Java选手,很高兴认识大家
  • 📕系列专栏:Spring源码、JUC源码、Kafka原理、分布式技术原理、数据库技术
  • 🔥如果感觉博主的文章还不错的话,请👍三连支持👍一下博主哦
  • 🍂博主正在努力完成2023计划中:源码溯源,一探究竟
  • 📝联系方式:nhs19990716,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬👀

文章目录

  • 腾讯经典面试题-如何做一个迷你版的微信抢红包呢?
    • 业务描述
    • 需求分析
    • 架构设计
      • 结论
    • 编码实现
      • RedPackageController

腾讯经典面试题-如何做一个迷你版的微信抢红包呢?

业务描述

在这里插入图片描述

需求分析

1 各种节假日,发红包+抢红包,不说了,100%高并发业务要求,不能用mysql来做

2 一个总的大红包,会有可能拆分成多个小红包,总金额= 分金额1+分金额2+分金额3…分金额N

3 每个人只能抢一次,你需要有记录,比如100块钱,被拆分成10个红包发出去,

总计有10个红包,抢一个少一个,总数显示(10/6)直到完,需要记录那些人抢到了红包,重复抢作弊不可以。

4 有可能还需要你计时,完整抢完,从发出到全部over,耗时多少?

5 红包过期,或者群主人品差,没人抢红包,原封不动退回。

6 红包过期,剩余金额可能需要回退到发红包主账户下。

由于是高并发不能用mysql来做,只能用redis,那需要要redis的什么数据类型?

架构设计

难点:

1 拆分算法如何

​ 红包其实就是金额,拆分算法如何 ?给你100块,分成10个小红包(金额有可能小概率相同,有2个红包都是2.58),

​ 如何拆分随机金额设定每个红包里面安装多少钱?

2 次数限制

每个人只能抢一次,次数限制

3 原子性

每抢走一个红包就减少一个(类似减库存),那这个就需要保证库存的-----------------------原子性,不加锁实现

你认为存在redis什么数据类型里面?set ?hash? list?

其关键点在于:

  • 发红包
  • 抢红包:抢,不加锁且原子性,还能支持高并发,且没人一次且有抢红包记录
  • 记红包:记录每个人抢了多少
  • 拆红包:按照拆红包算法,需要满足三个条件

1.所有人抢到金额之和等于红包金额,不能超过,也不能少于

2.每个人至少抢到一分钱

3.要保证所有人抢到金额的几率相等

结论

需要设计一个抢红包业务通用算法

二倍均值法

剩余红包金额为M,剩余人数为N,那么有如下公式:

每次抢到的金额 = 随机区间 (0, (剩余红包金额M ÷ 剩余人数N ) X 2)

这个公式,保证了每次随机金额的平均值是相等的,不会因为抢红包的先后顺序而造成不公平。

举个栗子:

假设有10个人,红包总额100元。

第1次:

100÷10 X2 = 20, 所以第一个人的随机范围是(0,20 ),平均可以抢到10元。假设第一个人随机到10元,那么剩余金额是100-10 = 90 元。

第2次:

90÷9 X2 = 20, 所以第二个人的随机范围同样是(0,20 ),平均可以抢到10元。假设第二个人随机到10元,那么剩余金额是90-10 = 80 元。

第3次:

80÷8 X2 = 20, 所以第三个人的随机范围同样是(0,20 ),平均可以抢到10元。 以此类推,每一次随机范围的均值是相等的。

编码实现

RedPackageController

@RestController
public class RedPackageController
{public static final String RED_PACKAGE_KEY = "redpackage:";public static final String RED_PACKAGE_CONSUME_KEY = "redpackage:consume:";@Resourceprivate RedisTemplate redisTemplate;/*** 拆分+发送红包* http://localhost:5555/send?totalMoney=100&redPackageNumber=5* @param totalMoney* @param redPackageNumber* @return*/@RequestMapping("/send")public String sendRedPackage(int totalMoney,int redPackageNumber){//1 拆红包,总金额拆分成多少个红包,每个小红包里面包多少钱Integer[] splitRedPackages = splitRedPackage(totalMoney, redPackageNumber);//2 红包的全局IDString key = RED_PACKAGE_KEY+IdUtil.simpleUUID();//3 采用list存储红包并设置过期时间redisTemplate.opsForList().leftPushAll(key,splitRedPackages);redisTemplate.expire(key,1,TimeUnit.DAYS);return key+"\t"+"\t"+ Ints.asList(Arrays.stream(splitRedPackages).mapToInt(Integer::valueOf).toArray());}/*** http://localhost:5555/rob?redPackageKey=上一步的红包UUID&userId=1*/// 之所以是这样是因为redis核心是由单线程实现的@RequestMapping("/rob")public String rodRedPackage(String redPackageKey,String userId){//1 验证某个用户是否抢过红包Object redPackage = redisTemplate.opsForHash().get(RED_PACKAGE_CONSUME_KEY + redPackageKey, userId);//2 没有抢过就开抢,否则返回-2表示抢过if (redPackage == null) {// 2.1 从list里面出队一个红包,抢到了一个Object partRedPackage = redisTemplate.opsForList().leftPop(RED_PACKAGE_KEY + redPackageKey);if (partRedPackage != null) {//2.2 抢到手后,记录进去hash表示谁抢到了多少钱的某一个红包redisTemplate.opsForHash().put(RED_PACKAGE_CONSUME_KEY + redPackageKey,userId,partRedPackage);System.out.println("用户: "+userId+"\t 抢到多少钱红包: "+partRedPackage);//TODO 后续异步进mysql或者RabbitMQ进一步处理return String.valueOf(partRedPackage);}//抢完return "errorCode:-1,红包抢完了";}//3 某个用户抢过了,不可以作弊重新抢return "errorCode:-2,   message: "+"\t"+userId+" 用户你已经抢过红包了";}/*** 1 拆完红包总金额+每个小红包金额别太离谱* @param totalMoney* @param redPackageNumber* @return*/private Integer[] splitRedPackage(int totalMoney, int redPackageNumber){int useMoney = 0;Integer[] redPackageNumbers = new Integer[redPackageNumber];Random random = new Random();for (int i = 0; i < redPackageNumber; i++){if(i == redPackageNumber - 1){redPackageNumbers[i] = totalMoney - useMoney;}else{int avgMoney = (totalMoney - useMoney) * 2 / (redPackageNumber - i);redPackageNumbers[i] = 1 + random.nextInt(avgMoney - 1);}useMoney = useMoney + redPackageNumbers[i];}return redPackageNumbers;}
}

这篇关于腾讯经典面试题-如何做一个迷你版的微信抢红包呢?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/550857

相关文章

W外链微信推广短连接怎么做?

制作微信推广链接的难点分析 一、内容创作难度 制作微信推广链接时,首先需要创作有吸引力的内容。这不仅要求内容本身有趣、有价值,还要能够激起人们的分享欲望。对于许多企业和个人来说,尤其是那些缺乏创意和写作能力的人来说,这是制作微信推广链接的一大难点。 二、精准定位难度 微信用户群体庞大,不同用户的需求和兴趣各异。因此,制作推广链接时需要精准定位目标受众,以便更有效地吸引他们点击并分享链接

荣耀嵌入式面试题及参考答案

在项目中是否有使用过实时操作系统? 在我参与的项目中,有使用过实时操作系统。实时操作系统(RTOS)在对时间要求严格的应用场景中具有重要作用。我曾参与的一个工业自动化控制项目就采用了实时操作系统。在这个项目中,需要对多个传感器的数据进行实时采集和处理,并根据采集到的数据及时控制执行机构的动作。实时操作系统能够提供确定性的响应时间,确保关键任务在规定的时间内完成。 使用实时操作系统的

一些其他面试题

阿里二面:那你来说说定时任务?单机、分布式、调度框架下的定时任务实现是怎么完成的?懵了。。_哔哩哔哩_bilibili 1.定时算法 累加,第二层每一个格子是第一层的总时间400 ms= 20 * 20ms 2.MQ消息丢失 阿里二面:高并发场景下引进消息队列有什么问题?如何保证消息只被消费一次?真是捏了一把汗。。_哔哩哔哩_bilibili 发送消息失败

zookeeper相关面试题

zk的数据同步原理?zk的集群会出现脑裂的问题吗?zk的watch机制实现原理?zk是如何保证一致性的?zk的快速选举leader原理?zk的典型应用场景zk中一个客户端修改了数据之后,其他客户端能够马上获取到最新的数据吗?zk对事物的支持? 1. zk的数据同步原理? zk的数据同步过程中,通过以下三个参数来选择对应的数据同步方式 peerLastZxid:Learner服务器(Follo

java常用面试题-基础知识分享

什么是Java? Java是一种高级编程语言,旨在提供跨平台的解决方案。它是一种面向对象的语言,具有简单、结构化、可移植、可靠、安全等特点。 Java的主要特点是什么? Java的主要特点包括: 简单性:Java的语法相对简单,易于学习和使用。面向对象:Java是一种完全面向对象的语言,支持封装、继承和多态。跨平台性:Java的程序可以在不同的操作系统上运行,称为"Write once,

uniapp设置微信小程序的交互反馈

链接:uni.showToast(OBJECT) | uni-app官网 (dcloud.net.cn) 设置操作成功的弹窗: title是我们弹窗提示的文字 showToast是我们在加载的时候进入就会弹出的提示。 2.设置失败的提示窗口和标签 icon:'error'是设置我们失败的logo 设置的文字上限是7个文字,如果需要设置的提示文字过长就需要设置icon并给

HotSpot虚拟机的经典垃圾收集器

读《深入理解Java虚拟机》第三版笔记。 关系 Serial、ParNew、Parallel Scavenge、Parallel Old、Serial Old(MSC)、Concurrent Mark Sweep (CMS)、Garbage First(G1)收集器。 如图: 1、Serial 和 Serial Old 收集器 2、ParNew 收集器 3、Parallel Sc

STL经典案例(四)——实验室预约综合管理系统(项目涉及知识点很全面,内容有点多,耐心看完会有收获的!)

项目干货满满,内容有点过多,看起来可能会有点卡。系统提示读完超过俩小时,建议分多篇发布,我觉得分篇就不完整了,失去了这个项目的灵魂 一、需求分析 高校实验室预约管理系统包括三种不同身份:管理员、实验室教师、学生 管理员:给学生和实验室教师创建账号并分发 实验室教师:审核学生的预约申请 学生:申请使用实验室 高校实验室包括:超景深实验室(可容纳10人)、大数据实验室(可容纳20人)、物联网实验

AIGC6: 走进腾讯数字盛会

图中是一个程序员,去参加一个技术盛会。AI大潮下,五颜六色,各种不确定。 背景 AI对各行各业的冲击越来越大,身处职场的我也能清晰的感受到。 我所在的行业为全球客服外包行业。 业务模式为: 为国际跨境公司提供不同地区不同语言的客服外包解决方案,除了人力,还有软件系统。 软件系统主要是提供了客服跟客人的渠道沟通和工单管理,内部管理跟甲方的合同对接,绩效评估,BI数据透视。 客服跟客人

【Kubernetes】常见面试题汇总(三)

目录 9.简述 Kubernetes 的缺点或当前的不足之处? 10.简述 Kubernetes 相关基础概念? 9.简述 Kubernetes 的缺点或当前的不足之处? Kubernetes 当前存在的缺点(不足)如下: ① 安装过程和配置相对困难复杂; ② 管理服务相对繁琐; ③ 运行和编译需要很多时间; ④ 它比其他替代品更昂贵; ⑤ 对于简单的应用程序来说,可能不