后端性能测试总结

三个指标:

并发用户数:取决于 业务并发用户数 和 用户行为模式。

响应时间:前端+后端响应时间。

系统吞吐量:是最能直接体现软件系统负载承受能力的指标。 Requests/Second ,系统吞吐量是最能直接体现软件系统承受负载能力的指标,但也必须和其他  指标一起使用才能更好地说明问题。

并发用户数、响应时间、系统吞吐量之间的关系:

阶段1 :空闲区间
2:  线性增长区间
3:  拐点:系统的处理能力逐渐趋于饱和
4: 过饱和区间:系统处于被压垮的状态。
  • 后端性能测试的测试负载,我们一般只会把它设计在“线性增长区间”内;而压力测试的测试负载,我们则会将它设计在系统“拐点”上下,甚至是“过饱和区间”。

测试方法:

  • 后端性能测试:是通过性能测试工具模拟大量的并发用户请求,然后获取系统性能的各项指标,并且验证各项指标是否符合预期的性能需求的测试手段。

    根据应用领域的不同,后端性能测试的场景设计主要包括以下两种方式:

    1.基于性能需求目标的测试验证;

    2.探索系统的容量,并验证系统容量的可扩展性。

  • 压力测试,通常指的是后端压力测试,一般采用后端性能测试的方法,不断对系统施加压力,并验证系统化处于或长期处于临界饱和阶段的稳定性以及性能指 标,并试图找到系统处于临界状态时的主要瓶颈点。所以,压力测试往往被用于系统容量规划的测试。

  • 配置测试,主要用于观察系统在不同配置下的性能表现,通常使用后端性能测试的方法:通过性能基准测试(Performance Benchmark)建立性能基线(Performance Baseline);在此基础上,调整配置;基于同样的性能基准测试,观察不同配置条件下系统性能的差异,根本目的是要找到特定压力模式下的最佳配置。这里需要注意的是,“配置”是一个广义配置的概念,包含了以下多个层面的配置:宿主操作系统的配置;应用服务器的配置;数据库的配置;JVM 的配置;网络环境的配置等。

  • 并发测试,指的是在同一时间,同时调用后端服务,期间观察被调用服务在并发情况下的行为表现,旨在发现诸如资源竞争、资源死锁之类的问题。

  • 可靠性测试,是验证系统在常规负载模式下长期运行的稳定性。时间长度跨越单位通常为天。

性能测试的四大应用领域:

web网站架构设计

1.高性能设计:

  • 前端高性能设计主要是:加快用户实际感受到前端页面展示的时间。主要受网络延时和资源加载因素影响。
  • 后端高性能设计:
    1) 使用缓存加快处理时间,降低后端运算负载。
    2) 服务器集群化,前置负载均衡,提高计算性能和并发能力。

2.高可用设计:

  • 后端服务器至少2台,前置一台负载均衡器,实现应用服务高可用。
  • 分布式数据存储,实现数据高可用。

3.伸缩性设计:
基于机器的集群的进行可伸缩性设计,或者基于一台机器的硬件配置进行伸缩。

基于集群的进行可伸缩性设计:

- 横切:单一功能按层次划分,例如购物功能,可以划分为:
|ui层|
|业务逻辑层|
|公共服务层|
|数据存储层|
每一层都可以独立实现可伸缩。

- 纵切:按业务种类垂直划分,物理隔离服务。比如:
按|用户管理|购物|支付|划分,而且|购物|业务按业务范围
还可以向下划分为:
|百货|电子产品|生鲜|

这样纵切得到的|购物-百货|业务,按照购物功能的横切划分,拥有自己的一套执行环境,即为:
ui静态服务器,
业务逻辑服务器集群,
数据存储服务器、集群。
而公共服务层可以抽象为一个业务,具有自己的集群。

web性能优化

web 性能优化:

1.基本方法:

a.客户端文本资源 minification。

比如 npm install –g minifier html-minify。
minify .css,.js。
htmlminify .html。

b.服务器开启压缩技术

c.优化图片 tinyPng

2.优化 css:

CSS shorthand properties not only are convenient, but also offer us a way to reduce the size of our style sheets by cutting down on excessive and verbose rules.

Using shallow CSS selectors can also significantly reduce the size of a style sheet, as well as make code more maintainable and modular.

Applying the DRY principle with the csscss redundancy checker can further winnow bloated CSS files by enabling you to remove superfluous properties.

避免使用@import。@import 会串行化资源请求。
尽量使用 <link>。可以并行化请求。 # 针对http1.x。

<head>里提前放置 css。
理由:
1) 防止flash of unstyled content 出现。
2) 减少 re-render and repaint the entire DOM.

使用性能更快的选择器。常见的 tag: div,descendant: div ul li ,class: .listitem,direct child: section > ul > li 性能都差不多,其他的 Sibling,Pseudo,Attribute 比较慢。

尽可能使用 flexbox 布局。

例如有 flexbox style:
        parent:> display: flex; justify-content: space-between; flex-flow: row, wrap;
        children: > flex-basis: 24.25%

比较 box model:
        parent: > display:block。
        children:> width: 24.25%;
        往往还会用到 :nth-child selector 调整尾端

The conclusion you can draw is that when it comes to rendering content, flexbox tends to be a better-performing solution. Better yet, it enjoys broad support without vendor-specific prefixes. When used with vendor prefixes, support only increases. If you’re not using flexbox on your websites, it’s rather trivial to retrofit in most cases.

3.images responsive:

  • Delivering responsive images in CSS with media queries, and how supplying the correct image sources to the proper devices can positively impact loading and processing time

  • Delivering responsive images in HTML by using the srcset attribute and the <picture> element

  • Providing polyfill support for the <picture> element and srcset attribute for older browsers in an optimal fashion

  • Using SVG images in both CSS and HTML, and the convenience and flexibility inherent to the format when it comes to optimal display on all devices

4.images else:

  • reduce size, compression or optimizing.
  • use webp.
  • lazy loading images.

5.fonts.

  • 只选择需要 weight 字体
  • 用 css @font-face 确定使用字体
  • 使用 font-display 声明字体未加载时策略
  • 使用 subset 减少字体文件大小

e.g.

@font-face{
    font-family: "Open Sans Light";
    font-weight: 300;
    font-style: normal;
    src: /*
         These local sources are commented out for debugging purposes.
         In any production site, they should be enabled.
         local("Open Sans Light"),
         local("OpenSans-Light"),
         */
         url("open-sans/OpenSans-Light-BasicLatin.woff2") format("woff2"),
         url("open-sans/OpenSans-Light-BasicLatin.woff") format("woff"),
         url("open-sans/OpenSans-Light-BasicLatin.eot") format("embedded-opentype"),
         url("open-sans/OpenSans-Light-BasicLatin.ttf") format("truetype");
         font-display: swap;     # local fallback
         unicode-range: U+0000-007F;    # subset
}  

6.script

- Depending on its position, the <script> tag can block rendering, which delays the display of the page in the browser. Placing <script> tags toward the bottom of the document can speed up the rendering of a page.
- The async attribute can provide further performance benefits if you can manage the execution of scripts that use it.
- Managing the execution of interdependent scripts that use async can be challenging. A third-party script-loading library such as Alameda or RequireJS can provide a convenient interface for managing script dependencies, while also providing the benefit of asynchronous script loading and execution。

7.cache assets

  • 使用 cache contorl:

    no-cache、no-store、stale-witle-revalidate && max-age
    对于各种资源的缓存策略举例:

  • 使用 cdn 资源。举例:

    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous"> 

8.http2

Techniques that reduce the size of assets are things you should still do on HTTP/2. These are techniques such as minification, server compression, and image optimization. Reducing the size of an asset contributes to lower load times, always and forever.

Techniques that combine files are things you should stop doing on HTTP/2. Although useful in alleviating latency in HTTP/1 client/server interactions, requests are much cheaper in HTTP/2, and combining files can have an adverse effect on your caching effectiveness.
  • http2 不去捆绑资源主要是考虑缓存。在一个tcp连接之下,请求更颗粒化,多次请求,延迟不是问题。不过传递资源始终会经历延时,所以内敛化资源比如 css 还是有效。

  • server push 是个好特性,这篇文章包含 preload hint、server push 和 No optimization 的对比:
    https://www.nginx.com/blog/nginx-1-13-9-http2-server-push/

  1. 使用 gulp 自动化以上任务。
    https://github.com/pluckhuang/useGulp

OLOO in JavaScript

在 Js 里 "OLOO" (objects-linked-to-other-objects) 较 OO (object-oriented):

  • 心智模型更直接
  • 语法更简洁
  • 生成的对象更少
  • 而 ES6中对 Class 的支持,只是可读性更好,本质上还是走原型委托。但是引用的 super 是静态绑定。而且还是会动态改变。

所以如果建模的话,选择 OLOO 。

refer https://github.com/getify/You-Dont-Know-JS/blob/2nd-ed/objects-classes/ch6.md#review-tldr

JavaScript 普通函数、箭头函数和this

  • 作为对象属性的普通函数,this 绑定为该对象。

  • 作为回调函数的普通函数,this 绑定为被触发对象。

  • 除了 call、apply、bind 以外能指定 this 参数,其余调用普通函数的地方,this 都绑定为全局对象。

  • 箭头函数没有自己的 this,也就是说没有自己的定义域。进一步说,就是会绑定定义时上层代码块的 this。

function Timer() {
    this.s1 = 0;
    this.s2 = 0;
    // 箭头函数
    setInterval(() => this.s1++, 1000);
    // 普通函数
    setInterval(function () {
        this.s2++;
    }, 1000);
}

假如 var timer = new Timer();
此时 timer.s1 可以正常绑定,.s2 被绑定到了全局对象。

再假如,直接调用函数 Timer(),此时 s1 也被绑定到了全局对象。
  • 即使作为对象方法,箭头函数内部的 this 也是指向的全局对象。所以箭头函数不适合作为函数方法。
  • 作为回调函数的箭头函数,this 绑定为全局对象。所以箭头函数不适合动态指定 this 的场合。
var x = 10;

let foo = {
  x: 20,

  // Dynamic `this`.
  bar() {
    return this.x;
  },

  // Lexical `this`.
  baz: () => this.x,

  qux() {
    // Lexical this within the invocation.
    let arrow = () => this.x;

    return arrow();
  },
};

console.log(
  foo.bar(), // 20, from `foo`
  foo.baz(), // 10, from global
  foo.qux(), // 20, from `foo` and arrow
);

一道有意思的Js 题,sum(1,2)(3,4).sumof()…

看到一道题有点意思:

题目:编写函数方法实现
  • 形式1:sum(1).sumof()
  • 形式2:sum(1,2).sumof()
  • 形式3:sum(1)(2, 3).sumof()
  • 最终求得的结果,需要是各个出现的数字的和。
目前想到的写法:
function sum(...lastArgs) {
    var callback = function(...args) {
        return sum(...[ ...lastArgs, ...args ]);
    };
    callback.sumof = function() {
        return lastArgs.reduce((aggregated, number) => aggregated + number, 0);
    };
    return callback;
}

为什么 promise 结合生成器使用更优雅?

promise 用来执行异步任务。

通过链式调用可以一步接一步的执行任务,那么为什么还要结合生成器呢?

比如这样的:

new promise(...).then(val1 => {resolve(val2)}).then(val2 => resolve()).catch();

变成这样的:

function* xx() {
    const val1 = yield new promise(...);
    const val2 = yield new promise(...);
}

最终产生了 async?
变成这样:

async function xx() {
    const val1 = await new promise(...);
    const val2 = await new promise(...);
    return
}

答案是用【同步写法】执行【异步任务】,这样看代码看的更清楚。而且使用了 async,还包括了自动执行,可以直接调用xx()来得到结果。

JavaScript 函数

  • 函数调用共有4种类型

    • 方法调用模式【作为对象属性], this 绑定为动态执行时的对象。因为方法可以被单独应用,所以this随着变化会变化。

      值得注意的是箭头函数,作为方法时 this 绑定到了 window 对象上了。

      因为内部箭头函数方法会在起初声明时就绑定不再变化。

    • 函数调用模式,this 绑定为 Window 对象或 undefined,取决于是否 “use strict”。

    • 构造器(构造函数)调用模式,this 绑定为动态运行时实际创建的对象。

      值得注意的是箭头函数,此时 this 绑定到了 新创建的这个对象上。

    • this,arguments 可被指定、每个函数都有的的 apply /call 调用模式,2者区别在于参数传递,前者传递一个参数数组,后者可传递多个参数,作为参数列表。