asgc-data-fix是傲世孤尘开源的一个基于node的数据修复框架。
- 使用简单:只需简单的配置即可使用。
- 例程丰富:对于本框架的使用,文档中提供了详细的示例。
- 容易扩展:支持插件扩展。
- 纯SQL方式
- 不支持跨库操作
- 对于修复数据逻辑稍微复杂的场景不太友好
- 写在Java项目中
- 若依赖Spring容器则可能启动缓慢,增加不必要的开销
- 若使用业务层DO对象,则耦合太深。若单独定义则过于繁琐
- 基于node,使用js语法编写,门槛低
- node原生支持json,db或其他操作结果无需映射转换
- asgc-data-fix内置mysql、mongo插件,编写脚本时只用专注于脚本逻辑,DB连接实例打开/关闭无需关心
- asgc-data-fix支持插件扩展,可根据需要替换/扩展功能
- 所有示例程序均随框架一起发布,因此安装完成之后可直接阅读源码及附带的示例
- 所有示例程序依赖的配置(如:数据库连接配置)文件已做脱敏处理,原配置文件不会发布。
如:脚本依赖
config.json文件,其中敏感信息已做处理。示例程序中的config.private.json文件不会发布。因此示例程序无法直接运行,需要自行修改。
npm install asgc-data-fix@1.0.4
对于数据库连接信息已做脱敏处理,需要自行修改
{
"dev": {
"chat": {
"type": "mongo",
"host": "***",
"port": 27017,
"userName": "***",
"password": "***",
"database": "wechat"
},
"test1": {
"type": "mysql",
"host": "***",
"port": 3306,
"userName": "***",
"password": "***",
"database": "test1"
}
},
}
// 引入依赖
let fix = require('asgc-data-fix');
/**
* 启动过脚本
* 1、第一个参数dev代表脚本执行环境为"dev",对应于配置文件中的dev部分
* 2、第二个参数是一个数组,数组前面几个参数代表需要的dev环境中的test1、chat实例,这里是mysql、mongo连接实例
* 3、数组最后一个参数是一个回调函数,是用户脚本的执行入口,回调函数的参数即前面声明的test1、chat实例
* 4、函数的async修饰根据需要使用,不需要时可以不写,此处需要配合await做异步转同步
* 5、会调函数执行开始前,框架会自动初始化服务实例(如打开数据库连接)。执行完毕后,框架会自动关闭服务实例(如关闭数据库连接)。
*/
fix.startup('dev', ['test1', 'chat', async function(test1DB, chatDB){
console.log('==========================mysql操作示例==========================');
// 新增
let insertResult = await test1DB.execute(`
insert into stu(id,name) values
(1, '张三'),
(2, '李四'),
(3, '王五')
`);
console.log('insertResult', insertResult);
// 查询列表
console.log('list', await test1DB.find(`select * from stu`));
// 更新
await test1DB.execute(`
update stu set name = '张三111' where id = 1
`);
// 查询单条
console.log('stu', await test1DB.findOne(`select * from stu where id = 1`));
// 删除
let deleteResult = await test1DB.execute(`delete from stu`);
console.log('deleteResult', deleteResult);
console.log('==========================mongo操作示例==========================');
// 新增
let insertWeChatInfoResult = await chatDB.insertOne('WeChatInfo', {
_id:'weChatId1',
weChatId: 'wxid_trufcrgkcnwr22',
nickName: '自相矛盾'
});
console.log('insertWeChatInfoResult', insertWeChatInfoResult.result);
// 查询
let weChatInfoList = await chatDB.find('WeChatInfo', {});
console.log('weChatInfoList', weChatInfoList);
// 删除
await chatDB.delete('WeChatInfo');
}]);
load config finished.
load plugins finished. [ 'mongo', 'mysql' ]
======================脚本执行开始
默认环境: dev
======================服务:test1开启成功!
======================服务:chat开启成功!
==========================mysql操作示例==========================
insertResult OkPacket {
fieldCount: 0,
affectedRows: 3,
insertId: 3,
serverStatus: 2,
warningCount: 0,
message: '&Records: 3 Duplicates: 0 Warnings: 0',
protocol41: true,
changedRows: 0
}
list [
RowDataPacket { id: 1, name: '张三' },
RowDataPacket { id: 2, name: '李四' },
RowDataPacket { id: 3, name: '王五' }
]
stu RowDataPacket { id: 1, name: '张三111' }
deleteResult OkPacket {
fieldCount: 0,
affectedRows: 3,
insertId: 0,
serverStatus: 34,
warningCount: 0,
message: '',
protocol41: true,
changedRows: 0
}
==========================mongo操作示例==========================
insertWeChatInfoResult { n: 1, ok: 1 }
weChatInfoList [
{
_id: 'weChatId1',
weChatId: 'wxid_trufcrgkcnwr22',
nickName: '自相矛盾'
}
]
======================服务:test1关闭.
======================服务:chat关闭.
======================脚本执行结束
如下所示:配置文件采用json格式
- dev、uat代表环境,可根据需要自行定义,当脚本在dev环境执行时,将取dev节点下面的配置
- test1、chat代表服务实例别名
- type,目前asgc-data-fix内置插件只包含mysql与mongo,type为mysql代表test1为mysql连接实例。若自定义插件,type可设置为自定义插件名
- 其他,除了type以外其他参数由插件自己定义,如下数据库连接参数是内置插件所需要的。若自定义插件,可根据需要定义相关配置项
{
// 环境配置
"dev": {
// 实例配置
"test1": {
"type": "mysql",
"host": "***",
"port": 3306,
"userName": "***",
"password": "***",
"database": "test1"
},
"chat": {
"type": "mongo",
"host": "***",
"port": 27017,
"userName": "***",
"password": "***",
"database": "test1"
}
},
"uat": {
}
}
fix.loadConfig()可指定配置文件路径进行加载,需要在fix.startup()执行之前,支持相对路径和绝对路径fix.loadConfig()传入路径可以不包含文件扩展名.json。当不包含扩展名时(如:myconfig),会先加载myconfig.json文件,然后加载myconfig.private.json文件,且优先使用后者的实例配置- 默认情况下,若不主动调用
fix.loadConfig()加载配置,直接fix.startup()启动脚本时,相当于调了fix.loadConfig('config').startup(),之后再执行脚本 - 多次调用
fix.loadConfig(),以最后一次加载的配置为准。
startup方法第一个参数传入一个字符串,可作为脚本默认执行环境
fix.startup('dev', ['test1',async function(test1DB){
}]);
startup方法第一个参数若直接省略,直接传入一个数组参数的情况下,脚本没有默认执行环境
fix.startup(['dev:test1','uat:chat',async function(test1DB){
}]);
startup方法数组参数除了最后一个参数为回调方法外,前面几个参数用于声明所需要的服务实例。如上脚本,需要dev环境的test1实例、uat环境的chat实例- 脚本声明的服务实例,依赖的环境优先取
dev:test1这种方式指定的环境,若此处未指定则会取默认环境 - 脚本声明的服务实例必须指定环境,若未指定则执行会报错
open打开mysql数据库连接,在执行脚本前,框架会自动创建mysql实例,调用open方法close关闭mysql数据库连接,在执行脚本结束后,框架会自动调用该实例的close方法execute执行sqlfindOne可用execute取代,但当查询结果确定最多只有一个时,可用此方法,避免每次取数组第一个
open打开mongodb数据库连接,在执行脚本前,框架会自动创建mongodb实例,调用open方法close关闭mongodb数据库连接,在执行脚本结束后,框架会自动调用该实例的close方法create创建一个表(mongodb中称之为集合)findOne查询一条记录findPage查询分页find查询列表count查询数据条数findAndModify查询并且更新,可指定返回更新之前或更新之后的数据insertOne新增单条记录insert批量新增updateOne更新单条记录update批量更新deleteOne删除单条记录delete批量删除drop删除表collections获取数据库下所有表对象collectionNames获取数据库下所有表名aggregate聚合操作ensureIndex新增索引indexInformation获取索引信息dropIndex删除索引
-
执行脚本前,框架会自动加载内置插件(mysql、mongo)。紧接着会加载脚本同级别目录下的插件(脚本同级别下的plugins目录)
-
加载插件时,若插件目录不存在,或插件目录下不存在
可加载的的插件,将直接忽略 -
插件的三要素
- 一个插件对应于插件目录下一个文件夹
- 插件文件夹下必须存在
index.js文件 - 插件文件夹下的
index.js中必须导出一个create方法
-
只要自定义插件满足插件三要素定义,就可以正常在我们脚本中使用了。
-
需要注意的是,插件三要素只规定了插件必须导出
create方法,并不要求提供open、close方法,若插件不涉及资源的打开及释放,可以不用提供。 -
插件目录结构示例:
--|plugins
--|myplugin
--|index.js
- 插件
index.js内容示例
具体内容可参见
asgc-data-fix/test/plugin-lab1
class MyPlugin {
constructor(options) {
console.log('MyPlugin create...', options);
this.name = options.name || '';
}
open(){
console.log('MyPlugin open...');
}
close(){
console.log('MyPlugin close...');
}
say(msg){
console.log(`MyPlugin sayHello ${this.name} ${msg}`);
}
}
module.exports = {
create: function(options) {
return new MyPlugin(options);
}
}
此类需求可能跨多个库,但涉及环境只有一个,因此可采用如下形式
fix.startup('dev', ['customer', 'employee', async function(customerDB, employeeDB){
// TODO
}]);
此类需求可用于数据备份,或将生产环境数据同步到测试环境,可在测试环境使用生产环境数据测试。这种情况跨环境,可采用如下形式
fix.startup(['pro:customer', 'uat:customer', async function(proCustomerDB, uatEmployeeDB){
// TODO
}]);
- 可使用
child_processfork多进程来实现多线程的效果。(参见示例程序asgc-data-fix/test/other/child_process)- 虽然进程数过多,有点浪费资源,但绝大多数情况下用这个足够了
- 父进程向子进程传递参数
可能需要转为json字符串,子进程需要还原为json对象,此处可能麻烦一丢丢 - Node.js v14.16.1 child_process文档
- 从Node.js的child_process模块来学习父子进程之间的通信
- (推荐)可使用
worker_threads实现多线程。(参见示例程序asgc-data-fix/test/other/worker_threads)- Node.js V10.5.0 开始引入
- 运行过程中不会产生多个进程
- 父进程向子进程传第参数,子进程获取参数没有上述问题
- Node.js v14.16.1 work_threads文档
- 深入理解 Node.js Worker Threads
- 理解Node.js中的"多线程"