mirror of
https://github.com/zhwei820/learn.lianglianglee.com.git
synced 2025-09-17 08:46:40 +08:00
705 lines
22 KiB
HTML
705 lines
22 KiB
HTML
<!DOCTYPE html>
|
||
|
||
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
|
||
|
||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||
|
||
<head>
|
||
|
||
<head>
|
||
|
||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
|
||
|
||
<link rel="icon" href="/static/favicon.png">
|
||
|
||
<title>08 性能测试场景的分类和意义.md.html</title>
|
||
|
||
<!-- Spectre.css framework -->
|
||
|
||
<link rel="stylesheet" href="/static/index.css">
|
||
|
||
<!-- theme css & js -->
|
||
|
||
<meta name="generator" content="Hexo 4.2.0">
|
||
|
||
</head>
|
||
|
||
|
||
|
||
<body>
|
||
|
||
|
||
|
||
<div class="book-container">
|
||
|
||
<div class="book-sidebar">
|
||
|
||
<div class="book-brand">
|
||
|
||
<a href="/">
|
||
|
||
<img src="/static/favicon.png">
|
||
|
||
<span>技术文章摘抄</span>
|
||
|
||
</a>
|
||
|
||
</div>
|
||
|
||
<div class="book-menu uncollapsible">
|
||
|
||
<ul class="uncollapsible">
|
||
|
||
<li><a href="/" class="current-tab">首页</a></li>
|
||
|
||
</ul>
|
||
|
||
|
||
|
||
<ul class="uncollapsible">
|
||
|
||
<li><a href="../">上一级</a></li>
|
||
|
||
</ul>
|
||
|
||
|
||
|
||
<ul class="uncollapsible">
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/说透性能测试/00 开篇词 为什么每个测试人都要学好性能测试?.md.html">00 开篇词 为什么每个测试人都要学好性能测试?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/说透性能测试/01 JMeter 的核心概念.md.html">01 JMeter 的核心概念.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/说透性能测试/02 JMeter 参数化策略.md.html">02 JMeter 参数化策略.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/说透性能测试/03 构建并执行 JMeter 脚本的正确姿势.md.html">03 构建并执行 JMeter 脚本的正确姿势.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/说透性能测试/04 JMeter 二次开发其实并不难.md.html">04 JMeter 二次开发其实并不难.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/说透性能测试/05 如何基于 JMeter API 开发性能测试平台?.md.html">05 如何基于 JMeter API 开发性能测试平台?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/说透性能测试/06 Nginx 在系统架构中的作用.md.html">06 Nginx 在系统架构中的作用.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/说透性能测试/07 你真的知道如何制定性能测试的目标吗?.md.html">07 你真的知道如何制定性能测试的目标吗?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
<a class="current-tab" href="/专栏/说透性能测试/08 性能测试场景的分类和意义.md.html">08 性能测试场景的分类和意义.md.html</a>
|
||
|
||
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/说透性能测试/09 如何制定一份有效的性能测试方案?.md.html">09 如何制定一份有效的性能测试方案?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/说透性能测试/10 命令行监控 Linux 服务器的要点.md.html">10 命令行监控 Linux 服务器的要点.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/说透性能测试/11 分布式服务链路监控以及报警方案.md.html">11 分布式服务链路监控以及报警方案.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/说透性能测试/12 如何把可视化监控也做得酷炫?.md.html">12 如何把可视化监控也做得酷炫?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/说透性能测试/13 Docker 的制作、运行以及监控.md.html">13 Docker 的制作、运行以及监控.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/说透性能测试/14 如何从 CPU 飙升定位到热点方法?.md.html">14 如何从 CPU 飙升定位到热点方法?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/说透性能测试/15 如何基于 JVM 分析内存使用对象?.md.html">15 如何基于 JVM 分析内存使用对象?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/说透性能测试/16 如何通过 Arthas 定位代码链路问题?.md.html">16 如何通过 Arthas 定位代码链路问题?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/说透性能测试/17 如何应对 Redis 缓存穿透、击穿和雪崩?.md.html">17 如何应对 Redis 缓存穿透、击穿和雪崩?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/说透性能测试/18 如何才能优化 MySQL 性能?.md.html">18 如何才能优化 MySQL 性能?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/说透性能测试/19 如何根治慢 SQL?.md.html">19 如何根治慢 SQL?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/说透性能测试/20 结束语 线上全链路性能测试实践总结.md.html">20 结束语 线上全链路性能测试实践总结.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
|
||
|
||
<div class="sidebar-toggle-inner"></div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
<script>
|
||
|
||
function add_inner() {
|
||
|
||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||
|
||
inner.classList.add('show')
|
||
|
||
}
|
||
|
||
|
||
|
||
function remove_inner() {
|
||
|
||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||
|
||
inner.classList.remove('show')
|
||
|
||
}
|
||
|
||
|
||
|
||
function sidebar_toggle() {
|
||
|
||
let sidebar_toggle = document.querySelector('.sidebar-toggle')
|
||
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
|
||
let content = document.querySelector('.off-canvas-content')
|
||
|
||
if (sidebar_toggle.classList.contains('extend')) { // show
|
||
|
||
sidebar_toggle.classList.remove('extend')
|
||
|
||
sidebar.classList.remove('hide')
|
||
|
||
content.classList.remove('extend')
|
||
|
||
} else { // hide
|
||
|
||
sidebar_toggle.classList.add('extend')
|
||
|
||
sidebar.classList.add('hide')
|
||
|
||
content.classList.add('extend')
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
function open_sidebar() {
|
||
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
|
||
let overlay = document.querySelector('.off-canvas-overlay')
|
||
|
||
sidebar.classList.add('show')
|
||
|
||
overlay.classList.add('show')
|
||
|
||
}
|
||
|
||
function hide_canvas() {
|
||
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
|
||
let overlay = document.querySelector('.off-canvas-overlay')
|
||
|
||
sidebar.classList.remove('show')
|
||
|
||
overlay.classList.remove('show')
|
||
|
||
}
|
||
|
||
|
||
|
||
</script>
|
||
|
||
|
||
|
||
<div class="off-canvas-content">
|
||
|
||
<div class="columns">
|
||
|
||
<div class="column col-12 col-lg-12">
|
||
|
||
<div class="book-navbar">
|
||
|
||
<!-- For Responsive Layout -->
|
||
|
||
<header class="navbar">
|
||
|
||
<section class="navbar-section">
|
||
|
||
<a onclick="open_sidebar()">
|
||
|
||
<i class="icon icon-menu"></i>
|
||
|
||
</a>
|
||
|
||
</section>
|
||
|
||
</header>
|
||
|
||
</div>
|
||
|
||
<div class="book-content" style="max-width: 960px; margin: 0 auto;
|
||
|
||
overflow-x: auto;
|
||
|
||
overflow-y: hidden;">
|
||
|
||
<div class="book-post">
|
||
|
||
<p id="tip" align="center"></p>
|
||
|
||
<div><h1>08 性能测试场景的分类和意义</h1>
|
||
|
||
<p>上一讲我带你学习了性能测试的分类和如何制定性能目标,这一讲我将带你学习性能测试场景的分类和意义,这也是决定你是否能做好性能测试的关键。</p>
|
||
|
||
<p>性能测试场景的重要程度类似于业务测试的 case,case 是你进行业务测试的指引,case 是否完善也直接决定了测试的覆盖率。同理,场景是传递执行性能测试的<strong>步骤和目的</strong>,关于这两点是你一定要清楚的。</p>
|
||
|
||
<p>首先带你认识下我认为最重要的三个性能场景:<strong>基准场景</strong>、<strong>单接口负载场景</strong>、<strong>混合场景负载测试</strong>,这几个场景也是我经常说的“基石场景”。接下来为你讲解这三个场景是怎么操作,以及核心目的是什么?</p>
|
||
|
||
<h3>基准场景</h3>
|
||
|
||
<p><strong>基准场景是指单线程或者少量线程</strong>(一般在 5 个线程以下)<strong>对单接口进行测试</strong>,然后将测试结果作为基准数据,在系统调优或者评估的过程中,通过运行相同的业务接口比较测试结果,为系统的优化以及后续测试流程提供决策数据。</p>
|
||
|
||
<p>有人觉得基准测试并不是在高并发下进行的,不算是性能测试,但我认为这其实是性能测试中重要的基础步骤,它有以下作用:</p>
|
||
|
||
<ul>
|
||
|
||
<li>验证测试脚本及测试参数的正确性,同时也可以验证脚本数据是否能够支持重复性测试等;</li>
|
||
|
||
<li>通过少量线程访问系统获取结果数据,作为对比参考基准;</li>
|
||
|
||
<li>根据测试结果,初步判断可能成为系统瓶颈的场景,并决定是否进行后续的测试;</li>
|
||
|
||
<li>基准场景的结果被一部分公司作为上线的基线指标,不达到要求是不允许上线的,这样的场景也经常被固化成自动化的脚本定时触发和巡检。</li>
|
||
|
||
</ul>
|
||
|
||
<h3>单接口负载场景</h3>
|
||
|
||
<p><strong>单接口负载场景就是通过模拟多线程对单接口进行负载测试</strong>。我的具体做法是选定线程数后持续循环运行一定时间,比如分别运行 100 线程、200 线程、300线程等,一般相同线程数运行 10~15 min,然后获取事务响应时间、TPS、报错率,监测测试系统的各服务器资源使用情况(CPU、内存、磁盘、网络等),把具体数据记录之后再开始跑下一个线程数。每一组线程数级别会有对应的 TPS,直到你找到 TPS 的拐点。如下图所示,横坐标是线程数,纵坐标是 TPS,线程数增加到 400 时出现了拐点。</p>
|
||
|
||
<p><img src="assets/Cip5yGAXxtmALS-eAAJEi_Hw2MM930.png" alt="1.png" /></p>
|
||
|
||
<p>这里需要<strong>注意的点</strong>有两个。</p>
|
||
|
||
<ul>
|
||
|
||
<li>使用工具做性能测试时,动辄就是上千的线程数,所以如果你是一位初学者,我更加倾向于你从一个相对<strong>比较低的线程数梯度增加</strong>,这样才能够比较清晰地找到 TPS 的拐点。</li>
|
||
|
||
<li>我还建议为<strong>每个虚拟用户级别做单独的场景</strong>,网上绝大部分的教程,在一个场景中做了很多梯度(如下图所示),这样只是看上去简单方便一些,其实很不利于分析和诊断,这个方式我并不推荐。因为并不是每一个量级的性能表现都是类似的,而且一个场景多梯度出来的报表也可能没你想象中的清晰明了。在 JMeter 的聚合报告中还会将结果数据平均化,<strong>这样的方式并不能准确地记录每个线程梯度对应的 TPS</strong>。而在一个场景里先固定虚拟用户可以将自己的精力聚焦在诊断上。</li>
|
||
|
||
</ul>
|
||
|
||
<p><img src="assets/CgpVE2AXxvSANDuYAAEQbLHr5vg312.png" alt="2.png" /></p>
|
||
|
||
<h3>混合场景负载测试</h3>
|
||
|
||
<p>混合场景是性能测试中最重要的场景之一,这个场景是为了最大程度模拟用户真实的操作。真实的线上操作不只有单接口的操作,一定是多种业务同时在进行,比如张三在浏览商品,李四在添加购物车等。</p>
|
||
|
||
<p>所以混合场景测试会将多个接口按照实际大促时候的比例混合起来,然后增加线程数找出多个接口 TPS 的和对应的峰值。这个比例也是混合场景的关键,在[《07 | 你真的知道如何制定性能测试的目标吗?》]中已经较为详细地阐述了制定比例的方法,本讲就不再赘述。加用户运行的基本策略可以参考上文的<strong>单接口负载测试</strong>。混合场景执行除了要观察总的 TPS,还有一个非常关键的因素就是如何控制接口之间的调用比例,使其不能偏离预期。</p>
|
||
|
||
<p><strong>如何使用 JMeter 去控制场景比例?</strong></p>
|
||
|
||
<p>相信你已经知道线程数可以改变接口的 TPS,但是如果每次通过线程数调整这个过程会比较烦琐。JMeter 提供了一个能较好地解决这个问题的插件,叫作<strong>吞吐量控制器</strong>,它在逻辑控制器组件中,如下图所示:</p>
|
||
|
||
<p><img src="assets/Cip5yGARB_2Ad2-zAACP3jPiNm4329.png" alt="Drawing 2.png" /></p>
|
||
|
||
<p>我来简单介绍一下这个插件配置规则,默认的情况下使用的是<strong>百分比模式</strong>,也就是 Percent Excutions。吞吐量一栏对应的是 TPS 占比,我用 login 和 register 这两个接口来模拟下,</p>
|
||
|
||
<p>login 接口配置比例是 80%,如下图所示,剩下的 20% 配置给 register。</p>
|
||
|
||
<p><img src="assets/Cip5yGARCASAO_NdAABzzrCLU6s777.png" alt="Drawing 3.png" /></p>
|
||
|
||
<p>看下运行后的效果,我直接在 JMeter 中添加聚合报告元件,如下图所示:</p>
|
||
|
||
<p><img src="assets/Cip5yGAXxxiAFWIcAAC-NsocAFk929.png" alt="5.png" /></p>
|
||
|
||
<p>实际计算下来的值为 1778.2/2222.7≈0.8,这个数据是比较准确的。</p>
|
||
|
||
<p>以上是我所说的基石场景,包括基准测试、负载测试、混合场景测试等,这三个场景是有<strong>依次执行的顺序关系</strong>的,按照顺序执行更容易发现问题且减少不必要的工作,比如你连基准测试都不通过,就没有必要进行负载测试了。所以我们在做每一次性能测试时,都不应该省去或者颠倒上述的场景步骤。</p>
|
||
|
||
<p>接着我带你继续学习其他性能测试场景,为了达到相对应的性能测试目的,这些场景可以根据需求进行选择。</p>
|
||
|
||
<h3>异常性能测试</h3>
|
||
|
||
<p>性能测试也是存在异常测试的,顾名思义就是在系统异常的情况下看系统的处理能力或者是通过处理后的恢复能力是如何的。</p>
|
||
|
||
<p>比如在架构的高可用方面,遇到服务的上下线、数据库的主从切换等这些情况的时延是多少、处理能力能不能达到预期标准。另外在目前的电商应用架构中,大促遇到紧急情况经常需要限流和熔断,可能你经常听到这两个词,但不是特别清楚两者的区别。</p>
|
||
|
||
<p>限流就是<strong>控制单位时间内的请求量</strong>,比如说早晚高峰坐地铁,很多入口都会放隔离带,降低乘客流动速度,这就是一种限流方式。</p>
|
||
|
||
<p>熔断就比较直接了,当判断到调用的依赖服务报错到达一定数量后,直接返回一个既定的数据,将不再访问该服务。就像家中的保险丝一样,到达一定条件后,会自行断电,以保障电路安全。所以我们也会测试触发限流和熔断所设置的阈值,并观察在触发后的系统表现是如何的。</p>
|
||
|
||
<h3>稳定性性能测试</h3>
|
||
|
||
<p>性能测试中的稳定性测试是通过给系统加载一定压力的情况下,运行较长一段时间,验证系统是否稳定。通常是采用典型混合场景,应用系统运行 72 小时,查看系统运行指数是否平稳。</p>
|
||
|
||
<p><strong>稳定性测试的注意点</strong></p>
|
||
|
||
<p>稳定性测试在性能测试中是一个<strong>相对严苛</strong>的场景,因为在 72 小时中可能发生的事情太多了,不仅仅是业务承载的问题,还包括你准备的数据、客户端稳定性,甚至硬件设备断网断电等。任何一项意外的发生,都会造成场景的失败。稳定性测试的监控级别也应当更高,一旦有问题,立即钉钉或者电话通知,所以稳定性测试之前需要有充足的预案和监控报警。</p>
|
||
|
||
<h3>经常被问到的问题</h3>
|
||
|
||
<p><strong>什么情况下可以停止负载测试?</strong></p>
|
||
|
||
<p>有同学问我,无论是单接口负载测试还是混合场景的负载测试都是<strong>梯度增加线程数</strong>,那线程数增加到多少程度才可以停止呢?</p>
|
||
|
||
<p>首先我们结合图 1 可以看到,在梯度增加线程数时,TPS 一般会随之发生变化,当你能够根据 TPS 的变化找到相应的峰值且这个值也是符合预期时,便可以停止负载测试了。</p>
|
||
|
||
<p>但是现实的情况并没有这么理想,很多时候当你还没有发现图 1 中的拐点时,接口就可能在报错了,那遇到这样的情况是继续测试还是停止测试呢?这其实是一个约定的问题,即测试的结束条件是什么?</p>
|
||
|
||
<ul>
|
||
|
||
<li>理想的情况下自然是达到目标就停止了。</li>
|
||
|
||
<li>那不理想呢?根据我的经验,会在测试之前组内协商出场景异常情况下的停止条件,比如 CPU 达到 70%,响应时间超过 500 ms,接口正确率低于 99% 等,当触发这些条件时,我将不会继续加线程进行测试了。</li>
|
||
|
||
</ul>
|
||
|
||
<p><strong>混合场景我选取哪个线程梯度的访问量进行测试?</strong></p>
|
||
|
||
<p>这个问题经常被问到,一些同学喜欢基于<strong>峰值处理能力</strong>去进行稳定性测试,这是一个很严格的要求。其实标准因公司的实际体量而异。今年的某电商双 11 实时支付峰值达到 50 w/s,有可能这个值也未必能平稳跑 72 h,但以这个访问量为执行标准已经足够用了。所以对于不同的公司而言,自行选择适当的线程梯度就可以。我经常听到一句话,今年的峰值流量就是明年的正常流量,对于这样飞速发展的公司,我想还是需要基于峰值去执行稳定性测试场景。</p>
|
||
|
||
<p>关于场景的命名一直有同学很困惑,感觉对于同样一个场景,怎么有的人说是混合场景,还有同学说是容量场景。关于场景名字的叫法,不仅不同的公司会不一样,就包括参考资料上也没有形成非常统一的规范,但我认为并不需要用很多精力研究场景的叫法,但你一定要能描述清楚场景的核心目的是什么,执行步骤是什么,这才是需要向你的协作伙伴传递的最准确的信息。</p>
|
||
|
||
<h3>总结</h3>
|
||
|
||
<p>本讲带你系统地学习了性能测试的场景设计,关于三大基石场景的作用以及意义,混合场景的操作方法以及稳定性场景的注意事项,这些是你在性能测试执行过程中经常遇到的问题,也许你还做过其他性能场景,欢迎在留言区留言和我交流。</p>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div>
|
||
|
||
<div style="float: left">
|
||
|
||
<a href="/专栏/说透性能测试/07 你真的知道如何制定性能测试的目标吗?.md.html">上一页</a>
|
||
|
||
</div>
|
||
|
||
<div style="float: right">
|
||
|
||
<a href="/专栏/说透性能测试/09 如何制定一份有效的性能测试方案?.md.html">下一页</a>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
|
||
|
||
</div>
|
||
|
||
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"70997c09aa8f3cfa","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
|
||
|
||
</body>
|
||
|
||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||
|
||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
|
||
|
||
<script>
|
||
|
||
window.dataLayer = window.dataLayer || [];
|
||
|
||
|
||
|
||
function gtag() {
|
||
|
||
dataLayer.push(arguments);
|
||
|
||
}
|
||
|
||
|
||
|
||
gtag('js', new Date());
|
||
|
||
gtag('config', 'G-NPSEEVD756');
|
||
|
||
var path = window.location.pathname
|
||
|
||
var cookie = getCookie("lastPath");
|
||
|
||
console.log(path)
|
||
|
||
if (path.replace("/", "") === "") {
|
||
|
||
if (cookie.replace("/", "") !== "") {
|
||
|
||
console.log(cookie)
|
||
|
||
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
setCookie("lastPath", path)
|
||
|
||
}
|
||
|
||
|
||
|
||
function setCookie(cname, cvalue) {
|
||
|
||
var d = new Date();
|
||
|
||
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
|
||
|
||
var expires = "expires=" + d.toGMTString();
|
||
|
||
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
|
||
|
||
}
|
||
|
||
|
||
|
||
function getCookie(cname) {
|
||
|
||
var name = cname + "=";
|
||
|
||
var ca = document.cookie.split(';');
|
||
|
||
for (var i = 0; i < ca.length; i++) {
|
||
|
||
var c = ca[i].trim();
|
||
|
||
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
|
||
|
||
}
|
||
|
||
return "";
|
||
|
||
}
|
||
|
||
|
||
|
||
</script>
|
||
|
||
|
||
|
||
</html>
|
||
|