前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >badjs开发指南

badjs开发指南

作者头像
IMWeb前端团队
发布2019-12-03 17:08:59
2.8K0
发布2019-12-03 17:08:59
举报
文章被收录于专栏:IMWeb前端团队IMWeb前端团队

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

Badjs开发指南

首先来粗略看看Badjs的架构

目录结构

badjs

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

基本架构

基本的架构如下。

然后,从宏观上的看一下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 来递归出调用堆栈,这一类信息是最直接的错误信息信息,所以是必须要捕获并上报的。

代码语言:javascript
复制
   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

基本实现

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

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

三、消息队列:Badjs-mq

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

默认使用axon-zmq

四、消息存储:Badjs-Storage

架构图

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

代码语言:javascript
复制
.
├── LICENSE
├── Readme.md
├── acceptor/
├── app.js
├── benchmarks/
├── cache/
├── service/
├── storage/
├── test/

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

代码语言:javascript
复制
//...此处略去若干

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。

代码语言:javascript
复制
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");
                            }
                        });
                    }
                });
            }
        }
    });
};

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

代码语言:javascript
复制
 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。整体的代码在放在几年前写的是非常优雅的。整个架构也非常清晰明了

架构

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

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

文件结构

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

代码语言:javascript
复制
.
├── 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去执行的。具体的实现方式是这样的。

代码语言:javascript
复制
//页面中
<a data-event-click="xxx">

//js中

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

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

  • 页面渲染逻辑

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

  • 接口请求逻辑

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

代码语言:javascript
复制
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请求。

代码语言:javascript
复制
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页面
功能截图

登录页与展示页

设置页面

代码目录
代码语言:javascript
复制
+-- 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

使用样例

代码语言:javascript
复制
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配置规则

架构图

统计实现

具体步骤

  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将数据分发到对应的子进程,匹配到的规则全部统计。

举例子

代码语言:javascript
复制
规则:1、ke.qq.com,2、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
代码语言:javascript
复制
    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' // 开发者,通过 英文 逗号 分格 } ]
代码语言:javascript
复制
  - 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 环境都是可以及时生效的。 正式环境,需要一天同步一次规则。

代码语言:javascript
复制
  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#!');
  }
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-08-06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Badjs开发指南
    • 首先来粗略看看Badjs的架构
      • 目录结构
      • 基本架构
    • 现在,我们详细看看每个组件的实现:
      • 一、上报端: Badjs-Reporter,嵌入移动设备或者PC网页
      • 二、消息接收和过滤: badjs-acceptor
      • 三、消息队列:Badjs-mq
      • 四、消息存储:Badjs-Storage
      • 五、服务端:badjs-web
      • 六、PC端:badjs-web
      • 七、移动端:badjs-mweb
    • PV|ERROR 统计实现
      • badjs-web配置规则
      • 架构图
      • 统计实现
      • 具体步骤
      • 登陆机器
      • mysql 数据库操作
      • mongodb 数据库操作
      • 前端上报
      • 数据通信 流转状态
      • 打分流程
      • 更新 匹配规则
相关产品与服务
云数据库 MongoDB
腾讯云数据库 MongoDB(TencentDB for MongoDB)是腾讯云基于全球广受欢迎的 MongoDB 打造的高性能 NoSQL 数据库,100%完全兼容 MongoDB 协议,支持跨文档事务,提供稳定丰富的监控管理,弹性可扩展、自动容灾,适用于文档型数据库场景,您无需自建灾备体系及控制管理系统。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档