第七周 - 总结
一、系统性能主要技术指标:
性能指标:
1、响应时间
2、并发数
3、吞吐量:单位时间内,系统处理请求的数量。TPS每秒事务数,HPS每秒HTTP请求数,QPS每秒查询数等。
4、性能计数器:是描述服务器或操作系统性能的一些数据指标。包括system load、对象与线程数、内存使用、CPU使用、磁盘与I/O等指标。
性能测试方法:
1、性能测试方法是一个总称,具体可分为性能测试、负载测试、压力测试、稳定性测试。
2、性能测试:以系统设计初期规划的性能指标为预期性能,对系统不断施加压力,验证系统在资源可接受的范围内,是否达到性能预期。
3、负载测试:对系统不断增加并发请求以增加系统压力,直到系统的某项或多项性能指标达到安全临界值。
4、压力测试:超过安全负载的情况下,对系统继续施加压力,直到系统崩溃或不能再处理任何请求。以此获得系统最大压力承受能力。
5、稳定性测试:被测试系统在特定硬件、软件、网络环境条件下,给系统加载一定业务压力,使系统运行一段较长时间,一次检测系统是否稳定。在生产环境,请求压力时不均匀的,呈波浪特性,因此为了更好的模拟生产环境,稳定性测试也应不均匀的对系统施加压力。
性能测试怎么做:
1、使用通过性能测试客户端,模拟并发的请求数目(最简单的用一个线程模拟一个并发),不断增加线程数,模拟并发数,然后向系统发出并发请求,然后测试除系统的响应时间TPS、返回的错误率、以及其它的一些性能(负载、cpu、内存、I/O)。
2、经过多轮测试,将这些数据记录下来。然后画出曲线,通过分析这些数据,直到哪些资源是系统的瓶颈,然后考虑升级这些系统资源(如更换CPU、加装内存、使用缓存等)或者是通过分析资源的使用情况,判断是否代码有问题(比如内存占用特别大,是不是内存溢出了)。
二、全链路压测:
全链路压测:
指在特定的业务常场景下,将相关的链路完整的串联起来同时施压,测可能的模拟出真实的用户行为,当系统整站流量都被打上来的时候,必定会暴露出性能瓶颈,才能够探测出系统整体的真实处理能力,以及有指导的在大促前进行容量规划和性能优化,这就是线上实施全链路压测的目的。
全链路压测的挑战:
压测的系统众多,且牵扯到整条链路上的所有基础设施和中间件,如何确保压测流量能够畅通无阻,没有死角(就是压测数据怎么构造出来并且全覆盖),与真实贴近。
全链路压测直接在线上真实环境进行模拟,怎么保障对线上无影响。
全链路压测具体做法:
1、数据构造:
直接dump得到线上用户真实的数据->筛选,脱敏,Id隔离,订正->放入压测基础数据池。
2、数据隔离:
逻辑隔离:通过特殊的标识符区分开。
虚拟隔离:在写数据的地方做mock,并不是真正的去写。
物理隔离:对写数据的压测请求识别,写入到不同的服务器中。
3、流量构造:
通过CDN服务器部署压测服务压测。
三、性能优化的分层思想(不能只盯着代码优化):
1、性能优化的两个基本原则:
你不能优化一个没有测试的软件。
你不能优化一个你不了解的软件。
2、性能优化的一般方法:
1. 性能测试,获得性能指标。
2. 指标分析,发现性能与资源瓶颈点。
3. 架构与代码分析,寻找性能与资源瓶颈关键所在。
4. 架构与代码及其它优化,优化关键技术点,平衡资源利用。
5. 性能测试,进入性能优化闭环,验证优化结果。
3、性能优化的分层:
1. 机房与骨干网络性能优化。
异地多活的多机房架构(高可用,高性能),专线网络与自主CDN建设。
2. 服务器与硬件性能优化。
使用更优的CPU,磁盘,内存,网卡。
3. 操作系统性能优化。
Linux系统的不同参数的影响性能。
4. 虚拟机性能优化。
JVM虚拟机的GC等。
5. 基础组件性能优化。
使用不同的基础组件部署,性能也不同。如使用Jetty代替JBoss。
6. 软件架构性能优化。
缓存:优化读性能。
异步:优化写性能。
集群:
7. 软件代码性能优化。
遵循面向对象的设计原则与设计编程模式。
并发编程,多线程与锁。
资源复用,线程池与对象池。
异步编程,生产者消费者。
数据结构,数组、链表、hash表、树。
四、锁:
1、锁原语CAS:
CAS(V,E,N):Compare And Swap,是比较并替换,V表示内存中要更新的值,E表示预期值,N表示新值。这是一个原子操作,只有当预期的值和内存中的值一样才会替换,否则替换失败。
CAS在一个CPU的一个核心上执行是能保证是原子的,但是多个CPU的核心同时读取一个数据进行CAS的时候就会有问题。办法:总线锁、缓存锁。
2、Java锁:
锁的一部分信息存放在Java的对象头的Mark Word里面。当线程获取锁的时候,第一次会将对象头的Mark Word里的锁线程ID修改为当前线程ID,修改偏向锁的标记为1,此时获得偏向锁。
当在有其它的线程来获取锁的时候,会将对象头的Mark Word的偏向锁标记修改为轻量级锁,然后记录锁记录的指针,该线程会经过几次自旋的CAS操作修改锁的信息,尝试获得锁。
如果还是没有获得锁,锁就会升级为重量级锁,创建一个锁的监视器,并记录下该监视器的地址引用,然后线程将自己的线程ID放到锁的监视器里面等待锁的释放,此时线程就会阻塞等待释放CPU资源。
只有重量级锁,线程才会被阻塞。
Sychronized使用在方法上,是对这个类的实例对象加锁了。如果该实例多个方法都加了sychronized关键字,那么它们的加锁对象都是同一个,这些加锁方法都是互斥的。在静态方法上加了锁,那就是锁住了这个类。
3、公平锁非公平锁:
轻量级锁的获取是非公平锁,因为每次线程被调用的时候是不一定的。
4、可重入锁。
5、悲观锁/乐观锁:
悲观锁:认为对同一个数据的并发操作,一定是会发生修改的,哪怕没有修改也会认为是修改。因此对于同一个数据的并发操作,悲观锁才去加锁的方式。悲观锁认为,不加锁的并发操作一定会发生问题。修改会频繁发生。
乐观锁:认为对于同一个数据的并发操作,是不会发生修改的。在更新数据的时候,检查是否已经被修改,如果被修改,就放弃。乐观锁实际上不是一种锁,没有进行加锁操作,只是利用检查修改,达到锁一样的功能(如CAS)。修改不会频繁发生。
6、分段锁:
分段锁的设计目的是细化锁的粒度,当操作不需要更新整个数组的时候,就仅仅针对数据的一段进行加锁操作。
7、自旋锁:
值线程采取循环的方式尝试获取锁。
五、异步并发编程Akka:
1、 特性:
更简单的并发
更简单的分布式
更简单的容错能力
2、 核心Actor:
Akka都是依赖于Actor来运行起来的。
评论