高性能验证码图片生成

高性能验证码图片设计

介绍

在涉及敏感信息输入或者用户信息获取、校验,经常会使用图形验证码提高安全性。在保障安全性的同时,验证码图片的生成也会给服务带来一定的性能损耗。如果验证码图片获取频率大或者被人恶意攻击,没做优化的话,会让服务器性能瞬间拉低,导致响应变长、拒绝服务。

验证码图片由什么组成呢,直观上看就是文字和背景图。文字并非无中生有的,而是来源于我们提供的字体库,有了字体,就需要将字体绘制到图片上,计算字体的偏移位置,同时为了增加干扰,也会增加一些不同颜色的线条。

验证码图片生成对服务性能的主要影响:

  1. 加载字体文件(消耗磁盘IO)
  2. 绘画(消耗CPU)

优化思路

​ 我们的目的无非就是获取到图片足够的快,首先想到的就是绘图足够的快,那性能就得上去,如果是使用PHP开发的话,那就得使用好的硬盘,比如固态硬盘,把io速度提高。 还要加CPU,提高算力。虽然达到目的了,但是服务器成本也会上去,后期达到瓶颈还得持续投入,这明显不是我们能接受的,一个非核心的东西却要消耗大量的成本,当然你有钱,请随意。也可以从代码优化入手,但这一般都是现成的开源库,会有维护者持续迭代优化,优化空间不大,费时费力,带来的效益低。

​ 从验证码这个场景来看,其实并不需要一次请求就要产生一个图片,我们更多的是想起到一个验证的作用,把门槛稍微提高一些。只要用户看到的不会一直都是同个图片就行,在一定时间内可能会看到一样的图片是可以接受的。

​ 从上可以得到一个优化思路:资源复用(缓存)。

设计方案

​ 对于加载字体文件而言,其实就是一个不会变的资源,只要加载一次到内存里,重复使用不进行释放就能减少磁盘IO。

​ 对于绘图,只要初始化时候,提前把图片绘制出来,保存一批次在内存里就行,当请求过来,只要从中挑取一个。当然图片要有淘汰机制,不能一直不变,不然容易被黑产枚举攻破,所以需要定时生成新的图片进行替换,保证图片是动态变化的。可以看出CPU只是在初始时候和定时会消耗,可以减少很多。跟之前的情况简单对比的话,如果qps是100来说,1min就会有6k的cpu使用消耗,而如果定时5s生成新图片,那1min只会有12次的CPU使用消耗。

功能开发

​ 语言的选择会影响具体方案的选择,

​ 比如PHP,作为一个解释型语言,每一次请求处理都是重新执行,包括内存也是执行完就释放,所以无法做到提供服务的同时只加载一次字体,减少绘画。退而求其次,可以选择将功能分离:

  1. 额外开发脚本,脚本常驻运行,只加载一次字体,然后定时进行绘画,然后将图片资源放在数据组件里(比如redid、mysql)。当获取验证码图片时候,接口按一定策略从数据组件获取,这时候就只消耗网络IO。

  2. 也可以将图片资源不放数据组件,直接生成本地图片,然后与服务共享图片,接口直接从本地读取图片,这时候就只消耗本地磁盘IO。

​ 而使用Go做接口时,是可以常驻提供服务,这样就可以将字体加载进内存进行复用;并且Go提供了协程的能力,就可以异步去执行绘画,当获取图片时候,就可以自己在进程内存内获取,相比PHP就可以把网络IO或者磁盘IO也给省掉。

​ 接下来使用Go介绍 github项目

  1. 初始化两个切片,用于指向图片内存。一个切片作为实际使用,另外一个作为备用进行切换,相当于主从。
    请添加图片描述

  2. 初始化资源池,存放图片的内存块,淘汰图片时,把内存块重置后放回资源池,避免频繁申请内存
    请添加图片描述

  3. 初始化指向偏移量,通过切片偏移量获取对应图片资源
    请添加图片描述

  4. 协程A定时修改切片的指向偏移量,让获取到的图片保持变化

请添加图片描述

  1. 协程B定时绘画,将指针放入备用切片,当备用切片补充完后,就会进行切换(主从)

请添加图片描述

请添加图片描述

对于接口而言,它只要去读取偏移量指定的图片就可以,复杂度O(1)

口说无凭,进行下压测对比优化前后的效果。

运行环境为k8s,限制CPU核心为( request:1, limit:2 ) 并发4 总数2000

  • 优化前
─────┬───────┬───────┬───────┬────────┬────────┬────────┬────────┬────────┬────────┬────────
 耗时│ 并发数│ 成功数│ 失败数│   qps  │最长耗时│最短耗时│平均耗时│下载字节│字节每秒│ 错误码
─────┼───────┼───────┼───────┼────────┼────────┼────────┼────────┼────────┼────────┼────────
   1s│      4│     43│      0│   44.49│  191.49│   43.51│   89.92│        │        │200:43
   2s│      4│     94│      0│   47.75│  191.49│   43.51│   83.76│        │        │200:94
   3s│      4│    141│      0│   47.47│  191.49│   31.42│   84.26│        │        │200:141
   4s│      4│    190│      0│   47.87│  191.49│   31.42│   83.56│        │        │200:190
   5s│      4│    236│      0│   47.62│  191.49│   31.42│   83.99│        │        │200:236
   6s│      4│    281│      0│   47.24│  191.49│   31.42│   84.67│        │        │200:281
   7s│      4│    328│      0│   47.15│  191.49│   31.42│   84.83│        │        │200:328
   8s│      4│    376│      0│   47.25│  191.49│   31.42│   84.67│        │        │200:376

省略N行...

*************************  结果 stat  ****************************
处理协程数量: 4
请求总数(并发数*请求数 -c * -n): 2000 总请求时间: 43.185 秒 successNum: 2000 failureNum: 0
*************************  结果 end   ****************************

请添加图片描述

─────┬───────┬───────┬───────┬────────┬────────┬────────┬────────┬────────┬────────┬────────
 耗时│ 并发数│ 成功数│ 失败数│   qps  │最长耗时│最短耗时│平均耗时│下载字节│字节每秒│ 错误码
─────┼───────┼───────┼───────┼────────┼────────┼────────┼────────┼────────┼────────┼────────
   1s│      4│    547│      0│  552.61│   16.71│    3.64│    7.24│        │        │200:547
   2s│      4│   1068│      0│  538.69│   16.71│    3.64│    7.43│        │        │200:1068
   3s│      4│   1548│      0│  519.89│   18.24│    3.64│    7.69│        │        │200:1548
   4s│      4│   2000│      0│  510.69│   18.24│    3.64│    7.83│        │        │200:2000


*************************  结果 stat  ****************************
处理协程数量: 4
请求总数(并发数*请求数 -c * -n): 2000 总请求时间: 3.965 秒 successNum: 2000 failureNum: 0
*************************  结果 end   ****************************

请添加图片描述
从上可以看出优化前的qps只有不到50,CPU就被限制了。优化后可到达550,CPU几乎无影响,稳定的运行。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值