Как генерировать изображения веб-страниц в высокопроизводительной среде?
Я пытаюсь создать изображения веб-страниц за секунду в среде на стороне сервера. Запросы могут поступать параллельно, в то же время из Интернета. Для этого я использую библиотеку Puppeteer-Sharp, которая работает довольно хорошо. С другой стороны, он использует Chromium для загрузки страницы, а затем снимает скриншот.
Проблема в том, что для начала требуется время. Например, обратите внимание на время (с моего компьютера) из примера кода readme.md:
var options = new new LaunchOptions {Headless = true, ExecutablePath = @"c:\foo\chrome.exe"};
var browser = await Puppeteer.LaunchAsync(options).Result; // ~500ms
var page = browser.NewPageAsync().Result; // ~215ms
var webPage = page.GoToAsync("http://www.google.com").Result; // ~500ms
var screenshot = page.ScreenshotAsync(outputFile);
screenshot.wait(); // ~300ms
Как вы можете видеть, это легко идет за секунду. Я не знаю, как работает Chromium, поэтому у меня есть пара вопросов, касающихся решений, о которых я думаю.
- Это
PuppeteerSharp.Browser
объект потокобезопасный и / или повторно входящий? Могу ли я использовать один и тот же объект браузера из разных потоков? Я не думаю, потому что это связано с конкретным случаем хрома в памяти. - Если я вырежу
.LaunchAsync
а также.NetPageAsync
от каждого запроса, который значительно ускорит работу. Будет бассейнPuppeteerSharp.Browser
объекты работают? Например, я могу предварительно выделить 5 из них и выполнить.NetPageAsync
на них. Тогда входящие запросы будут использовать объекты из пула. Это жизнеспособный подход?
1 ответ
Несмотря на то, что еще много улучшений, Puppeteer-Sharp является поточно-ориентированным. Чтобы улучшить производительность загрузки, есть несколько подходов, которые вы можете использовать.
Запустите один браузер, а затем подключитесь к нему.
Вы можете запустить один (настоящий) браузер, а затем использовать ConnectAsync
способ подключиться к нему.
await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision);
var browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = false,
});
var theBrowser1 = await Puppeteer.ConnectAsync(new ConnectOptions { BrowserWSEndpoint = browser.WebSocketEndpoint });
var theBrowser2 = await Puppeteer.ConnectAsync(new ConnectOptions { BrowserWSEndpoint = browser.WebSocketEndpoint });
var page1 = await theBrowser1.NewPageAsync();
var page2 = await theBrowser2.NewPageAsync();
await Task.WhenAll(
page1.GoToAsync("https://www.stackru.com"),
page2.GoToAsync("https://serverfault.com/")
);
Я знаю, что код не работает параллельно, но у вас возникнет идея повторно использовать тот же браузер.
Создать новые страницы в том же браузере
Если вы используете TPL, у вас не должно возникнуть проблем при создании новых страниц из разных потоков с использованием одного и того же браузера.
await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision);
var browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = false,
});
var urls = new string[]
{
"https://www.stackru.com",
"https://www.stackru.com",
"https://www.stackru.com",
"https://www.stackru.com",
"https://www.stackru.com",
"https://www.stackru.com",
"https://www.stackru.com",
"https://www.stackru.com",
"https://www.stackru.com",
"https://www.stackru.com",
"https://www.stackru.com"
};
await Task.WhenAll(
urls.Select(url => Task.Factory.StartNew(async () =>
{
var page = await browser.NewPageAsync();
return page.GoToAsync(url);
})));
Опять же, этот пример просто чтобы дать вам представление о том, как этого можно достичь.
Очередь страниц
Есть один пользователь, который создал очередь из X страниц (для x от 0 до X => NewPage), а затем он брал страницы из этой очереди. Вы можете увидеть пример здесь.