【java】高并发之限流 RateLimiter使用

2023-11-07

在这里插入图片描述

1.概述

转载原文:高并发之限流

你可能知道高并发系统需要限流这个东西,但具体是限制的什么,该如何去做,还是模凌两可。我们接下来系统性的给它归个小类,希望对你有所帮助。

google guava中提供了一个限流实现: RateLimiter,这个类设计的非常精巧,可以适用于我们日常业务中大多数流控的场景,但鉴于使用场景的多样性,使用时也需要相当小心。

前面已经使用两篇简单的文章进行了预热。
信号量限流,高并发场景不得不说的秘密
没有预热,不叫高并发,叫并发高

这次不同。本篇文章将详细的,深入的介绍限流的各种场景和属性,然后分析guava这个限流器的核心源码,并对其特性进行总结。属于稍高级的进阶篇。

2.限流场景

弄清楚你要限制的资源,是这个过程中最重要的一环。我大体将它分为三类。

在这里插入图片描述

3.1 代理层

比如SLB、nginx或者业务层gateway等,都支持限流,通常是基于连接数(或者并发数)、请求数进行限流。限流的维度通常是基于比如IP地址、资源位置、用户标志等。更进一步,还可以根据自身负载情况动态调整限流的策略(基准)。

3.2 服务调用者

服务调用方,也可以叫做本地限流,客户端可以限制某个远端服务的调用速度,超过阈值,可以直接进行阻塞或者拒绝,是限流的协作方。

3.3 服务接收方

基本同上,流量超过系统承载能力时,会直接拒绝服务。通常基于应用本身的可靠性考虑,属于限流的主体方。我们常说的限流,一般发生在此处。本文主要结合RateLimiter讨论基于限流主体方的使用方式,其他的都类似。

4.限流策略

限流策略有时候很简单,有时候又很复杂,但常见的就三种。其他的都是在这之上进行的改进和扩展。

在这里插入图片描述

4.1 根据并发级别限流

这是一种简单的、易于实施的限流方式,可以使用我们前面提到的java信号量实现。它的使用场景也有着比较鲜明的特点:

  1. 每次请求,所需要的资源开支都比较均衡,比如,每个请求对CPU的消耗、IO消耗等,都差不多,请求的RT时间都基本接近。
  2. 请求密度或稀疏或高频,这个我们不去关注。
  3. 资源本身不需要繁琐的初始化工作(预热),或者初始化工作的开支可以忽略。(会增加复杂度)
  4. 对待流量溢出的策略比较简单,通常是直接拒绝而不是等待,因为等待往往意味着故障。

这种策略通常在适用在流量的顶层组件上,比如代理层、中间件等对并发连接数的限制。而尝试获取凭证的超时时间,就叫做溢出等待。很上档次很装b的词,对不对?

4.2 漏桶算法

请求流量以不确定速率申请资源,程序处理以恒定的速率进行,就是漏桶算法的基本原理。有点像制作冰激凌的过程。-.- 有关漏桶模型,大家可以去研究一下相关资料。

大体有以下几个概念。

4.2.1 桶 buffer

请求首先尝试进入队列,如果队列溢满,则拒绝此请求。进入队列以后,请求则等待执行。

由此可见,请求究竟何时被执行,还存在一些变数,这直接取决于队列中pending的请求数。有时候,挑剔的设计者会考虑增加有关限制请求等待的时间阈值,这个时间就是请求入队、出队的最大时差。buffer的大小设计,通常与速率有直接关系。

4.2.2 漏:请求出队

这个出队,有些讲究,不同的设计理念实现就有所不同。有抢占式、有调度式。其中“抢占式”就是处理线程(或者进程,比如nginx worker进程)在上一个请求处理完毕之后即从buffer队列中poll新的请求,无论当前线程(或者进程)的处理速率是否超过设定的速率,这种策略下buffer大小就限定了速率的上限。

调度式,就比较易于理解,需要额外的调度线程(进程),并严格按照设定的速率,从buffer中获取请求,并轮训的方式将请求交给其他worker线程,如果已有的worker线程繁忙,则直接创建新线程,目的就是确保速率是有保障的,这种方式下,buffer大小主要取决于等待时间。

4.2.3 溢出

就是因为漏桶的速率限制比较稳定,所以其面临流量突发(bursty)几乎没有应对能力,简单来说,超出buffer,就直接拒绝。

多么可怜的请求们。

4.2.4 流量突发

尽管buffer的设计在一定层面上兼顾流量突发,但是还是太脆弱了,比如某个瞬间,请求密度很高(最尴尬的就是,只大了一点),将buffer溢满,或许buffer再“大一点点”就能够在合理时间内被处理;对于请求方,就会有些迷惑,“我只不过是稍微超了一点,你就给了我一连串无法工作的信息,so nave!!!”。

这种策略,也很常用,但是通常适用在限流的协作方,也是就客户端层面。请求发出之前,做流控,如果有溢出,就要用其他可靠的策略来保障结果,比如重试等;反正 “对面的服务压垮了,别怪我,我很自律”。

4.3 令牌桶

设计模型,我就不再介绍,大家可以去wiki深入了解一下。

令牌桶的基本思想,跟老一辈的集体公社时代一样,每个月的供销是限额的,有资源才分配给个人,不足部分下个月再说,你可以排队赊账。

令牌的个数,就是可以允许获取资源的请求个数(我们假设每个请求只需要一个令牌)。事实上,我们并不会真的去创建令牌实体,因为这是没有必要的,我们使用带有时间特征的计数器来表示令牌的可用个数即可。跟漏桶算法相比,令牌桶的“桶”不是用来buffer请求的、而是用来计量可用资源数量(令牌)的。虽然我们并不会创建令牌实体,但是仍然可以假想,这个桶内每隔X时间就会新增一定数量的令牌,如果没有请求申请令牌,那么这个令牌桶是会溢出的…你会发现,这个设计跟漏桶算法从IO方向上是相反的。

那么漏桶算法的缺点,也正好成为了令牌桶的专长:流量突发;令牌桶成了buffer,如果请求密度低,或者处于冷却状态,那么令牌桶就会溢满,此后如果流量突发,则过去积累的结余资源则可以直接被“借用”。

令牌桶算法,使用场景很多,适应程度很高,现实中流量突发是常见的,而且从设计角度考虑,令牌桶更易于实现。回到正题,RateLimiter,就是一个基于令牌桶思想的实现。

我们的口子越缩越小,终于到正题了。

5.RateLimiter使用

guava的api已经把它的设计思想阐述的比较清楚了,但是这个注释阅读起来还是稍微有点“哲学派”,我们先看两个栗子,然后从源码层面看下它的设计原理。

//RateLimiter limiter = RateLimiter.create(10,2, TimeUnit.SECONDS);//QPS 100
RateLimiter limiter = RateLimiter.create(10);
long start = System.currentTimeMillis();
for (int i= 0; i < 30; i++) {
    double time = limiter.acquire();
    long after = System.currentTimeMillis() - start;
    if (time > 0D) {
        System.out.println(i + ",limited,等待:" + time + ",已开始" + after + "毫秒");
    } else {
        System.out.println(i + ",enough" + ",已开始" + after + "毫秒");
    }
    //模拟冷却时间,下一次loop可以认为是bursty开始
    if (i == 9) {
        Thread.sleep(2000);
    }
}
System.out.println("total time:" + (System.currentTimeMillis() - start));

此例为简单的流控,只有一种资源,QPS为10;在实际业务场景中,可能不同的资源速率是不同的,我们可以创建N多个limeter各自服务于资源。

acquire()方法就是获取一个令牌(源码中使用permit,许可证),如果permit足够,则直接返回而无需等待,如果不足,则等待1/QPS秒。

此外,你会发现, limiter并没有类似于锁机制中的release()方法 ,这意味着“只要申请,总会成功”、且退出时也无需归还。

RateLimiter内部有两种实现:(下文中,“资源”、“令牌”、“permits”为同一含义)

6.SmoothBursty

可以支持“突发流量”的限流器,即当限流器不被使用时间,可以额外存储一些permits以备突发流量,当突发流量发生时可以更快更充分的使用资源,流量平稳后(或者冷却期,积累的permits被使用完之后)速率处于限制状态。

其重点就是,冷却期间,permits会积累,且在突发流量时,可以消耗此前积累的permits而且无需任何等待。就像一个人,奔跑之后休息一段时间,再次起步可以有更高的速度。

由此可见,如果你的资源,冷却(不被使用)一段时间之后,再次被使用时可以提供比正常更高的效率,这个时候,你可以使用SmoothBursty。

创建方式

RateLimiter.create(double permitsPerSecond)

结果类似

0,enough,已开始1毫秒
1,limited,等待:0.098623,已开始105毫秒
2,limited,等待:0.093421,已开始202毫秒
3,limited,等待:0.098287,已开始304毫秒
4,limited,等待:0.096025,已开始401毫秒
5,limited,等待:0.098969,已开始505毫秒
6,limited,等待:0.094892,已开始605毫秒
7,limited,等待:0.094945,已开始701毫秒
8,limited,等待:0.099145,已开始801毫秒
9,limited,等待:0.09886,已开始905毫秒
10,enough,已开始2908毫秒
11,enough,已开始2908毫秒
12,enough,已开始2908毫秒
13,enough,已开始2908毫秒
14,enough,已开始2908毫秒
15,enough,已开始2908毫秒
16,enough,已开始2908毫秒
17,enough,已开始2908毫秒
18,enough,已开始2908毫秒
19,enough,已开始2908毫秒
20,enough,已开始2909毫秒
21,limited,等待:0.099283,已开始3011毫秒
22,limited,等待:0.096308,已开始3108毫秒
23,limited,等待:0.099389,已开始3211毫秒
24,limited,等待:0.096674,已开始3313毫秒
25,limited,等待:0.094783,已开始3411毫秒
26,limited,等待:0.097161,已开始3508毫秒
27,limited,等待:0.099877,已开始3610毫秒
28,limited,等待:0.097551,已开始3713毫秒
29,limited,等待:0.094606,已开始3809毫秒
total time:3809

7.SmoothWarmingUp

具有warming up(预热)特性,即突发流量发生时,不能立即达到最大速率,而是需要指定的“预热时间”内逐步上升最终达到阈值;它的设计哲学,与SmoothBursty相反,当突发流量发生时,以可控的慢速、逐步使用资源(直到最高速率),流量平稳后速率处于限制状态。

其重点是,资源一直被使用,那么它可以持续限制稳定的速率;否则,冷却时间越长(有效时长为warmup间隔)获取permits时等待的时间越长,需要注意,冷却时间会积累permits,但是获取这些permits仍然需要等待。

由此可见,如果你的资源,冷却(不被使用)一段时间之后,再次被使用时它需要一定的准备工作,此时它所能提供的效率比正常要低;比如链接池、数据库缓存等。

创建方式

RateLimiter.create(double permitsPerSecond,long warnupPeriod,TimeUnit unit)

执行结果如下,可以看到有一个明显的增长过程。

0,enough,已开始1毫秒
1,limited,等待:0.288847,已开始295毫秒
2,limited,等待:0.263403,已开始562毫秒
3,limited,等待:0.247548,已开始813毫秒
4,limited,等待:0.226932,已开始1041毫秒
5,limited,等待:0.208087,已开始1250毫秒
6,limited,等待:0.189501,已开始1444毫秒
7,limited,等待:0.165301,已开始1614毫秒
8,limited,等待:0.145779,已开始1761毫秒
9,limited,等待:0.128851,已开始1891毫秒
10,enough,已开始3895毫秒
11,limited,等待:0.289809,已开始4190毫秒
12,limited,等待:0.264528,已开始4458毫秒
13,limited,等待:0.247363,已开始4710毫秒
14,limited,等待:0.225157,已开始4939毫秒
15,limited,等待:0.206337,已开始5146毫秒
16,limited,等待:0.189213,已开始5337毫秒
17,limited,等待:0.167642,已开始5510毫秒
18,limited,等待:0.145383,已开始5660毫秒
19,limited,等待:0.125097,已开始5786毫秒
20,limited,等待:0.109232,已开始5898毫秒
21,limited,等待:0.096613,已开始5999毫秒
22,limited,等待:0.096321,已开始6098毫秒
23,limited,等待:0.097558,已开始6200毫秒
24,limited,等待:0.095132,已开始6299毫秒
25,limited,等待:0.095495,已开始6399毫秒
26,limited,等待:0.096352,已开始6496毫秒
27,limited,等待:0.098641,已开始6597毫秒
28,limited,等待:0.097883,已开始6697毫秒
29,limited,等待:0.09839,已开始6798毫秒
total time:6798

8.acquire方法源码分析

上面两个类都继承自SmoothRateLimiter,最终继承自RateLimiter;RateLimiter内部核心的方法:

  1. double acquire():获取一个permit,如果permits充足则直接返回,否则等待1/QPS秒。此方法返回线程等待的时间(秒),如果返回0.0表示未限流、未等待。

  2. double acquire(int n):获取n个permits,如果permits充足则直接返回,否则限流并等待,等待时间为“不足的permits个数 / QPS”。(暂且这么解释)

下面就是这个方法的伪代码啦。

//伪代码
public double acquire(int requiredPermits) {
    long waitTime = 0L;
    synchronized(mutex) {
          boolean cold = nextTicketTime > now;
          if (cold) {
             storedPermits = 根据冷却时长计算累积的permits;
             nextTicketTime = now;
          }
          //根据storedPermits、requiredPermits计算需要等待的时间
          //bursty:如果storePermits足够,则waitTime = 0
          //warmup:平滑预热,storePermits越多(即冷却时间越长),等待时间越长
          if(storedPermits不足) {
              waitTime += 欠缺的permits个数 / QPS;
          }
          if(bursty限流) {
              waitTime += 0;//即无需额外等待
          }
          if(warmup限流) {
              waitTime += requiredPermits / QPS;
              if(storedPermits > 0.5 * maxPermits) {
                waitTime += 阻尼时间;
              }
          }

          nextTicketTime += waitTime
    }
    if (waitTime > 0L) {
      Thread.sleep(waitTime);
    }
    return waitTime;
}
  1. Object mutex:同步锁,如上述伪代码所示,在计算存量permits、实际申请permits(包括计算)的过程中全部是同步的;我们需要知道,RateLimiter内部确实使用了锁同步机制。

  2. maxPermits:最大可存储的许可数量(tickets数量),SmoothBurstySmoothWarimingUp默认实现中,有所不同:

  3. SmoothBusty,其值为maxBurstSecond QPS,就是允许“突发流量持续时间” QPS,这种设计可以理解,不过RateLimiter将maxBustSecond硬编码为1.0,最终此值等于QPS。

  4. SmoothWarmingUp:默认算法值为warmupPeriod QPS,简单说就是“预热时长” QPS。

此参数主要限制,无论冷却多长时间,其storedPermits不能超过此值;此值在设定QPS之后,则不会再改变。

  1. storedPermits:已存储的permits数量,此值取决于冷却时间,简单来说冷却的时间越久,此值越大,但不会超过maxPermits,起始值为0。
    4.1. 当一个请求,申请permit之前,将会计算上一次令牌申请(nexFreeTicketTime)的时间与now之间的时差,并根据令牌产生速率(1/QPS)计算此冷却期间可以存储的令牌数,也就是storedPermits。
    4.2. permits申请完毕之后,将当前时间(如果需要等待,额外加上等待时间)作为下一次令牌申请的起始时间,此时冷却时间结束。
    3)申请完毕之后,storedPermits将会减去申请的permits个数,直到为0。

冷却时长和申请频次,都决定了storedPermits大小,其中冷却时间会导致storePermits增加,acquire操作将导致storePermits减少。

  1. nextFreeTicketMicros(时间戳,微妙单位):下一个可以自由获取的令牌的时间,此值可以为未来的某个时间,此时表示限流已经开始,部分请求已经在等待,此值主要用来标记“冷却状态”。(赊账)
    1)如果处于冷却期,那么此值通常是过去式,即此值小于now。
    2)如果此时有请求申请permits,则会通过此值与now的时差,计算storedPermits,同时将此值设置为now。
    3)如果此值是未来时刻,即大于now,则无需计算storedPermits,也无需重置此值。
    4)申请tickets后,从storedPermits减去需要的tickets个数,如果触发限速等待(比如预热期、permits不足),则会将2)操作之后额外增加等待时间作为nextFreeTicketsTime值。
    5)基于2),对于warmingUp限流,冷却期之后的首个请求是不需要等待的,只是将此值设置为now + 阻尼性质的等待时间waitTime(),这意味着在此后waitTime期间再有请求,则会触发等待,并继续延续nextFreeTicketMicros值。此值的延续,在warming up期间,阻尼waitTime计算比较复杂,由1/QPS + 额外值,这个额外值,随着预热时间增长而减小。
    6)基于2),对于bursty限流,如果storedPermits大于0,则总是不需要等待,只是简单将此值设为为now;否则,则按照正常的1/QPS间隔计算其应该被推延的时间点。

5、对于warming up限流,将maxPermits * 0.5作为一个阈值分割线,当storedPermits小于此分割线时,在限流时使用正常等待时间(申请permits个数 / QPS);在此分割线之上时,则4)增加额外阻尼,即预热阻尼。

6、我们发现,RateLimiter内部并不会真的生成tickets实体,而是根据冷却时长、在申请资源时才计算存量tickets(对应为storedPermits)。无论何种限流,storedPermits都是优先使用。

小总结

是时候总结一下了。

RateLimiter是线程安全的,所以在并发环境中可以直接使用,而无需额外的lock或者同步。

考虑到RateLimiter内部的同步锁,我们通常在实际业务开发中,每个资源(比如URL)使用各自的RateLimiter而不是公用一个,占用的内存也不大。

这个限流器内部无额外的线程,也没有其他的数据结构用来存储tickets实体,所以它非常的轻量级,这也是优势所在。

RateLimiter最大的问题,就是acquire方法总会成功,内部的tickets时间点会向后推移; 如果并发很高,严重超过rate阈值时,后续被限流的请求,其等待时间将会基于时间线累加,导致等待时间不可控,这和信号量同病相怜。

为了避免上面的问题,我们通常先使用tryAcquired检测,如果可行再去acquire;如果令牌不足,适当拒绝。所以 基于RateLimiter,并没有内置的拒绝策略,这一点需要我们额外开发。

我们不能简单依赖于acquire方法,来实现限流等待,否则这可能带来严重问题。我们通常需要封装RateLimiter,并使用额外的属性记录其是否“处于限流状态”、“已经推延的tickets时间点”,如果“已经推延的时间点非常遥远”且超过可接受范围,则直接拒绝请求。简单来说,封装acquire方法,增加对请求可能等待时间的判断,如果超长,则直接拒绝。

RateLimiter存在一个很大的问题,就是几乎没法扩展:子类均为protected。反射除外哦。

一个实践

还是上一段代码吧,能更加清晰的看到我们所做的工作:FollowCotroller.java:流控器,如果限流开始,则只能有max个请求因此而等待,超过此值则直接拒绝

public class FollowController {

    private final RateLimiter rateLimiter;

    private int maxPermits;

    private Object mutex = new Object();

    //等待获取permits的请求个数,原则上可以通过maxPermits推算
    private int maxWaitingRequests;

    private AtomicInteger waitingRequests = new AtomicInteger(0);

    public FollowController(int maxPermits,int maxWaitingRequests) {
        this.maxPermits = maxPermits;
        this.maxWaitingRequests = maxWaitingRequests;
        rateLimiter = RateLimiter.create(maxPermits);
    }

    public FollowController(int permits,long warmUpPeriodAsSecond,int maxWaitingRequests) {
        this.maxPermits = maxPermits;
        this.maxWaitingRequests = maxWaitingRequests;
        rateLimiter = RateLimiter.create(permits,warmUpPeriodAsSecond, TimeUnit.SECONDS);
    }

    public boolean acquire() {
        return acquire(1);
    }

    public boolean acquire(int permits) {
        boolean success = rateLimiter.tryAcquire(permits);
        if (success) {
            rateLimiter.acquire(permits);//可能有出入
            return true;
        }
        if (waitingRequests.get() > maxWaitingRequests) {
            return false;
        }
        waitingRequests.getAndAdd(permits);
        rateLimiter.acquire(permits);

        waitingRequests.getAndAdd(0 - permits);
        return true;
    }

}

以上代码,都可以在github找到。

https://github.com/sayhiai/example-ratelimit
End
可以看到,guava提供了一个非常轻量而全面的限流器。它本身没有使用多线程去实现,但它是线程安全的。相比较信号量,它的使用简单的多。但鉴于限流场景的多样性,使用时同样要非常小心。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【java】高并发之限流 RateLimiter使用 的相关文章

  • java中监视目录变化

    我正在使用 WatchService 来监视目录中的更改 特别是目录中新文件的创建 下面是我的代码 package watcher import java nio file import static java nio file Stand
  • Java:扩展类并实现具有相同方法的接口

    可能无法完成以下操作 我收到编译错误 继承的方法 A doSomthing int 无法隐藏 B 中的公共抽象方法 public class A int doSomthing int x return x public interface
  • Java Logger 未记录到 Netbeans 中的输出

    我正在 Netbeans 中使用 Maven 启动一个 Java 项目 我编写了一些代码来使用 Logger 类进行日志记录 但是 日志记录似乎不起作用 在程序开始时 我运行 Logger getLogger ProjectMainClas
  • 如何在java中将数组值排序为循环格式?

    我的数组值如下 String value 1 2 3 4 5 6 7 8 9 10 假设如果我将值 5 传递给 tat 数组 它应该按如下顺序排序 5 6 7 8 9 10 1 2 3 4 怎么办 有人帮忙吗 感谢你 你需要的就是所谓的轮换
  • 两个整数乘积的模

    我必须找到c c a b mod m a b c m 是 32 位整数 但 a b 可以超过 32 位 我正在尝试找出一种计算 c 的方法 而不使用 long 或任何 gt 32 位的数据类型 有任何想法吗 如果m是质数 事情可以简化吗 注
  • eclipse行号状态行贡献项是如何实现的?

    我需要更新状态行编辑器特定的信息 我已经有了自己的实现 但我想看看 eclipse 贡献项是如何实现的 它显示状态行中的行号 列位置 谁能指点一下 哪里可以找到源代码 提前致谢 亚历克斯 G 我一直在研究它 它非常复杂 我不确定我是否了解完
  • 如何在 Java 中向时间戳添加/减去时区偏移量?

    我正在使用 JDK 8 并且玩过ZonedDateTime and Timestamp很多 但我仍然无法解决我面临的问题 假设我得到了格式化的Timestamp在格林威治标准时间 UTC 我的服务器位于某处 假设它设置为Asia Calcu
  • 提供节点名或服务名,或未知 Java

    最近我尝试运行我的 Java 项目 每当我运行它并将其打开到我得到的服务器地址时 Unable to determine host name java net UnknownHostException Caused by java net
  • 断言 Kafka 发送有效

    我正在使用 Spring Boot 编写一个应用程序 因此要写信给 Kafka 我这样做 Autowired private KafkaTemplate
  • 如何在字段值无效的情况下更改 Struts2 验证错误消息?

    我在 Web 表单上使用 Struts2 验证 如果字段假设为整数或日期 则
  • 如何仅从 Firestore 获取最新更新的数据?

    在 Firestore 上发现任何更改时始终获取整个文档 如何只获取最近更新的数据 这是我的数据 我需要在第一次加载时在聊天中按对象顺序 例如 2018 09 17 30 40 msg和sendby 并且如果数据更新则仅获取新的msg和se
  • 如何在 ant 中为 junit 测试设置 file.encoding?

    我还没有完全完成file encoding 和 ant https stackoverflow com questions 1339352 how do i set dfile encoding within ants build xml
  • Akka 与现有 java 项目集成的示例

    如果我已经有现有的javaWeb 应用程序使用spring and servlet容器 将 Akka 集成到其中的正确方法是什么 就像我将会有Actor1 and Actor2互相沟通的 开始使用这些演员的切入点是什么 例如 1 把它放在那
  • 如何使用 JMagick 转换色彩空间?

    如何使用 JMagick API 转换色彩空间 例如 CMYK gt RGB 和 RGB gt CMYK None
  • Hibernate 本机查询 - char(3) 列

    我在 Oracle 中有一个表 其中列 SC CUR CODE 是 CHAR 3 当我做 Query q2 em createNativeQuery select sc cur code sc amount from sector cost
  • 在java中以原子方式获取多个锁

    我有以下代码 注意 为了可读性 我尽可能简化了代码 如果我忘记了任何关键部分 请告诉我 public class User private Relations relations public User relations new Rela
  • Java/Python 中的快速 IPC/Socket 通信

    我的应用程序中需要两个进程 Java 和 Python 进行通信 我注意到套接字通信占用了 93 的运行时间 为什么通讯这么慢 我应该寻找套接字通信的替代方案还是可以使其更快 更新 我发现了一个简单的修复方法 由于某些未知原因 缓冲输出流似
  • 抛出 Java 异常时是否会生成堆栈跟踪?

    这是假设我们不调用 printstacktrace 方法 只是抛出和捕获 我们正在考虑这样做是为了解决一些性能瓶颈 不 堆栈跟踪是在构造异常对象时生成的 而不是在抛出异常对象时生成的 Throwable 构造函数调用 fillInStack
  • java'assert'和'if(){}else exit;'之间的区别

    java和java有什么区别assert and if else exit 我可以用吗if else exit代替assert 也许有点谷歌 您应该记住的主要事情是 if else 语句应该用于程序流程控制 而assert 关键字应该仅用于
  • Java 和/C++ 在多线程方面的差异

    我读过一些提示 多线程实现很大程度上取决于您正在使用的目标操作系统 操作系统最终提供了多线程能力 比如Linux有POSIX标准实现 而windows32有另一种方式 但我想知道编程语言水平的主要不同 C似乎为同步提供了更多选择 例如互斥锁

随机推荐

  • 解析新时代所需要的创客教育DNA

    创客与教育的 碰撞 改变的不仅仅是学习方式 更重要的价值在于教学内容的改变 真正的创客教育的课程内容应该是什么样 美国非盈利组织Maker Ed Maker Education Initiative 创客教育倡议 提出 创客教育的任务是给年
  • QtAV的windows编译

    1 编译准备 QtAV的安装编译总指导说明 https github com wang bin QtAV wiki Build QtAV QtAV的源代码 https github com wang bin QtAV FFmpeg的开发依赖
  • 修改 WordPress 搜索结果的链接样式

    站长朋友们在使用WordPress建站的过程中会发现 WordPress 默认的搜索结果页面的链接样式是这个样子的 http www yourdomain com s search query 下面来举个例子说明一下 https www 9
  • Java中使用枚举的方法

    枚举是用于将变量分配给一组预定义常量的特殊数据类型 枚举主要用于将特定范围的值分配给所述变量时 例如 当声明彩虹的颜色时 我们通常被限制在代表各种颜色的7个常量 我们可以用枚举来定义这些颜色 并有一种方法来表示这些常量 枚举是单个这意味着对
  • 《随机函数random(n)的实现》

    随机函数random n 实现 rand 随机函数实现原理 关于 随机数 的产生有许多算法 但无论如何 都不可能产生真正的随机数 因为电脑程序是个确定状态转换机 一种输入必定产生一种确定的输出 但要实现 不可预知 还是可以做到的 只需有 不
  • 非对称加密用私钥还是公钥进行加密[原创]

    经常看到类似的说法 公钥 私钥 一般用来加密 私钥 公钥 一般用来解密 也一直没有仔细去考虑 只是留下了一个用于加密 另外一个用于解密的印象 用公钥加密的数据 只有用私钥才能解密 用私钥加密的数据 也只有公钥才能解密 昨天看书时 又遇到了这
  • Ajax

    jQuery Ajax 实例 全解析 jQuery确实是一个挺好的轻量级的JS框架 能帮助我们快速的开发JS应用 并在一定程度上改变了我们写JavaScript代码的习惯 废话少说 直接进入正题 我们先来看一些简单的方法 这些方法都是对jQ
  • 【js】根据给定的数组和属性从源数组中获取数据

    想从一个数组中 根据该数组的某个属性值 例如id 取出相对应的数据 可以参考下面的方法 getDataByGivenArray function sourceArray indexArray indexKey var array for v
  • 2.FreeRTOS系统任务调度简介及任务状态

    目录 一 基础知识 1 FreeRTOS 任务状态 1 运行态 2 就绪态 3 阻塞态 4 挂起态 二 任务调度简介 1 抢占式调度 2 时间片调度 3 协程式调度 一 基础知识 1 FreeRTOS 任务状态 FreeRTOS 中任务存在
  • 前端CSS文字阴影text-shadow记录

    前端CSS文字阴影text shadow记录 一 文字阴影
  • 当贝显示服务器生病,【当贝市场】电视盒子卡顿的三大原因

    原标题 当贝市场 电视盒子卡顿的三大原因 很多不明真相的网友在购买了电视盒子之后会出现同样的问题 家里的电视盒子越用越卡 尤其是在看直播节目的时候出现很明显的卡顿掉帧现象 可能你会问 家里的电视盒子越用越卡是不是跟网速有关 通过小编长时间的
  • mysql分表的3种方法

    参考 https blog csdn net czh500 article details 89735492 一 先说一下为什么要分表 一张的数据达到几百万时 你查询一次所花的时间会变多 如果有联合查询的话 我想有可能会死在那儿了 分表的目
  • S-DES的简单理解和实例

    S DES是一个供教学而非安全使用的加密算法 与DES的特性和结构类似 但它参数小 明文分组为8位 主密钥分组为10位 采用两轮选代 例题 采用手工方式使用 S DES用密钥 01111 11101 解密比特串 1010 0010 写出每个
  • 观察进程的内存占用情况

    概述 想必在linux上写过程序的同学都有分析进程占用多少内存的经历 或者被问到这样的问题 你的程序在运行时占用了多少内存 物理内存 通常我们可以通过top命令查看进程占用了多少内存 这里我们可以看到VIRT RES和SHR三个重要的指标
  • Go高级编程详解

    一 数据操作 1 读取用户的输入 我们如何读取用户的键盘 控制台 输入呢 从键盘和标准输入 os Stdin 读取输入 最简单的办法是使用 fmt 包提供的 Scan 和 Sscan 开头的函数 请看以下程序 示例 readinput1 g
  • sklearn实现支持向量机

    机器学习入门实验之支持向量机 简单实验 使用sklearn完成 代码如下 注 本文章主要是为方便自己以后复习所学内容而写 并不做详细介绍 支持向量机 这里是线性核 sklearn自带软间隔支持向量机 import pandas as pd
  • 用计算机数字语言研究中医理论,试论中医基础理论数字模型.pdf

    试论中医基础理论数字模型 pdf 维普资讯 知 盈 世界科学技术 中药现代化 中医现代化 医 学 课 堂 摘 要 中医基础理论研究的现代化是中医现代化的基础和前提 提出将中医学的基本概念进行 进制数字编码 根据中医原理和规律确定算法 建立数
  • macos 解决 Failed building wheel for xxx

    Failed building wheel for greenlet meinheld ujson 一 手动下载whl文件 二 xcode select install 三 多个版本MacOSX sdk pip install时可能会失败
  • 城市内涝积水监测预警系统的重要性

    一 系统概述 随着我国城镇化快速发展 城市建设产生的大量地面硬底化 大部分的降雨将形成地表径流 仅有少量雨水渗入地下 导致城市内涝等一系列问题 当前 全国多地发生洪涝 我国南北方全面进入主汛期 需要重点防治暴雨造成的城乡内涝 山体滑坡等安全
  • 【java】高并发之限流 RateLimiter使用

    1 概述 转载原文 高并发之限流 你可能知道高并发系统需要限流这个东西 但具体是限制的什么 该如何去做 还是模凌两可 我们接下来系统性的给它归个小类 希望对你有所帮助 google guava中提供了一个限流实现 RateLimiter 这