Skip to content

Commit cbcad51

Browse files
committed
keep browser running
1 parent 2da4278 commit cbcad51

File tree

5 files changed

+88
-54
lines changed

5 files changed

+88
-54
lines changed

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@ By executing `SLOWMO_MS=250 npm run local`, you can check the operation while ac
1212

1313
## Packaging & Deploy
1414

15-
Lambda's memory is set to 384 MB or more.
15+
Lambda's memory needs to be set to at least 384 MB, but the more memory, the better the performance of any operations.
16+
17+
```
18+
512MB -> goto(youtube): 6.481s
19+
1536MB(Max) -> goto(youtube): 2.154s
20+
```
1621

1722
### chrome in package (recommended)
1823

@@ -30,6 +35,8 @@ Run `npm run package-nochrome`, deploy the package.zip, and set following env va
3035

3136
## Build Headless-Chrome (optional)
3237

38+
This kit includes Chrome built by myself because official build Chrome installed by Puppeteer has problems about running on Lambda (missing shared library etc.).
39+
3340
If you want to use latest chrome, run chrome/buildChrome.sh on EC2 having at least 16GB memory and 30GB volume.
3441
See also [serverless-chrome](https://github.com/adieuadieu/serverless-chrome/blob/master/chrome/README.md).
3542
Once you build it, link to `headless_shell.tar.gz` in `chrome` dir.

src/index.js

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,19 @@
1-
const puppeteer = require('puppeteer');
2-
const config = require('./starter-kit/config');
3-
const util = require('./starter-kit/util');
1+
const setup = require('./starter-kit/setup');
42

53
exports.handler = async (event, context, callback) => {
6-
exports.run({
7-
headless: true,
8-
executablePath: await util.setupChrome(),
9-
args: config.launchOptionForLambda,
10-
dumpio: !!util.DEBUG,
11-
}).then(
4+
// For keeping the browser launch
5+
context.callbackWaitsForEmptyEventLoop = false;
6+
const browser = await setup.getBrowser();
7+
exports.run(browser).then(
128
(result) => callback(null, result)
139
).catch(
1410
(err) => callback(err)
1511
);
1612
};
1713

18-
exports.run = async (launchOption) => {
19-
const browser = await puppeteer.launch(launchOption);
20-
21-
util.debugLog(`Chrome launched, version ${await browser.version()}`);
22-
14+
exports.run = async (browser) => {
2315
const page = await browser.newPage();
2416
await page.goto('https://www.google.co.jp');
25-
browser.close();
26-
17+
await page.close();
2718
return 'done';
2819
};

src/starter-kit/config.js

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,32 @@
1-
exports.launchOptionForLambda = [
1+
const path = require('path');
2+
3+
const launchOptionForLambda = [
24
// error when launch(); No usable sandbox! Update your kernel
35
'--no-sandbox',
46
// error when launch(); Failed to load libosmesa.so
57
'--disable-gpu',
68
// freeze when newPage()
79
'--single-process',
810
];
11+
12+
const localChromePath = path.join('headless_shell.tar.gz');
13+
const remoteChromeS3Bucket = process.env.CHROME_BUCKET;
14+
const remoteChromeS3Key = process.env.CHROME_KEY || 'headless_shell.tar.gz';
15+
16+
const setupChromePath = path.join(path.sep, 'tmp');
17+
const executablePath = path.join(
18+
setupChromePath,
19+
'headless_shell'
20+
);
21+
22+
const DEBUG = process.env.DEBUG;
23+
24+
module.exports = {
25+
launchOptionForLambda,
26+
localChromePath,
27+
remoteChromeS3Bucket,
28+
remoteChromeS3Key,
29+
setupChromePath,
30+
executablePath,
31+
DEBUG,
32+
};

src/starter-kit/local.js

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1+
process.env.LOCAL = 1;
12
const index = require('../index');
2-
const util = require('./util');
3+
const config = require('./config');
4+
const puppeteer = require('puppeteer');
35

4-
index.run({
5-
headless: false,
6-
slowMo: process.env.SLOWMO_MS,
7-
dumpio: !!util.DEBUG,
8-
// use chrome installed by puppeteer
9-
}).then(
10-
(result) => console.log(result)
11-
).catch(
12-
(err) => console.error(err)
13-
);
6+
(async () => {
7+
const browser = await puppeteer.launch({
8+
headless: false,
9+
slowMo: process.env.SLOWMO_MS,
10+
dumpio: !!config.DEBUG,
11+
// use chrome installed by puppeteer
12+
});
13+
await index.run(browser)
14+
.then((result) => console.log(result))
15+
.catch((err) => console.error(err));
16+
await browser.close();
17+
})();
Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,62 @@
1-
const CHROME_BUCKET = process.env.CHROME_BUCKET;
2-
const CHROME_KEY = process.env.CHROME_KEY || 'headless_shell.tar.gz';
31
const aws = require('aws-sdk');
42
const s3 = new aws.S3({apiVersion: '2006-03-01'});
53
const fs = require('fs');
64
const tar = require('tar');
7-
const path = require('path');
5+
const puppeteer = require('puppeteer');
6+
const config = require('./config');
87

9-
const localChromePath = path.join('headless_shell.tar.gz');
10-
const setupChromePath = path.join(path.sep, 'tmp');
11-
12-
exports.DEBUG = process.env.DEBUG;
8+
exports.getBrowser = (() => {
9+
let browser;
10+
return async () => {
11+
if (typeof browser === 'undefined') {
12+
await setupChrome();
13+
browser = await puppeteer.launch({
14+
headless: true,
15+
executablePath: config.executablePath,
16+
args: config.launchOptionForLambda,
17+
dumpio: !!exports.DEBUG,
18+
});
19+
debugLog(`launch done: ${await browser.version()}`);
20+
}
21+
return browser;
22+
};
23+
})();
1324

14-
exports.setupChrome = async () => {
25+
const setupChrome = async () => {
1526
if (!await existsExecutableChrome()) {
1627
if (await existsLocalChrome()) {
17-
exports.debugLog('setup local chrome');
28+
debugLog('setup local chrome');
1829
await setupLocalChrome();
1930
} else {
20-
exports.debugLog('setup s3 chrome');
31+
debugLog('setup s3 chrome');
2132
await setupS3Chrome();
2233
}
23-
exports.debugLog('setup done');
34+
debugLog('setup done');
2435
}
25-
return executablePath;
2636
};
2737

28-
const executablePath = path.join(
29-
setupChromePath,
30-
'headless_shell'
31-
);
32-
3338
const existsLocalChrome = () => {
3439
return new Promise((resolve, reject) => {
35-
fs.exists(localChromePath, (exists) => {
40+
fs.exists(config.localChromePath, (exists) => {
3641
resolve(exists);
3742
});
3843
});
3944
};
4045

4146
const existsExecutableChrome = () => {
4247
return new Promise((resolve, reject) => {
43-
fs.exists(executablePath, (exists) => {
48+
fs.exists(config.executablePath, (exists) => {
4449
resolve(exists);
4550
});
4651
});
4752
};
4853

4954
const setupLocalChrome = () => {
5055
return new Promise((resolve, reject) => {
51-
fs.createReadStream(localChromePath)
56+
fs.createReadStream(config.localChromePath)
5257
.on('error', (err) => reject(err))
5358
.pipe(tar.x({
54-
C: setupChromePath,
59+
C: config.setupChromePath,
5560
}))
5661
.on('error', (err) => reject(err))
5762
.on('end', () => resolve());
@@ -60,18 +65,21 @@ const setupLocalChrome = () => {
6065

6166
const setupS3Chrome = () => {
6267
return new Promise((resolve, reject) => {
63-
const params = {Bucket: CHROME_BUCKET, Key: CHROME_KEY};
68+
const params = {
69+
Bucket: config.remoteChromeS3Bucket,
70+
Key: config.remoteChromeS3Key,
71+
};
6472
s3.getObject(params)
6573
.createReadStream()
6674
.on('error', (err) => reject(err))
6775
.pipe(tar.x({
68-
C: setupChromePath,
76+
C: config.setupChromePath,
6977
}))
7078
.on('error', (err) => reject(err))
7179
.on('end', () => resolve());
7280
});
7381
};
7482

75-
exports.debugLog = (log) => {
76-
if (exports.DEBUG) console.log(log);
83+
const debugLog = (log) => {
84+
if (config.DEBUG) console.log(log);
7785
};

0 commit comments

Comments
 (0)