This is an automated email from the ASF dual-hosted git repository.
markt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/main by this push:
new 137c9f4da9 Refactor RateLimiter FastRateLimiter for extension
137c9f4da9 is described below
commit 137c9f4da9c83b35377baa9622f1a1bac1d3dcc8
Author: Mark Thomas <[email protected]>
AuthorDate: Fri Mar 7 13:51:00 2025 +0000
Refactor RateLimiter FastRateLimiter for extension
Preparation for #794
---
java/org/apache/catalina/util/FastRateLimiter.java | 76 +----------
java/org/apache/catalina/util/RateLimiter.java | 7 +-
java/org/apache/catalina/util/RateLimiterBase.java | 145 +++++++++++++++++++++
3 files changed, 154 insertions(+), 74 deletions(-)
diff --git a/java/org/apache/catalina/util/FastRateLimiter.java
b/java/org/apache/catalina/util/FastRateLimiter.java
index 486a133a64..17544c5d28 100644
--- a/java/org/apache/catalina/util/FastRateLimiter.java
+++ b/java/org/apache/catalina/util/FastRateLimiter.java
@@ -14,92 +14,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.apache.catalina.util;
import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import jakarta.servlet.FilterConfig;
-
-import org.apache.tomcat.util.threads.ScheduledThreadPoolExecutor;
/**
* A RateLimiter that compromises accuracy for speed in order to provide
maximum throughput.
*/
-public class FastRateLimiter implements RateLimiter {
-
- private static AtomicInteger index = new AtomicInteger();
-
- TimeBucketCounter bucketCounter;
-
- int duration;
-
- int requests;
-
- int actualRequests;
-
- int actualDuration;
-
- // Initial policy name can be rewritten by setPolicyName()
- private String policyName = "fast-" + index.incrementAndGet();
-
- @Override
- public String getPolicyName() {
- return policyName;
- }
-
- @Override
- public void setPolicyName(String name) {
- this.policyName = name;
- }
+public class FastRateLimiter extends RateLimiterBase {
@Override
- public int getDuration() {
- return actualDuration;
+ protected String getDefaultPolicyName() {
+ return "fast";
}
- @Override
- public void setDuration(int duration) {
- this.duration = duration;
- }
-
- @Override
- public int getRequests() {
- return actualRequests;
- }
-
- @Override
- public void setRequests(int requests) {
- this.requests = requests;
- }
@Override
- public int increment(String ipAddress) {
- return bucketCounter.increment(ipAddress);
+ protected TimeBucketCounterBase newCounterInstance(int duration,
ScheduledExecutorService executorService) {
+ return new TimeBucketCounter(duration, executorService);
}
- @Override
- public void destroy() {
- bucketCounter.destroy();
- }
-
- @Override
- public void setFilterConfig(FilterConfig filterConfig) {
-
- ScheduledExecutorService executorService = (ScheduledExecutorService)
filterConfig.getServletContext()
- .getAttribute(ScheduledThreadPoolExecutor.class.getName());
-
- if (executorService == null) {
- executorService = new
java.util.concurrent.ScheduledThreadPoolExecutor(1);
- }
-
- bucketCounter = new TimeBucketCounter(duration, executorService);
- actualRequests = (int) Math.round(bucketCounter.getRatio() * requests);
- actualDuration = bucketCounter.getActualDuration() / 1000;
- }
public TimeBucketCounter getBucketCounter() {
- return bucketCounter;
+ return (TimeBucketCounter) bucketCounter;
}
}
diff --git a/java/org/apache/catalina/util/RateLimiter.java
b/java/org/apache/catalina/util/RateLimiter.java
index bdd8b27736..72effc8498 100644
--- a/java/org/apache/catalina/util/RateLimiter.java
+++ b/java/org/apache/catalina/util/RateLimiter.java
@@ -14,7 +14,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.apache.catalina.util;
import jakarta.servlet.FilterConfig;
@@ -46,13 +45,13 @@ public interface RateLimiter {
void setRequests(int requests);
/**
- * Increments the number of requests by the given ipAddress in the current
time window.
+ * Increments the number of requests by the given identifier in the
current time window.
*
- * @param ipAddress the ip address
+ * @param identifier the identifier for which the number of associated
requests should be incremented
*
* @return the new value after incrementing
*/
- int increment(String ipAddress);
+ int increment(String identifier);
/**
* Cleanup no longer needed resources.
diff --git a/java/org/apache/catalina/util/RateLimiterBase.java
b/java/org/apache/catalina/util/RateLimiterBase.java
new file mode 100644
index 0000000000..1f4c699462
--- /dev/null
+++ b/java/org/apache/catalina/util/RateLimiterBase.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.catalina.util;
+
+import java.util.Objects;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import jakarta.servlet.FilterConfig;
+
+/**
+ * Base implementation of {@link RateLimiter}, provides runtime data
maintenance mechanism monitor.
+ */
+public abstract class RateLimiterBase implements RateLimiter {
+
+ private static final AtomicInteger index = new AtomicInteger();
+
+ TimeBucketCounterBase bucketCounter;
+
+ int requests;
+ int actualRequests;
+
+ int duration;
+ int actualDuration;
+
+ // Initial policy name can be rewritten by setPolicyName()
+ private String policyName = null;
+
+ /*
+ * The self-owned utility executor, will be instantiated only when
ScheduledThreadPoolExecutor is absent during
+ * filter configure phase.
+ */
+ private ScheduledThreadPoolExecutor internalExecutorService = null;
+
+ /**
+ * If policy name has not been specified, the first call of {@link
#getPolicyName()} returns a auto-generated policy
+ * name using the default policy name as prefix and followed by
auto-increase index.
+ *
+ * @return default policy name, as a prefix of auto-generated policy name.
+ */
+ protected abstract String getDefaultPolicyName();
+
+
+ @Override
+ public String getPolicyName() {
+ if (policyName == null) {
+ policyName = getDefaultPolicyName() + "-" +
index.incrementAndGet();
+ }
+ return policyName;
+ }
+
+
+ @Override
+ public void setPolicyName(String name) {
+ Objects.requireNonNull(name);
+ this.policyName = name;
+ }
+
+
+ @Override
+ public int getDuration() {
+ return actualDuration;
+ }
+
+
+ @Override
+ public void setDuration(int duration) {
+ this.duration = duration;
+ }
+
+
+ @Override
+ public int getRequests() {
+ return actualRequests;
+ }
+
+
+ @Override
+ public void setRequests(int requests) {
+ this.requests = requests;
+ }
+
+
+ @Override
+ public int increment(String identifier) {
+ return bucketCounter.increment(identifier);
+ }
+
+
+ @Override
+ public void destroy() {
+ bucketCounter.destroy();
+ if (internalExecutorService != null) {
+ try {
+ internalExecutorService.shutdown();
+ } catch (SecurityException e) {
+ // ignore
+ }
+ }
+ }
+
+
+ /**
+ * Instantiate an instance of {@link TimeBucketCounterBase} for specific
time bucket size. Concrete classes
+ * determine its counter policy by returning different implementation
instances.
+ *
+ * @param duration size of each time bucket in seconds
+ * @param utilityExecutor the executor
+ *
+ * @return counter instance of {@link TimeBucketCounterBase}
+ */
+ protected abstract TimeBucketCounterBase newCounterInstance(int duration,
ScheduledExecutorService utilityExecutor);
+
+
+ @Override
+ public void setFilterConfig(FilterConfig filterConfig) {
+
+ ScheduledExecutorService executorService = (ScheduledExecutorService)
filterConfig.getServletContext()
+ .getAttribute(ScheduledThreadPoolExecutor.class.getName());
+
+ if (executorService == null) {
+ internalExecutorService = new
java.util.concurrent.ScheduledThreadPoolExecutor(1);
+ executorService = internalExecutorService;
+ }
+
+ bucketCounter = newCounterInstance(duration, executorService);
+ actualDuration = bucketCounter.getBucketDuration();
+ actualRequests = (int) Math.round(bucketCounter.getRatio() * requests);
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]