质量属性——开发效率

定义

性能与时间有关。事件发生时,系统必须对其做出响应。事件到达和响应有很多特性,但性能基本上与事件发生时,将要耗费系统多长时间做出响应有关。

在使性能变得复杂的众多因素中,其中一个因素就是事件源的数量和到达模式。事件可以来自用户请求、其他系统或系统内部。到达模式可以分为周期性的、随机的或偶然性的。

可以用等待时间、处理期限、系统吞吐量、响应抖动(等待时间的变化)、缺失率(由于系统太忙因而无法做出响应所导致的未处理事件的数量)、数据丢失(因为系统太忙所丢失的数据)等指标来度量。

性能战术

性能战术的目标就是对在一定的时间限制内到达系统的事件生成一个响应。到达系统的可以是单个事件,也可以是事件流的形式,它是请求执行计算的触发器。它可以是消息的到达、定时器到时、系统环境中重要的状态变化的检测,等等。系统对事件进行处理并生成一个响应。性能战术控制生成响应的时间。等待时间是事件到达和对该事件生成响应之间的时间。

 

资源需求

事件流是资源需求的源。需求的两个特性是:资源流中的事件之间的时间;每个请求所消耗的资源是多少。

减少等待时间的一个战术就是减少处理一个事件流所需要的资源。

  • 提高计算效率
    处理事件或消息中的一个步骤就是应用某个算法。改进在关键的地方所使用的算法将减少等待时间。有时可以用一种资源换取另一种资源,这取决于时间和空间资源的可用性。

  • 减少计算开销
    如果没有资源请求,就可以减少处理需求。

减少等待时间的另一个战术就是减少所处理事件的数量。

  • 管理事件率
    如果可以降低监视环境变量处的取样频率,就可以减少需求。如果系统进行了超量设计的话,这样做是可行的,其他时候使用不必要的高采样率来建立多个流之间的和谐的周期。也就是说,某个流或事件流被过采样,以使它们可以被同步化。

  • 控制采样频率
    如果没有对外部生成的事件的到达进行控制,则可以用一个较低的频率对排队的请求进行采样,这可能会导致请求的丢失。

用于减少或管理需求的其他战术包括控制资源的使用。

  • 限制执行时间
    限制用多少执行时间对事件做出响应。有时这样做有意义,有时没有意义。对于迭代式、依赖于数据的算法,限制迭代的数量就是限制执行时间的一个方法。

  • 限制队列大小
    这控制了排成队列的到达事件的最大数量,因此控制了用来处理到达事件的资源。

资源管理

  • 引入并发
    如果可以并行处理请求,就可以减少闭锁时间。可以通过在不同的线程上处理不同的事件流或创建额外的线程来处理不同的活动集来引入并发。

  • 高并发解决思路与手段
    扩容:水平扩容、垂直扩容

    缓存:Redis、Memcache、GuavaCache等

    队列:Kafka、RabitMQ、RocketMQ等

    应用拆分:服务化Dubbo与微服务Spring Cloud

    限流:Guava RateLimiter使用、常用限流算法、自己实现分布式限流等

    服务降级与服务熔断:服务降级的多重选择、Hystrix

    数据库切库,分库分表:切库、分表、多数据源

    高可用的一些手段:任务调度分布式elastic-job、主备curator的实现、监控报警机制

  • 维持数据或计算的多个副本
    客户机-服务器模式中的客户机是计算的副本。使用副本的目的是减少在中央服务器上进行所有的计算时出现的争用。高速缓存是在不同速度的存储库或单独的存储库上复制数据的战术,目的是减少争用。因为被高速缓存的数据通常是现有数据的一个副本,因此使副本一致和同步就变成了系统必须承担的责任。

  • 增加可用资源
    速度更快的处理器、额外的处理器、额外的内存以及速度更快的网络都可能减少等待时间。

资源仲裁

线程的优先级用数字来表示,默认范围是1到10,即Thread.MIN_PRIORITY到Thread.MAX_PRIORTY.一个线程的默认优先级是5,即Thread.NORM_PRIORTY

对优先级操作的方法:

int getPriority():得到线程的优先级

void setPriority(int newPriority):当线程被创建后,可通过此方法改变线程的优先级

得到的结论是:线程的优先级无法保障线程的执行次序。只不过,优先级高的线程获取CPU资源的概率较大。

 1 class MyThread extends Thread{
 2     String message;
 3     MyThread(String message){
 4         this.message=message;
 5     }
 6     public void run(){
 7         for(int i=0;i<3;i++){
 8             System.out.println(message+" "+getPriority());
 9         }
10     }
11 } 
12 
13 public class PriorityThread{
14     public static void main(String args[]){
15         Thread t1=new MyThread("T1");
16         t1.setPriority(Thread.MIN_PRIORITY);
17         t1.start();
18         
19         Thread t2=new MyThread("T2");
20         t2.setPriority(Thread.MAX_PRIORITY);
21         t2.start();
22         
23         Thread t3=new MyThread("T3");
24         t3.setPriority(Thread.MAX_PRIORITY);
25         t3.start();
26     }
27 }

 

 

当存在资源争用时,必须对资源进行调度。我们需要对处理器、缓冲器和网络进行高度安排。设计师的目标是理解每个资源使用的特性,并选择与之一致的调度策略。

  • 先进/先出
    FIFO队列同等看待对资源的所有请求,并依次对其进行处理。

    •  1 一、java8新特性java并发流操作(结合纳姆达表达式)
       2 
       3        List<String> list = new ArrayList<>();
       4 
       5         list..stream().parallel().forEach(list1 ->{
       6 
       7   // 调用方法 method
       8 
       9 });
      10 
      11       注:其中list1为list一个子集。然后通过java流(steam)操作并发(parellel),同时开启多个线程去执行方法(method);
      12 
      13 所以当一个线程执行完成之后会继续执行集合中另外的子集,直到list循环结束。
      14 
      15  
      16 
      17 二、采用阻塞队列的形式
      18 
      19   1、List<Qcbatch> qcBatchList = new ArrayList<>();
      20 
      21   注:QcBatch  为一个对象,此处为一个示例,然后在qcBatchList 集合中放入自己的元素
      22 
      23   2、// 创建阻塞队列跑批
      24 
      25   ConcurrentLinkedQueue<QcBatch> queue = new ConcurrentLinkedQueue<>(); // 线程同步的队列
      26   queue.addAll(qcBatchList );
      27   int count = 10; // 此处为设置线程数位10
      28   final CountDownLatch countDownLatch = new CountDownLatch(count); //将线程放入计数器中
      29   注:CountDownLatch是Java1.5之后引入的Java并发工具类,放在java.util.concurrent包下,CountDownLatch能够使一个或多个线程等待其他线程完成 各自的工作后再执行
      30   3.循环操作线程
      31   for (int i = 0; i < count; i++) {
      32     new Thread(() -> {
      33     CheckResult<QcBatch> t;
      34     try {
      35     // queue.poll 从队列中删除第一个元素,知道为空,会返回null对象(与remove()有些不同)
      36     while ((t = queue.poll()) != null) {
      37     // 此处调用自己的业务逻辑
      38     }
      39     } finally {
      40     countDownLatch.countDown(); // 每调用一次这个方法,在构造函数中初始化的count值就减1
      41       //所以当N个线程都调 用了这个方法,count的值等于0,然后主线程就能通过await()方法,恢复执行自己的任务。
      42     }
      43   }).start();
      44   }
      45   countDownLatch.await(); // 等待所有跑批线程执行结束
      46 
      47  两种方式比较:第一种方式是每个线程各自执行,任务会有先后顺序;
      48 
      49        第二种方式是等时执行,在效果上是同一时间完成10个任务;

       

  • 固定优先级调度
    固定优先级调度为每个请求资源的源分配一个特定的优先级,并按该优先级顺序分配资源。该策略能够确保为优先级较高的请求提供更好的服务,但是,对一些优先级较低但重要的请求来说,可能要等很长的时间才能得到服务,因为它前面有很多优先级较高的请求在等待提供服务。3个常见的优先级策略为:

    • 语义重要性
      每个流都根据生成它的任务的某个与领域特性被静态地分配一个优先级。

    • 时限时间单调
      时限时间单调是一种静态优先级分配,它将较高的优先级分配给具有较短时限时间的流。在调度的不同优先级流具有实时时限时间时,使用该调度策略。

    • 速率单调
      速率单调是周期流的一种静态优先级分配,它将较高的优先级分配给具有较短周期的流。该调度策略是时限时间单调的一种特殊情况。

  • 动态优先级调度

    • 轮转
      轮转是一种调度策略,它对请求进行排序,然后在允许的时候,把资源分配给该排序中的下一个请求。

    • 时限时间最早优先
      时限时间最早优先根据具有最早的时限时间的挂起请求来分配优先级。

  • 静态调度
    循环执行调度是一种调度策略,在该策略中,离线确定先占点和资源分配顺序

posted @ 2020-03-09 21:29  我叫张小凡  阅读(278)  评论(0编辑  收藏  举报