mirror of
https://github.com/Xiao-MoMi/craft-engine.git
synced 2025-12-19 15:09:15 +00:00
改用滑动窗口
This commit is contained in:
@@ -85,5 +85,6 @@ tasks {
|
||||
relocate("io.netty.handler.codec.rtsp", "net.momirealms.craftengine.libraries.netty.handler.codec.rtsp")
|
||||
relocate("io.netty.handler.codec.spdy", "net.momirealms.craftengine.libraries.netty.handler.codec.spdy")
|
||||
relocate("io.netty.handler.codec.http2", "net.momirealms.craftengine.libraries.netty.handler.codec.http2")
|
||||
relocate("io.github.bucket4j", "net.momirealms.craftengine.libraries.bucket4j")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,6 +172,7 @@ tasks {
|
||||
relocate("io.netty.handler.codec.rtsp", "net.momirealms.craftengine.libraries.netty.handler.codec.rtsp")
|
||||
relocate("io.netty.handler.codec.spdy", "net.momirealms.craftengine.libraries.netty.handler.codec.spdy")
|
||||
relocate("io.netty.handler.codec.http2", "net.momirealms.craftengine.libraries.netty.handler.codec.http2")
|
||||
relocate("io.github.bucket4j", "net.momirealms.craftengine.libraries.bucket4j")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,3 +35,4 @@ amazon-sdk-s3=${amazon_awssdk_version}
|
||||
amazon-sdk-eventstream=${amazon_awssdk_eventstream_version}
|
||||
evalex=${evalex_version}
|
||||
jimfs=${jimfs_version}
|
||||
bucket4j=${bucket4j_version}
|
||||
@@ -21,7 +21,7 @@ dependencies {
|
||||
implementation("net.momirealms:sparrow-nbt-codec:${rootProject.properties["sparrow_nbt_version"]}")
|
||||
implementation("net.momirealms:sparrow-nbt-legacy-codec:${rootProject.properties["sparrow_nbt_version"]}")
|
||||
// S3
|
||||
implementation("net.momirealms:craft-engine-s3:0.8")
|
||||
implementation("net.momirealms:craft-engine-s3:0.9")
|
||||
// Util
|
||||
compileOnly("net.momirealms:sparrow-util:${rootProject.properties["sparrow_util_version"]}")
|
||||
// Adventure
|
||||
@@ -69,6 +69,8 @@ dependencies {
|
||||
compileOnly("com.mojang:authlib:${rootProject.properties["authlib_version"]}")
|
||||
// concurrentutil
|
||||
compileOnly("ca.spottedleaf:concurrentutil:${rootProject.properties["concurrent_util_version"]}")
|
||||
// bucket4j
|
||||
compileOnly("com.bucket4j:bucket4j_jdk17-core:${rootProject.properties["bucket4j_version"]}")
|
||||
}
|
||||
|
||||
java {
|
||||
@@ -107,6 +109,7 @@ tasks {
|
||||
relocate("io.netty.handler.codec.rtsp", "net.momirealms.craftengine.libraries.netty.handler.codec.rtsp")
|
||||
relocate("io.netty.handler.codec.spdy", "net.momirealms.craftengine.libraries.netty.handler.codec.spdy")
|
||||
relocate("io.netty.handler.codec.http2", "net.momirealms.craftengine.libraries.netty.handler.codec.http2")
|
||||
relocate("io.github.bucket4j", "net.momirealms.craftengine.libraries.bucket4j") // bucket4j
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.momirealms.craftengine.core.pack.host.impl;
|
||||
|
||||
import io.github.bucket4j.Bandwidth;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory;
|
||||
@@ -12,6 +13,7 @@ import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
@@ -76,14 +78,19 @@ public class SelfHost implements ResourcePackHost {
|
||||
boolean oneTimeToken = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("one-time-token", true), "one-time-token");
|
||||
String protocol = arguments.getOrDefault("protocol", "http").toString();
|
||||
boolean denyNonMinecraftRequest = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("deny-non-minecraft-request", true), "deny-non-minecraft-request");
|
||||
Map<String, Object> rateMap = MiscUtils.castToMap(arguments.get("rate-map"), true);
|
||||
Map<String, Object> rateMap = MiscUtils.castToMap(arguments.getOrDefault("rate-map", arguments.get("rate-limit")), true);
|
||||
int maxRequests = 5;
|
||||
int resetInterval = 20_000;
|
||||
int resetInterval = 20;
|
||||
if (rateMap != null) {
|
||||
maxRequests = ResourceConfigUtils.getAsInt(rateMap.getOrDefault("max-requests", 5), "max-requests");
|
||||
resetInterval = ResourceConfigUtils.getAsInt(rateMap.getOrDefault("reset-interval", 20), "reset-interval") * 1000;
|
||||
maxRequests = Math.max(ResourceConfigUtils.getAsInt(rateMap.getOrDefault("max-requests", 5), "max-requests"), 1);
|
||||
resetInterval = Math.max(ResourceConfigUtils.getAsInt(rateMap.getOrDefault("reset-interval", 20), "reset-interval"), 1);
|
||||
}
|
||||
selfHostHttpServer.updateProperties(ip, port, url, denyNonMinecraftRequest, protocol, maxRequests, resetInterval, oneTimeToken);
|
||||
Bandwidth limit = Bandwidth.builder()
|
||||
.capacity(maxRequests)
|
||||
.refillGreedy(maxRequests, Duration.ofSeconds(resetInterval))
|
||||
.initialTokens(maxRequests / 2) // 修正首次可以直接突破限制请求 maxRequests * 2 次
|
||||
.build();
|
||||
selfHostHttpServer.updateProperties(ip, port, url, denyNonMinecraftRequest, protocol, limit, oneTimeToken);
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package net.momirealms.craftengine.core.pack.host.impl;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.github.benmanes.caffeine.cache.Scheduler;
|
||||
import io.github.bucket4j.Bandwidth;
|
||||
import io.github.bucket4j.Bucket;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.*;
|
||||
@@ -23,6 +25,7 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.Duration;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
@@ -34,17 +37,20 @@ public class SelfHostHttpServer {
|
||||
.scheduler(Scheduler.systemScheduler())
|
||||
.expireAfterWrite(1, TimeUnit.MINUTES)
|
||||
.build();
|
||||
private final Cache<String, IpAccessRecord> ipAccessCache = Caffeine.newBuilder()
|
||||
private final Cache<String, Bucket> ipRateLimiters = Caffeine.newBuilder()
|
||||
.maximumSize(256)
|
||||
.scheduler(Scheduler.systemScheduler())
|
||||
.expireAfterWrite(10, TimeUnit.MINUTES)
|
||||
.expireAfterAccess(10, TimeUnit.MINUTES)
|
||||
.build();
|
||||
|
||||
private final AtomicLong totalRequests = new AtomicLong();
|
||||
private final AtomicLong blockedRequests = new AtomicLong();
|
||||
|
||||
private int rateLimit = 1;
|
||||
private long rateLimitInterval = 1000;
|
||||
private Bandwidth limit = Bandwidth.builder()
|
||||
.capacity(1)
|
||||
.refillGreedy(1, Duration.ofSeconds(1))
|
||||
.initialTokens(1)
|
||||
.build();
|
||||
private String ip = "localhost";
|
||||
private int port = -1;
|
||||
private String protocol = "http";
|
||||
@@ -72,15 +78,13 @@ public class SelfHostHttpServer {
|
||||
String url,
|
||||
boolean denyNonMinecraft,
|
||||
String protocol,
|
||||
int maxRequests,
|
||||
int resetInterval,
|
||||
Bandwidth limit,
|
||||
boolean token) {
|
||||
this.ip = ip;
|
||||
this.url = url;
|
||||
this.denyNonMinecraft = denyNonMinecraft;
|
||||
this.protocol = protocol;
|
||||
this.rateLimit = maxRequests;
|
||||
this.rateLimitInterval = resetInterval;
|
||||
this.limit = limit;
|
||||
this.useToken = token;
|
||||
|
||||
if (port <= 0 || port > 65535) {
|
||||
@@ -214,22 +218,12 @@ public class SelfHostHttpServer {
|
||||
}
|
||||
|
||||
private boolean checkRateLimit(String clientIp) {
|
||||
IpAccessRecord record = ipAccessCache.getIfPresent(clientIp);
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
if (record == null) {
|
||||
record = new IpAccessRecord(now, 1);
|
||||
ipAccessCache.put(clientIp, record);
|
||||
return false;
|
||||
Bucket rateLimiter = ipRateLimiters.get(clientIp, k -> Bucket.builder().addLimit(limit).build());
|
||||
if (rateLimiter == null) { // 怎么可能null?
|
||||
rateLimiter = Bucket.builder().addLimit(limit).build();
|
||||
ipRateLimiters.put(clientIp, rateLimiter);
|
||||
}
|
||||
|
||||
if (now - record.lastAccessTime > rateLimitInterval) {
|
||||
record.lastAccessTime = now;
|
||||
record.accessCount = 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
return ++record.accessCount > rateLimit;
|
||||
return !rateLimiter.tryConsume(1);
|
||||
}
|
||||
|
||||
private boolean validateToken(String token) {
|
||||
@@ -312,14 +306,4 @@ public class SelfHostHttpServer {
|
||||
CraftEngine.instance().logger().severe("SHA-1 algorithm not available", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static class IpAccessRecord {
|
||||
long lastAccessTime;
|
||||
int accessCount;
|
||||
|
||||
IpAccessRecord(long lastAccessTime, int accessCount) {
|
||||
this.lastAccessTime = lastAccessTime;
|
||||
this.accessCount = accessCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -419,7 +419,8 @@ public abstract class CraftEngine implements Plugin {
|
||||
Dependencies.LZ4,
|
||||
Dependencies.EVALEX,
|
||||
Dependencies.NETTY_HTTP,
|
||||
Dependencies.JIMFS
|
||||
Dependencies.JIMFS,
|
||||
Dependencies.BUCKET_4_J
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -372,6 +372,13 @@ public class Dependencies {
|
||||
List.of(Relocation.of("jimfs", "com{}google{}common{}jimfs"))
|
||||
);
|
||||
|
||||
public static final Dependency BUCKET_4_J = new Dependency(
|
||||
"bucket4j",
|
||||
"com{}bucket4j",
|
||||
"bucket4j_jdk17-core",
|
||||
List.of(Relocation.of("bucket4j", "io{}github{}bucket4j"))
|
||||
);
|
||||
|
||||
public static final Dependency NETTY_HTTP = new Dependency(
|
||||
"netty-codec-http",
|
||||
"io{}netty",
|
||||
|
||||
@@ -56,6 +56,7 @@ amazon_awssdk_eventstream_version=1.0.1
|
||||
jimfs_version=1.3.1
|
||||
authlib_version=7.0.60
|
||||
concurrent_util_version=0.0.3
|
||||
bucket4j_version=8.15.0
|
||||
|
||||
# Proxy settings
|
||||
#systemProp.socks.proxyHost=127.0.0.1
|
||||
|
||||
Reference in New Issue
Block a user