ブラウザレンダリングワーカーを耐久オブジェクトでデプロイする
このガイドに従うことで、ブラウザレンダリングAPIとDurable Objectsを使用して、ウェブページのスクリーンショットを取得し、それをR2に保存するワーカーを作成します。
耐久オブジェクトを使用してブラウザセッションを永続化することで、新しいブラウザセッションを立ち上げるのにかかる時間を排除し、パフォーマンスが向上します。耐久オブジェクトはセッションを再利用するため、必要な同時セッションの数を減らします。
- Sign up for a Cloudflare account ↗.
- Install
npm↗. - Install
Node.js↗.
Node.js version manager
Use a Node version manager like Volta ↗ or nvm ↗ to avoid permission issues and change Node.js versions. Wrangler, discussed later in this guide, requires a Node version of 16.17.0 or later.
Cloudflare Workersは、インフラストラクチャを設定または維持することなく、新しいアプリケーションを作成したり、既存のアプリケーションを拡張したりできるサーバーレス実行環境を提供します。あなたのワーカーアプリケーションは、ヘッドレスブラウザと対話してスクリーンショットを取得するなどのアクションを実行するためのコンテナです。
次のコマンドを実行して、browser-workerという名前の新しいワーカープロジェクトを作成します。
npm create cloudflare@latest -- browser-workeryarn create cloudflare@latest browser-workerpnpm create cloudflare@latest browser-worker耐久オブジェクトを有効にするには、Workersの有料プランを購入する必要があります。
- Cloudflareダッシュボード ↗にログインし、アカウントを選択します。
- Workers & Pages > Plansに移動します。
- Purchase Workers Paidを選択し、耐久オブジェクトを有効にするための支払いプロセスを完了します。
browser-workerディレクトリ内で、CloudflareのPuppeteerのフォークをインストールします。
npm install @cloudflare/puppeteer --save-dev本番用と開発用の2つのR2バケットを作成します。
バケット名は小文字でなければならず、ダッシュのみを含むことができます。
wrangler r2 bucket create screenshotswrangler r2 bucket create screenshots-testバケットが作成されたことを確認するには、次のコマンドを実行します。
wrangler r2 bucket listlistコマンドを実行すると、作成したバケット名を含むすべてのバケット名が表示されます。
browser-workerプロジェクトのwrangler.tomlファイルを構成し、ブラウザのバインディングとNode.js互換フラグを追加します。ブラウザバインディングは、ワーカーとヘッドレスブラウザ間の通信を可能にし、スクリーンショットの取得やPDFの生成などのアクションを実行できます。
次のようにwrangler.toml構成ファイルを更新します。
name = "rendering-api-demo"main = "src/index.js"compatibility_date = "2023-09-04"compatibility_flags = [ "nodejs_compat"]account_id = <ACCOUNT_ID>
# ブラウザレンダリングAPIバインディングbrowser = { binding = "MYBROWSER" }
# R2バケットをバインド[[r2_buckets]]binding = "BUCKET"bucket_name = "screenshots"preview_bucket_name = "screenshots-test"
# 耐久オブジェクトへのバインディング[[durable_objects.bindings]]name = "BROWSER"class_name = "Browser"
[[migrations]]tag = "v1" # 各エントリに対して一意である必要がありますnew_classes = ["Browser"] # 新しいクラスの配列以下のコードは、耐久オブジェクトを使用してPuppeteerを使用してブラウザをインスタンス化します。その後、異なる解像度のウェブページを一連に開き、それぞれのスクリーンショットを取得し、R2にアップロードします。
耐久オブジェクトは、最後の使用から60秒間ブラウザセッションをオープンに保ちます。ブラウザセッションがオープンしている場合、リクエストは新しいセッションを作成するのではなく、既存のセッションを再利用します。次のコードをコピーして、ワーカーコードを更新します。
import puppeteer from "@cloudflare/puppeteer";
export default { async fetch(request, env) { let id = env.BROWSER.idFromName("browser"); let obj = env.BROWSER.get(id);
// 耐久オブジェクトにリクエストを送信し、その応答を待ちます。 let resp = await obj.fetch(request.url);
return resp; },};
const KEEP_BROWSER_ALIVE_IN_SECONDS = 60;
export class Browser { constructor(state, env) { this.state = state; this.env = env; this.keptAliveInSeconds = 0; this.storage = this.state.storage; }
async fetch(request) { // テストする画面解像度 const width = [1920, 1366, 1536, 360, 414]; const height = [1080, 768, 864, 640, 896];
// 現在の日付と時刻を使用してR2のフォルダ構造を作成 const nowDate = new Date(); var coeff = 1000 * 60 * 5; var roundedDate = new Date( Math.round(nowDate.getTime() / coeff) * coeff, ).toString(); var folder = roundedDate.split(" GMT")[0];
//ブラウザセッションがオープンしている場合は再利用 if (!this.browser || !this.browser.isConnected()) { console.log(`Browser DO: 新しいインスタンスを開始しています`); try { this.browser = await puppeteer.launch(this.env.MYBROWSER); } catch (e) { console.log( `Browser DO: ブラウザインスタンスを開始できませんでした。エラー: ${e}`, ); } }
// DOへの各呼び出し後にkeptAliveをリセット this.keptAliveInSeconds = 0;
const page = await this.browser.newPage();
// 各画面サイズのスクリーンショットを取得 for (let i = 0; i < width.length; i++) { await page.setViewport({ width: width[i], height: height[i] }); await page.goto("https://workers.cloudflare.com/"); const fileName = "screenshot_" + width[i] + "x" + height[i]; const sc = await page.screenshot({ path: fileName + ".jpg" });
await this.env.BUCKET.put(folder + "/" + fileName + ".jpg", sc); }
// ページでの作業が完了したらタブを閉じる await page.close();
// DOに対するタスクを実行した後にkeptAliveをリセット this.keptAliveInSeconds = 0;
// DOを生かしておくための最初のアラームを設定 let currentAlarm = await this.storage.getAlarm(); if (currentAlarm == null) { console.log(`Browser DO: アラームを設定しています`); const TEN_SECONDS = 10 * 1000; await this.storage.setAlarm(Date.now() + TEN_SECONDS); }
return new Response("成功"); }
async alarm() { this.keptAliveInSeconds += 10;
// ブラウザDOの寿命を延ばす if (this.keptAliveInSeconds < KEEP_BROWSER_ALIVE_IN_SECONDS) { console.log( `Browser DO: ${this.keptAliveInSeconds}秒間生かされています。寿命を延ばしています。`, ); await this.storage.setAlarm(Date.now() + 10 * 1000); // 作業がない場合は自動的に閉じることができます // 例えば、`await this.browser.version()`をリクエストすることでws接続を維持できます } else { console.log( `Browser DO: ${KEEP_BROWSER_ALIVE_IN_SECONDS}sを超えました。`, ); if (this.browser) { console.log(`ブラウザを閉じています。`); await this.browser.close(); } } }}npx wrangler dev --remoteを実行して、Cloudflareのグローバルネットワークにデプロイする前にワーカーをリモートでテストします。ブラウザレンダリングのローカルモードサポートは存在しないため、--remoteが必要です。
npx wrangler deployを実行して、ワーカーをCloudflareのグローバルネットワークにデプロイします。