社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
包装秒杀令牌前置,需要验证码来错峰
数学公式验证码生成器
新建CodeUtil实现生成验证码
package com.miaoshaproject.util;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import javax.imageio.ImageIO;
public class CodeUtil {
private static int width = 90;// 定义图片的width
private static int height = 20;// 定义图片的height
private static int codeCount = 4;// 定义图片上显示验证码的个数
private static int xx = 15;
private static int fontHeight = 18;
private static int codeY = 16;
private static char[] codeSequence = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
/**
* 生成一个map集合
* code为生成的验证码
* codePic为生成的验证码BufferedImage对象
* @return
*/
public static Map<String,Object> generateCodeAndPic() {
// 定义图像buffer
BufferedImage buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// Graphics2D gd = buffImg.createGraphics();
// Graphics2D gd = (Graphics2D) buffImg.getGraphics();
Graphics gd = buffImg.getGraphics();
// 创建一个随机数生成器类
Random random = new Random();
// 将图像填充为白色
gd.setColor(Color.WHITE);
gd.fillRect(0, 0, width, height);
// 创建字体,字体的大小应该根据图片的高度来定。
Font font = new Font("Fixedsys", Font.BOLD, fontHeight);
// 设置字体。
gd.setFont(font);
// 画边框。
gd.setColor(Color.BLACK);
gd.drawRect(0, 0, width - 1, height - 1);
// 随机产生40条干扰线,使图象中的认证码不易被其它程序探测到。
gd.setColor(Color.BLACK);
for (int i = 0; i < 30; i++) {
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(12);
int yl = random.nextInt(12);
gd.drawLine(x, y, x + xl, y + yl);
}
// randomCode用于保存随机产生的验证码,以便用户登录后进行验证。
StringBuffer randomCode = new StringBuffer();
int red = 0, green = 0, blue = 0;
// 随机产生codeCount数字的验证码。
for (int i = 0; i < codeCount; i++) {
// 得到随机产生的验证码数字。
String code = String.valueOf(codeSequence[random.nextInt(36)]);
// 产生随机的颜色分量来构造颜色值,这样输出的每位数字的颜色值都将不同。
red = random.nextInt(255);
green = random.nextInt(255);
blue = random.nextInt(255);
// 用随机产生的颜色将验证码绘制到图像中。
gd.setColor(new Color(red, green, blue));
gd.drawString(code, (i + 1) * xx, codeY);
// 将产生的四个随机数组合在一起。
randomCode.append(code);
}
Map<String,Object> map =new HashMap<String,Object>();
//存放验证码
map.put("code", randomCode);
//存放生成的验证码BufferedImage对象
map.put("codePic", buffImg);
return map;
}
public static void main(String[] args) throws Exception {
//创建文件输出流对象
OutputStream out = new FileOutputStream("/Users/hzllb/Desktop/javaworkspace/miaoshaStable/"+System.currentTimeMillis()+".jpg");
Map<String,Object> map = CodeUtil.generateCodeAndPic();
ImageIO.write((RenderedImage) map.get("codePic"), "jpeg", out);
System.out.println("验证码的值为:"+map.get("code"));
}
}
在OrderController中加入生成验证码
@RequestMapping(value = "/generateverifycode",method = {RequestMethod.POST,RequestMethod.GET})
@ResponseBody
public void generateeverifycode(HttpServletResponse response) throws BusinessException, IOException {
//根据token获取用户信息
String token = httpServletRequest.getParameterMap().get("token")[0];
if(StringUtils.isEmpty(token)){
throw new BusinessException(EmBusinessError.USER_NOT_LOGIN,"用户还未登陆,不能生成验证码");
}
//获取用户的登陆信息
UserModel userModel = (UserModel) redisTemplate.opsForValue().get(token);
if(userModel == null){
throw new BusinessException(EmBusinessError.USER_NOT_LOGIN,"用户还未登陆,不能生成验证码");
}
Map<String,Object> map = CodeUtil.generateCodeAndPic();
ImageIO.write((RenderedImage) map.get("codePic"), "jpeg", response.getOutputStream());
redisTemplate.opsForValue().set("verify_code_"+userModel.getId(),map.get("code"));
redisTemplate.expire("verify_code_"+userModel.getId(),10,TimeUnit.MINUTES);
}
生成秒杀令牌前验证验证码的有效性
//通过verifycode验证验证码的有效性
String redisVerifyCode = (String) redisTemplate.opsForValue().get("verify_code_"+userModel.getId());
if(StringUtils.isEmpty(redisVerifyCode)) {
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"请求非法");
}
if(!redisVerifyCode.equalsIgnoreCase(redisVerifyCode)) {
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"请求非法,验证码错误");
}
对同一时间固定访问接口的线程数做限制,利用全局计数器,在下单接口OrderController处加一个全局计数器,并支持并发操作,当controller在入口的时候,计数器减1,判断计数器是否大于0,在出口时计数器加一,就可以控制同一时间访问的固定。
令牌桶算法可以做到客户端一秒访问10个流量,下一秒就是下一个10个流量,限定某个时刻的最大值
漏桶算法的目的用来平滑网络流量,没有办法应对突发流量
RateLimiter没有实现令牌桶内定时器的功能,
reserve方法是当前秒的令牌数,如果当前秒内还有令牌就直接返回;
若没有令牌,需要计算下一秒是否有对应的令牌,有一个下一秒计算的提前量
使得下一秒请求过来的时候,仍然不需要重复计算
RateLimiter的设计思想比较超前,没有依赖于人为定时器的方式,而是将整个时间轴
归一化到一个数组内,看对应的这一秒如果不够了,预支下一秒的令牌数,并且让当前的线程睡眠;
如果当前线程睡眠成功,下一秒唤醒的时候令牌也会扣掉,程序也实现了限流
private RateLimiter orderCreateRateLimiter;
@PostConstruct
public void init(){
executorService = Executors.newFixedThreadPool(30);
orderCreateRateLimiter = RateLimiter.create(300);
}
—————————————————————————————————
【本课程已整理完毕】
01_电商秒杀商品回顾
02_云端部署
03_分布式扩展
04_查询性能优化技术之多级缓存
05_查询性能优化技术之页面静态化
06_交易性能优化技术之缓存库存
07_交易性能优化技术之事务型消息
08_流量削峰技术
09_防刷限流技术
10_课程总结
—————————————————————————————————
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!