Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

20171013 #1

Merged
merged 89 commits into from
Oct 13, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
3c5c0b8
docs: Fix objects.md typo (#969)
zousandian May 27, 2017
5b6fe2b
feat: use lru to aovid oom in dns cache httpclient (#961)
dead-horse May 27, 2017
35fa5a9
fix: set maxSockets defautl value to Number.MAX_SAFE_INTEGER (#938)
coolme200 May 27, 2017
5b959e0
docs: translate tutorials/progressive.md to English version (#966)
DarrenWong May 27, 2017
5d8ca65
docs: translatebasics/controller.md (#889)
lslxdx May 27, 2017
7370a62
docs: translate tutorials/restful.md (#908)
DarrenWong May 27, 2017
8fc63fd
release 1.4.0 (#971)
dead-horse May 28, 2017
2f232f3
docs: file must appear after other fiels when using getFileStream (#982)
dead-horse Jun 2, 2017
3de963f
docs(basics/structure.md): [translate] (#970)
Azard Jun 2, 2017
20a5d91
test: disable coverage for schedule (#987)
popomore Jun 2, 2017
9084c24
docs: add plugin list (#988)
popomore Jun 2, 2017
8a120fd
docs: remove max time limit at schdule (#995)
atian25 Jun 5, 2017
52865b4
docs: devtool inspect at 8.x (#1018)
atian25 Jun 8, 2017
c6eb7b2
doc: fix view config doc (#991)
xcodebuild Jun 8, 2017
057bc47
test: add doc test (#989)
popomore Jun 9, 2017
1b108a7
docs: remove api that is from egg-rest (#1022)
popomore Jun 9, 2017
13b7c19
test: node 8 (#976)
fengmk2 Jun 10, 2017
3d04199
docs: typo (#1029)
perzy Jun 10, 2017
9b50725
docs: (tutorials/index.md & async-function.md ): [translate] Done (#1…
DarrenWong Jun 11, 2017
1d72e37
docs: fix caseStyle link (#1033)
Jun 12, 2017
9d705e4
test: make sure app close (#1030)
fengmk2 Jun 12, 2017
4890eda
docs: Uniform the standards that we should acquire this parsed parame…
Ryqsky Jun 12, 2017
f1b510c
feat: add config.logger.disableConsoleAfterReady (#1001)
fengmk2 Jun 12, 2017
ef7c864
docs: add ant.design link (#1037)
popomore Jun 12, 2017
78a13d5
docs: add more description at quickstart (#1042)
atian25 Jun 13, 2017
4e510b2
chore: use app.httpRequest() instead of supertest (#1041)
fengmk2 Jun 13, 2017
64d1b00
docs: add chrome devtools debug information (#1050)
okoala Jun 14, 2017
bfb8df5
docs: typo (#1060)
chenbin92 Jun 15, 2017
a4ba2a2
feat: enable overrideMethod middleware by default (#1069)
fengmk2 Jun 19, 2017
2b1644e
feat: add tsd (#1027)
shepherdwind Jun 20, 2017
1d02601
tsd: add another properties of FileStream (#1080)
Rwing Jun 21, 2017
b31e096
Release 1.5.0 (#1079)
fengmk2 Jun 21, 2017
82d2158
docs: add Enclose.IO to Links (#1089)
pmq20 Jun 22, 2017
c31bc15
test: wait logger to flush (#1090)
popomore Jun 22, 2017
9099be9
docs: unify config in quickstart (#1094)
dead-horse Jun 23, 2017
5ae7814
chore: comments in english (#1101)
dead-horse Jun 24, 2017
7c2e436
docs: improve feature describe (#1102)
dead-horse Jun 24, 2017
daa8227
feat(tsd): add ctx.logger and logger.error support Error object (#1108)
shepherdwind Jun 26, 2017
4322212
docs: add missing class in objects.md
kaiye Jun 27, 2017
0052351
docs: improve plugin dependencies (#1061)
luicfer Jun 28, 2017
c7a87a8
docs: adjust objects docs (#1140)
atian25 Jul 3, 2017
7c70beb
docs: change istanbul to nyc (#1150)
atian25 Jul 4, 2017
b80bb14
fix: don't cache the intermediate locals for application (#1146)
JacksonTian Jul 4, 2017
74c8a54
feat: dump `run/${type}_config_meta.json` (#1155)
popomore Jul 5, 2017
5dc56fa
feat: ignore any key contains "secret" (#1156)
fengmk2 Jul 5, 2017
dea0a29
Release 1.6.0 (#1152)
fengmk2 Jul 5, 2017
367e1d6
docs: fix typo (#1191)
Jul 14, 2017
05f4785
chore: add travis_wait to avoid deploying document timeout (#1201)
popomore Jul 17, 2017
410633b
chore: kill ssh-agent after deploy (#1204)
popomore Jul 17, 2017
894005c
docs: (core/i18n): [translate] Done (#1194)
DarrenWong Jul 18, 2017
988b8c8
fix: make sure config.httpclient.httpAgent.timeout >= 30000 (#1165)
fengmk2 Jul 19, 2017
594eaa6
Release 1.6.1 (#1210)
fengmk2 Jul 19, 2017
2b78b4c
docs: Fix config name from egg-Plugin to eggPlugin in plugin's doc (#…
hansenyang Jul 20, 2017
24f2790
docs: new VScode 1.14 default protocol changed. (#1212)
fantasyroot Jul 21, 2017
c3c9fce
docs(controller): examples use controller class (#1221)
dead-horse Jul 25, 2017
96b3786
docs(core/error-handling): translate error-handling.md in English (#1…
gztchan Jul 25, 2017
e9f93cf
refactor: export app.HttpClient that can be overwritten (#1234)
popomore Jul 26, 2017
3ef1de9
feat: set cluster options, include path, port, hostname (#1231)
popomore Jul 27, 2017
dda386e
test: add test and doc for listen options (#1246)
popomore Jul 27, 2017
45bea3c
docs(core-deployment): translate deployment.md in English (#1235)
gztchan Jul 27, 2017
4f2ebfd
docs: fix const define (#1249)
atian25 Jul 28, 2017
7733430
docs: only deploy docs at 8 (#1252)
atian25 Jul 28, 2017
dd07cac
docs: fix typo on CONTRIBUTING.zh-CN.md (#1266)
superewe Jul 31, 2017
c6e1fdc
Release 1.7.0 (#1247)
popomore Aug 1, 2017
ebbbcd5
chore: skip docs deploy at ci cron (#1268)
atian25 Aug 2, 2017
734854c
docs(unittest): add bootstrap usage (#1278)
dead-horse Aug 2, 2017
08ed1b3
docs(unittest): typo of egg-mock (#1284)
atian25 Aug 3, 2017
4feae70
docs: add egg-scripts to deployment (#1279)
atian25 Aug 4, 2017
aaacd56
docs: remove egg-scripts env default description (#1318)
atian25 Aug 16, 2017
4daf497
docs(en/core/docs-logger): finish logger.md translation in English (#…
gztchan Aug 17, 2017
4994543
docs: curl(url, opts) add parameter introduction (#1351) (#1352)
Aug 24, 2017
eef30fa
docs: adjust webstorm debug config (#1367)
atian25 Aug 30, 2017
a9936a3
fix: typo (#1388)
waitingsong Sep 6, 2017
3aaee8f
fix: should extends from egg-core BaseContextClass (#1392)
fengmk2 Sep 7, 2017
f7c0d85
feat: support app.httpclient and agent.httpclient auto set tracer (#1…
leoner Sep 10, 2017
105e194
docs: translate basics/objects (#1238)
Azard Sep 11, 2017
6cf17c1
docs: (core/httpclient): [translate] Done (#1409)
DarrenWong Sep 12, 2017
54be7dc
docs(core/cluster-and-ipc): fix some typo (#1415)
vincenthou Sep 12, 2017
53968f0
Release 1.8.0 (#1405)
leoner Sep 12, 2017
95fbd47
docs(deployment): port should be number (#1424)
atian25 Sep 14, 2017
5a9531a
feat: don’t force logger to use INFO level in prod (#1218)
AnzerWall Sep 14, 2017
e3f29de
docs(development): adjust debug docs with new egg-bin debug (#1427)
atian25 Sep 14, 2017
c56274b
docs(development): fix devtools debug (#1428)
atian25 Sep 14, 2017
6eac07e
refactor: httpclient args tracer to be enforced (#1421)
leoner Sep 15, 2017
d0797b1
docs: correct sample codes (#1434)
Jawnkuin Sep 19, 2017
21425e7
feat: make cluster client configurable in egg (#1459)
gxcsoccer Sep 25, 2017
0a7ba95
Release 1.9.0 (#1460)
gxcsoccer Sep 25, 2017
6846bad
docs: add plugin.js description (#1499)
atian25 Oct 11, 2017
7b37d23
docs: multipart example use yield parts() (#1518)
dead-horse Oct 12, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: use lru to aovid oom in dns cache httpclient (eggjs#961)
  • Loading branch information
dead-horse authored and fengmk2 committed May 27, 2017
commit 5b6fe2b187b2c1a4bcee4693b2b1043f2724fe68
2 changes: 2 additions & 0 deletions config/config.default.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,8 @@ module.exports = appInfo => {
maxSockets: Infinity,
maxFreeSockets: 256,
enableDNSCache: false,
dnsCacheMaxLength: 1000,
dnsCacheMaxAge: 10000,
};

/**
Expand Down
9 changes: 7 additions & 2 deletions docs/source/zh-cn/core/httpclient.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,9 +267,14 @@ exports.httpclient = {
maxSockets: Infinity,
// 最大空闲 socket 数
maxFreeSockets: 256,
// 是否开启本地 DNS 缓存,默认关闭
// 一旦设置开启,则每个域名的 DNS 查询结果将在进程内缓存 10 秒
// 是否开启本地 DNS 缓存,默认关闭,开启后有两个特性
// 1. 所有的 DNS 查询都会默认优先使用缓存的,即使 DNS 查询错误也不影响应用
// 2. 对同一个域名,在 dnsCacheLookupInterval 的间隔内(默认 10s)只会查询一次
enableDNSCache: false,
// 对同一个域名进行 DNS 查询的最小间隔时间
dnsCacheLookupInterval: 10000,
// DNS 同时缓存的最大域名数量,默认 1000
dnsCacheMaxLength: 1000,
};
```

Expand Down
36 changes: 24 additions & 12 deletions lib/core/dnscache_httpclient.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
'use strict';

const dns = require('dns');
const LRU = require('ylru');
const urlparse = require('url').parse;
const urllib = require('urllib');
const utility = require('utility');

const IP_REGEX = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;

class DNSCacheHttpClient extends urllib.HttpClient {
constructor(options) {
super(options);

this.app = options.app;
this.dnsCacheMaxAge = 10000;
this.dnsCache = new Map();
this.dnsCacheLookupInterval = this.app.config.httpclient.dnsCacheLookupInterval;
this.dnsCache = new LRU(this.app.config.httpclient.dnsCacheMaxLength);
}

request(url, args, callback) {
Expand Down Expand Up @@ -62,24 +66,31 @@ class DNSCacheHttpClient extends urllib.HttpClient {
_dnsLookup(url, args, callback) {
const parsed = typeof url === 'string' ? urlparse(url) : url;
// hostname must exists
const host = parsed.hostname;
const hostname = parsed.hostname;

// don't lookup when hostname is IP
if (hostname && IP_REGEX.test(hostname)) {
return callback(null, { url, args });
}

args = args || {};
args.headers = args.headers || {};
// set host header is not exists
if (!args.headers.host && !args.headers.Host) {
args.headers.host = host;
// host must combine with hostname:port, node won't use `parsed.host`
args.headers.host = parsed.port ? `${hostname}:${parsed.port}` : hostname;
}

const record = this.dnsCache.get(host);
const record = this.dnsCache.get(hostname);
const now = Date.now();
if (record) {
const needUpdate = now - record.timestamp >= this.dnsCacheMaxAge;
const needUpdate = now - record.timestamp >= this.dnsCacheLookupInterval;
if (needUpdate) {
// make sure next request don't refresh dns query
record.timestamp = now;
}
callback(null, {
url: this._formatDnsLookupUrl(host, url, record.ip),
url: this._formatDnsLookupUrl(hostname, url, record.ip),
args,
});
if (!needUpdate) {
Expand All @@ -90,22 +101,22 @@ class DNSCacheHttpClient extends urllib.HttpClient {
callback = null;
}

dns.lookup(host, { family: 4 }, (err, address) => {
dns.lookup(hostname, { family: 4 }, (err, address) => {
const logger = args.ctx ? args.ctx.coreLogger : this.app.coreLogger;
if (err) {
logger.warn('[dnscache_httpclient] dns lookup error: %s(%s) => %s', host, url, err);
logger.warn('[dnscache_httpclient] dns lookup error: %s(%s) => %s', hostname, url, err);
// no cache, return error
return callback && callback(err);
}

logger.info('[dnscache_httpclient] dns lookup success: %s(%s) => %s', host, url, address);
this.dnsCache.set(host, {
logger.info('[dnscache_httpclient] dns lookup success: %s(%s) => %s', hostname, url, address);
this.dnsCache.set(hostname, {
timestamp: Date.now(),
ip: address,
});

callback && callback(null, {
url: this._formatDnsLookupUrl(host, url, address),
url: this._formatDnsLookupUrl(hostname, url, address),
args,
});
});
Expand All @@ -115,6 +126,7 @@ class DNSCacheHttpClient extends urllib.HttpClient {
if (typeof url === 'string') {
url = url.replace(host, address);
} else {
url = utility.assign({}, url);
url.hostname = url.hostname.replace(host, address);
if (url.host) {
url.host = url.host.replace(host, address);
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
"mime-types": "^2.1.15",
"sendmessage": "^1.1.0",
"urllib": "^2.22.0",
"utility": "^1.12.0"
"utility": "^1.12.0",
"ylru": "^1.0.0"
},
"devDependencies": {
"autod": "^2.8.0",
Expand Down
57 changes: 46 additions & 11 deletions test/lib/core/dnscache_httpclient.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ const utils = require('../../utils');
describe('test/lib/core/dnscache_httpclient.test.js', () => {
let app;
let url;
let host;

before(function* () {
app = utils.app('apps/dnscache_httpclient');
yield app.ready();
url = yield utils.startLocalServer();
url = url.replace('127.0.0.1', 'localhost');
host = urlparse(url).host;
});

afterEach(mm.restore);
Expand All @@ -24,7 +26,7 @@ describe('test/lib/core/dnscache_httpclient.test.js', () => {
yield request(app.callback())
.get('/?url=' + encodeURIComponent(url + '/get_headers'))
.expect(200)
.expect(/"host":"localhost"/);
.expect(/"host":"localhost:\d+"/);
yield request(app.callback())
.get('/?url=' + encodeURIComponent(url + '/get_headers') + '&host=localhost.foo.com')
.expect(200)
Expand All @@ -46,31 +48,31 @@ describe('test/lib/core/dnscache_httpclient.test.js', () => {
yield request(app.callback())
.get('/?url=' + encodeURIComponent(url + '/get_headers'))
.expect(200)
.expect(/"host":"localhost"/);
.expect(/"host":"localhost:\d+"/);
// mock local cache expires and mock dns lookup throw error
app.httpclient.dnsCache.get('localhost').timestamp = 0;
mm.error(dns, 'lookup', 'mock dns lookup error');
yield request(app.callback())
.get('/?url=' + encodeURIComponent(url + '/get_headers'))
.expect(200)
.expect(/"host":"localhost"/);
.expect(/"host":"localhost:\d+"/);
});

it('should app.curl work', function* () {
const result = yield app.curl(url + '/get_headers', { dataType: 'json' });
assert(result.status === 200);
assert(result.data.host === 'localhost');
assert(result.data.host === host);

const result2 = yield app.httpclient.curl(url + '/get_headers', { dataType: 'json' });
assert(result2.status === 200);
assert(result2.data.host === 'localhost');
assert(result2.data.host === host);
});

it('should callback style work', done => {
app.httpclient.curl(url + '/get_headers', (err, data, res) => {
data = JSON.parse(data);
assert(res.status === 200);
assert(data.host === 'localhost');
assert(data.host === host);
done();
});
});
Expand All @@ -88,7 +90,7 @@ describe('test/lib/core/dnscache_httpclient.test.js', () => {
assert(!err);
const data = JSON.parse(result.data);
assert(result.res.status === 200);
assert(data.host === 'localhost');
assert(data.host === host);
done();
});
});
Expand All @@ -104,27 +106,60 @@ describe('test/lib/core/dnscache_httpclient.test.js', () => {
it('should app.curl work on lookup error', function* () {
const result = yield app.curl(url + '/get_headers', { dataType: 'json' });
assert(result.status === 200);
assert(result.data.host === 'localhost');
assert(result.data.host === host);

// mock local cache expires and mock dns lookup throw error
app.httpclient.dnsCache.get('localhost').timestamp = 0;
mm.error(dns, 'lookup', 'mock dns lookup error');
const result2 = yield app.httpclient.curl(url + '/get_headers', { dataType: 'json' });
assert(result2.status === 200);
assert(result2.data.host === 'localhost');
assert(result2.data.host === host);
});

it('should app.curl(obj)', function* () {
const obj = urlparse(url + '/get_headers');
const result = yield app.curl(obj, { dataType: 'json' });
assert(result.status === 200);
assert(result.data.host === 'localhost');
assert(result.data.host === host);

const obj2 = urlparse(url + '/get_headers');
// mock obj2.host
obj2.host = null;
const result2 = yield app.curl(obj2, { dataType: 'json' });
assert(result2.status === 200);
assert(result2.data.host === 'localhost');
assert(result2.data.host === host);
});

it('should dnsCacheMaxLength work', function* () {
mm.data(dns, 'lookup', '127.0.0.1');

// reset lru cache
mm(app.httpclient.dnsCache, 'max', 1);
mm(app.httpclient.dnsCache, 'size', 0);
mm(app.httpclient.dnsCache, 'cache', new Map());
mm(app.httpclient.dnsCache, '_cache', new Map());

let obj = urlparse(url + '/get_headers');
let result = yield app.curl(obj, { dataType: 'json' });
assert(result.status === 200);
assert(result.data.host === host);

assert(app.httpclient.dnsCache.get('localhost'));

obj = urlparse(url.replace('localhost', 'another.com') + '/get_headers');
result = yield app.curl(obj, { dataType: 'json' });
assert(result.status === 200);
assert(result.data.host === obj.host);

assert(!app.httpclient.dnsCache.get('localhost'));
assert(app.httpclient.dnsCache.get('another.com'));
});

it('should not cache ip', function* () {
const obj = urlparse(url.replace('localhost', '127.0.0.1') + '/get_headers');
const result = yield app.curl(obj, { dataType: 'json' });
assert(result.status === 200);
assert(result.data.host === obj.host);
assert(!app.httpclient.dnsCache.get('127.0.0.1'));
});
});