文/侯本文
大促是电商的一个重要推广方式,它通过各种活动实现人气、销售额与品牌的全方位提升。一个促销活动成功与否取决于吸引了多少用户,创造了多少收入,让多少人了解到你这个品牌。所以大促的特点是:
一次大促的成功不仅取决于市场的推广、运营的设计,和产品的稳定性更是密不可分,对于测试和开发来说,同样面临巨大的挑战:
那针对这些挑战,我们是怎么应对的呢?
我们以一张图来描述应对一次电商大促性能测试的主要流程以及相关工作内容。
从上图中可以看到,整个流程中需要进行需求抽取,包含得到一些预期指标以及测试场景等。然后针对需求进行方案设计,包括场景设计以及选取测试手段等。在方案设计的基础上,即可进行测试执行。最后,根据测试的结果整理测试报告并提出潜在的风险。
下面我们以一次电商大促为例,介绍下大促性能测试的完整过程以及遇到的一些问题和解决方法。
需求分析中的重点是沟通,和运营以及市场推广的同事进行沟通,和相关功能开发同事进行沟通。 和运营以及市场同事沟通的目的在于确定大促期间会有哪些活动以及活动期间预期的UV、PV数。例如,本次大促期间有以下活动:一元秒杀、省钱榜、炫富、买就送等。且经过市场的推广,预计日UV在300W左右,PV数在1500w左右。
在活动确定以后,和相关业务开发沟通哪些活动需要进行性能压测。一般情况下我们遵循以下三个原则进行场景的筛选:高频调用的请求,业务逻辑实现复杂度高的请求,以及涉及电商的核心业务,如下单、支付等。
基于这些原则和上面的活动,我们确定这次大促需要覆盖以下场景:
确定好本次大促的测试场景后,需要对测试场景设计测试方案,在设计测试方案之前,我们需要先基于了解到的UV、PV预估下并发量。
并发量的预估一般基于历史统计:
例如秒杀的UV大约是日UV的0.5%~1%,则可以预估参与这次秒杀人数为1.5w~3w人;主会场的PV是日PV的3~5%,则基于web产品常见的80-50原则,即80%的用户访问集中在12个小时内,则每秒钟大概有15个人访问主会场。
性能测试场景设计是否合理直接关系到测试结果是否准确,是否能发现潜在的性能BUG。一般我们在设计一个场景时,需要考虑以下问题:
下面我们以秒杀场景为例,简单描述下该场景的设计方案:
上面介绍的大促活动场景只是指针对这次大促的新增活动进行单场景测试,防止新增活动出现性能问题。但是针对整个大促来说,仅仅这些测试场景是不够的,我们还需要在混合场景下进行整体性能回归。
混合场景的设计原则是需要覆盖所有的核心业务以及高频业务,尽可能地保障整个系统无性能问题,所以混合场景包含:背景压力场景和活动场景两大类。
所谓背景压力即常规压力,和大促无关,是日常用户访问的请求,这些请求的提取原则同样为:
提取方式基于对业务的了解以及线上方位日志的统计,因此我们背景压力场景设计如下:
活动场景的设计原则是需要包含这次促销中涉及到的各种活动,例如包含:
常规情况下,我们使用性能测试平台即可以完成普通场景的测试工作,但是对于特殊的场景来说,需要特殊的手段进行测试:
(1) 秒杀场景的测试过程
秒杀场景具有一定的特殊性,参与秒杀的用户都要等待秒杀开始那一刻发送下单请求,这种场景,我们需要利用集合点的概念进行模拟。
所谓集合点即并发用户等待同一时刻发送请求。Grinder支持集合点的实现。
测试脚本简单实现如下:
from net.grinder.script.Grinder import grinder
class TestRunner:
def __init__(self):
# Each worker thread joins the barrier.
self.phase1CompleteBarrier = grinder.barrier("Phase 1")
def __call__(self):
# ... Phase 1 actions.
# Wait for all worker threads to reach this point before proceeding.
self.phase1CompleteBarrier.await()
# ... Further actions.
(2) HttpAsyncClient进行洪水压力的测试
常用的测试工具Grinder、Jmeter等都是同步测试工具,下一个请求的处理是在等待上一个请求结束后才发出的。但有些时候,我们需要模拟洪水压力,不会等待上一个请求结束才发起下一个请求。这个时候:我们需要异步压测工具,这里给大家推荐HttpAsyncClient。HttpAsyncClient是在HttpClient进化到4.x后,官方提供的基于NIO的异步版本。
Java利用httpasyncclient进行异步HTTP请求的部分代码实现如下:
public class AsyncClientHttpExchange {
public static void main(final String[] args) throws Exception {
CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault();// 默认的配置
try {
httpclient.start();
HttpGet request = new HttpGet("http://www.kaola.com/");
Future<HttpResponse> future = httpclient.execute(request, null);
HttpResponse response = future.get();// 获取结果
System.out.println("Response: " + response.getStatusLine());
System.out.println("Shutting down");
} finally {
httpclient.close();
}
System.out.println("Done");
}
}
参考文档:http://hc.apache.org/httpcomponents-asyncclient-dev/examples.html
线下测试只是保障手段的一种,对于这种促销活动做好线上保护同样至关重要。目前遇到过的保护措施主要有以下几个方面:
(1) 尽可能降低秒杀对后端服务的压力
对于秒杀这种特殊场景,瞬间会有大量的用户执行下单等操作,为了减少数据库等后端服务的压力,我们需要在压力达到数据库之前做好限流保护。
保护策略1:隔离大部分请求在下单服务外:用户可以先去秒杀等服务中获取资格,拥有资格的用户才能进行下一步下单请求;
保护策略2:通过反垃圾手段过滤非常规请求,同一个IP地址、用户名、DeviceID等请求在一秒内不允许多次访问;
保护策略3:通过验证码等手段降低到后端数据库的压力;
保护策略4:通过信号量等控制服务器端同时执行下单请求的个数,从而降低数据库压力。
(2) varnish静态化减少服务压力
页面静态化是搭建高性能网站常用的方式之一,通过页面静态化可以有效的提升系统响应速度。varnish是一种常见的内存缓存服务,具备三种功能:反向代理、高速缓存以及静态页面和动态内容在服务器端的整合。
配置策略:
参考手册:https://www.varnish-cache.org/docs/4.1/users-guide/index.html
(3) Nginx层做限流保护
Nginx自身有请求限制模块ngx_http_limit_req_module、流量限制模块ngx_stream_limit_conn_module。基于令牌桶算法,可以方便的控制令牌速率,自定义调节限流,实现基本的限流控制。
主要实现方式如下:
limit_req_zone $uri zone=limit_site:64m rate=2000r/s;
#针对uri进行限制,显示请求频率为 2000/s
location /activity/page.html {
limit_req zone=limit_site burst=1000 nodelay;
#burst表示缓存住的请求数目
proxy_pass http://upstreamsite;
}
参考手册:http://nginx.org/en/docs/http/ngx_http_limit_req_module.html
侯本文 2012年2月入职网易,先后对Lofter、云课堂、网易宝、跨境电商、云信等多个产 |
网易云新用户大礼包:https://www.163yun.com/gift
本文来自网易实践者社区,经作者侯本文授权发布。