概述
本章介紹了常見的黃牛入侵手段舵匾,以及如何使用對(duì)應(yīng)的防刷手段防止黃牛入侵。同時(shí)業(yè)務(wù)的發(fā)展預(yù)估永遠(yuǎn)可能高于系統(tǒng)可承載的能力,因此介紹了使用多種限流技術(shù)保證系統(tǒng)的穩(wěn)定门怪。
1. 驗(yàn)證碼
- 包裝秒殺令牌設(shè)置,需要驗(yàn)證碼來錯(cuò)峰锅纺,分散用戶的請(qǐng)求掷空;
- 數(shù)學(xué)公式驗(yàn)證碼生成器;
1.1 代碼實(shí)現(xiàn)
@RequestMapping(value = "/generateverifycode", method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
public void generateverifycode(HttpServletResponse response) throws BusinessException, IOException {
String token = httpServletRequest.getParameterMap().get("token")[0];
if (StringUtils.isEmpty(token)) {
throw new BusinessException(EmBusinessError.USER_NOT_LOGIN, "用戶還未登陸囤锉,不能生成驗(yàn)證碼");
}
UserModel userModel = (UserModel) redisTemplate.opsForValue().get(token);
if (userModel == null) {
throw new BusinessException(EmBusinessError.USER_NOT_LOGIN, "用戶還未登陸坦弟,不能生成驗(yàn)證碼");
}
Map<String, Object> map = CodeUtil.generateCodeAndPic();
redisTemplate.opsForValue().set("verify_code_" + userModel.getId(), map.get("code"));
System.out.println("驗(yàn)證碼為" + map.get("code"));
redisTemplate.expire("verify_code_" + userModel.getId(), 10, TimeUnit.MINUTES);
ImageIO.write((RenderedImage) map.get("codePic"), "jpeg", response.getOutputStream());
}
Map<String, Object> map = CodeUtil.generateCodeAndPic();
1.生成驗(yàn)證碼字符串與之對(duì)應(yīng)的圖片
2.將字符串返回給
2. 限流
流量遠(yuǎn)比你想象的要多;
系統(tǒng)能運(yùn)行或者總比掛了要好官地;
寧愿讓少數(shù)人能用酿傍,也不要讓所有人不能用;
限流方案
2.1 限并發(fā)
例如同一時(shí)間固定訪問接口的線程數(shù)驱入,利用全局計(jì)數(shù)器赤炒,當(dāng)ServerController被喚醒某一個(gè)需要限制的接口瓮具,那我們就將下單接口Controller的入口處加一個(gè)全局計(jì)數(shù)器务蝠,并且要支持并發(fā)下的減和加的操作块蚌,當(dāng)controller在入口的時(shí)候尔艇,將計(jì)數(shù)器減1睛藻,判斷一下計(jì)數(shù)器的數(shù)字是否大于0剧蚣,在controller出口的時(shí)候?qū)⒂?jì)數(shù)器加1意敛,就可以做到同一時(shí)間內(nèi)對(duì)計(jì)數(shù)器的操作是固定的眷细,一旦減到0或者變?yōu)樨?fù)數(shù)巡通,就要處理對(duì)應(yīng)的問題尘执;
2.2 令牌桶算法
假設(shè)有一個(gè)桶內(nèi)放了許多令牌舍哄,假設(shè)用戶要請(qǐng)求對(duì)應(yīng)的實(shí)體,需要先獲取一個(gè)令牌正卧;初始狀態(tài)下令牌桶內(nèi)有10個(gè)令牌蠢熄,客戶端獲取一個(gè)令牌,令牌數(shù)減一炉旷;設(shè)置一個(gè)定時(shí)器签孔,每秒會(huì)往令牌桶內(nèi)放置10個(gè)令牌,這樣就可以做到客戶端一秒可以訪問10個(gè)對(duì)應(yīng)的流量進(jìn)去窘行,下一秒就是下一個(gè)10個(gè)饥追;可以限定某一時(shí)刻的最大值,應(yīng)對(duì)突發(fā)流量罐盔;
2.3 漏桶算法原理
有一個(gè)桶但绕,初始是滿的,有10滴水惶看,每秒流出一滴水捏顺;客戶端請(qǐng)求的時(shí)候是往客戶端里面加一滴水;
如果桶是滿的這一滴水就加不進(jìn)去纬黎;漏桶算法沒有辦法應(yīng)對(duì)突發(fā)流量幅骄,其目的是用來平滑網(wǎng)絡(luò)流量,固定的速度對(duì)應(yīng)的操作本今。
接口維度
總維度
假設(shè)系統(tǒng)有10個(gè)接口拆座,分別是商品詳情,下單列表冠息、用戶登錄注冊(cè)等挪凑,假設(shè)每個(gè)接口都可以承載5tps的流量,對(duì)應(yīng)10個(gè)接口就是50tps,那我們的系統(tǒng)真的能承載50tps嗎逛艰?躏碳,一般要比接口維度的總和要小20%左右;
限流范圍
集群限流:依賴Redis或其它中間件技術(shù)做統(tǒng)一計(jì)數(shù)器散怖,往往會(huì)產(chǎn)生性能瓶頸唐断;
單機(jī)限流:負(fù)載均衡的前提下單機(jī)平均限流效果更好;
限流代碼實(shí)現(xiàn)
private RateLimiter orderCreateRateLimiter;
@PostConstruct
public void init() {
executorService = new ThreadPoolExecutor(20, 20,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(1024), new ThreadPoolExecutor.AbortPolicy());
orderCreateRateLimiter = RateLimiter.create(300);
}
//封裝下單請(qǐng)求
@RequestMapping(value = "/createorder", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED})
@ResponseBody
public CommonReturnType createOrder(@RequestParam(name = "itemId") Integer itemId,
@RequestParam(name = "amount") Integer amount,
@RequestParam(name = "promoId", required = false) Integer promoId,
@RequestParam(name = "promoToken", required = false) String promoToken) throws BusinessException {
if (orderCreateRateLimiter.acquire() < 0) {
throw new BusinessException(EmBusinessError.RATELIMIT);
}
}