我使用WebClient
对象向服务器发送Http Post请求。
它非常迅速地发送大量请求(QueueChannel
中有大约4000条消息)。问题是......似乎服务器响应速度不够快......所以我收到很多服务器错误500并且连接器过早关闭。
有没有办法限制每秒的请求数量?或者限制它使用的线程数?
编辑:
QueueChannel中的消息端点处理消息:
@MessageEndpoint
public class CustomServiceActivator {
private static final Logger logger = LogManager.getLogger();
@Autowired
IHttpService httpService;
@ServiceActivator(
inputChannel = "outputFilterChannel",
outputChannel = "outputHttpServiceChannel",
poller = @Poller( fixedDelay = "1000" )
)
public void processMessage(Data data) {
httpService.push(data);
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
WebClient服务类:
@Service
public class HttpService implements IHttpService {
private static final String URL = "http://www.blabla.com/log";
private static final Logger logger = LogManager.getLogger();
@Autowired
WebClient webClient;
@Override
public void push(Data data) {
String body = constructString(data);
Mono<ResponseEntity<Response>> res = webClient.post()
.uri(URL + getLogType(data))
.contentLength(body.length())
.contentType(MediaType.APPLICATION_JSON)
.syncBody(body)
.exchange()
.flatMap(response -> response.toEntity(Response.class));
res.subscribe(new Consumer<ResponseEntity<Response>>() { ... });
}
}
答案 0 :(得分:5)
问题Limiting rate of requests with Reactor提供了两个回复者(一个在评论中)
zip与另一个充当速率限制器的通量
.zipWith(Flux.interval(Duration.of(1,ChronoUnit.SECONDS)))
只是延迟每个网络请求
使用 delayElements 功能
编辑:以下答案对于阻止RestTemplate有效,但不太适合反应模式。
WebClient无法限制请求,但您可以使用合成轻松添加此功能。
您可以使用Guava /中的RateLimiter从外部限制客户端 (https://google.github.io/guava/releases/19.0/api/docs/index.html?com/google/common/util/concurrent/RateLimiter.html)
在本教程http://www.baeldung.com/guava-rate-limiter中,您将了解如何以阻止方式或超时使用速率限制器。
我会在单独的类
中修饰所有需要限制的调用答案 1 :(得分:2)
我希望参加聚会不迟到。无论如何,限制请求的速率只是一周前我创建爬网程序时遇到的问题之一。这里是问题:
这是解决方案:
"""
---------------------------
d.txt
---------------------------
alice
wonderland
alice-again
oh-dear-alice
---------------------------
alice.txt
---------------------------
aline
alice
oh-no-alice
---------------------------
"""
dictionary = list(open('d.txt','r'))
dictionary = set([i.strip() for i in dictionary])
#Once you have your list of words
#dictionary = set(get_dict_list())
input_file = list(open('alice.txt','r'))
input_file = set([i.strip() for i in input_file])
#input_file = set(get_story_list())
misspelled_words = input_file - dictionary
答案 2 :(得分:1)
我用它来限制活动请求的数量:
public DemoClass(WebClient.Builder webClientBuilder) {
AtomicInteger activeRequest = new AtomicInteger();
this.webClient = webClientBuilder
.baseUrl("http://httpbin.org/ip")
.filter(
(request, next) -> Mono.just(next)
.flatMap(a -> {
if (activeRequest.intValue() < 3) {
activeRequest.incrementAndGet();
return next.exchange(request)
.doOnNext(b -> activeRequest.decrementAndGet());
}
return Mono.error(new RuntimeException("Too many requests"));
})
.retryWhen(Retry.anyOf(RuntimeException.class)
.randomBackoff(Duration.ofMillis(300), Duration.ofMillis(1000))
.retryMax(50)
)
)
.build();
}
public Mono<String> call() {
return webClient.get()
.retrieve()
.bodyToMono(String.class);
}
答案 3 :(得分:0)
Resilience4j对Project Reactor的无阻塞速率限制提供了出色的支持。
必需的依赖项(在Spring WebFlux旁边):
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-reactor</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-ratelimiter</artifactId>
<version>1.6.1</version>
</dependency>
示例:
import io.github.resilience4j.ratelimiter.RateLimiter;
import io.github.resilience4j.ratelimiter.RateLimiterConfig;
import io.github.resilience4j.reactor.ratelimiter.operator.RateLimiterOperator;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.concurrent.atomic.AtomicInteger;
public class WebClientRateLimit
{
private static final AtomicInteger COUNTER = new AtomicInteger(0);
private final WebClient webClient;
private final RateLimiter rateLimiter;
public WebClientRateLimit()
{
this.webClient = WebClient.create();
// enables 3 requests every 5 seconds
this.rateLimiter = RateLimiter.of("my-rate-limiter",
RateLimiterConfig.custom()
.limitRefreshPeriod(Duration.ofSeconds(5))
.limitForPeriod(3)
.timeoutDuration(Duration.ofMinutes(1)) // max wait time for a request, if reached then error
.build());
}
public Mono<?> call()
{
return webClient.get()
.uri("https://jsonplaceholder.typicode.com/todos/1")
.retrieve()
.bodyToMono(String.class)
.doOnSubscribe(s -> System.out.println(COUNTER.incrementAndGet() + " - " + LocalDateTime.now()
+ " - call triggered"))
.transformDeferred(RateLimiterOperator.of(rateLimiter));
}
public static void main(String[] args)
{
WebClientRateLimit webClientRateLimit = new WebClientRateLimit();
long start = System.currentTimeMillis();
Flux.range(1, 16)
.flatMap(x -> webClientRateLimit.call())
.blockLast();
System.out.println("Elapsed time in seconds: " + (System.currentTimeMillis() - start) / 1000d);
}
}
示例输出:
1 - 2020-11-30T15:44:01.575003200 - call triggered
2 - 2020-11-30T15:44:01.821134 - call triggered
3 - 2020-11-30T15:44:01.823133100 - call triggered
4 - 2020-11-30T15:44:04.462353900 - call triggered
5 - 2020-11-30T15:44:04.462353900 - call triggered
6 - 2020-11-30T15:44:04.470399200 - call triggered
7 - 2020-11-30T15:44:09.461199100 - call triggered
8 - 2020-11-30T15:44:09.463157 - call triggered
9 - 2020-11-30T15:44:09.463157 - call triggered
11 - 2020-11-30T15:44:14.461447700 - call triggered
10 - 2020-11-30T15:44:14.461447700 - call triggered
12 - 2020-11-30T15:44:14.461447700 - call triggered
13 - 2020-11-30T15:44:19.462098200 - call triggered
14 - 2020-11-30T15:44:19.462098200 - call triggered
15 - 2020-11-30T15:44:19.468059700 - call triggered
16 - 2020-11-30T15:44:24.462615 - call triggered
Elapsed time in seconds: 25.096
文档:https://resilience4j.readme.io/docs/examples-1#decorate-mono-or-flux-with-a-ratelimiter
答案 4 :(得分:0)
我们可以自定义ConnectionBuilder以对WebClient上的活动连接进行速率限制。
由于队列的默认大小始终为2 * maxConnections,因此需要为队列中的等待请求数添加endingAquiredMaxCount。
此速率限制了Web客户端一次处理请求。
ConnectionProvider provider = ConnectionProvider.builder('builder').maxConnections(maxConnections).pendingAcquireMaxCount(maxPendingRequests).build()
TcpClient tcpClient = TcpClient
.create(provider)
WebClient client = WebClient.builder()
.baseUrl('url')
.clientConnector(new ReactorClientHttpConnector(HttpClient.from(tcpClient)))