Skip to content

Commit

Permalink
Add redisson cluster level limiter
Browse files Browse the repository at this point in the history
  • Loading branch information
hexiaofeng committed Jan 3, 2025
1 parent 69579e6 commit a0735fb
Show file tree
Hide file tree
Showing 34 changed files with 1,155 additions and 26 deletions.
13 changes: 13 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,19 @@ joylive-flowcontrol-resilience4j
io.github.resilience4j -> com.jd.live.agent.shaded.io.github.resilience4j
io.vavr -> com.jd.live.agent.shaded.io.vavr
org.slf4j -> com.jd.live.agent.shaded.org.slf4j depend on joylive-logger-slf4j
joylive-flowcontrol-redisson
org.redisson -> com.jd.live.agent.shaded.org.redisson
io.netty -> com.jd.live.agent.shaded.io.netty
javax.cache -> com.jd.live.agent.shaded.javax.cache
rx -> com.jd.live.agent.shaded.rx
reactor -> com.jd.live.agent.shaded.reactor
org.reactivestreams -> com.jd.live.agent.shaded.org.reactivestreams
org.objenesis -> com.jd.live.agent.shaded.org.objenesis
com.esotericsoftware -> com.jd.live.agent.shaded.com.esotericsoftware
com.fasterxml -> com.jd.live.agent.shaded.com.fasterxml depend on joylive-parser-jackson
org.yaml -> com.jd.live.agent.shaded.org.yaml depend on joylive-parser-jackson
net.bytebuddy -> com.jd.live.agent.shaded.net.bytebuddy depend on joylive-bytekit-bytebuddy
org.slf4j -> com.jd.live.agent.shaded.org.slf4j depend on joylive-logger-slf4j
joylive-service-nacos
com.alibaba.nacos -> com.jd.live.agent.shaded.com.alibaba.nacos
org.codehaus -> com.jd.live.agent.shaded.org.codehaus depend on joylive-event-opentelemetry
Expand Down
5 changes: 5 additions & 0 deletions joylive-bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,11 @@
<artifactId>joylive-flowcontrol-resilience4j</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.jd.live</groupId>
<artifactId>joylive-flowcontrol-redisson</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.jd.live</groupId>
<artifactId>joylive-classloader-springboot2</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.jd.live.agent.governance.policy.service.limit.RateLimitPolicy;
import com.jd.live.agent.governance.policy.service.limit.SlidingWindow;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
Expand Down Expand Up @@ -99,19 +100,20 @@ private void addRecycler() {
*/
private void recycle() {
long expireTime = governanceConfig.getServiceConfig().getRateLimiter().getExpireTime();
for (Map.Entry<Long, AtomicReference<RateLimiter>> entry : limiters.entrySet()) {
AtomicReference<RateLimiter> reference = entry.getValue();
RateLimiter limiter = reference.get();
if (limiter != null && (System.currentTimeMillis() - limiter.getLastAcquireTime()) > expireTime) {
reference = limiters.remove(entry.getKey());
if (reference != null) {
limiter = reference.get();
if (limiter != null && (System.currentTimeMillis() - limiter.getLastAcquireTime()) <= expireTime) {
limiters.putIfAbsent(entry.getKey(), reference);
List<RateLimiter> recycles = new ArrayList<>();
limiters.forEach((key, reference) -> {
limiters.compute(key, (k, ref) -> {
if (ref != null) {
RateLimiter limiter = ref.get();
if (limiter != null && limiter.isExpired(expireTime)) {
recycles.add(limiter);
return null;
}
}
}
}
return ref;
});
});
recycles.forEach(RateLimiter::recycle);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,12 @@ default boolean acquire(long timeout, TimeUnit timeUnit) {
* @return policy
*/
RateLimitPolicy getPolicy();

default boolean isExpired(long expireTime) {
return System.currentTimeMillis() - getLastAcquireTime() > expireTime;
}

default void recycle() {

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.jd.live</groupId>
<artifactId>joylive-flowcontrol</artifactId>
<version>${revision}</version>
</parent>

<artifactId>joylive-flowcontrol-redisson</artifactId>

<properties>
<redisson.version>3.41.0</redisson.version>
</properties>

<dependencies>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>${redisson.version}</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<configuration>
<artifactSet>
<includes>
<include>org.redisson:*</include>
<include>io.netty:*</include>
<include>javax.cache:*</include>
<include>org.objenesis:*</include>
<include>com.esotericsoftware:*</include>
<include>io.projectreactor:reactor-core</include>
<include>org.reactivestreams:reactive-stream</include>
<include>io.reactivex.rxjava3:rxjava</include>
<include>org.jodd:jodd-util</include>
</includes>
</artifactSet>
<transformers>
<!-- This transformer will merge the contents of META-INF/services -->
<transformer
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"/>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformer"/>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformer">
<addHeader>false</addHeader>
</transformer>
</transformers>
<relocations>
<relocation>
<pattern>org.redisson</pattern>
<shadedPattern>com.jd.live.agent.shaded.org.redisson</shadedPattern>
</relocation>
<relocation>
<pattern>io.netty</pattern>
<shadedPattern>com.jd.live.agent.shaded.io.netty</shadedPattern>
</relocation>
<relocation>
<pattern>javax.cache</pattern>
<shadedPattern>com.jd.live.agent.shaded.javax.cache</shadedPattern>
</relocation>
<relocation>
<pattern>rx</pattern>
<shadedPattern>com.jd.live.agent.shaded.rx</shadedPattern>
</relocation>
<relocation>
<pattern>reactor</pattern>
<shadedPattern>com.jd.live.agent.shaded.reactor</shadedPattern>
</relocation>
<relocation>
<pattern>org.reactivestreams</pattern>
<shadedPattern>com.jd.live.agent.shaded.org.reactivestreams</shadedPattern>
</relocation>
<relocation>
<pattern>org.objenesis</pattern>
<shadedPattern>com.jd.live.agent.shaded.org.objenesis</shadedPattern>
</relocation>
<relocation>
<pattern>com.esotericsoftware</pattern>
<shadedPattern>com.jd.live.agent.shaded.com.esotericsoftware</shadedPattern>
</relocation>
<relocation>
<pattern>com.fasterxml</pattern>
<shadedPattern>com.jd.live.agent.shaded.com.fasterxml</shadedPattern>
</relocation>
<relocation>
<pattern>org.yaml</pattern>
<shadedPattern>com.jd.live.agent.shaded.org.yaml</shadedPattern>
</relocation>
<relocation>
<pattern>net.bytebuddy</pattern>
<shadedPattern>com.jd.live.agent.shaded.net.bytebuddy</shadedPattern>
</relocation>
<relocation>
<pattern>org.slf4j</pattern>
<shadedPattern>com.jd.live.agent.shaded.org.slf4j</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright © ${year} ${owner} (${email})
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jd.live.agent.implement.flowcontrol.ratelimit.redisson;

import com.jd.live.agent.bootstrap.logger.Logger;
import com.jd.live.agent.bootstrap.logger.LoggerFactory;
import com.jd.live.agent.governance.invoke.ratelimit.AbstractRateLimiter;
import com.jd.live.agent.governance.policy.service.limit.RateLimitPolicy;
import com.jd.live.agent.governance.policy.service.limit.SlidingWindow;
import com.jd.live.agent.implement.flowcontrol.ratelimit.redisson.client.RedisClient;
import com.jd.live.agent.implement.flowcontrol.ratelimit.redisson.client.RedisClientManager;
import com.jd.live.agent.implement.flowcontrol.ratelimit.redisson.client.RedisConfig;
import org.redisson.api.RRateLimiter;
import org.redisson.api.RateType;

import java.time.Duration;
import java.util.concurrent.TimeUnit;

/**
* RedissonRateLimiter
*
* @since 1.6.0
*/
public class RedissonRateLimiter extends AbstractRateLimiter {

private static final Logger logger = LoggerFactory.getLogger(RedissonRateLimiter.class);

private final RedisClient client;

private final RRateLimiter limiter;

public RedissonRateLimiter(RedisClientManager manager, RateLimitPolicy policy, SlidingWindow window) {
this(manager, policy, window, policy.getName());
}

public RedissonRateLimiter(RedisClientManager manager, RateLimitPolicy policy, SlidingWindow window, String name) {
super(policy, TimeUnit.MILLISECONDS);
this.client = manager.getOrCreateClient(new RedisConfig(policy.getId(), option));
this.limiter = client == null ? null : client.getRateLimiter("LiveAgent-limiter-" + policy.getId());
if (limiter != null) {
limiter.trySetRate(RateType.OVERALL, window.getThreshold(), Duration.ofMillis(window.getTimeWindowInMs()));
}
}

@Override
protected boolean doAcquire(int permits, long timeout, TimeUnit timeUnit) {
if (client != null) {
client.setLastAccessTime(System.currentTimeMillis());
}
try {
return limiter == null || limiter.tryAcquire(permits, Duration.ofNanos(timeUnit.toNanos(timeout)));
} catch (Throwable e) {
logger.error(e.getMessage(), e);
return true;
}
}

@Override
public void recycle() {
if (client != null) {
client.decReference();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright © ${year} ${owner} (${email})
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jd.live.agent.implement.flowcontrol.ratelimit.redisson;

import com.jd.live.agent.core.extension.annotation.Extension;
import com.jd.live.agent.core.inject.annotation.Inject;
import com.jd.live.agent.core.inject.annotation.Injectable;
import com.jd.live.agent.core.util.time.Timer;
import com.jd.live.agent.governance.invoke.ratelimit.AbstractRateLimiterFactory;
import com.jd.live.agent.governance.invoke.ratelimit.RateLimiter;
import com.jd.live.agent.governance.policy.service.limit.RateLimitPolicy;
import com.jd.live.agent.governance.policy.service.limit.SlidingWindow;
import com.jd.live.agent.implement.flowcontrol.ratelimit.redisson.client.RedisClientManager;

import java.util.List;

/**
* RedissonRateLimiterFactory
*
* @since 1.6.0
*/
@Injectable
@Extension(value = "RedissonCluster")
public class RedissonRateLimiterFactory extends AbstractRateLimiterFactory {

@Inject(Timer.COMPONENT_TIMER)
private Timer timer;

private transient volatile RedisClientManager manager;

@Override
protected RateLimiter create(RateLimitPolicy policy) {
List<SlidingWindow> windows = policy.getSlidingWindows();
RedisClientManager manager = getManager();
return windows.size() == 1
? new RedissonRateLimiter(manager, policy, windows.get(0))
: new RedissonRateLimiterGroup(manager, policy);
}

/**
* Retrieves the singleton instance of {@link RedisClientManager}.
*
* @return The singleton instance of {@link RedisClientManager}.
*/
private RedisClientManager getManager() {
if (manager == null) {
synchronized (this) {
if (manager == null) {
manager = new RedisClientManager(timer);
}
}
}
return manager;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright © ${year} ${owner} (${email})
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jd.live.agent.implement.flowcontrol.ratelimit.redisson;

import com.jd.live.agent.governance.invoke.ratelimit.AbstractRateLimiterGroup;
import com.jd.live.agent.governance.invoke.ratelimit.RateLimiter;
import com.jd.live.agent.governance.policy.service.limit.RateLimitPolicy;
import com.jd.live.agent.governance.policy.service.limit.SlidingWindow;
import com.jd.live.agent.implement.flowcontrol.ratelimit.redisson.client.RedisClientManager;

/**
* RedissonRateLimiterGroup
*
* @since 1.6.0
*/
public class RedissonRateLimiterGroup extends AbstractRateLimiterGroup {

private final RedisClientManager manager;

public RedissonRateLimiterGroup(RedisClientManager manager, RateLimitPolicy policy) {
super(policy);
this.manager = manager;
}

@Override
protected RateLimiter create(SlidingWindow window, String name) {
return new RedissonRateLimiter(manager, policy, window, name);
}
}
Loading

0 comments on commit a0735fb

Please sign in to comment.