高并发红包整体设计方案
公司前段时间根据业务方需求需要做一个抢红包的活动,网上也搜索了很多资料。记录下整体的设计思路以及运营过程中的各种问题。
产品需求:
1.红包支持配置开始时间、结束时间、类型(随机金额或固定金额)、单个最小红包金额、单个最大红包金额
2.可领取红包的业务条件(根据业务信息指定某些满足条件的人可以抢)
设计思路:
难点1:红包算法(根据红包配置最大、最小金额、数量生成符合条件的红包集合)
因为红包有配置单个红包的最大和最小金额,所以不能完全使用随机分配的方式。
所以要求:
* 单个红包金额既要大于最小金额,又要小于最大金额
* 根据红包总金额和个数要正好将钱分完
* 单个红包精确到分,也就是小数点后两位
实现代码:
/** @todo 设置随机红包金额* return array*/public function setRandMoney(){$result = [];//取小数点后两位将金额乘100$this->total = $this->total * 100;//红包总金额$this->min = $this->min * 100;//单个红包最小金额$this->max = $this->max * 100;//单个红包最大金额//获取红包平均金额$average = $this->total / $this->num;for ($i = 0; $i < $this->num; $i++) {//因为小红包的数量通常是要比大红包的数量要多的,因为这里的概率要调换过来。//当随机数>平均值,则产生小红包//当随机数<平均值,则产生大红包if (rand($this->min, $this->max) > $average) {// 在平均线上减钱$temp = $this->min + $this->xRandom($this->min, $average);$result[$i] = $temp;$this->total -= $temp;} else {// 在平均线上加钱$temp = $this->max - $this->xRandom($average, $this->max);$result[$i] = $temp;$this->total -= $temp;}}// 如果还有余钱,则尝试加到小红包里,如果加不进去,则尝试下一个。while ($this->total > 0) {for ($i = 0; $i < $this->num; $i++) {if ($this->total > 0 && $result[$i] < $this->max) {$result[$i]++;$this->total--;}}}// 如果钱是负数了,还得从已生成的小红包中抽取回来while ($this->total < 0) {for ($i = 0; $i < $this->num; $i++) {if ($this->total < 0 && $result[$i] > $this->min) {$result[$i]--;$this->total++;}}}if (!empty($result)) {//将红包放入队列之中foreach ($result as $val) {$this->redis->lPush($this->redpack_money_queue . $this->act_id, $val / 100);}return ['code' => '0', 'msg' => 'success'];}return ['code' => '1', 'msg' => '创建红包失败,请检查参数'];}/*** 生产min和max之间的随机数,但是概率不是平均的,从min到max方向概率逐渐加大。* 先平方,然后产生一个平方值范围内的随机数,再开方,这样就产生了一种“膨胀”再“收缩”的效果。*/private function xRandom($bonus_min, $bonus_max){$sqr = intval($this->sqr($bonus_max - $bonus_min));$rand_num = rand(0, ($sqr - 1));return intval(sqrt($rand_num));}private function sqr($n){return $n * $n;}
因为取最小和最大金额之间随机数的时候使用了intval()函数导致该算法只能处理整数,故在处理的时候将金额乘100 ,在最后入队列的时候再将其 除100,这样就将其精确到小数点后两位。
难点2:高并发时对服务器的访问压力
类似抢红包、1元抢购,秒杀等业务场景都是在同一时间大量请求堆积到服务器,从而导致服务器资源紧张,程序处理不过来。那么我们要做的就是将流量控制住,不让大量的请求透过web服务器直接打到数据库层。那么从用户访问url到收到返回结果整体流程是什么样子呢?
客户端层
,用户在微信中打开URL,DNS解析域名至服务器web服务器层
, Apache、Nginx或Tomcat等服务器层
,分配php-fpm进程,代码接收参数进行逻辑处理数据持续化层次
,将结果保存至mysql或Redis层次
客户端层优化方案:(限流)
- 前端URL使用html静态页面显示内容,并将页面显示图片尽量压缩,减少服务器带宽压力。推荐使用base64解码图片
- 使用连接池控制流量,用户点击抢红包时,发起
ajax
请求,调用后台使用java写的redis incr
接口,每次调用则键值 +1,并将自增id返回,当后台代码处理完后再将其键值减掉,因为incr
自增为原子级别,所以前端可以根据当前有多少用户在等待中。 根据自身服务器配置以及业务场景预估N多请求会导致服务器出现问题,如果当前等待处理的请求数大于N则前端提示用户 "当前请求过多,请稍后再试",反之则可以正常发起请求。
Web层优化方案(lua+nginx实现频率控制)
- Nginx来处理访问控制的方法有多种,实现的效果也有多种,访问IP段,访问内容限制,访问频率限制等。
用Nginx+Lua+Redis来做访问限制主要是考虑到高并发环境下快速访问控制的需求。
Nginx处理请求的过程一共划分为11个阶段,分别是:
`post-read、server-rewrite、find-config、rewrite、post-rewrite、 preaccess、access、post-access、try- files、content、log`.在openresty中,可以找到:`set_by_lua`,`access_by_lua`,`content_by_lua`,`rewrite_by_lua`等方法。那么访问控制应该是,`access`阶段。
2.根据请求的ip段来控制访问流量,每次接收到抢红包的url后将redis连接池中id自增,当超过某个峰值时跳转到等待页。
具体配置方案参考:http://homeway.me/2015/08/11/...
php代码层(防止出现发多、重复领取、权限等情况)
- 使用
redis queue
队列功能来控制超发的情况,将每个算出来的小红包lpush
至队列中,每次收到请求后消费最后一个小红包,因为redis的的队列为阻塞模式,所以当队列中为空时是不返回数据的,也就可以保证出现并发时不会一个红包分配给多人。 - 使用
redis list
集合来控制重复领取的情况,每次接收到请求后将用户id放置已领取的集合中(这点很重要,一定要在消费队列前放置集合中,要不会出现因为并发导致重复领取),消费成功则跳出,反之则将其移出已领取集合。 - 因为业务需求处理起来很繁琐,所以在活动创建的时候就根据活动规则将可领取的人员放置集合中,权限判断可以使用待领取集合来控制。
以下为我的代码实现(小菜一枚,大神勿喷)
/** @todo 获取红包金额* @return array*/public function doRush(){$act_info = $this->getPackInfo($this->act_id);if(empty($act_info)){return ['code'=>'1','msg'=>'活动信息错误,请联系管理员'];}if($act_info['start_time'] > now()){return ['code'=>'2','msg'=>"红包尚未开抢,请稍后再试"];}if($act_info['end_time'] <= now()){return ['code'=>'1','msg'=>'活动已结束'];}//将请求用户先放置已领取的集合中if(!$this->redis->sAdd($this->rushed_list_key,$this->user_id)){return ['code'=>'1','msg'=>'每个红包只能领取一次哦'];}$money = $this->redis->lPop($this->redpack_money_queue);if(empty($money)){$this->redis->sRem($this->rushed_list_key,$this->user_id);return ['code'=>'1','msg'=>'您来完了呦,红包已抢光'];}//将已抢的用户和金额记录至队列中$add_res = $this->amountAdd($money);if($add_res['code'] != 0){return ['code'=>'1','msg'=>'系统繁忙,请稍后再试'];}return ['code'=>'0','msg'=>'success','data'=>$money.'元'];}
数据层(使用异步持续化)
- 用户领取成功后,将用户id及领取的金额存至已领取的
redis queue
中,异步进程根据其中的user_id和money值将其数据更新至mysql表中
--------------------------------------------------我是万恶的分割线------------------------------------------------------------------
补充说明:
本人第一次将实际开发过程以及想法落实到书面上,对于我这种小菜来说已经很不错了,恳求各位大神勿喷。其中红包算法和一些处理方案也是第一次接触,参考了网上很多资料,学到了很多。如果你有更好的方案的话多多交流~~
----PHP小菜一枚------
高并发红包整体设计方案相关推荐
- springboot实现高并发红包系统(java 全网最全包括语音口令 文字口令 普通 拼手气)
博主技术笔记 博主开源微服架构前后端分离技术博客项目源码地址,欢迎各位star springboot实现高并发红包系统(全网最全) 下面的业务处理请根据你们实际的场景进行处理 1.sql设计 CREA ...
- 高并发资金交易系统设计方案—百亿双十一、微信红包背后的技术架构
21CTO社区导读 : 今天带来的是一个长篇文章.主要讲解高可用的互联网交易系统架构,包括双十一.支付宝&微博红包技术架构,以及微信红包的技术架构,希望能给各位提供价值. 概述 话说每逢双十一 ...
- 互联网产品之百万级高并发技术整体架构
高并发是由于移动APP或网站PV(page view)即页面浏览量或点击量大,单台服务器无法承载大量访问所带来的压力,因此会采用服务器集群技术,用N台服务器进行分流,对于每次访问采取负载均衡策略,被分 ...
- 高并发高可用处理大数据量
教学大纲: 教学内容 大型互联网三大问题-高并发,高可用,大数据量 第一天内容如下: 1:什么是高并发? 2:为什么要解决高并发 3:画图分析:1) 多用户访问单台App服务器及数据库时,性能分析,瓶 ...
- 面试官绝杀:系统是如何支撑高并发的?
作者 | 中华石杉 责编 | 伍杏玲 很多人面试的时候被问到一个让人特别手足无措的问题:你的系统如何支撑高并发? 大多数同学被问到这个问题压根儿没什么思路去回答,不知道从什么地方说起,其实本质就是没经 ...
- 面试官,再也别问我的系统如何支持高并发了
1.一道面试题的背景引入 这篇文章,我们聊聊大量同学问我的一个问题,面试的时候被问到一个让人特别手足无措的问题:你的系统如何支撑高并发? 大多数同学被问到这个问题压根儿没什么思路去回答,不知道从什么地 ...
- 怎么检测并发处理能力达每秒2万次_系统如何支撑高并发
目录 概述 最简单的系统架构 负载均衡+分库分表 + 读写分离系统架构 负载均衡+分库分表 + 读写分离+缓存集群系统架构 负载均衡+分库分表 + 读写分离+缓存集群+消息中间件集群系统架构 php7 ...
- 500并发 一台服务器的性能_面试官绝杀:系统是如何支撑高并发的?
作者 | 中华石杉 责编 | 伍杏玲 本文经授权转载石杉的架构笔记(ID:shishan100) 很多人面试的时候被问到一个让人特别手足无措的问题:你的系统如何支撑高并发? 大多数同学被问到这个问题压 ...
- redis 缓存数据_Redis 缓存数据方案对比:常规 VS 高并发服务器
1 Redis 是什么 Redis 是一种开源的非关系型数据库.起源于负载较大时,当前关系型数据库无法承载的情况. 到目前为止,Redis 可以用作数据库.缓存.消息处理.Redis 可以存储键和5种 ...
- 面试官问我有没有高并发架构经验,我慌的一批…
目录 一.1 道面试题的背景引入 二.先考虑一个最简单的系统架构 三.系统集群化部署 四.数据库分库分表 + 读写分离 五.缓存集群引入 六.引入消息中间件集群 七.现在能hold住高并发面试题了吗 ...
最新文章
- java 中 bean 的生命周期
- Conda创建环境失败:CondaHTTPError: HTTP 000 CONNECTION FAILED
- js原生方法传参的细节(面试必问)
- nanodet学习笔记 tensorrt
- 解决LiteIDE 中的error process failed to start.
- 在本地库不连接远远程库的情况下操作远程库-----sql server
- IPMI远程管理一点记录
- 0.5px边框,css及sass
- 关于二分查找 使用 lower_bound
- 无法读取项目文件 .csproj
- and5.1PowerManagerService深入分析(四)PMS与Display模块
- 面试题——内网相关(一)
- 计算机网络协议各协议的作用,计算机网络通信协议有哪些?作用是什么?
- Vue项目实现web端飘窗
- PCB封装欣赏了解之旅(上篇)—— 常用元器件
- 20155313 杨瀚 《网络对抗技术》实验五 MSF基础应用
- 用html创建数独,数独做不出来怎么办
- 电源纹波怎么测量,纹波和噪声的区别
- .考试倒计时43天!来提分啦!
- 超详细,Python当中的pip常用命令大全