简介:目前用的常用测试工具对比
目录
bin:核心可执行文件,包含配置
jmeter.bat: windows启动文件(window系统一定要配置显示文件拓展名)
jmeter: mac或者linux启动文件
jmeter-server:mac或者Liunx分布式压测使用的启动文件
jmeter-server.bat:window分布式压测使用的启动文件
jmeter.properties: 核心配置文件
extras:插件拓展的包
lib:核心的依赖包
Jmeter语言版本中英文切换
配置文件修改
简介:讲解Jmeter里面GUI菜单栏主要组件
添加->threads->线程组(控制总体并发)
线程数:虚拟用户数。一个虚拟用户占用一个进程或线程
准备时长(Ramp-Up Period(in seconds)):全部线程启动的时长,比如100个线程,20秒,则表示20秒内 100个线程都要启动完成,每秒启动5个线程
循环次数:每个线程发送的次数,假如值为5,100个线程,则会发送500次请求,可以勾选永远循环
线程组->添加-> Sampler(采样器) -> Http (一个线程组下面可以增加几个Sampler)
名称:采样器名称
注释:对这个采样器的描述
web服务器:
默认协议是http
默认端口是80
服务器名称或IP :请求的目标服务器名称或IP地址
路径:服务器URL
查看测试结果
线程组->添加->监听器->察看结果树
线程组->添加->监听器->聚合报告
常规压测流程
简介:调用第三方服务组件改造+Jmeter5.x性能压测实践
简介:高并发下异步请求解决方案一- @Async组件应用实战
//启动类增加 @EnableAsync
// @Override
@Async
public void testSend() {
// try {
// TimeUnit.MILLISECONDS.sleep(2000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
long beginTime = CommonUtil.getCurrentTimestamp();
ResponseEntity<String> forEntity = restTemplate.getForEntity("http://old.xdclass.net", String.class);
String body = forEntity.getBody();
long endTime = CommonUtil.getCurrentTimestamp();
log.info("耗时={},body={}",endTime-beginTime,body);
}
简介:异步调用-压测高QPS后的背后原因和问题拆解
简介:高并发下异步请求 @Async+ThreadPoolTaskExecutor自定义线程池实战
@Configuration
@EnableAsync
public class ThreadPoolTaskConfig {
@Bean("threadPoolTaskExecutor")
public ThreadPoolTaskExecutor threadPoolTaskExecutor(){
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
//线程池创建的核心线程数,线程池维护线程的最少数量,即使没有任务需要执行,也会一直存活
//如果设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭
threadPoolTaskExecutor.setCorePoolSize(4);
//最大线程池数量,当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务
//当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常
threadPoolTaskExecutor.setMaxPoolSize(8);
//缓存队列(阻塞队列)当核心线程数达到最大时,新任务会放在队列中排队等待执行
threadPoolTaskExecutor.setQueueCapacity(124);
//当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize
//允许线程空闲时间60秒,当maxPoolSize的线程在空闲时间到达的时候销毁
//如果allowCoreThreadTimeout=true,则会直到线程数量=0
threadPoolTaskExecutor.setKeepAliveSeconds(30);
//spring 提供的 ThreadPoolTaskExecutor 线程池,是有setThreadNamePrefix() 方法的。
//jdk 提供的ThreadPoolExecutor 线程池是没有 setThreadNamePrefix() 方法的
threadPoolTaskExecutor.setThreadNamePrefix("小滴课堂Spring自带Async前缀:");
threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true);
// rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CallerRunsPolicy():交由调用方线程运行,比如 main 线程;如果添加到线程池失败,那么主线程会自己去执行该任务,不会等待线程池中的线程去执行
//AbortPolicy():该策略是线程池的默认策略,如果线程池队列满了丢掉这个任务并且抛出RejectedExecutionException异常。
//DiscardPolicy():如果线程池队列满了,会直接丢掉这个任务并且不会有任何异常
//DiscardOldestPolicy():丢弃队列中最老的任务,队列满了,会将最早进入队列的任务删掉腾出空间,再尝试加入队列
threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
}
//使用实战, 启动类可以不加@EnableAsync,改上面加
@Async("threadPoolTaskExecutor")
简介:ThreadPoolTaskExecutor线程池的面试题你知道怎么回答不
简介:实践出真知-线程池多参数调整-现象报告对比分析
异步发送 + resttemplate未池化
threadPoolTaskExecutor.setCorePoolSize(4);
threadPoolTaskExecutor.setMaxPoolSize(16);
threadPoolTaskExecutor.setQueueCapacity(32);
异步发送+resttemplate未池化
threadPoolTaskExecutor.setCorePoolSize(32);
threadPoolTaskExecutor.setMaxPoolSize(64);
threadPoolTaskExecutor.setQueueCapacity(10000);
//如果等待队列长度为10万,则qps瞬间很高8k+,可能oom
问题
简介: RestTemplate里面的存在的问题你知道多少
简介: 高性能RestTemplate封装配置实战
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
return new RestTemplate(factory);
}
@Bean
public ClientHttpRequestFactory httpRequestFactory() {
return new HttpComponentsClientHttpRequestFactory(httpClient());
}
/**
* @return
*/
@Bean
public HttpClient httpClient() {
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLConnectionSocketFactory.getSocketFactory())
.build();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
//设置整个连接池最大连接数
connectionManager.setMaxTotal(500);
//MaxPerRoute路由是对maxTotal的细分,每个主机的并发,这里route指的是域名
connectionManager.setDefaultMaxPerRoute(200);
RequestConfig requestConfig = RequestConfig.custom()
//返回数据的超时时间
.setSocketTimeout(20000)
//连接上服务器的超时时间
.setConnectTimeout(10000)
//从连接池中获取连接的超时时间
.setConnectionRequestTimeout(1000)
.build();
return HttpClientBuilder.create()
.setDefaultRequestConfig(requestConfig)
.setConnectionManager(connectionManager)
.build();
}
简介: 【10倍+提升】Jmeter5.x压测 优化后RestTemplate前后性能对比
简介:调用第三方短信验证码组件性能优化实战
简介: 说说小滴课堂前不久的被薅羊毛的事情+防范解决方案
请大家先看一个自动化脚本技术视频
短信邮箱轰炸机
手机短信轰炸机是批量、循环给手机无限发送各种网站的注册验证码短信的方法。
最早发明是用来整治街头广告电话号泛滥的一种手段,采用“手机短信轰炸机”软件可无限发送垃圾短信到牛皮癣小广告的手机号码上,使对方的手机快速消耗电量,变成高频率振动棒,且无法正常使用。
“短信轰炸机”可严厉打击城市“牛皮癣”,还城市明净容颜。
某次大型程序员相亲现场-老王得罪了小王, 小王不爽,就道听途说知道了”短信轰炸机“,1天50元,轰炸了5天还打折300元。
一天内接到来自全国各地数千个陌生电话短信的轰炸骚扰,导致个人通讯中断,被工作生活受到严重影响,连刚相亲到的女友没没法联系上了。
原理
很多人都用手机注册一些网站的验证了,比如手机验证码。先填手机号,然后发一条验证码过去,输入验证码,完成验证,注册成功。
* 寻找大量肉鸡网站,寻找发送验证码的请求接口
* 如果找不到接口,也可以使用自动化UI工具触发
* 编写程序和调度任务,相关脚本录入数据库
* 输入目标手机号或者邮箱,触发攻击
公司带来的损失
如何避免自己的网站成为”肉鸡“或者被刷呢
是否可以一劳永逸???
小滴课堂注册页面-短信验证码没加防范,被刷了,也不知道意图如何,反正就亏了几万块吧
简介:谷歌开源kaptcha图形验证码开发
<!--kaptcha依赖包-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>kaptcha-spring-boot-starter</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>kaptcha-spring-boot-starter</artifactId>
</dependency>
@Configuration
public class CaptchaConfig {
/**
* 验证码配置
* Kaptcha配置类名
*
* @return
*/
@Bean
@Qualifier("captchaProducer")
public DefaultKaptcha kaptcha() {
DefaultKaptcha kaptcha = new DefaultKaptcha();
Properties properties = new Properties();
// properties.setProperty(Constants.KAPTCHA_BORDER, "yes");
// properties.setProperty(Constants.KAPTCHA_BORDER_COLOR, "220,220,220");
// //properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_COLOR, "38,29,12");
// properties.setProperty(Constants.KAPTCHA_IMAGE_WIDTH, "147");
// properties.setProperty(Constants.KAPTCHA_IMAGE_HEIGHT, "34");
// properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_SIZE, "25");
// //properties.setProperty(Constants.KAPTCHA_SESSION_KEY, "code");
//验证码个数
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
// properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Courier");
//字体间隔
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_SPACE,"8");
//干扰线颜色
// properties.setProperty(Constants.KAPTCHA_NOISE_COLOR, "white");
//干扰实现类
properties.setProperty(Constants.KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise");
//图片样式
properties.setProperty(Constants.KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.WaterRipple");
//文字来源
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_STRING, "0123456789");
Config config = new Config(properties);
kaptcha.setConfig(config);
return kaptcha;
}
}
简介:池化思想应用-Redis6.X配置连接池实战
连接池好处
连接池配置 common项目
<!--redis客户端-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
配置Redis连接
redis:
client-type: jedis
host: 120.79.150.146
password: xdclass.net
port: 6379
jedis:
pool:
# 连接池最大连接数(使用负值表示没有限制)
max-active: 100
# 连接池中的最大空闲连接
max-idle: 100
# 连接池中的最小空闲连接
min-idle: 100
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: 60000
序列化配置
@Configuration
public class RedisTemplateConfiguration {
/**
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 使用Jackson2JsonRedisSerialize 替换默认序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
// 设置key和value的序列化规则
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
// 设置hashKey和hashValue的序列化规则
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
return redisTemplate;
}
}
简介:账号微服务开发图形验证码接口+Try-with-resource知识巩固
/**
*临时使用10分钟有效,方便测试
*/
private static final long CAPTCHA_CODE_EXPIRED = 60 * 1000 * 10;
/**
* 获取图形验证码
* @param request
* @param response
*/
@GetMapping("captcha")
public void getCaptcha(HttpServletRequest request, HttpServletResponse response) {
String captchaText = captchaProducer.createText();
log.info("图形验证码:{}", captchaText);
//存储
redisTemplate.opsForValue().set(getCaptchaKey(request),
captchaText, CAPTCHA_CODE_EXPIRED, TimeUnit.MILLISECONDS);
BufferedImage bufferedImage = captchaProducer.createImage(captchaText);
try (ServletOutputStream outputStream = response.getOutputStream()){
ImageIO.write(bufferedImage, "jpg", outputStream);
outputStream.flush();
} catch (IOException e) {
log.error("获取图形验证码异常:{}", e);
}
}
简介:注册短信验证码接口开发
@Override
public JsonData sendCode(SendCodeEnum sendCodeType, String to) {
if(CheckUtil.isEmail(to)){
//邮箱验证码
}else if(CheckUtil.isPhone(to)){
//短信验证码
}
return JsonData.buildResult(BizCodeEnum.CODE_TO_ERROR);
}
public class CheckUtil {
/**
* 邮箱正则
*/
private static final Pattern MAIL_PATTERN = Pattern.compile("^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$");
/**
* 手机号正则
*/
private static final Pattern PHONE_PATTERN = Pattern.compile("^((13[0-9])|(14[0-9])|(15[0-9])|(17[0-9])|(18[0-9]))\\d{8}$");
/**
* @param email
* @return
*/
public static boolean isEmail(String email) {
if (null == email || "".equals(email)) {
return false;
}
Matcher m = MAIL_PATTERN.matcher(email);
return m.matches();
}
/**
*
* @param phone
* @return
*/
public static boolean isPhone(String phone) {
if (null == phone || "".equals(phone)) {
return false;
}
Matcher m = PHONE_PATTERN.matcher(phone);
boolean result = m.matches();
return result;
}
}
简介:注册短信验证码防刷方案你能想到几个
需求:一定时间内禁止重复发送短信,大家想下有哪几种实现方式
方式一:前端增加校验倒计时,不到60秒按钮不给点击
简单
不安全,存在绕过的情况
方式二:增加Redis存储,发送的时候设置下额外的key,并且60秒后过期
非原子操作,存在不一致性
增加的额外的key - value存储,浪费空间
/**
* 前置:判断是否重复发送
*
* 1、存储验证码到缓存
*
* 2、发送短信验证码
*
* 后置:存储发送记录
**/
方式三:基于原先的key拼装时间戳
简介:注册邮箱验证码防刷落地和整体测试
@Override
public JsonData sendCode(SendCodeEnum sendCodeEnum, String to) {
String cacheKey = String.format(RedisKey.CHECK_CODE_KEY,sendCodeEnum.name(),to);
String cacheValue = redisTemplate.opsForValue().get(cacheKey);
//如果不为空,再判断是否是60秒内重复发送 0122_232131321314132
if(StringUtils.isNotBlank(cacheValue)){
long ttl = Long.parseLong(cacheKey.split("_")[1]);
//当前时间戳-验证码发送的时间戳,如果小于60秒,则不给重复发送
long leftTime = CommonUtil.getCurrentTimestamp() - ttl;
if( leftTime < (1000*60)){
log.info("重复发送短信验证码,时间间隔:{}秒",leftTime);
return JsonData.buildResult(BizCodeEnum.CODE_LIMITED);
}
}
String code = CommonUtil.getRandomCode(6);
//生成拼接好验证码
String value = code+"_"+CommonUtil.getCurrentTimestamp();
redisTemplate.opsForValue().set(cacheKey,value,CODE_EXPIRED,TimeUnit.MILLISECONDS);
if(CheckUtil.isEmail(to)){
//发送邮箱验证码 TODO
}else if(CheckUtil.isPhone(to)){
//发送手机验证码
smsComponent.send(to,smsConfig.getTemplateId(),code);
}
return JsonData.buildSuccess();
}