目录
- 引出
- Redis的项目应用(一):验证码
- 1.整体流程
- 2.雪花ID
- 1)UUID(Universally Unique Identifier,通用唯一识别码)
- 2)Twitter 的雪花算法(SnowFlake)
- 雪花ID优缺点
- 优点
- 缺点
- 时钟回拨
- 3.雪花id的工具类SnowFlakeUtil.java
- 4.生成验证码
- 前端显示
- 5.JMeter测试验证码生成
- JMeter
- 什么是JMeter?
- JMeter安装配置
- 1.官网下载
- 2.下载后解压
- 3.汉语设置
- JMeter的使用方法
- 1.新建线程组
- 2.设置参数
- 3.添加取样器
- 4.设置参数:协议,ip,端口,请求方式,路径
- 5.添加查看结果树
- 6.启动+查看结果
- 总结
引出
1.验证码:如何生成一个唯一的ID;
2.UUID和雪花ID,雪花ID递增趋势,纯数字;
3.验证码应用,生成验证码,过期时间,存redis,前端显示;
4.JMeter高并发测试,官网下载,汉语设置;
5.JMeter的使用方法;
Redis的项目应用(一):验证码
凡是和抢相关的,redis+MQ
1.整体流程
2.雪花ID
1)UUID(Universally Unique Identifier,通用唯一识别码)
UUID(Universally Unique Identifier,通用唯一识别码)是按照开放软件基金会(OSF)制定的标准计算,用到了以太网卡地址、纳秒级时间、芯片 ID 码和许多可能的数字。
UUID 是由一组 32 位数组成,由16 进制数字所构成,是故 UUID 理论上的总数为16的32次方。这个总数是多大呢?打个比方,如果每纳秒产生 1 百万个 UUID,要花 100 亿年才会将所有 UUID 用完。
UUID 通常以连字号分隔的五组来显示,形式为 8-4-4-4-12
,总共有 36 个字符(即 32 个英数字母和 4 个连字号)。例如: 123e4567-e89b-12d3-a456-426655440000 。
JDK 从 1.5 开始在 java.util 包下提供了一个 UUID 类用来生成 UUID:
UUID uuid = UUID.randomUUID();
String uuidStr1 = uuid.toString();
String uuidStr2 = uuidStr1.replaceAll("-","");
UUID 的缺点和一个『好』ID 的标准
UUID的缺点:
为了得到一个全局唯一 ID,很自然地就会想到 UUID 算法。但是,UUID 算法有明显的缺点:
-
UUID 太长了,通常以 36 长度的字符串表示,很多场景不适用。
-
非纯数字。UUID 中会出现 ABCDEF 这些十六进制的字母,因此,在数据库和代码中,自然就不能存储在整型字段或变量。因此,在数据库中以它作为主键,建立索引的代价比较大,性能有影响。
-
不安全。UUID 中会包含网卡的 MAC 地址。
一个『好』ID 的标准应该有哪些:
-
最好是由纯数字组成。
-
越短越好,最好能存进整型变量和数据库的整型字段中。
-
信息安全。另外,『ID 连续』并非好事情。
-
在不连续的情况下,最好是递增的。即便不是严格递增,至少也应该是趋势递增。
2)Twitter 的雪花算法(SnowFlake)
Snowflake 是 Twitter(美国推特公司)开源的分布式 ID 生成算法。最初 Twitter 把存储系统从 MySQL 迁移到 Cassandra(它是NoSQL数据库),因为Cassandra 没有顺序 ID 生成机制,所以 Twitter 开发了这样一套全局唯一 ID 生成服务。
SnowFlake 优点:
整体上按照时间自增排序,并且整个分布式系统内不会产生 ID 碰撞(由数据中心 ID 和机器 ID 作区分),并且效率较高。经测试,SnowFlake 每秒能够产生 26 万 ID 左右。
Snowflake 会生成一个 long 类型的数值,long是8个字节,一共是64位,Snowflake 对于 long 的各个位都有固定的规范:
位数 | 作用 |
---|---|
时间戳41位 | 当前时间戳与指定的时间戳之间的差值,即:(指定时间戳 - 当前时间戳)。 |
数据中心5位 | 配置文件或环境构成,比如使用ip地址生成。 |
实例(机器标志位)5位 | 同一数据中心,机器码设置。 |
12位毫秒内的计数器 | 1毫秒之内产生不同id |
-
最高位标识(1 位)
由于 long 基本类型在 Java 中是带符号的,最高位是符号位,正数是 0,负数是 1,因为 id 一般是正数,所以最高位是 0 。
-
毫秒级时间戳(41 位)
注意,41 位时间戳不是存储当前时间的时间戳,而是存储时间的差值(当前时间戳 - 开始时间戳) 得到的值,这里的的开始时间,一般是我们的 id 生成器开始使用的时间,由我们程序来指定的(如下面程序 IdGenerator 类的 startTime 属性)。
41 位的时间截,可以使用 69 年。
2的41次方 除以 (1000毫秒 * 60 * 60 * 24 * 365) = 69
-
数据机器位(10 位)
10-bit机器可以分别表示1024台机器,这 10 位的机器位实际上是由 5 位的 互联网数据中心(datacenterId) 和 5 位的工作机器id(workerId) 。这样就可以有32个互联网数据中心(机房)(2的5次方),每个互联网数据中心可以有32台工作机器 。即,总共允许存在 1024 台电脑各自计算 ID 。
每台电脑都由 data-center-id 和 worker-id 标识,逻辑上类似于联合主键的意思。
-
12位的自增序列号,用来记录同毫秒内产生的不同id,就是一毫秒内最多可以产生4096个id
毫秒内的计数,12为的自增序列号 支持每个节点每毫秒(同一机器,同一时间截)产生 4096(2的12次方) 个 ID 序号,这种分配方式可以保证在任何一个互联网数据中心的任何一台工作机器在任意毫秒内生成的ID都是不同的
面试常问:如果是并发量高,同一台机器一毫秒有5000个id,那么id会不会重复,不会,根据源码如果一毫秒内超过4096个id,则会阻塞到下一毫秒再生成
雪花ID优缺点
优点
1、生成速度快经测试,SnowFlake每秒能够产生26万ID左右。
2、生成id有序,雪花算法生成的id整体是递增的,但是不是连续的。
3、本地即可生成,无需消耗额外的资源,如zookeeper、redis等。
缺点
1、id长度比较长(大概18位)
2、id不连续,生成的id是无规则的。
3、如果机器时钟回拨,就会导致id生成失败。
4、不同机器的时钟不是完全一致的,导致全局的id并不是统一向上自增。
时钟回拨
- 人为原因,把系统环境的时间改了。
- 有时候不同的机器上需要同步时间,可能不同机器之间存在误差,那么可能会出现时间回拨问题。
Leaf——美团点评分布式ID生成系统 - 美团技术团队 (meituan.com)
uid-generator/README.zh_cn.md at master · baidu/uid-generator · GitHub
3.雪花id的工具类SnowFlakeUtil.java
<!-- 雪花id-->
<!-- 工具包-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.11</version>
</dependency>
package com.tianju.springboot.util;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.IdUtil;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.PostConstruct;
/**
* 雪花id的工具类
*/
@Slf4j
public class SnowFlakeUtil {
private static long workerId = 0;
private static long datacenterId = 1;
private static Snowflake snowflake = IdUtil.getSnowflake(workerId,datacenterId);
@PostConstruct // 自动调用,在构造方法前做
public void init(){
try {
workerId = NetUtil.ipv4ToLong(NetUtil.getLocalhostStr());
log.info("当前机器的workId:{}",workerId);
}catch (Exception e){
log.error("当前机器的workId获取失败",e);
workerId = NetUtil.getLocalhostStr().hashCode();
}
}
public static synchronized long snowflakeId(){
return snowflake.nextId();
}
public static synchronized String snowflakeIdStr(){
return String.valueOf(snowflake.nextId());
}
public static void main(String[] args) {
System.out.println(SnowFlakeUtil.snowflakeIdStr());
}
}
4.生成验证码
流程:
- 生成4位的验证码;
- 产生唯一的id,作为验证码的key;
- 以key,value的形式存储到redis中;
- 前端显示验证码图片;
设置验证码的有效时间
// 3.redis中存储,key-value,设置有效时间为 60 s
stringRedisTemplate.opsForValue().set(key,code,60,TimeUnit.SECONDS);
package com.tianju.springboot.controller;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import com.tianju.springboot.dto.HttpRespSimple;
import com.tianju.springboot.util.SnowFlakeUtil;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* 雪花id的调用,返回一张图片
*/
@RestController
@RequestMapping("/api/user/code")
public class CodeDemoController {
@Resource
private StringRedisTemplate stringRedisTemplate;
@GetMapping("/snowcode.jpg")
public HttpRespSimple createCode(HttpServletResponse response) throws IOException {
// 1.产生验证码
LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(200, 100, 4, 10);
String code = lineCaptcha.getCode();
// 2.产生唯一的值,作为key,用雪花id作为唯一的key
String key = SnowFlakeUtil.snowflakeIdStr(); // 生成雪花id,string类型的
// 3.redis中存储,key-value,设置有效时间为 60 s
stringRedisTemplate.opsForValue().set(key,code,60,TimeUnit.SECONDS);
// 4.返回给浏览器cookie
response.addCookie(new Cookie("vc", key));
// 5.显示到前端
lineCaptcha.write(response.getOutputStream());
System.out.println(">>>>>>>>>>>>验证码为:"+code);
return new HttpRespSimple(20001, "验证码创建成功");
}
@GetMapping("/inputcode")
public HttpRespSimple inputCode(HttpServletRequest request){
for (Cookie cookie: request.getCookies()) {
if (cookie.getName().equals("vc")){
String key = cookie.getValue();
System.out.println("从redis取出存入的验证码:"+stringRedisTemplate.opsForValue().get(key));
if (Objects.isNull(stringRedisTemplate.opsForValue().get(key))){
return new HttpRespSimple(40001, "验证码无效");
}else {
return new HttpRespSimple(20002, "验证码成功");
}
}
}
return new HttpRespSimple(20003, "验证码不存在");
}
}
前端显示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>验证码</title>
</head>
<body>
<!--http://localhost:9090/api/user/code/snowcode.jpg-->
<img src="/api/user/code/snowcode.jpg">
</body>
</html>
5.JMeter测试验证码生成
1)新建线程组
2)设置参数
3)添加取样器
4)设置参数:协议,ip,端口,请求方式,路径
5)添加查看结果树
6)启动+查看结果
JMeter
什么是JMeter?
Apache JMeter™
The Apache JMeter™ application is open source software, a 100% pure Java application designed to load test functional behavior and measure performance(接口性能),It was originally designed for testing Web Applications but has since expanded to other test functions.
JMeter安装配置
1.官网下载
2.下载后解压
3.汉语设置
JMeter的使用方法
1.新建线程组
2.设置参数
3.添加取样器
4.设置参数:协议,ip,端口,请求方式,路径
5.添加查看结果树
6.启动+查看结果
总结
1.验证码:如何生成一个唯一的ID;
2.UUID和雪花ID,雪花ID递增趋势,纯数字;
3.验证码应用,生成验证码,过期时间,存redis,前端显示;
4.JMeter高并发测试,官网下载,汉语设置;
5.JMeter的使用方法;