以前とりあえず作ってみたサーバーが不安定なので、書き直すことにした。
Dockerで動いてGETパラメータでURLを渡したらPDFが返ってくるというシンプルなもの。
PDFのサイズ(現在はA4固定)等のオプションは起動時に固定してしまうので、変えられない。
ローカルネットワーク内でAPIとして使うので、外に直接公開することは考えていない。
そのほか詳細は以前の記事参照
uyamazak.hatenablog.com
Dockerfile
FROM node:9 RUN mkdir /varuna/ WORKDIR /varuna/ # Install google-chrome from .deb file using gdebi for dependencies. RUN apt-get update && \ apt-get install -y gdebi COPY google-chrome-stable_current_amd64.deb ./ RUN gdebi --non-interactive google-chrome-stable_current_amd64.deb # Install chrome-headless-render-pdf RUN npm install \ chrome-headless-render-pdf@1.5 \ express \ connect-timeout RUN chmod 700 ./node_modules/chrome-headless-render-pdf/cli/chrome-headless-render-pdf.js # add option --no-sandbox COPY node_modules/chrome-headless-render-pdf/index.js node_modules/chrome-headless-render-pdf/index.js # Install fonts COPY fonts /usr/share/fonts # COPY my app dir COPY app app EXPOSE 8000 CMD ["node", "app/pdf-server-proto.js"]
chromeのインストールファイルgoogle-chrome-stable_current_amd64.debは下記ページの「別のプラットフォーム向けの Chrome をダウンロード」から取得して同じディレクトリに配置しておく。
ついでにnodeのバージョンも9にした。
chrome-headless-render-pdfも1.5になり、paper-width、paper-heightを渡せるようになったが、まだ Docker上で動かすのに必要な--no-sandboxを渡せないので、これだけindex.jsの192行目に追加してとりあえず使う。
プルリしたいけどまだ自信ない。
サーバー本体
pdf-server-proto.js
const execSync = require('child_process').execSync; const chromeBinary = '/usr/bin/google-chrome-stable' const chromeVersion = execSync(chromeBinary + " --no-sandbox --version"); console.log("chrome version:", chromeVersion.toString()); const options = { printLogs: true, printErrors: true, chromeBinary: chromeBinary, noMargins: true, landscape: true, paperWidth: 11.70, paperHeight: 8.26772, includeBackground: true }; console.log("RenderPDF options\n", options); const RenderPDF = require('chrome-headless-render-pdf/index'); const renderer = new RenderPDF(options); renderer.spawnChrome(); renderer.waitForDebugPort(); const express = require('express'); const app = express(); const timeout = require('connect-timeout'); const timeout_msec = 5000; app.use(timeout(timeout_msec)); app.listen(8000, function(){ console.log('Listening on 8000'); }); app.get('/', async (req, res) => { console.log("async start"); var url = req.query.url; if (! url) { res.status(404); res.end('parameter "url" is not set'); return; } try { const buff = await renderer.renderPdf(url, renderer.generatePdfOptions()); res.contentType("application/pdf"); res.send(buff); res.status(200); } catch (e) { renderer.error('error:', e); console.log(e); res.status(503); } res.end(); }); // Health Check app.get('/hc', function (req, res) { console.log('health check ok'); res.status(200); res.end('ok'); }); process.on('SIGINT', function() { console.log('process exit with SIGINT'); renderer.killChrome(); process.exit(); });
ざっと下記の部分を直した
- 2重にexecSync(chrome-headless-render-pdfコマンドと、その内部)していたのを、RenderPDFを直接使うようにして省いた。
- 一度ファイルに書き出して、それを読み込んでいたのを直接バッファを返すようにした
- chromeをアクセスのたび起動したり、停止したりしていたのを、起動しっぱなしにした。
- 最初にChromeのバージョンとかoptionを出してみたり。
まだ本番利用はしていないが、レスポンスはかなり早くなり(A4一枚の書類で200ms程度)、たまに起きる謎の待ち時間、フリーズも見られなくなった。
動きは普通のChromeなので、外部のjs、cssなどのライブラリはなるべくインライン化し、サイズの大きなWEBフォント読み込みはやめてサーバーにインストールしておくと早くなる。キャッシュはしないっぽい?
変数宣言はできるだけconstにしておいた。
まだまだ己のnode.js力の足りなさを実感する。
async、awaitは初めて使ったけどこれなら理解できそう。
JS+Node.jsによるWebクローラー/ネットエージェント開発テクニック
- 作者: クジラ飛行机
- 出版社/メーカー: ソシム
- 発売日: 2015/08/31
- メディア: 単行本
- この商品を含むブログ (2件) を見る