首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >令牌桶算法原理及应用

令牌桶算法原理及应用

作者头像
张乘辉
发布于 2022-01-24 03:26:58
发布于 2022-01-24 03:26:58
4.3K00
代码可运行
举报
文章被收录于专栏:后端进阶后端进阶
运行总次数:0
代码可运行

前言

限流

最近在参与一个业务迁移的项目。走读代码时,接触到一些限流相关的代码。向老司机请教后了解到,有些业务承载了很高量级的扣款请求,尤其对于一些热点商户,其单点的请求量很大,但某些瓶颈系统的处理能力有限,因此需要做好限流,以保障业务流程中各系统的稳定性。

限流机制修改

在走读原有代码时,发现对限流机制做过一次优化。原先的限流机制简单描述为:通过tair计数器来限流,当请求过来时获取资源,计数器+1;业务完成后释放资源,计数器-1;当计数器达到限流阈值时,拒绝这次请求。

理论上,这种机制能够达到限流目的。但如果在业务处理过程中,被某些系统流程 (如日志打印)阻塞了业务线程的正常执行,会导致tair计数器在释放资源时超时失败。由此导致tair计数器数值一直处于高位,真正能通过的请求就寥寥无几了。最终导致业务请求的成功率下跌。

为了根治这个问题,需要对限流机制做优化。tair计数器限流的根本问题在于,有获取和释放资源两步,不能保证获取之后一定能成功释放。需要一个不需要释放资源的限流机制来弥补这一缺陷。令牌桶算法就是一个很好的选择。

算法原理

什么是令牌桶

想象有一座城堡,入口是一道城门,城外的人必须在城门口获得许可才能进入。为了保证城堡的安全,把守城门的卫兵需要控制单位时间进入城门的人口数量。卫兵的做法是这样的:在城门口放一个桶,桶里有一些令牌,只有拿到令牌的人才能够通过。卫兵每隔一个小时就往桶里扔100个令牌,并且桶最多能容纳100个令牌,如果桶满了就不会再往里扔令牌了。这样一来,平均每小时最多只有100人能进入。

令牌桶的特点

可能有人会问,为什么需要令牌这么麻烦,直接控制每个小时最多只能进入100人不就行了。但是可能会有一种情况,就是早晚集市的时候,一个小时可能不止100人要入城。管理人员既想控制人口流速,又不想一刀切地限制为一个固定的流速。举个例子,在8点的时候,桶里存有100个令牌(之前积累的),8点-9点之间有超过100个人想通过城门。因为8点-9点之间本来就会增加100个令牌,加上桶里留存的100个,这一个小时内最多可以允许200人通过城门,可以满足早集的需求。

类比到接口的限流,也是一样的道理。我们希望控制的是一个平均的流量,同时又想较好地处理突增的高峰流量。从上面的例子中能看出,通过令牌桶算法,在统计意义上,我们做到了限制流量在一个阈值以下。同时,基于令牌桶中“预留”的令牌,又能比较平稳地处理突发的高流量(最多能允许两倍的流量通过)。

RateLimiter 实现令牌桶

令牌桶算法的原理很容易理解,但是真正实现起来就比较有讲究了。看完上面的原理,可能大家的第一感觉就是,用阻塞队列模拟令牌桶,开一个定时器,定时队列里放令牌,使用生产者-消费者模式实现即可。这个方式看起来好像没什么问题,但开启定时器需要新开线程,限流本就是在高并发场景下使用,额外开启线程会给系统带来更多开销。另外,假设我们是针对热点商户进行限流,如果有1万个热点商户,我们就要开启1万个定时器,这个开销是很大的。RateLimiter使用一种巧妙的方式,基于请求的间隔时间,来“模拟”出定时的效果,下面具体来分析一下。

看看效果:

RateLimiter是google开发的guava项目中包含的一个限流类,是基于令牌桶算法实现的。我们先试着使用一下。如下图代码所示,我们先用create方法创建一个RateLimiter限流器,规定每秒往桶里放2个令牌。然后用acquire模拟短时间内的3次请求,分别要取4、4、2个令牌,然后将时间消耗的时间打印出来。

从运行结果,我们可以先直观地理解一下。第一次请求没有任何等待就获取到了4个令牌。此时,因为第一次请求了4个令牌,需要4/2=2s才能“恢复”,所以第二次请求等待了2s。同理,第三次请求也等待了2s才获取到令牌。

看到这里大家可能会疑惑,为什么第一次获取4个令牌不需要任何等待呢?这里涉及到RateLimiter实现令牌桶的一个重要概念,就是“预支”。这里可以简单理解为,第一次请求,“预支”了后面的令牌,而预支的时间,由下一次请求来“承受”。换句话说,这一次你取的多了,下一次就需要等待更久的时间。比如突发流量,我先让它过去,但是后面再来的流量就得等着。这里先这么直观理解,后面通过源码分析会更清晰一些。

RateLimiter源码分析-SmoothBursty

现在我们来看下RateLimiter的源码实现。RateLimiter有两种实现,一个是SmoothBursty,一个是SmoothWarmingUp。

我们先看SmoothBursty实现。SmoothBursty是为了应对突发的高流量。

从刚刚的使用可以看出,外部调用只感知create和acquire两个方法。先看create方法。

初始化RateLimiter

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static RateLimiter create(double permitsPerSecond) {
    return create(SleepingStopwatch.createFromSystemTimer(), permitsPerSecond);
}
@VisibleForTesting
static RateLimiter create(SleepingStopwatch stopwatch, double permitsPerSecond) {
    //permitsPerSecond是我们设定的限流值
    //这里maxBurstSeconds固定是1.0,表示桶里最多预留1倍的permitsPerSecond
    //比如限流值是10,桶里最多就只能放10个令牌。
    RateLimiter rateLimiter = new SmoothBursty(stopwatch, 1.0 /* maxBurstSeconds */);
    rateLimiter.setRate(permitsPerSecond);
    return rateLimiter;
}
public final void setRate(double permitsPerSecond) {
    checkArgument(
        permitsPerSecond > 0.0 && !Double.isNaN(permitsPerSecond), "rate must be positive");
    synchronized (mutex()) {
        doSetRate(permitsPerSecond, stopwatch.readMicros());
    }
}
final void doSetRate(double permitsPerSecond, long nowMicros) {
    resync(nowMicros);
    double stableIntervalMicros = SECONDS.toMicros(1L) / permitsPerSecond;
    this.stableIntervalMicros = stableIntervalMicros;
    doSetRate(permitsPerSecond, stableIntervalMicros);
}

看到这里,核心是doSetRate这个方法,这是个抽象方法,在SmoothBursty和SmoothWarmingUp分别实现。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//这是SmoothBursty的实现
@Override
void doSetRate(double permitsPerSecond, double stableIntervalMicros) {
    //有oldMaxPermits和maxPermits是因为,可以动态地修改permitsPerSecond的值
    double oldMaxPermits = this.maxPermits;
    maxPermits = maxBurstSeconds * permitsPerSecond;
    if (oldMaxPermits == Double.POSITIVE_INFINITY) {
        storedPermits = maxPermits;
    } else {

        //动态修改限流值时,桶里的剩余令牌数也要按照比例来缩放
        storedPermits = (oldMaxPermits == 0.0)
            ? 0.0 // initial state
            : storedPermits * maxPermits / oldMaxPermits;
    }
}

create方法到这里就结束了。还是比较简单的,可以理解为设置了限流值。

获取令牌

重点在下面的acquire方法。我们一层层进去看。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public double acquire(int permits) {
    //1. 计算这次请求需要等到多少时间
    long microsToWait = reserve(permits);
    //2. 阻塞这次请求上面计算出的时间
    stopwatch.sleepMicrosUninterruptibly(microsToWait);
    //3. 返回等待的时间(转化为微秒)
    return 1.0 * microsToWait / SECONDS.toMicros(1L);
}
final long reserve(int permits) {
    //检查参数,不多说
    checkPermits(permits);
    synchronized (mutex()) {
        //获取等待的时间
        return reserveAndGetWaitLength(permits, stopwatch.readMicros());
    }
}
final long reserveAndGetWaitLength(int permits, long nowMicros) {
    //又包了一层方法,获取等待时间
    long momentAvailable = reserveEarliestAvailable(permits, nowMicros);
    //等待时间一定是>0的
    return max(momentAvailable - nowMicros, 0);
}

最核心的方法来了

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
final long reserveEarliestAvailable(int requiredPermits, long nowMicros) {
    //将时间重新“同步”到当前时间,直观理解成给桶里补充令牌
    resync(nowMicros);
    //将下次获取令牌的时间返回。
    long returnValue = nextFreeTicketMicros;
    //判断存储的令牌数够不够这次请求用的。如果够,那么下面的freshPermits就是0,就不用往后推迟时间
    //如果存储的不够,就需要把“下次获取令牌时间”往后推迟
    //下面这段代码将的就是计算推迟时间
    double storedPermitsToSpend = min(requiredPermits, this.storedPermits);
    double freshPermits = requiredPermits - storedPermitsToSpend;
    long waitMicros = storedPermitsToWaitTime(this.storedPermits, storedPermitsToSpend)
        + (long) (freshPermits * stableIntervalMicros);

    try {
        this.nextFreeTicketMicros = LongMath.checkedAdd(nextFreeTicketMicros, waitMicros);
    } catch (ArithmeticException e) {
        this.nextFreeTicketMicros = Long.MAX_VALUE;
    }

    //桶里存储的令牌要扣除掉本次消耗的
    this.storedPermits -= storedPermitsToSpend;
    return returnValue;
}

stableIntervalMicros是产生一个令牌需要的时间,比如限流值是2个/s,那么stableIntervalMicros就是500ms也就是500000微秒。

这里关注一下。nextFreeTicketMicros是下次可以获取令牌的时间。从代码中可以看出,我们会根据此次请求需要的令牌数,往后推迟这个时间。这也是为什么,在使用时,前一次请求获取了更多的令牌,要让后面的请求去等待。这里大家可能有疑问,为什么返回的是更新前的nextFreeTicketMicros,而不是更新后的呢?可以这样理解,我们在最开始补充了令牌,并把时间同步到了此刻(看下面一段),那我就告诉调用方,此时就可以获取令牌了,这一次请求不用等待了。等待的时间让下一次请求承受吧。这也就是所谓的“预支”令牌。

看到这里大家可能还会有一个疑问,不是说最多只能通过2倍的限流量吗?按照这种说法,是不是可以预支n多倍的令牌数,然后让后面的请求等待更多的时间呢?比如我限流10,但是我一次请求100个令牌,让后面的请求等着就行了,那我这一秒的流量不就到100了吗?这里我个人是这样理解的。一般情况下,一次业务请求只需要获取一个令牌。比如我们限流了10,短时间内虽然可能来了100个请求,但是每次请求都是只要获取1个令牌。假设桶里有10个令牌,当第11次请求来的时候,“下次获取令牌的时间”就开始往后推迟了0.1s了,第20个请求来的时候,就推迟了1s了,那么从第0s到第1s这一秒钟的时间内,就通过了20个请求,正好是2倍的限流量了。

刚刚看到reserveEarliestAvailable这个方法最开始有一个resync方法,这个我们再看一下源码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void resync(long nowMicros) {
    // if nextFreeTicket is in the past, resync to now
    if (nowMicros > nextFreeTicketMicros) {
        //补充令牌,注意不能超了
        storedPermits = min(maxPermits,
                            storedPermits + (nowMicros - nextFreeTicketMicros) / coolDownIntervalMicros());
        //下次可以获取令牌的时间变成现在
        nextFreeTicketMicros = nowMicros;
    }
}

这个怎么理解呢。其实从之前的reserveEarliestAvailable方法我们可以大概看到,我们只是在请求过来的时候,把桶里存储的令牌减去了,但是没有做补充令牌的操作。根据令牌桶原理,我们需要定时添加令牌,那怎么办呢。我们就在每次请求过来的最开始,计算出差了多少时间,把中间这段时间应该放的令牌补上就可以了。

那为什么会有一个时间判断条件if (nowMicros > nextFreeTicketMicros)呢?注意,如果两次请求间隔很短,那么在这两次请求之间本来就不应该补。那我们怎么知道要不要补充呢?从之前的方法我们看出,我们维护了一个时间,是“下次可以获取令牌的时间”,也就是nextFreeTicketMicros。当一次请求过来,如果这个nextFreeTicketMicros还没到,那么我们就不需要做“补充”操作。如果这个nextFreeTicketMicros已经过去了,那我们就需要把这个时间段内的令牌给补充上。这就是所谓的把时间“同步到此刻”。

举个例子:

到这里SmoothBursty的源码就分析完了。分析到这里有一种感觉,就是看代码逻辑是这么个道理,但是总感觉很抽象,不知道这个代码为什么这么写的。我们再详细举个例子,能更有体感一些。

就假设我们设定了限流值是10,假设在第0秒的时候一下子来了20个请求。

一种情况,在第0秒的时候,桶里有10个令牌,那么第一个请求过来的时候,经过resync,nextFreeTicketMicros是此刻(0s)。因为桶里的令牌数足够,所以此时不需要推迟nextFreeTicketMicros。这次请求完,桶里还剩9个令牌。

以此类推,前10个请求,都不需要推迟nextFreeTicketMicros。完成后,桶里没有令牌了。

第11个请求来了,此时桶里没有令牌了,需要推迟nextFreeTicketMicros,推迟0.1s,但是此时返回的因为是更新前的nextFreeTicketMicros,所以第11个请求还是立刻就通过了。

第12个请求来的时候,此时还是第0s(因为我们假设一瞬间来了20个请求),nextFreeTicketMicros是第0.1s了,此时第12个请求要等0.1s了,并且把nextFreeTicketMicros更新到第0.2s处了。

以此类推,第20个请求来的时候,需要等到第1s才能获取到令牌。由此看出,这1s的时间内,通过了20个请求。

那么如果,在第0s的时候,桶里没有令牌呢,那么从第1个请求开始,nextFreeTicketMicros就会每次都推迟0.1s,那么1s内就只能通过10个请求了。

RateLimiter 源码分析-SmoothWarmingUp

分析完了SmoothBursty,我们再分析一下SmoothWarmingUp这种实现。SmoothWarmingUp预热这种实现是有点抽象的,但是我们数形结合着来看,可以帮助理解其核心思想。

理解预热

首先我们理解一下为什么需要预热,如上文所说,如果长时间没有请求过来,一下次来了一个突增的请求,会导致系统的压力比较大。这里就有两个问题,1怎么预热?2怎么衡量系统的冷热程度?

怎么预热呢?我们的目的是让请求通过的不那么快,那么我们让令牌产生的速度变慢,不就可以了吗?换言之,我们让“下次获取令牌的时间”推迟得更多一点不就可以了吗?

怎么衡量系统的冷热程度呢?我们换个角度想,为什么系统会冷,是因为请求来得慢,请求来得慢会有什么表现呢?那就是桶里的令牌多。由此,我们可以用令牌数来表征系统冷热程度,令牌越多,系统越冷,需要让“下次获取令牌的时间”推迟得更多。

我们先来理解一下这张图:

这张图的x轴是桶内的令牌数,thresholdPermits指的是预热阈值,也就是说,当桶内的令牌数超过这个值时,生成每个令牌所需的时间就会成比例增加。stableIntervalMicros表示稳定运行时产生每个令牌需要的时间,coldIntervalMicros表示最冷的时候产生每个令牌需要的时间。

这张图我们一定要理解的点是,最开始系统最冷的时候,桶内令牌数是maxPermits,预热阶段是从最右边往左走的过程。第二个点是,这个坐标轴围成的面积是什么意思,是需要往后推迟的生产令牌的时间!这个点不是很好理解,可以类比稳定阶段来理解,举例来说,比如在稳定阶段,stableIntervalMicros为100ms,某一次请求需要4个令牌,那么下次获取令牌的时间就要往后推迟400ms。而在预热阶段,每消耗一个令牌需要推迟的时间是和桶内令牌数成正比的,因此上图中,围成的梯形面积就是在预热阶段,需要推迟的获取令牌的时间。

初始化RateLimiter

ok,有了上面的基本思想,我们再来看源码。源码核心的就是在计算上面提到的推迟时间。

上面提到,在create阶段,create方法中带了参数几个参数

warmupPeriod是预热时长,可以自己指定。3.0是硬编码的coldFactor,即所谓的预热因子,这个coldFactor表示,在系统最冷的时候,产生一个令牌所需的时间是系统稳定运行时产生一个令牌的时间的3倍。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static RateLimiter create(double permitsPerSecond, long warmupPeriod, TimeUnit unit) {
    checkArgument(warmupPeriod >= 0, "warmupPeriod must not be negative: %s", warmupPeriod);
    return create(SleepingStopwatch.createFromSystemTimer(), permitsPerSecond, warmupPeriod, unit,
                  3.0);
}

通过之前的分析我们知道,create的核心方法就是doSetRate,SmoothWarmingUp的实现是下面这样的。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
void doSetRate(double permitsPerSecond, double stableIntervalMicros) {

    double oldMaxPermits = maxPermits;
    //coldFactor固定是3
    double coldIntervalMicros = stableIntervalMicros * coldFactor;
    //0.5的系数也是固定的,可以认为令牌的阈值是一个折中值
    thresholdPermits = 0.5 * warmupPeriodMicros / stableIntervalMicros;
    //见下方解释1
    maxPermits = thresholdPermits
        + 2.0 * warmupPeriodMicros / (stableIntervalMicros + coldIntervalMicros);
    //见下方解释2
    slope = (coldIntervalMicros - stableIntervalMicros) / (maxPermits - thresholdPermits);
    if (oldMaxPermits == Double.POSITIVE_INFINITY) {
        // if we don't special-case this, we would get storedPermits == NaN, below
        storedPermits = 0.0;
    } else {
        storedPermits = (oldMaxPermits == 0.0)
            ? maxPermits // initial state is cold
            : storedPermits * maxPermits / oldMaxPermits;
    }
}

上面代码有两个地方解释下。

  1. 计算maxPermits,这个只要理解了预热时长warmupPeriodMicros是梯形面积就很好理解了。warmupPeriodMicros=(stableIntervalMicros + coldIntervalMicros)*(maxPermits-thresholdPermits)/2,

变换一下即可得到maxPermits。

  1. 斜率,从图中也可以很快理解。

获取令牌

最后看一下SmoothWarmingUp的acquire方法。其中获取下一次令牌时间的计算方法如下,大致流程和smoothbursty相似,重点要看计算推迟时间的方法storedPermitsToWaitTime。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
final long reserveEarliestAvailable(int requiredPermits, long nowMicros) {
    resync(nowMicros);
    long returnValue = nextFreeTicketMicros;
    double storedPermitsToSpend = min(requiredPermits, this.storedPermits);
    double freshPermits = requiredPermits - storedPermitsToSpend;
    //计算“下次可获取令牌”需要推迟的时间,见下面的代码分析
    long waitMicros = storedPermitsToWaitTime(this.storedPermits, storedPermitsToSpend)
        + (long) (freshPermits * stableIntervalMicros);

    try {
        this.nextFreeTicketMicros = LongMath.checkedAdd(nextFreeTicketMicros, waitMicros);
    } catch (ArithmeticException e) {
        this.nextFreeTicketMicros = Long.MAX_VALUE;
    }
    this.storedPermits -= storedPermitsToSpend;
    return returnValue;
}
long storedPermitsToWaitTime(double storedPermits, double permitsToTake) {

    double availablePermitsAboveThreshold = storedPermits - thresholdPermits;
    long micros = 0;
    //判断是否会经过预热阶段
    if (availablePermitsAboveThreshold > 0.0) {
        //注意这一行,将两种情况合并为一个公式计算,具体理解可以看下面的图示
        double permitsAboveThresholdToTake = min(availablePermitsAboveThreshold, permitsToTake);
        //这里计算,在预热阶段的令牌会多出多少时间
        //这个就是一个计算梯形面积的公式,(上底+下底)*高/2
        micros = (long) (permitsAboveThresholdToTake
                         * (permitsToTime(availablePermitsAboveThreshold)
                            + permitsToTime(availablePermitsAboveThreshold - permitsAboveThresholdToTake)) / 2.0);
        permitsToTake -= permitsAboveThresholdToTake;
    }
    // 这个是假设都在稳定阶段,所需要的时间
    micros += (stableIntervalMicros * permitsToTake);
    return micros;
}
//这个方法是计算在当前令牌数下,生产每个令牌需要的时间
private double permitsToTime(double permits) {
    return stableIntervalMicros + permits * slope;
}

看完上面的计算,大家可能还是比较懵,下面再举个例子来对着代码分析一下,就一目了然了。

情况1:全部都在预热阶段,如下图所示。此时对应的需要计算的时间是梯形ABCD的面积。先分别用permitsToTime方法计算出DE和CF的长度,然后算出梯形CDEF的面积,最后加上矩形ABEF的面积,即可。

情况二:一部分在预热阶段,一部分在稳定阶段,如下图所示。此时需要计算的时间为多边形ABCED的面积。注意此时代码里的permitsAboveThresholdToTake和permitsAboveThresholdToTake是相等的,所以代码中计算的梯形面积退化成了三角形CEF的面积(上底退化为0)。最后计算出的面积为CEF+ABFD。

从上面两种情况可以看出,代码中利用这一行permitsAboveThresholdToTake = min(availablePermitsAboveThreshold, permitsToTake);将两种情况统一为一个“梯形”面积计算公式,还是很巧妙的。

由此,我们计算得到了消耗permitsToTake个令牌需要推迟的时间,就可以得到下次获取令牌的时间。到这里,SmoothWarmingUp实现也就分析完了。其实说白了,SmoothWarmingUp就是为了预热而推迟了更多的时间,上面那一大段的计算,只是为了计算要多推迟多久而已。

总结

令牌桶算法的原理和RateLimiter的实现就分析到这里了。写完这篇文章也有一些感慨,最开始去看令牌桶算法的时候,几句话就看明白了基本思路,感觉是一个很简单的算法。但是真正去看了RateLimiter的实现源码,才发现实现起来没那么简单,要考虑的因素很多,代码实现中的一些思想也是值得仔细思考的,比如不用定时器怎么模拟生产令牌,怎么应对突发流量,怎么预热等。另外一点感受,一个问题,自己理解了和能让别人理解是有很大差别的,可能按照我自己理解写的这篇文章,也没有完全表述清楚我的想法,这就需要更多的交流和碰撞,在反复提问和反思中才会加深理解。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-01-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 后端进阶 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
WordPress获取所有文章链接
填一下以前文章中提到的WordPress获取所有文章链接的坑。 使用方法 在以下的PHP代码中选择一个复制 新建GetId.php文件,将代全部码粘贴 将GetId.php文件上传至网站根目录 通过浏览器访问该文件即可(例如:www.qcgzxw.cn/GetId.php) 显示内容即为所有已发布的文章链接,复制后保存至本地即可(文件使用完毕后记得删了) PHP代码 1.获取所有已发布文章(ID) 文章链接:https://www.qcgzxw.cn/2579.html 红色字体即为前缀,绿色即为后缀 <
神无月
2018/06/25
4.8K2
WordPress更改固定链接诸项事宜
本文目录 前言 具体思路 更改固定链接 添加网站改版规则 规则选择说明 规则编写 后言 前言 记一次站点更改WordPress固定链接并提交百度站长平台链接改版。近日,由于看着 朴素 的链接不顺眼,
神无月
2018/06/22
2K0
php代码获取WordPress网站所有的文章链接
1、网站根目录新建geturl.php文件,将代全部码粘贴通过浏览器访问该文件即可(例如:域名/geturl.php)
小狐狸说事
2022/11/17
1K0
php代码获取WordPress网站所有的文章链接
wordpress代码实现相关文章的几种方法
相关文章分类方法主要有根据文章标签以及文章所属分类来进行区分,以下代码分别使用wordpress自带的query_posts()函数以及直接读取数据库来获取文章的相关文章。另附上获取文章作者的其他文章方法。
子润先生
2021/06/24
5410
WordPress非插件添加文章浏览次数统计功能
本文最后更新于2021年9月6日,已超过1年没有更新,如果文章内容或图片资源失效,请留言反馈,我们会及时处理,谢谢!
回忆大大
2023/03/09
8370
五个wordpress调用随机文章的方法
  分享几个WordPress不用插件调用随机文章的方法,不仅增强用户粘性,而且当蜘蛛来爬你的文章的时候每次都会有变化,搜索引擎很喜欢。主要用到的是orderby rand参数,下面就随ytkah一起来看看吧
ytkah
2019/10/24
1.3K0
自定义wordpress侧边栏小工具
作者:matrix 被围观: 1,726 次 发布时间:2014-01-25 分类:Wordpress | 4 条评论 »
HHTjim 部落格
2022/09/26
3520
自定义wordpress侧边栏小工具
WordPress添加随便看看
随便看看顾名思义就是随机给出文章看看,就是本博客菜单栏上的随便看看的功能。很小但很实用。 原理: 随机在博客中抽取一篇文章来访问。 操作方法: 将以下代码加入到主题目录中的“functions.php
沈唁
2018/05/24
7610
DIY网站统计:WordPress排除管理员评论及精准友链数的方法
今天关注了一下网站统计,发现留言 1600+,想想肯定是把我自己的留言也算进去了,感觉太水了,不真实!另外友链数目也不对,明显是把所有链接都加进去了! 于是研究了一下统计代码,在排除自己的评论、以及友
张戈
2018/03/23
6850
DIY网站统计:WordPress排除管理员评论及精准友链数的方法
WordPress加入”随便看看”菜单功能
实现方法: 将以下代码加入到主题目录中的“functions.php”文件中即可。 function random_postlite() { global $wpdb; $query = "SELECT ID FROM $wpdb->posts WHERE post_type = 'post' AND post_password = '' AND post_status = 'publish' ORDER BY RAND() LIMIT 1"; if ( isset( $_GET['random_c
空木白博客
2019/12/15
4020
WordPress代码实现防止发表重复标题的文章
其实所有的插件这些的就等于放到function.php的代码片段。function.php里面的插件集成出来也可以做成一个插件,反过来插件也可以集成到function.php里面,插件的好处就是方便管理,但也添加了静态的资源请求,所以就导致了,插件越多也会导致网站卡顿的情况,扯得有点远了,直接上代码。
七辰
2023/10/05
5200
给网站加一个定时发布《每天60s读懂世界》功能
Andromeda
2023/10/21
6370
给网站加一个定时发布《每天60s读懂世界》功能
wordpress调用函数大全
WordPress Header头部 PHP代码 (注: 也就是位于<head>和</head>之间的PHP代码)
纯情
2023/04/26
7640
【说站】sql命令选出WordPress数据库中指定分类的文章
在WordPress程序里面,如果从后台找出同一分类的文章是比较容易的,但如果必须从数据库里面筛选出同一分类的文章,并从文章表单wp_posts里面选出文章的id、title等其他字段该如何操作呢?
很酷的站长
2022/11/24
7090
【说站】sql命令选出WordPress数据库中指定分类的文章
【说站】WordPress网站文章ID不连续如何解决?
对于WordPress网站文章ID不连续的问题困扰了我很久,今天将WordPress文章ID不连续的原因和具体解决办法做详细的说明。
很酷的站长
2022/11/24
7140
【说站】WordPress网站文章ID不连续如何解决?
WordPress强迫症技巧:让文章(ID)地址完美连续(障眼法)
这几天博客一直被 CC 攻击,虽然也没带来什么影响,但是因为多 IP 的并发会导致带宽不够,所以暂时用上了百度云加速。因此,导致之前前台刷新缓存功能也就失效了,因为百度云加速节点还缓存了一份呢! 所以,这些天折腾了下 WordPress 评论的动态加载机制,而且已成功实现!打算先测试 2 天,没有问题之后再发文分享,确保读者拿到的是最完善的的代码与教程。 今天有博友在中国博客联盟的群里提到了 WordPress 文章 ID 不同步的问题。我作为强迫症,又不喜欢用乱糟糟的文章名称来作为固定链接,所以对于 Wo
张戈
2018/03/23
1.1K0
WordPress强迫症技巧:让文章(ID)地址完美连续(障眼法)
我是如何 SEO WordPress 的 3:首页
我们知道一个网站的首页是最重要的页面,也是搜索引擎最经常来访问的页面,所以该页面我们应该尽量把更多的重要页面链接显示到首页。但是也要注意,千万不要超过100个链接。
Denis
2023/04/15
1840
分享WordPress各种标签大全集合 以及如何调用
wordpress程序日渐成熟,开发者越来越多,各种模版层出不穷,但是想要做一个好的wordpress模版,前提不只是要掌握HTML5前端技术,还必须了解wordpress的各种标签如何调用才可以。今天全百科网就把整理的WordPress各种标签以及是如何调用分享给大家。
于飞云计算
2019/07/22
3.5K0
分享WordPress各种标签大全集合 以及如何调用
Wordpress中文章的特色图像Featured Image究竟存在哪里?
最近项目需要,分析了一下Wordpress的特色图像 Feature Image的上传、保存方式,这一分析觉得Wordpress的数据结构设计还真是有想法。
大江小浪
2019/02/22
1.8K0
Wordpress中文章的特色图像Featured Image究竟存在哪里?
WordPress完美解决文章ID不连续问题
一、禁用文章修订版 所谓的文章修订版就是你每次修改一次文章,它都会自动帮你保存修改之前的文章版本,专业术语叫做版本控制,这样保证了在误修改的情况下可以还原之前的内容,这个在维基文档的维护方面是有很大帮助的,但是作为我们的小博客,似乎没多大用处,而且这个修订版在数据库中是占据一个ID的,这也是导致文章ID不连续的问题之一。要想禁用文章修订版,可以在 wp-config.php文件中添加:
超级小可爱
2023/02/20
1.7K0
推荐阅读
相关推荐
WordPress获取所有文章链接
更多 >
交个朋友
加入腾讯云官网粉丝站
蹲全网底价单品 享第一手活动信息
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验