Merge branch 'feature/sale' of http://git.dev.yoho.cn/web/yohobuywap-node into feature/sale
Showing
6 changed files
with
243 additions
and
41 deletions
@@ -145,7 +145,7 @@ const getChannelResource = (params) => { | @@ -145,7 +145,7 @@ const getChannelResource = (params) => { | ||
145 | params.new_device = true; // eslint-disable-line | 145 | params.new_device = true; // eslint-disable-line |
146 | } | 146 | } |
147 | 147 | ||
148 | - return api.get('operations/api/v5/resource/home', sign.apiSign(params)).then(result => { | 148 | + return api.get('operations/api/v5/resource/home', sign.apiSign(params), 300).then(result => { |
149 | if (result && result.code === 200) { | 149 | if (result && result.code === 200) { |
150 | return processFloor(result.data.list); | 150 | return processFloor(result.data.list); |
151 | } else { | 151 | } else { |
@@ -162,7 +162,7 @@ const getChannelResource = (params) => { | @@ -162,7 +162,7 @@ const getChannelResource = (params) => { | ||
162 | const getLeftNav = (choosed) => { | 162 | const getLeftNav = (choosed) => { |
163 | choosed = choosed || 'all'; | 163 | choosed = choosed || 'all'; |
164 | 164 | ||
165 | - return api.get('operations/api/v6/category/getCategory', sign.apiSign({})).then(result => { | 165 | + return api.get('operations/api/v6/category/getCategory', sign.apiSign({}), 300).then(result => { |
166 | if (result && result.code === 200) { | 166 | if (result && result.code === 200) { |
167 | return processSideBar(result.data, choosed); | 167 | return processSideBar(result.data, choosed); |
168 | } else { | 168 | } else { |
@@ -179,7 +179,7 @@ const getLeftNav = (choosed) => { | @@ -179,7 +179,7 @@ const getLeftNav = (choosed) => { | ||
179 | exports.getChannelDate = (params) => { | 179 | exports.getChannelDate = (params) => { |
180 | var channelData = {}; | 180 | var channelData = {}; |
181 | 181 | ||
182 | - return Promise.all([getChannelResource(params), getLeftNav(params.gender)]).then((data) => { | 182 | + return api.all([getChannelResource(params), getLeftNav(params.gender)]).then((data) => { |
183 | channelData.content = data[0]; // 资源位数据 | 183 | channelData.content = data[0]; // 资源位数据 |
184 | channelData.sideNav = data[1]; // 侧边栏数据 | 184 | channelData.sideNav = data[1]; // 侧边栏数据 |
185 | 185 |
@@ -15,6 +15,13 @@ module.exports = { | @@ -15,6 +15,13 @@ module.exports = { | ||
15 | api: 'http://192.168.102.207:8080/gateway/', // http://devapi.yoho.cn:58078/ http://testapi.yoho.cn:28078/ | 15 | api: 'http://192.168.102.207:8080/gateway/', // http://devapi.yoho.cn:58078/ http://testapi.yoho.cn:28078/ |
16 | service: 'http://devapi.yoho.cn:58078/' | 16 | service: 'http://devapi.yoho.cn:58078/' |
17 | }, | 17 | }, |
18 | + useCache: true, | ||
19 | + memcache: { | ||
20 | + master: ['192.168.102.168:12580'], | ||
21 | + slave: ['192.168.102.168:12580'], | ||
22 | + session: ['192.168.102.168:12580'], | ||
23 | + timeout: 5000 | ||
24 | + }, | ||
18 | loggers: { | 25 | loggers: { |
19 | infoFile: { | 26 | infoFile: { |
20 | name: 'info', | 27 | name: 'info', |
@@ -7,12 +7,16 @@ | @@ -7,12 +7,16 @@ | ||
7 | 'use strict'; | 7 | 'use strict'; |
8 | 8 | ||
9 | const rp = require('request-promise'); | 9 | const rp = require('request-promise'); |
10 | +const qs = require('querystring'); | ||
11 | +const md5 = require('md5'); | ||
10 | const _ = require('lodash'); | 12 | const _ = require('lodash'); |
11 | const log = require('./logger'); | 13 | const log = require('./logger'); |
12 | -const api = require('../config/common').domains.api; | ||
13 | -const serviceApi = require('../config/common').domains.service; | ||
14 | -const searchApi = require('../config/common').domains.search; | 14 | +const cache = require('./cache'); |
15 | const Timer = require('./timer'); | 15 | const Timer = require('./timer'); |
16 | +const config = require('../config/common'); | ||
17 | +const api = config.domains.api; | ||
18 | +const serviceApi = config.domains.service; | ||
19 | +const searchApi = config.domains.search; | ||
16 | 20 | ||
17 | 21 | ||
18 | let ApiUrl; | 22 | let ApiUrl; |
@@ -24,50 +28,107 @@ class API { | @@ -24,50 +28,107 @@ class API { | ||
24 | } | 28 | } |
25 | 29 | ||
26 | /** | 30 | /** |
27 | - * get | ||
28 | - * @param url String | ||
29 | - * @param data Obejct | 31 | + * 获取请求 ID |
30 | */ | 32 | */ |
31 | - get(url, data) { | ||
32 | - | ||
33 | - let options = { | ||
34 | - url: `${ApiUrl}${url}`, | ||
35 | - qs: data, | ||
36 | - json: true | ||
37 | - }; | 33 | + _getReqId(options) { |
34 | + return md5(`${options.url}?${qs.stringify(options.qs || options.form)}`); | ||
35 | + } | ||
38 | 36 | ||
37 | + /** | ||
38 | + * 调用接口 | ||
39 | + */ | ||
40 | + _requestFromAPI(options, cacheOption, reqId) { | ||
39 | let timer = new Timer(); | 41 | let timer = new Timer(); |
40 | 42 | ||
41 | timer.put('getApi');// 统计时间开始 | 43 | timer.put('getApi');// 统计时间开始 |
42 | 44 | ||
43 | - let ret = rp(options); | ||
44 | - | ||
45 | - return ret.catch((error)=>{ | ||
46 | - let duration = timer.put('getApi');// 接口返回 | ||
47 | - | ||
48 | - log.error('API GET: %s, parms: %j , durationMs: %d ms error: %s , statusCode: %d', | ||
49 | - options.url, options.qs, duration, error.message, error.statusCode); | 45 | + log.info(`get api: ${options.url}?${qs.stringify(options.qs)}`); |
46 | + return rp(options).then((result) => { | ||
47 | + let duration = timer.put('getApi');// 统计时间结束 | ||
48 | + | ||
49 | + log.info(`get api success: use: ${duration}ms`); | ||
50 | + if (config.useCache && cacheOption) { | ||
51 | + reqId = reqId || this._getReqId(options); | ||
52 | + | ||
53 | + // 数据校验无误,写缓存, 否则返回 Slave 缓存服务器的数据 | ||
54 | + if (result && result.code) { | ||
55 | + let cacheTime = _.isNumber(cacheOption) ? cacheOption : 60; | ||
56 | + | ||
57 | + cache.set(`apiCache:${reqId}`, result, cacheTime); | ||
58 | + cache.setSlave(`apiCache:${reqId}`, result, cacheTime); | ||
59 | + } else { | ||
60 | + return this._requestFromCache(options, true); | ||
61 | + } | ||
62 | + } | ||
63 | + return result; | ||
64 | + }).catch((error)=>{ | ||
65 | + let duration = timer.put('getApi');// 统计时间结束 | ||
66 | + | ||
67 | + log.error(`get api fail: use: ${duration}ms, statusCode: ${error.statusCode}, error: ${error.message}`); | ||
68 | + | ||
69 | + // 使用缓存的时候,读取二级缓存 | ||
70 | + if (config.useCache) { | ||
71 | + return this._requestFromCache(options, true); | ||
72 | + } | ||
73 | + return Promise.reject({ | ||
74 | + error: '接口调用失败' | ||
75 | + }); | ||
50 | }); | 76 | }); |
51 | } | 77 | } |
52 | 78 | ||
53 | /** | 79 | /** |
54 | - * multi get | ||
55 | - * @params: urls => Array[Object[url[string], data[object]]] | 80 | + * 读取缓存 |
81 | + * @param {[object]} options | ||
82 | + * @param {[boolean]} slave true: 读取二级缓存 | ||
83 | + * @return {[type]} | ||
56 | */ | 84 | */ |
57 | - multiGet(urls) { | ||
58 | - var rps = []; | ||
59 | - | ||
60 | - _.forEach(urls, function(el) { | ||
61 | - rps.push(rp({ | ||
62 | - url: `${ApiUrl}${el.url}`, | ||
63 | - qs: el.data, | ||
64 | - json: true | ||
65 | - })); | 85 | + _requestFromCache(options, slave) { |
86 | + let reqId = this._getReqId(options); | ||
87 | + let getCache = slave ? cache.getFromSlave : cache.get; | ||
88 | + | ||
89 | + log.info(`get cache: ${reqId}, url: ${options.url}?${qs.stringify(options.qs)}`); | ||
90 | + return getCache(`apiCache:${reqId}`).then((result) => { | ||
91 | + if (!_.isNil(result)) { | ||
92 | + try { | ||
93 | + result = JSON.parse(result); | ||
94 | + } finally { | ||
95 | + log.info(slave ? 'get slave cache success' : 'get master cache success'); | ||
96 | + return result; | ||
97 | + } | ||
98 | + } | ||
99 | + | ||
100 | + if (!slave) { | ||
101 | + return this._requestFromAPI(options, true, reqId); | ||
102 | + } | ||
103 | + }).catch(() => { | ||
104 | + log.error(slave ? 'get slave cache fail' : 'get master cache fail'); | ||
105 | + if (!slave) { | ||
106 | + return this._requestFromAPI(options, true, reqId); | ||
107 | + } | ||
66 | }); | 108 | }); |
109 | + } | ||
67 | 110 | ||
68 | - return Promise.all(rps).then((d) => { | ||
69 | - return d; | ||
70 | - }); | 111 | + /** |
112 | + * 使用 get 请求获取接口 | ||
113 | + * @param {[string]} url | ||
114 | + * @param {[object]} data | ||
115 | + * @param {[bool or number]} cacheOption 使用数字时,数字表示缓存时间 | ||
116 | + * @return {[type]} | ||
117 | + */ | ||
118 | + get(url, data, cacheOption) { | ||
119 | + let options = { | ||
120 | + url: `${ApiUrl}${url}`, | ||
121 | + qs: data, | ||
122 | + json: true, | ||
123 | + timeout: 3000 | ||
124 | + }; | ||
125 | + | ||
126 | + // 从缓存获取数据 | ||
127 | + if (config.useCache && cacheOption) { | ||
128 | + return this._requestFromCache(options); | ||
129 | + } | ||
130 | + | ||
131 | + return this._requestFromAPI(options, cacheOption); | ||
71 | } | 132 | } |
72 | 133 | ||
73 | /** | 134 | /** |
@@ -76,12 +137,22 @@ class API { | @@ -76,12 +137,22 @@ class API { | ||
76 | * @param data Obejct | 137 | * @param data Obejct |
77 | */ | 138 | */ |
78 | post(url, data) { | 139 | post(url, data) { |
79 | - return rp({ | 140 | + let options = { |
80 | url: `${ApiUrl}${url}`, | 141 | url: `${ApiUrl}${url}`, |
81 | - method: 'post', | ||
82 | form: data, | 142 | form: data, |
83 | - json: true | ||
84 | - }); | 143 | + method: 'post', |
144 | + json: true, | ||
145 | + timeout: 3000 | ||
146 | + }; | ||
147 | + | ||
148 | + return this._requestFromAPI(options); | ||
149 | + } | ||
150 | + | ||
151 | + all(list) { | ||
152 | + if (_.isArray(list)) { | ||
153 | + return Promise.all(list); | ||
154 | + } | ||
155 | + throw Error('the parameters of api all method should be Array!'); | ||
85 | } | 156 | } |
86 | } | 157 | } |
87 | 158 |
library/cache.js
0 → 100644
1 | +/** | ||
2 | + * 缓存封装 | ||
3 | + * 前期使用 memcache, 写方法的时候考虑一下如何转换为 redis | ||
4 | + * @author bikai kai.bi@yoho.cn | ||
5 | + * @date 2016/05/16 | ||
6 | + */ | ||
7 | + | ||
8 | +'use strict'; | ||
9 | +const Promise = require('bluebird'); | ||
10 | +const Memcached = require('memcached'); | ||
11 | +const _ = require('lodash'); | ||
12 | +const log = require('./logger'); | ||
13 | +const config = require('../config/common'); | ||
14 | + | ||
15 | +let master = new Memcached(config.memcache.master, config.memcache); | ||
16 | +let slave = new Memcached(config.memcache.slave, config.memcache); | ||
17 | + | ||
18 | +master = Promise.promisifyAll(master); | ||
19 | +slave = Promise.promisifyAll(slave); | ||
20 | + | ||
21 | +/** | ||
22 | + * 获取缓存 | ||
23 | + * @param {[string]} key 键 | ||
24 | + * @return {[type]} | ||
25 | + */ | ||
26 | +exports.get = (key) => { | ||
27 | + if (_.isString(key)) { | ||
28 | + return master.getAsync(key); | ||
29 | + } | ||
30 | + | ||
31 | + return Promise.resolve(); | ||
32 | +}; | ||
33 | + | ||
34 | +/** | ||
35 | + * 批量获取缓存 | ||
36 | + * @param {[array]} list 字符串数组 | ||
37 | + * @return {[type]} | ||
38 | + */ | ||
39 | +exports.getMulti = (list) => { | ||
40 | + if (_.isArray(list)) { | ||
41 | + return master.getMultiAsync(list); | ||
42 | + } | ||
43 | + return Promise.resolve(); | ||
44 | +}; | ||
45 | + | ||
46 | +/** | ||
47 | + * 获取缓存(从 Slave 服务器) | ||
48 | + * @param {[string]} key 键 | ||
49 | + * @return {[type]} | ||
50 | + */ | ||
51 | +exports.getFromSlave = (key) => { | ||
52 | + if (_.isString(key)) { | ||
53 | + return slave.getAsync(key); | ||
54 | + } | ||
55 | + return Promise.resolve(); | ||
56 | +}; | ||
57 | + | ||
58 | +/** | ||
59 | + * 批量获取缓存(从 Slave 服务器) | ||
60 | + * @param {[array]} list 字符串数组 | ||
61 | + * @return {[type]} | ||
62 | + */ | ||
63 | +exports.getMultiFromSlave = (list) => { | ||
64 | + if (_.isArray(list)) { | ||
65 | + return slave.getMultiAsync(list); | ||
66 | + } | ||
67 | + return Promise.resolve(); | ||
68 | +}; | ||
69 | + | ||
70 | +/** | ||
71 | + * 写缓存 | ||
72 | + * @param {[type]} key 键 | ||
73 | + * @param {[type]} value 值 | ||
74 | + * @param {[type]} lifetime 生命周期 | ||
75 | + * @return {[type]} | ||
76 | + */ | ||
77 | +exports.set = (key, value, lifetime) => { | ||
78 | + lifetime = lifetime || 86400; | ||
79 | + | ||
80 | + log.info(`write cache: ${key}`); | ||
81 | + if (_.isObject(value)) { | ||
82 | + value = JSON.stringify(value); | ||
83 | + } | ||
84 | + | ||
85 | + if (_.isString(key)) { | ||
86 | + return master.setAsync(key, value, lifetime); | ||
87 | + } | ||
88 | + return Promise.resolve(); | ||
89 | +}; | ||
90 | + | ||
91 | +/** | ||
92 | + * 写缓存(到 Slave 服务器) | ||
93 | + * @param {[type]} key 键 | ||
94 | + * @param {[type]} value 值 | ||
95 | + * @param {[type]} lifetime 生命周期 | ||
96 | + * @return {[type]} | ||
97 | + */ | ||
98 | +exports.setSlave = (key, value, lifetime) => { | ||
99 | + lifetime = lifetime || 86400; | ||
100 | + | ||
101 | + if (_.isObject(value)) { | ||
102 | + value = JSON.stringify(value); | ||
103 | + } | ||
104 | + | ||
105 | + if (_.isString(key)) { | ||
106 | + return slave.setAsync(key, value, lifetime); | ||
107 | + } | ||
108 | + return Promise.resolve(); | ||
109 | +}; | ||
110 | + | ||
111 | +/** | ||
112 | + * 删除缓存 | ||
113 | + * @param {[string]} key 键 | ||
114 | + * @return {[type]} | ||
115 | + */ | ||
116 | +exports.del = (key) => { | ||
117 | + if (_.isString(key)) { | ||
118 | + return master.delAsync(key); | ||
119 | + } | ||
120 | + return Promise.resolve(); | ||
121 | +}; |
@@ -30,6 +30,7 @@ | @@ -30,6 +30,7 @@ | ||
30 | }, | 30 | }, |
31 | "license": "MIT", | 31 | "license": "MIT", |
32 | "dependencies": { | 32 | "dependencies": { |
33 | + "bluebird": "^3.3.5", | ||
33 | "body-parser": "^1.15.0", | 34 | "body-parser": "^1.15.0", |
34 | "cookie-parser": "^1.4.1", | 35 | "cookie-parser": "^1.4.1", |
35 | "express": "^4.13.1", | 36 | "express": "^4.13.1", |
@@ -37,6 +38,7 @@ | @@ -37,6 +38,7 @@ | ||
37 | "influxdb-winston": "^1.0.1", | 38 | "influxdb-winston": "^1.0.1", |
38 | "lodash": "^4.12.0", | 39 | "lodash": "^4.12.0", |
39 | "md5": "^2.1.0", | 40 | "md5": "^2.1.0", |
41 | + "memcached": "^2.2.1", | ||
40 | "morgan": "^1.7.0", | 42 | "morgan": "^1.7.0", |
41 | "oneapm": "^1.2.20", | 43 | "oneapm": "^1.2.20", |
42 | "request-promise": "^3.0.0", | 44 | "request-promise": "^3.0.0", |
-
Please register or login to post a comment