① nodejs的错误Cannot read property 'length' of undefined,怎么解决啊我是菜鸟
1、在使用connect-mongo包进行连接mongo数据库时,会报Cannot read property 'Store' of undefined的错误
2、出现这个问题的原因是connect-mongo版本的问题,以后大家在遇到类似的问题的时候,先看看相关包目录下的readme文件,里面有这个包的新用法,已经这个版本和以前版本的区别。
3、我们看到connect-mongo目录下的readme文件中有这么一段话:With express:var express = require('express'); var MongoStore = require('connect-mongo')(express); app.use(express.session({ secret: settings.cookie_secret, store: new MongoStore({db: settings.db},With connect:var connect = require('connect');var MongoStore = require('connect-mongo')(connect);
4、所以我们需要这样修改程序:将var MongoStore = require('connect-mongo') 换成var MongoStore = require('connect-mongo')(express);且var express = require('express');这句必须在前面。这样修改问题就解决了。
② 基于NodeJS的高性能分布式游戏日志系统
最近我司需要做一个统一的游戏日志系统,要求有一定的通用性,能应对公司所有的游戏业务。接下来分享一下这次日志系统的项目经验。
目前流行的日志系统为ELK,由Beats、Logstash、Elasticsearch、Kibana等组件共同实现,但万变不离其宗,一个基本的日志系统架构类似如下:
游戏分析,与其它服务系统不同的是,游戏内的系统可能是天马行空的,数据类型是多样的,甚至频繁变化的。我们要在变化中总结到不变的内容,例如系统经济产出,玩家物品消耗,商店购买等进行分析。所以这次的游戏日志系统要满足以下需求:
虽然ELK在安装配置方面不算困难,插件众多,例如Filebeat,读log文件,过滤格式,转发,但谁来生产这些log文件,没有提及。实际上,业务具有多样性,只要有日志文件的地方,它就可以用。例如多数会使用Nginx进行日志收集。我们也需要考虑到日志生产者的问题,责权分离,需要单独一台机子进行日志采集。
游戏是一种技术与艺术结合的产品,数据庞杂,形态各异,光日志埋点也花不少功夫复杂,但不能因此放弃治疗。好的游戏日志,还可以帮我们还原玩家玩家画像。游戏更新周期短,数据变化大,需要提供更实时参照报表,为非技术人员更好友的查询界面,才能更好的服务于游戏数据分析。ELK 在这方面,基本解决了采集和储存的问题,但实现分析方面还不能满足我们的需求。
经过一翻思索,我们可以用现有工具,粘合多个套件,所以,我们有了以下思路:
这个框架主要使用到了Fluentd,ElasticSearch,以及NodeJS,我就称它为 FEN 架构吧,如下图。
上图看出,这样的日志架构和第一个图基本没什么不同,只是多了后面的分析与分批入库处理,并且大量使用了NodeJS。
注:在这里不会介绍各组件的详细的安装配置方法,网上有太多了,怎样使用好每一个组件才是关键。
先介绍我们用到的工具:
Fluentd是一个完全开源免费的log信息收集软件,支持超过125个系统的log信息收集。Fluentd在收集源日志方面非常方便而且高性能,通过HTTP GET就可以,这类似于Nginx的日志记录行为。它的优点是,日志文件可以高度定制化,例如我们这里每5秒生成一个文件,这样每分钟有12个文件,每个文件体积非常小。为什么要这样做?下面会介绍。Fluentd还有非常多的插件,例如直接存入MongoDB,亚马逊云等,要是熟悉Ruby,也可以自己写插件。
有人使用MongoDB进行日志收集,是非常不明智的,只有几千万条还可以,如果半个月生产10亿条日志呢?日志文件需要保存一个月甚至更长,那么集群和硬盘维护就非常重要。使用便利性也很重要,例如分词检索,在客服回溯玩家日志,分析游戏 BUG 的时候非常有用。下文的 ES 也是该组件的简称。
NodeJS不适合做 CPU 密集型任务,但在网络应用方面还不错,并且是我们正好熟悉的。日志系统并不对实时性要求并不高,延时半小时以内都是允许的,事实上,正常情况延时也就10来秒。下面的读与转发日志的Pusher,收集日志的logger,分析日志并数据落袋为安的的analyser,都是由NodeJS实现的。
下面继续介绍用 NodeJS实现的每一个部分:
上面说到,为什么Fluentd使用分割成多个小文件的方式,因为NodeJS在大文件处理方面并不友好,并且要考虑到通过网络发送到另一台机,转发速度比读慢太多了,所以必须实现续传与断点记录功能。想想,如果读几百 M 的文件,出现中断后,需要永久记录上次位置,下次再从此处读起,这就增加了程序复杂度。NodeJS虽然有readline模块,但测过发现并不如文件流那样可控,访模块用于交互界面尚可。相反,如果日志分割成多个小文件,则读的速度非常高效,并且每5秒一个文件,哪怕有上万条记录,文件也大不到哪里去,内存也不会占用太多,在断点续传与出错重试方面都能自如应对。如果游戏日志增多,可以增加节点来缓解文件过大的压力。
为什么不直接让日志生产者直接发到Koa上?因为效率与带宽。NodeJS的适合做网站,但比专业的HTTP服务器要弱太多,4核心主机面对3000QPS就吃力,更多的关于NodeJS的性能问题,可以参考网络文章。在高并发量下,带宽是个很大的问题,尤其是需要做统一服务,面对的情况是日志机器与游戏并不在同一内网中。在10万日活下,带宽超过了50M,非常吓人,带宽可是很贵的,过高的带宽费用在这里性价比太低了。
这里我们使用Koa作为日志采集器。使用Koa,无论在性能还是开发效率上,都比expressJS高效。我们还用到了Redis作为缓存,而不是直接在这里做分析任务,是为了尽量提高与Pusher的对接效率,毕竟日志的生产速度是很快的,但网络传送是相对低效的。
注:pm2 3.2.2的集群可能出现集群内端口冲突的吊诡问题,建议用3.0.3
分析器读取Redis的内容,这里就是单进程的队列操作。到这一步,日志怎么分析,就可以很自由了。
因为我们本身有后台管理系统,所以我们很方便的把用户画像与其它分析点接了入去,在查询玩家行为时,我们搜索ES,在查询分析报表时,我们查询MongoDB中的数据。当然我们也使用了Kibana来满足可能的需求。
目前该日志系统运行1个半月,由纯MongoDB到结合 ES,走了不少弯路,还好现在终于稳定下来。目前在性能方面,logger 与 analyser都在同一台机,平均 CPU 为23%左右,高峰47%左右,说明还有更大的机器压榨空间。
内存方面,在高峰期5G 以内,总体非常平稳没多大波动,其中redis内存使用为800MB以内,但机器是16G,还有很大余量保障。
NodeJS 的脚本中,logger的CPU占用更小,3条进程,每条才3%,每条内存占用不到100MB。analyser 的 CPU 与内存占用多一点,这一点可以通过脚本内的参数调整,例如内存计数的内容清理得更快,使用pm2的话设置max_memory_restart : ƊG' 都可以提高稳定性。
以上是我在游戏日志系统中的经验总结。
参考文献:
③ Memory Cache NodeJS
yarn add memory-cache
https://www.npmjs.com/package/memory-cache
④ 如何一体化一个NodeJs的MVC开发框架
本框架适合使用NodeJs进行web开发的MVC框架模式,本框架使用了express框架作为nodejs的web开发支撑,使用mysql作为数据库开发源,下面我们就简单的介绍如何利用本框架进行一个简单的web应用开发。当然本框架并搏尺非官方,也并非专业设计,希望开发者共同来把本框架设计好,以便我们可以在国内实现一个NodeJs的Web开发框架。
一、项目文件夹介绍
项目文件夹主要是根据传统的MVC设计模式,设计出来的框架。
enter image description here
二、 入口文件介绍
本框架的入口文件为index.js,该入口你可以添加多种全局静态变量,例如你所需要的各个文件夹路径,以及一些模块。
举例如下:
//========================全局变量定义===============================
global.BASE_DIR = __dirname;
global.APP = global.BASE_DIR + "/application/";
global.CON = global.APP + "/controller/";
global.CORE = global.APP + "/core/";
global.MODEL = global.APP + "/model/";
global.CONF = global.BASE_DIR + "/conf/";
global.log = global.BASE_DIR + "/log/";
global.PUBLIC = global.BASE_DIR + "/public/";
global.VIEW = global.BASE_DIR + "/view/";
/**
moles引入
*/
global.express = require(‘express’);
global.sio = require(‘socket.io’);
global.fs=require(‘fs’);
global.path = require(‘path’);
global.url = require(‘url’);
global.parseCookie = require(‘connect’).utils.parseCookie;
global.MemoryStore = require(‘./node_moles/connect/lib/middleware/session/memory’);
global.Session = require(‘./node_moles/connect/lib/middleware/session/session’);
global.sys = require(‘util’);
代码2-1:index.js
在index.js中你需要将你所有的文件夹路径、模块使用全局变量进行替换,该方法的优势在于,避免用户在编码中引入过长的文件路径,只需衡判要使用简单的变量进行替换。
urlResolve = require(CORE + “url_resolve”);
urlResolve.getActionInfo();
代码:2-2:路由处理逻辑
本代码包含进逻辑处理类,同时应用逻辑处理类中的getActionInfo方法,创建服务器,并且处理url请求逻辑。
三、 路由处理逻辑
主要有六个方法,其中的getActionInfo是exports,其他咐银改方法均为私有方法。
exports.getActionInfo = function(){
systemConfig();
app.get('/:key', function(req, res){
callUrlRequest(req, res);
});
app.post('/:key', function(req, res){
callUrlRequest(req, res);
});
listenPort();
};
function callUrlRequest(req, res){
var routerMsg = getUrlConf();
var key = req.params.key;
var session = checkSession(req, key);
if(key == "favicon.ico"){return;};
if(session == 0){
res.redirect('/index');
return;
}
console.log("[key:"+ key +"] " + "[class:" + routerMsg[key].cla + "] " + "[controller:" + routerMsg[key].fun +"]");
require(CON + routerMsg[key].con);
var controllerObj = eval("new " + routerMsg[key].cla);
controllerObj.init(req, res);
controllerObj[routerMsg[key].fun].call();
}
代码2-3:路由处理getActionInfo
SystemConfig是配置express框架的相应数据,配置静态文件夹以及express框架的相应配置数据。之后添加两种url请求方式,分别是get和post方法,由于两种方法请求资源的路由处理都是一样的,因此使用callUrlRequest来处理。
callUrlRequest 获取路由配置文件信息getUrlConf;
2、获取当前访问的key值,根据key值得到相应的配置信息,配置文件可以展示如下:
"test": {
"con" : "test",
"cla" : "test",
"fun" :"test"
},
"favicon.ico" : {
"con" : "",
"cla" : "",
"fun" : ""
},
"login" : {
"con" : "index_controller",
"cla" : "IndexController",
"fun" : "loginAct"
},
"index" : {
"con" : "index_controller",
"cla" : "IndexController",
"fun" : "loginPageAct"
},
"loginS" : {
"con" : "index_controller",
"cla" : "IndexController",
"fun" : "toMainPageAct"
}
}
代码2-4:配置文件信息
如果当前key为test那么我们就可以得到相应的controller、class和function。同时因为nodejs服务器每次请求数据的时候都会加入favicon.ico,因此在代码中我们需要将其剔除。对于checkSession就是验证登录信息。
3. 得到controller、class和function,首先require相应的controller,然后使用eval来new相应对象,使用controllerObj[routerMsg[key].fun].call();该方法进行调用。(本部分处理,涉及到一个javaScript的小技巧,如何对一个字符串进行new,同时调用一个对象的方法,该方法名为字符串变量)
4. 最后就是listenPort();进行监听事件,也是服务器开始启动。这样一个基本的路由处理就完成实现了。
**四、 数据层实现**
本系统数据层基类是在core文件夹下的base_model.js,该类主要包含数据库的一般方法,主要含有数据库链接、数据库操作基本方法add、update、deleteItem、query、select等,具体实现方式,就不细讲。
BaseModel为基类,其他对应于相应的表的类都继承来自BaseModel基类
继承方法使用JavaScript的原型继承:
IndexController.prototype = new BaseController();
global.IndexController = IndexController;
**五、 逻辑层实现**
类同于数据层的实现方法,其继承都是来自于基类BaseController,BaseController现只包含三个方法:init、displayHtml、displayJade。
**六、 代码规范
** 本框架不要求开发者是如何去定义代码规范,但本框架实现的代码规范是如下:
变量命名:私有变量统一使用”_name”,全局变量使用大写”VIEW”,简单变量请使用骆驼峰”myName”
方法命名:所有方法请使用骆驼峰”getUrlRequest”
类命名:统一使用首字母大写骆驼峰”BaseController”
文件命名:统一使用下划线分割,类使用下划线分割base_controller.js
总结:整体上就可以实现一个MVC开发的MyWeb框架,其中的方法以及实现都还是处于稚嫩期,希望有开发者愿意加入,并且能够团队合作开发出我们国内优秀的NodeJs的MVC框架。
⑤ nodejs_bug修复
1、Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
<--- Last few GCs --->
[3628:0000020F92556310] 101267813 ms: Mark-sweep 2045.8 (2063.6) -> 2045.7 (2061.1) MB, 823.6 / 0.0 ms (+ 217.9 ms in 2 steps since start of marking, biggest step 217.9 ms, walltime since start of marking 1052 ms) (average mu = 0.052, current mu = 0.011)
<--- JS stacktrace --->
==== JS stack trace =========================================
Security context: 0x016990671ba1 <JSObject>
1: parseMessage [00000302F1DB3371] [C:\Platform\node\service\node_moles\pg\lib\connection.js:~377] [pc=0000028DA566DDB0](this=0x03d27f47c261 <Connection map = 0000012A6A060F51>,0x03e134ba35c9 <Uint8Array map =
0000012A6A065321>)
2: /* anonymous */ [0000020ACD5EAF31] [C:\Platform\node\service\node_moles\pg\lib\connection.js:~119] [pc=0000028DA56...
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
1: 00007FF78BAC094F napi_wrap+124431
2: 00007FF78BA62696 v8::base::CPU::has_sse+34502
3: 00007FF78BA63356 v8::base::CPU::has_sse+37766
4: 00007FF78C266F4E v8::Isolate::+94
5: 00007FF78C24EF91 v8::SharedArrayBuffer::Externalize+833
6: 00007FF78C11C85C v8::internal::Heap::+1436
7: 00007FF78C127C00 v8::internal::Heap::+1312
8: 00007FF78C124734 v8::internal::Heap::PageFlagsAreConsistent+3204
9: 00007FF78C119FC3 v8::internal::Heap::CollectGarbage+1283
10: 00007FF78C118794 v8::internal::Heap::AddRetainedMap+2356
11: 00007FF78C140623 v8::internal::Factory::NewRawOneByteString+83
12: 00007FF78C1434D2 v8::internal::Factory::NewStringFromUtf8+130
13: 00007FF78C26386A v8::String::NewFromUtf8+298
14: 00007FF78B9D76EF node::tracing::TraceEventHelper::SetAgent+40751
15: 00007FF78BA79BFD v8::internal::Malloced::operator delete+1661
16: 00007FF78C61341C v8::internal::SetupIsolateDelegate::SetupHeap+4562817: 0000028DA566DDB0
2、<--- JS stacktrace --->
Cannot get stack trace in GC.
FATAL ERROR: MarkCompactCollector: semi-space , fallback in old gen Allocation failed - JavaScript heap out of memory
这个问题是node内存泄漏造成的。因为node是单线程的,所有的访问走的一个线程,如果执行完一个,占用内存增加,但是没有释放的话,就造成内存泄漏,内存泄漏其实就是存储泄漏,而造成的原因就是内存分配超过了v8内存的限制数量,而且进程所占用的内存,并没有慢慢释放回来,
解决办法:node --max-old-space-size=6096 analysis.js
⑥ Nodejs的WebSocket模块怎么设置连接超时时间
在建立socket连接时可以很容易设置socket通信的发送和接收超时时间,但是在建立socket通信时,如果是Win98系统,则如果连接失败,则程序会一直等待在哪里,Windows2000默认超时时间是30秒,当然,这个超时时间不算长,但是加入我们要循环扫描一系列端口并且建立连接的话,总的等待时间就会让人忍受不了,下面就以delphi为例进行说明,如何在建立socket的时候设置其超时时间:
//连接,发送和接收时间都设为2秒
SctTimeOut := 2000;
//设置接收数据通信超时
setsockopt(hSock,SOL_SOCKET,SO_RCVTIMEO,@SctTimeOut,SizeOf(Integer));
//设置发送数据通信超时
setsockopt(hSock,SOL_SOCKET,SO_SNDTIMEO,@SctTimeOut,SizeOf(Integer));
//首先,设置通讯为非阻塞模式
dwArg := 1;
RecvLen := ioctlsocket(hSock,FIONBIO,dwArg);
//其次,连接服务器
ZeroMemory(@addr, sizeof(addr));
addr.sin_family := AF_INET;
addr.sin_addr.S_addr := inet_addr(pchar(SvrIP));
addr.sin_port := htons(Strtoint(SvrPort));
RecvLen := 0;
RecvLen := connect(hSock, addr, sizeof(addr));
//再次,设置连接超时时间为2秒
tmOut.tv_sec := 2;
tmOut.tv_usec := 0;
FD_ZERO(recvSet);
FD_SET(hSock, recvSet);
RecvLen := select(0, @recvSet, @recvSet, nil, @tmOut);
//连接失败,报错误信息
if (RecvLen = 0) or (RecvLen = SOCKET_ERROR) then
begin
ErrMsg := '连接服务器失败!';
exit;
end;
//最后,设置通讯为阻塞模式
dwArg := 0;
RecvLen := ioctlsocket(hSock,FIONBIO,dwArg);
//end modify
⑦ nodejs怎么占内存那么多
node基于v8构建,所以在node中使用的js对象基本上都是通过v8自己的方式来进行分配和管理的。
在v8中,所有的js对象都是通过堆来进行分配的。
process.memoryUsage();
{
rss:24473600,
heapTotal: 7331840,
heapUsed: 5736952,
external: 8727
}
v8内存分代
v8中,主要将内存分为新生代和老生代。新生代中为存活时间较短的对象,老生代中为存活时间较长的对象。
新生代垃圾回收:
新生代中的对象主要通过Scavenge算法进行垃圾回收。将新生代中的内存空间一分为二,处于使用状态的为From空间,处于闲置状态的为To空间。在进行垃圾回收时,检查From空间的存活对象并复制到To空间,非存活对象占用空间释放。之后From和To空间角色对调。
对象晋升:
如果一个对象经历过一次新生代垃圾回收,或者To空间的内存占比超过25%,则此对象从新生代中移动到老生代中,此过程称为晋升。
老生代垃圾回收:
主要采用Mark-Sweep(标记清除)和Mark-Compact(标记整理)两种方式进行垃圾回收。
标记清除在标记阶段遍历堆中的所有对象,并标记活着的对象。随后的清除中只清除没有被标记的对象。会产生内存碎片。为解决这个问题,标记整理被提了出来,在对象被标记为死亡后,在整理的过程中,将活着的对象往一端移动,移动完成后直接清理掉边界外的内存。v8中混合使用这两种方法。一般在空间不足以对从新生代晋升过来的对象进行分配时,才使用标记整理。
高效使用内存:
在js中无法立即回收的内存有闭包和全局变量引用这两种情况。此情况会导致新生代中的对象数量增多。
⑧ webpack打包报javaScript heap out of memory,怎么解决
哎,我终于自己搞定了。其实,现在发现我已开始的解决问题的思路没有错,就是往node命令行里面添加 "--max_old_space_size"。错就错在,加的地方加错了(可能是针对我这种环境不起作用?)。一开始我是参考了segmentfault上面一个同样的weback打包导致nodejs内存溢出的一个问题所提供的方案来做的,就是在全局的webpack.cmd(路径是:C:.cmd)里面添加,