dekuchen

2018-08-06 13:43

badjs开发指南

本文作者:IMWeb dekuchen 原文出处:IMWeb社区 未经同意,禁止转载

Badjs开发指南

首先来粗略看看Badjs的架构

目录结构

badjs

  • badjs-acceptor :负责数据接收和转发
  • badjs-mq:负责数据过滤和分发
  • badjs-mweb:移动端展示UI
  • badjs-report:前端上报组件
  • badjs-storage:基于mongodb的数据存储
  • badjs-web:PC端的数据展示和管理后台
  • config:配置文件

  • doc:文档相关

基本架构

基本的架构如下。

2018080615335330391223.png

然后,从宏观上的看一下BadJs都干了些什么。

在浏览器端

这一部分,主要是badjs-report,他的任务是捕捉js的报错,并把报错进行上报。这一部分,主要是要在页面中引入js,并配置,这一部分并不属于二次开发的范畴中,所以,不详述了。

在服务器端

在服务端,整套badjs包括接收端,存储端和管理端共三个部分,这三个部分都是基于express的框架。

  • badjs-web

  • badjs-storage

  • badjs-accepter

其中,accepter负责接收badsjs-report上报过来的数据,也就是页面上报过来的数据。然后将数据整理一下,校验一下是否是有效数据,然后通过zmq组件将数据传递给badjs-storagebadjs-storage则负责将传递过来的数据进行存储,这里使用了mongoDB作为主存储,file作为辅助cache,在badjs-storage中使用了map-stream作为其数据流的管理。而badjs-web则是将badjs-storage的数据用一种更人性化的形式呈现出来,这里用到了mysql作为存储。嗯,整个体系比较简单的看就是这样的。

现在,我们详细看看每个组件的实现:

一、上报端: Badjs-Reporter,嵌入移动设备或者PC网页

基本实现

主要捕获两种错误

1、JS脚本里边存着语法错误; 2、JS脚本在运行时发生错误。

那么我们如何来捕获这种异常呢,有两种方法,

  1. 第一种是try..catch,主动上报
  2. 第二种是 window.onerror

由于try.catch没法捕捉到全局的错误事件,也即是说 只有try,catch的块里边运行出错才会被你捕捉到。所以我们这里是属于主动上报方案,监控采用第二种方法,也就是window.onerror方法。

window.onerror

部分过时的浏览器只能提供部分数据。它的用法如下:

window.onerror = function (message, url, lineNo, columnNo, error)

五个参数的含义如下: 1、message {String} 错误信息。直观的错误描述信息,不过有时候你确实无法从这里面看出端倪,特别是压缩后脚本的报错信息,可能让你更加疑惑。 2、url {String} 发生错误对应的脚本路径,比如是你的http://a.js报错了还是http://b.js报错了。 3、lineNo {Number} 错误发生的行号。 4、columnNo {Number} 错误发生的列号。 5、error {Object} 具体的 error 对象,包含更加详细的错误调用堆栈信息,这对于定位错误非常有帮助。

兼容性问题:

不同浏览器对同一个错误的 message 是不一样的。 IE10以下浏览器只能获取到 messageurllineNo这三个参数,获取不到columnNoerror 不过 window.event 对象提供了 errorLineerrorCharacter,以此来对应相应的行列号信息。 在使用onerror的时候,我们可以使用arguments.callee.caller 来递归出调用堆栈,这一类信息是最直接的错误信息信息,所以是必须要捕获并上报的。

   var orgError = global.onerror;
    // rewrite window.oerror
    global.onerror = function(msg, url, line, col, error) {
        var newMsg = msg;

        if (error && error.stack) {
            newMsg = _processStackMsg(error);
        }

        if (_isOBJByType(newMsg, "Event")) {
            newMsg += newMsg.type ? ("--" + newMsg.type + "--" + (newMsg.target ? (newMsg.target.tagName + "::" + newMsg.target.src) : "")) : "";
        }

    //其他处理
    };

相关代码可以在这里看到 badjs-report

主要改造

前端上报badjs-report改造 1)badjs.init 的时候,上报 关键字 !#imweb-badjs-pv#! 2)切换路由 的时候,业务需要执行 BJ_REPORT.pv 上报

二、消息接收和过滤: badjs-acceptor

基本实现

20180806153353317340826.png

acceptor用来对消息进行过滤,分拣,并通过message queue进行分发。

acceptor提供了四种message队列的组件,axon,nodeNet,redis,zmq,这为之后存储的扩容和单机向多机的拓展提供了可能性。

三、消息队列:Badjs-mq

badjs-mq 即接收,也发送。 接受来自 acceptor 发送来的消息, 发送给 storage 要存储的消息。

默认使用axon-zmq

四、消息存储:Badjs-Storage

架构图

2018080615335333091323.png

基于mongodb的存储,使用zmqdispatch消息队列,接受mq传输的插入数据,写入mongodb。这个组件的服务是基于node的,所以,使用了express作为其整体的骨架,嗯,文件的目录结构就是这样的。

.
├── LICENSE
├── Readme.md
├── acceptor/
├── app.js
├── benchmarks/
├── cache/
├── service/
├── storage/
├── test/

嗯,从文件上来看,主要是acceptorservicestorage这几个部分。嗯,就像我们都知道的那样,express的入口文件当然使我们的app.js,看看它都干了什么。并不是很长,我就直接贴出来吧。

//...此处略去若干

var dispatcher = require(GLOBAL.pjconfig.acceptor.module)
  , save = require('./storage/MongodbStorage');
var cacheCount = require('./service/cacheErrorCount');


// use zmq to dispatch
dispatcher()
  .pipe(save());


logger.log('badjs-storage start ...');

setTimeout(function (){
    require('./service/query')();
    cacheCount.insertAuto();
    require('./service/autoClear')();
},1000);

其实前面有一大堆都是进行环境判断的,在启动的时候,附加不同的参数,将会使用不同的配置文件。

存储逻辑

在app.js的第3行,这里使用了调度器,而这个调度器指向的是acceptor\zmq.js,在这个zmp中,拉起了一个和badjs-accepter的tcp连接。这里通过map-stream来保证每个tcp数据流能以一个队列的形式来执行map定义的函数。。。不知道理解的对不对啊。可以直接看这里,你也可以看这里来理解。而调度器指向的存储是storage里面的mongostorage,这个文件将得到的数据存入分布式的mongoDB中。注意哦,这里使用是分布式的mongoDB,所以在使用一些函数的时候,要注意是不是支持分布式。

查询逻辑

存储逻辑比较好理解,因为为了保证高并发的数据流能够得到很好的处理,所以,越是简单的设计,越是可靠~!然后,我们来看看复杂的查询逻辑。这里的查询逻辑是交给了service的query.js来处理。嗯,这个文件有点长,我们不直接贴了,看一下折叠后的代码,理解下逻辑。 代码图 嗯,很直白的express的用法,connect中间件将请求分流导向不同的处理函数,在处理函数里处理自己的逻辑即可。处理逻辑中,比较建议的写法是只在函数中处理请求检查,函数response填充处理。将具体的逻辑处理抽象成一个函数放在exports的外部,如果是比较重的逻辑,则可以当初写成一个service来执行。

mongo

会有一台前置机,负责如何是分配存储和读取,在处理的时候,请注意mongo命令中对分布式的支持。 嗯,说两个比较复杂的,其他的就很好理解的。一个是在数据插入的时候。也就是storage里面的MongoStorage。

var insertDocuments = function(db , model) {
    var collectionName = 'badjslog_' + model.id;
    var collection = db.collection(collectionName); //获取数据集
    collection.insert([  //插入数据
        model.model
    ] , function (err , result){
        if(err){
            logger.debug('err,err is'+err);
            return;
        }
        if (hadCreatedCollection[collectionName]) {
            return ;
        }
        collection.indexExists('date_-1_level_1' , function (err , result ){ //判断数据集合索引
            if(!result){
                collection.createIndex( {date : -1 , level : 1 } , function (err , result){//建立索引,其中-1表示降序,1表示升序,前端的key表示对象。

                });
                if (global.MONGO_SHARD) {
                    shardCollection(db, collection, collectionName);
                }
            }
            hadCreatedCollection[collectionName] = true;
        })
    });

    logger.debug("save one log : " + JSON.stringify(model.model));
};

// shard new collection when created
var shardCollection = function (db, collection, collectionName) {
    collection.createIndex({_id: "hashed"}, function (err, result) {
        if (err) {
            logger.info("failed to create hashed index");
        } else {
            logger.info("hashed index created");

            var adminDb = db.admin();
            if (global.MONGO_ADMIN_USER && global.MONGO_ADMIN_PASSWORD) {
                adminDb.authenticate(global.MONGO_ADMIN_USER, global.MONGO_ADMIN_PASSWORD, function (err, result) { //权限认证,因为这里使用的是admin,要执行命令
                    if (err) {
                        logger.info("failed to access adminDB");
                    } else {
                        adminDb.command({ //执行mongo命令
                            shardcollection: "badjs." + collectionName,
                            key: {_id: "hashed"}
                        }, function (err, info) {
                            if (err) {
                                logger.info("failed to shardcollection " + collectionName);
                            } else {
                                logger.info(collectionName + " shard correctly");
                            }
                        });
                    }
                });
            }
        }
    });
};

这个函数的作用是,插入数据的时候看一下是不是有分布式,如果是,则使用分布式。 嗯,另一个例子看起来简单一点。

 var cursor = collection.aggregate([
    {$match: {'date': {$lt: endDate, $gt: startDate}}},
    {
        $group: {
            _id: {
                time: {$dateToString: {format: "%Y-%m-%d %H:%M", date: '$date'}}
            },
            count: {$sum: 1}
        }
    },
    {$sort: {"_id": 1}}
]);

其实这里一点都不简单,这里使用的是一个聚合查询,同时使用了聚合通道,具体的话,可以参考官方的说明文档,这里做一个说明,group,mapReduce这两个都是聚合查询的,但是group是不支持分布的,mapReduce使用的是map-reduce框架,但是实现的比较复杂,而且性能比聚合通道低。英文看不懂的话,可以看这篇博文

五、服务端:badjs-web

badjs-web是一个典型的几年前的web管理平台,具有使用express搭建的后台,mysql做数据持久化存储,前端使用jquery和bootstrap。整体的代码在放在几年前写的是非常优雅的。整个架构也非常清晰明了

架构

20180806153353349131863.png

同时通过一个进程池维护websocket连接。

对数据库的增删查改等操作,对后台接口的修改,都可以查看这里的说明文档readme

文件结构

389ef26e063d2fa6

实际上整体的结构很复杂。。。这里画的比较简单,把worker和service放在了一起,整体说明。 在和mysql连接这块使用了orm,用数据岛的模式来做对象化的数据处理。简单的说,就是可以像操作对象一下操作数据库。

.
├── app.js
├── controller/  包含所有数据库操作文件
├── dao/         orm对象模型
├── db/             数据库相关
├── model/       数据库模型
├── oos/         登录相关
├── plugin/      一些插件,主要是oa登录等
├── service/     封装了对数据库的增删查改
├── sh/          一些脚本
├── static/      打包后的静态资源
├── test/        测试文件
├── views/       打包后的前端页面
├── webpack.config.js
├── workflow/    工作流程文件
├── workflow.config.json

请求逻辑

请求逻辑是指从url过来的请求解析逻辑,包括请求html,接口请求,还有静态请求。

  • 静态资源请求

这个最简单,通过express框架,直接指向相应的资源文件。单独拿出来,是因为,这个地方的js是使用的模块化开发,webpack打包。每个页面的逻辑代码在static\module下的对应文件夹里,这里的文件其实是source文件,因为在发布前,要执行webpack命令,通过webpack将页面的内容打包到module下的接口文件(entry.*.js)里。主页面的逻辑是基于事件的,因为,渲染逻辑在请求html的时候就已经走完了。所有的事件都是委托给document.body去执行的。具体的实现方式是这样的。

//页面中
<a data-event-click="xxx">

//js中

$(document.body).on('click','xxx',function(){})

还有一部分是websocket的,因为自己不是很懂,就不详述了。

  • 页面渲染逻辑

嗯,实话实说,这个页面渲染的逻辑相对比较简单,在badjs-web中,使用的页面渲染引擎是一个内部人员自行开发的micro-tpl引擎,说明文档嘛,看这个吧。请求走的是express工作流,从router出来,简单的没有复杂的页面逻辑的请求,直接渲染模板,并返回,又复杂页面渲染逻辑的,则会通过action调用不同的service来实现逻辑获取,并渲染模板。

  • 接口请求逻辑

这里着重讲一下我们对于既有的二次开发的接口。原有的接口请求是这样的。

app.use("/",function(req, res , next){
        //controller 请求action

           //....此处略去若干

            //根据不同actionName 调用不同action
            try{
                switch(action){
                    case "user": UserAction[operation](params,req, res);break;
                    case "apply": ApplyAction[operation](params,req,  res);break;
                    case "approve": ApproveAction[operation](params,req, res);break;
                    case "log" : LogAction[operation](params,req, res); break;
                    case "userApply": UserApplyAction[operation](params,req, res);break;
                    case "statistics" : StatisticsAction[operation](params, req, res); break;

                    default  : next();
                }
            }catch(e){
                res.send(404, 'Sorry! can not found action.');
            }
            return;
    });

这个请求是通过正则匹配来获取不同的请求参数,进行请求分流。嗯,相对而言,我做的就比较简单粗暴了。直接的监听,get请求。

app.get('/xxx',function(){})

捕捉到相应的请求之后,将请求的参数传递给相应的action去处理。例如,log页面的请求交给LogAction,在action中,对不同动作,如果需要数据流,则会调用不同的底层服务(service)来实现和数据流的交互,那么,在logAction中,我们使用的就是LogService。在service中发出http请求去拉去badjs-storage的数据,或者,通过数据岛(DAO)来实现和mysql的交互。

整理一下就是,action处理动作,service处理数据流,dao负责和数据库(mysql)交互。

六、PC端:badjs-web

传统的jquery+bootstrap的代码。使用webpack构建。

七、移动端:badjs-mweb

badjs-mobile ui using react redux

为badjs写的一套移动端UI

功能模块

  • 导航Tab
    • 历史日志
    • 实时日志
  • 页面
    • 登录页面(包括GitHub第三方登录)
    • 404页面

功能截图

登录页与展示页 页面展示

设置页面 设置页面

代码目录

+-- build/                                  ---打包的文件目录
+-- config/                                 ---npm run eject 后的配置文件目录
+-- node_modules/                           ---npm下载文件目录
+-- public/                                 
|   --- index.html                            ---首页入口html文件
+-- src/                                    ---核心代码目录
|   +-- axios                               ---http请求存放目录
|   |    --- index.js
|   +-- components                          ---各式各样的组件存放目录
|   |    +-- pages                          ---页面组件
|   |    |    +-- HistoryTable.jsx          ---页面组件
|   |    |    +-- Login.jsx                 ---页面组件
|   |    |    +-- NotFound.jsx              ---页面组件
|   |    |    +-- NotFound.jsx              ---页面组件
|   |    |    --- ...   
|   |    +-- dashboard                      ---首页组件
|   |    |    --- ...   
|   |    +-- forms                          ---表单组件
|   |    |    --- ...   
|   |    +-- pages                          ---页面组件
|   |    |    --- ...   
|   |    +-- tables                         ---表格组件
|   |    |    --- ...   
|   |    +-- ui                             ---ui组件
|   |    |    --- ...   
|   |    --- BreadcrumbCustom.jsx           ---面包屑组件
|   |    --- HeaderCustom.jsx               ---顶部导航组件
|   |    --- Page.jsx                       ---页面容器
|   |    --- SiderCustom.jsx                ---左边菜单组件
|   +-- style                               ---项目的样式存放目录,主要采用less编写
|   +-- utils                               ---工具文件存放目录
|   --- App.js                              ---组件入口文件
|   --- index.js                            ---项目的整体js入口文件,包括路由配置等
--- .env                                    ---启动项目自定义端口配置文件
--- .eslintrc                               ---自定义eslint配置文件,包括增加的react jsx语法限制
--- package.json
  • 增加响应式布局
    • 替换antd Col 组件的响应式栅格为md(具体参数用法请查看antd官方文档)
    • 初始化页面是获取当前浏览器宽度设置菜单显示类型
    • 监听window的onresize函数,设置菜单显示类型。PS:浏览器宽度存入redux中,方便组件之间传递。

代码说明

技术栈:react+redux+react-router全家桶。

值得说的几个点:

Http请求封装
  1. axios代码封装为了提高http请求的复用性,在src/axios文件夹下封装了getpost请求。通过config配置请求的接口。
Reducer的封装
  1. 全局只封装了一个httpdata的reducer,只有receiveDatarequestData两种action,对于异步请求添加了isFetching的状态封装,代码见src/rereducer
action的封装
  1. 对外只提供两个fetchdata(异步)和receiveDataaction creator

使用样例

const mapStateToProps = state => {
    const { useritems = { data: { items: [] } },logConfig={ data: {}} ,logging={data:{logging:false}}} = state.httpData;
    return { items: useritems.data.items ,logConfig, logging: logging.data.logging}
}

const mapDispatchToProps = dispatch => ({
    fetchData: bindActionCreators(fetchData, dispatch),
    receiveData: bindActionCreators(receiveData, dispatch),
    getState: bindActionCreators(receiveData,dispatch)
})
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(PanelMweb));
进一步思考

其实mapstateToProps以及mapDispatchToProps都是标准的样板代码,是不是可以用高阶组件进一步抽象出来。

使用到的工具

  • bindActionCreators:用来减少代码重复度,减少写dispatch的次数
  • withRouter:将redux注入组件
  • redux-thunk处理异步redux action

PV|ERROR 统计实现

  1. badjs-web 管理后台配置规则;
  2. badjs-storage 获取配置规则;
  3. 匹配规则优先级: badjs-id、level、ruler、msg;

具体这么做的原因是:

  1. 减少对业务的侵入性。
  2. 使用正则可以使单页应用的URL可以匹配对应模块,方便进行数据聚合

badjs-web配置规则

15330507386851

架构图

15330507610007

统计实现

15330508373525

具体步骤

  1. ruler同步规则
    • badjs-storage获取规则
    • badjs-ruler收到全部数据
    • ruler通过master hash分发给worker
  2. 数据统计
    • badjs-acceptor接受请求,判断是否继续
    • 数据通过badjs-acceptor -> badjs-mq -> badjs-storage
    • 通过'!#imweb-badjs-pv!#'和level:2来定义一次pv上报
    • 通过id将数据分发到对应的子进程,匹配到的规则全部统计。

举例子

规则:1、ke.qq.com2、ke.qq.com/A,3、ke.qq.com/B
数据: 
          ke.qq.com/A     PV = 1000,Error = 10 
          ke.qq.com/A     PV = 100,Error = 5
          ke.qq.com         PV = 1100,Error = 15
这个是否是个问题,目前没办法区分首页,这个问题是否需要解决

登陆机器

ssh haigecao@sng.mnet2.com -p 36000 http://iaas.isd.com/password/select 查询root密码 ssh root@10.185.18.34


mysql 数据库操作

数据库操作

  • 创建库和表 mysql -u username -p < ../badjs-web/db/badjs.sql;
  • **除数据库 drop database badjs;
  • 操作表 b_apply
    • select * from b_apply; 申请项目名称 和 匹配 URL
    • update b_apply set id = 1361 where id = 1; 更新 id 为指定的上报 id
    • DELETE FROM b_apply WHERE id=1361;
  • 操作表 b_match_set 规则表,添加规则
    • insert into b_match_set (apply_id, match_url, developer, disable, project) values (1316,"fudao.qq.com/tiku/exam.html", "haigecao, fredwu", 1, "企鹅辅导题库");

mongodb 数据库操作

  • 启动 mongodb —— sudo mongod
  • 链接 mongodb —— mongo
  • 使用 badjs 数据库: use badjs
  • 查看所有集合: show collections;
  • 查看该集合有多少数据: db.badjslog_1361.find().count();

前端上报

badjs-report

    BJ_REPORT.init({id : 1});     // id 申请的业务ID
    BJ_REPORT.report('level 4')   // 上报错误
    BJ_REPORT.info('level 2')     // 上报日志
    BJ_REPORT.info('!#imweb-badjs-pv#!')  // 测试统计PV

数据通信 流转状态

(开发者) 指配置过匹配规则的

(上报id) 规则生效的前提是 有上报id,统计也是在某一个上报id下生效

  • 1、appkey 更新 流程
    • 1)badjs-web 提交编辑后的 appkey, 通过 controller/applyAction/addApply.do 接口 发送;
    • 2)db 更新之后,在通过 http 请求,发送给 badjs-acceptor 接口 【 getProjects 】;
    • 3)badjs-acceptor 内部在更新,更新之后,写入 project.db 文件中;
  • 2、ruler 规则,每天凌晨进行数据更新
    • 选择由 badjs-web 发送请求接口 [ asyncRuler ] 向 badjs-storage 同步数据;
    • badjs-storage 接到数据,进行内存中 ruler 的更新,
    • 发送分为两种 cmd = updateRuler(更新) 和 startRuler(服务重启),
    • badjs-storage 启动会先预加载本地文件,如果存在,就使用本地。
    • 当 cmd = startRuler 时,如果 badjs-stroage 有数据,就不更新;
    • cmd 如果是 updateRuler ,就强制更新规则。 (触发时机是每天 0 点)
    • 原因是,统计规则是按天,进行核算的,规则明天生效,
    • 这里注意 【 global.appkeys 】和 ruler 是同步更新的
  • 3、按天 统计所有项目的 PV 和 error 数量
    • badjs-stroage 每天凌晨,按天统计 基于项目、开发者纬度 PV 和 error 的数据;
    • 选择由 badjs-web 通过接口 【 getAllDeveloperPVAndError 】 向 badjs-storage 请求;
    • badjs-storage 将计算好的数据,返回给 badjs-web,
    • badjs-web 写入数据库 中,等待 邮件服务的计算。
  • 4、统计每个人前20的错误,Error 的数据,
    • badjs-storage 服务,通过 cache/count 下面有昨天所有的 err 错误;
    • 通过 cache/count 数据,有 errorkey 对象字段内,获取对应人的 md5 值;
    • 通过 appkey 和 error md5 值,可以拿到对应的错误内容,并将所有的 md5 值排序;
    • 通过 获取的 md5 列表内,去 cache/total;
    • 讲聚合后的数据,写入文件,存储基于 开发者 的 top20 错误,
    • badjs-storage 提供 http 接口,将指定日期 开发者的错误数据返回给 badjs-web;
    • badjs-web 将数据,写入 DB;

打分流程

  • 0、badjs-reoprt 上报 PV 和 error
    • badjs.init 的时候,会上报 PV
    • 通过 BJ_REPORT.info('!#imweb-badjs-pv#!')
    • 切换模块时,业务开发者,需要手动 BJ_REPORT.pv() 上报该模块的PV
    • error 上报是 BJ_REPORT.report 上报
  • 1、【 master 主进程 】获取匹配的 URL 规则
    • badjs 首次启动时,[badjs-web] 获取数据库表 b_match_set 中所有规则;
    • 发送给 [badjs-storage],更新过滤规则,
    • [badjs-web] 定时任务,每天 凌晨 0 点,获取 最新的 匹配规则;
    • 发送给 [badjs-storage] ,更新内存中的过滤规则,
    • 【 master send message to worker 】将规则,给子进程传递规则。
  • 2、【 master 主进程 】统计 PV 思路
    • 子进程获取规则:需要通过进程间,定期同步更新数据来解决;
    • 将匹配规则的按 [ from.length % 3 ], 分配给子进程统计,
    • 每个子进程,都是全量的匹配计算,统计错误的数量,写入到子进程对应的文件中
  • 3、【 worker 子进程 】统计 PV 和 Error 数量
    • 通过 appkey 首先过滤,判断是否需要统计
    • 通过 规则列表进行匹配,符合规则的 PV + 1
    • 统计 error 是将 error 的 message 进行 md5
    • 将 error 的 md5 值 存如 errorkey 对象内,默认是 1
    • 将 appkey 、规则、md5 一样的时候,就 +1
  • 4、【 master 主进程 】定时任务,定期统计
    • 通过获取进程数量,配置文件中获取, global.pjconfig.realTotal
    • 通过 0 - Index 遍历,进行数据聚合。
    • 写入到对应的 cache/count/pverr$(date).json 文件中
  • 5、【 master 主进程 】提供接口
    • 获取 badjs-storage 中存储的数据,写入 db
    • 定时任务 获取昨天,所有的数据,进行计算。

更新 匹配规则

  • 1、思考一个问题,更新 匹配规则 时机
    • 1)服务启动的时候,重启、首次启动、
    • 2)每天0点更新规则,新的一天,按照新规则统计;
    • 3)先清空文件,在写入临时文件保存;
  • 2、要将 1 和 2,区分开
    • 每一次重启读配置,读取配置文件,无配置文件,使用启动规则,进行更新
    • 每天 0 点,更新的时候,重置配置文件
    • 更新 临时 文件
  • 3、具体实现
    • 1、
      • badjs-web 首次启动时 获取 db 中表数据
      • 采用 http 接口的形式,badjs-web 加入名字:cmd = startRuler; 发送给 badjs-storage,
    • 2、
      • badjs-storage 首次启动的时候,读取 rulers,进行项目初始化
      • 收到 cmd,如果是 startRuler,看是否有数据,如果没有,就覆盖,有就忽略;
    • 3、
      • 定时更新,badjs-web 发送 cmd = updateRuler ,进行规则更新;
      • bajds-storage 收到 cmd,然后,进行覆盖和文件更新;

邮件表结构

1、owner 表结构

速度优化

  • 1、目前查询查询的 mongodb 内字段是 all,可以做更细化的查询

存储流程

  • 1、目前 mongo db 存储 level = 2 和 4 等级的数据。

文件目录

  • badjs-storage

    • cache 目录保存 临时数据

      • count 保存 pv 和 error 数量

        • 文件命名 是通过 时间_index 进行保存

          [
            {
              appkey: 1361, // 索引id
              index: 1,     // 在规则表中的索引位置
              ruler: /fudao.qq.com\/tiku\/exam.html/, // 匹配规则
              pv: 0,  // PV 数量
              err: 1, // 错误数量
              errorkey: { e5a4f0716c8f6c44de0f1aceace6bbb2: 1 },  // 错误 md5 值 和 对应 的 统计 数量
              developer: 'haigecao, fredwu'   // 开发者,通过 英文 逗号 分格
            }
          ]
          
  - error message 保存错误信息
  - total 数据汇总【后期可以干掉】,total 中有 2种 命名格式
    - 日期_index (一个下划线) —— 记录了 汇总的数据
      - appid 下对应的 所有错误数量,以及所有错误内容
        ```
        数据结构
        {
          1361 : {                    // appkey
            total: num,               // 错误数量
            errorMap: {
              error1: {               // message 的 md5 值
                total: num,           // 这个错误数量
                msg: "error message"  // 这个错误内容
              }
            }
          }
        }
        ```
    - 日期__index   两个下划线

工具函数

  • 1、查询机器占用端口
    • netstat -apn|grep 127.0.0.1

测试

本地环境 和 dev 环境都是可以及时生效的。 正式环境,需要一天同步一次规则。

  PV:
  for (var i = 0 ;i < 10; i++) {
    BJ_REPORT.info('!#imweb-badjs-pv#!');
  }

  Error:
  for (var i = 0 ;i < 10; i++) {
    BJ_REPORT.report('!#imweb-bad123213js-pv#!');
  }
0条评论

    您需要 注册 一个IMWeb账号或者 才能进行评论。