Przeszukuję wiele linków z modułem request równolegle z połączeniem modułu async.
Zauważam wiele błędów ETIMEDOUT
i ESOCKETTIMEDOUT
, mimo że linki są osiągalne i szybko reagują za pomocą chrome.Moduł żądania Node.js pobiera ETIMEDOUT i ESOCKETTIMEDOUT
Ograniczam numery maxSockets
do 2 i timeout
do 10000 w opcjach żądań. Używam async.filterLimit()
z limitem 2, aby nawet zmniejszyć równoległość do 2 żądania za każdym razem. Mam więc 2 gniazda, 2 żądania i limit czasu 10 sekund na oczekiwanie na odpowiedź nagłówków z serwera, ale dostaję te błędy.
tutaj; s konfiguracja prośba używam:
{
...
pool: {
maxSockets: 2
},
timeout: 10000
,
time: true
...
}
Oto fragment kodu używam do fecth linki:
var self = this;
async.filterLimit(resources, 2, function(resource, callback) {
request({
uri: resource.uri
}, function (error, response, body) {
if (!error && response.statusCode === 200) {
...
} else {
self.emit('error', resource, error);
}
callback(...);
})
}, function(result) {
callback(null, result);
});
Słuchałem zdarzenia błędu i widzę, gdy kod błędu jest ETIMEDOUT
obiekt connect jest albo prawda/fałsz, więc czasami jest to limit czasu połączenia, a czasami nie jest (zgodnie z dokumentami żądania)
UPDATE: postanowiłem zwiększyć w górę maxSockets
do Infinity
więc brak połączenia będzie rozłączyć z powodu braku dostępnych gniazd:
pool: {
maxSockets: Infinity
}
In-aby kontrolować przepustowość I wdrożone metody requestLoop
które obsługują żądania z maxAttemps
i retryDelay
parametrów do sterowania żądania:
async.filterLimit(resources, 10, function(resource, callback) {
self.requestLoop({
uri: resource.uri
}, 100, 5000, function (error, response, body) {
var fetched = false;
if (!error) {
...
} else {
....
}
callback(...);
});
}, function(result) {
callback(null, result);
});
Implementation of requestLoop:
requestLoop = function(options, attemptsLeft, retryDelay, callback, lastError) {
var self = this;
if (attemptsLeft <= 0) {
callback((lastError != null ? lastError : new Error('...')));
} else {
request(options, function (error, response, body) {
var recoverableErrors = ['ESOCKETTIMEDOUT', 'ETIMEDOUT', 'ECONNRESET', 'ECONNREFUSED'];
var e;
if ((error && _.contains(recoverableErrors, error.code)) || (response && (500 <= response.statusCode && response.statusCode < 600))) {
e = error ? new Error('...');
e.code = error ? error.code : response.statusCode;
setTimeout((function() {
self.requestLoop(options, --attemptsLeft, retryDelay, callback, e);
}), retryDelay);
} else if (!error && (200 <= response.statusCode && response.statusCode < 300)) {
callback(null, response, body);
} else if (error) {
e = new Error('...');
e.code = error.code;
callback(e);
} else {
e = new Error('...');
e.code = response.statusCode;
callback(e);
}
});
}
};
Więc Podsumowując: - Wzmocnione maxSockets
do Infinity
spróbować przezwyciężyć błąd przekroczenia limitu czasu połączenia gniazda - Implemnted requestLoop
metody kontrolowania powiodło się żądanie i maxAttemps
jak również retryDelay
takich wniosków - Jest tam również numer maxium współbieżnego żądania ustawionego przez liczbę przekazaną do async.filterLimit
Chcę zauważyć, że grałem również z ustawieniami wszystkiego tutaj, aby uzyskać darmowe indeksowanie, ale jak dotąd próby nie powiodły się.
Ciągle szukam pomocy w rozwiązaniu tego problemu.
UPDATE2: Zdecydowałem się upuścić async.filterLimit i utworzyć własny mechanizm limitów. mam tylko 3 zmienne, które pomogą mi osiągnąć to:
pendingRequests
- tablica żądanie który będzie posiadał wszystkie żądania (wyjaśni później) activeRequests
- liczba aktywnych żądań maxConcurrentRequests
- liczba maksymalne dozwolone jednoczesnych żądań
do tablica pendingRequests, wciskam kompleksowy obiekt zawierający odniesienie do funkcji requestLoop jak również argumenty tablicę zawierającą argumenty mają być przekazywane do funkcji pętli:
self.pendingRequests.push({
"arguments": [{
uri: resource.uri.toString()
}, self.maxAttempts, function (error, response, body) {
if (!error) {
if (self.policyChecker.isMimeTypeAllowed((response.headers['content-type'] || '').split(';')[0]) &&
self.policyChecker.isFileSizeAllowed(body)) {
self.totalBytesFetched += body.length;
resource.content = self.decodeBuffer(body, response.headers["content-type"] || '', resource);
callback(null, resource);
} else {
self.fetchedUris.splice(self.fetchedUris.indexOf(resource.uri.toString()), 1);
callback(new Error('Fetch failed because a mime-type is not allowed or file size is bigger than permited'));
}
} else {
self.fetchedUris.splice(self.fetchedUris.indexOf(resource.uri.toString()), 1);
callback(error);
}
self.activeRequests--;
self.runRequest();
}],
"function": self.requestLoop
});
self.runRequest();
Ty „” zauważyć połączenie do runRequest()
na końcu. Funkcja ta praca jest zarządzanie żądania i prośby pożaru, jeżeli to możliwe przy zachowaniu maksymalnego activeRequests
poniżej granicy maxConcurrentRequests
:
var self = this;
process.nextTick(function() {
var next;
if (!self.pendingRequests.length || self.activeRequests >= self.maxConcurrentRequests) {
return;
}
self.activeRequests++;
next = self.pendingRequests.shift();
next["function"].apply(self, next["arguments"]);
self.runRequest();
});
To powinno rozwiązać wszelkie błędy limity czasu, przez moje Próby Tho, mam jeszcze zauważyłem niektóre limity czasu na konkretnych stronach, na których to testowałem. Nie mogę być w 100% pewny, ale myślę, że wynika to z charakteru strony internetowej, która wspiera serwer http ograniczając żądania użytkowników do maksimum, wykonując sprawdzanie ip i zwracając niektóre wiadomości HTTP 400 aby zapobiec ewentualnemu "atakowi" na serwerze.
Czy zdarzyło Ci się kiedyś dowiedzieć czegoś takiego @Jorayen? – DvideBy0
Zgadnij, nie, masz podobny problem czasami – Denny
@ DvideBy0 Zaktualizowano rozwiązanie – Jorayen