nodejs定时轮询数据库“排队消费”

目录可直接阅读核心部分, 其他内容为辅助

五、 核心部分

零、脚本中方法调用流水线概括

一、 方法解释:脚本中依赖包引入与脚本运行入口

二、 方法解释:远程接口调用

三、 方法解释:抽象sql公共部分

四、 方法解释:格式化打印日志logformat

六、 方法解释:脚本中使用的nodejs依赖

七、 总结


零、脚本中方法调用流水线概括

  • ★处标明序号

    1. start(); # 入口启动
    function start() {
    	setInterval(()=>{3. init(); # 获取待消费数据方法
    	}, 4000);2. impl(); # 第二个setInterval定时调用方法
    }
    
    async function init() {
    	if(libproc) {
    		return;
    	}
    	sharesql =4.getsharesql(); # 获取公共sql的方法(有多处使用了相同的sql字段)
    	logformat('重复执行start')
    	libproc = true;5.sumtody(); # 获取分页总数
    }
    
    async  function sumtody() {
       let sumsql = `SELECT  ...nt(*) as sum ${sharesql}`;
       mysql_pool.query(sumsql, function(err, rows, fields){
            sum = rows.shift().sum;
       	logformat(getNowDateTime()+'. sum:',sum);6.handlepage(sum); # 具体分页处理内容(其中包括了分页期间轮询等待)
       });
    }
    
    async function handlepage(sum) {
    	let totalPage = Math.floor(sum/pageSize);
    	let remain = sum % pageSize;
    	if(remain > 0) {
    		totalPage += 1;
    	}
    	for(let i=1; i<=totalPage; i++) {
                    let sql = `SELECT  b.stu_se... limit ?, ?`;
     	        let startcount = (i-1)*pageSize;
    		...
    		mysql_pool.query(sql, [(i-1)*pageSize, pageSize], function(err, rows, fields){
    			if(err) {
    				logformat(err);
    			}7.pagequeue = rows; # 塞入数组等待另一个setInterval消费
    		});
    		await sleep(1000);// 等待mysql异步查询结果
    		while(pagequeue.length == 0) {
    		   await sleep(1000);
    		}
    		while(pagequeue.length > 0) {
    		   await sleep(1000);
    		}
    	}8.libproc = false; # 修改标识, 第二个setInterval方法中对标识做判断
    
    }
    
    function impl() {
    	setInterval(()=>{9.if(pagequeue.length > 0) { # 这里是第二个setInterval方法, 如果数组队列中有值则进行消费
    				★9.1 let item = pagequeue.shift(); # 取出数组中的元素
    				logformat('patient_id:', item.patient_id, 'reg_id:', item.reg_id, 'orgCode:', ...lm_orgCode);
    				var jsonobj = {
    					regId: item.reg_id,
    					orgCode: ...lm_orgCode
    					};
    				var ...gCount  = 0;
    				var ...gFilePath  = "";
    				var sqlstr = `selec... `
    					...s.query(sqlstr, [item.reg_id], (err, result) => {
    						...10. handleSyncFlag(jsonobj, ...ImgCount); # 处理业务逻辑
    					});
    		}
    	},2000);
    
    }
    function handleSyncFlag(jsonobj, ...unt) {
       request.post(`${zycloud...ceAddress}/get...Count`).send(jsonobj).end(function (err, resp) {
    	... 
    	let ...Count = resp.body.data;
    	logformat('...像数量:',...gCount,'本地图片数量:',...Count);   
    	if(...mgCount != ...gCount) {11.updat...yncFlag(jsonobj); # 处理业务逻辑           	
    	} 
    
      }); 
    }
    
    
一、脚本中依赖包引入与脚本运行入口
  • 介绍使用setInterval+标识判断模拟队列消费(此处获取的队列值为现查询数据库获取需要处理的业务数据)
  • 引入moment依赖。进行时间格式化操作sql拼接
  • 引入superagent依赖。进行远程接口调用接口调用
    var moment = require('moment');
    var request = require('superagent')
    
    // 启动入口
    start();
    function start() {
    	setInterval(()=>{
    		init();
    	}, 4000);
    	impl();
    }
    async function init() {
    	if(libproc) {
    		return;
    	}
    	sharesql = getsharesql();
    	logformat('重复执行start')
    	libproc = true;
    	sumtody();
    }
    ...
    
二、 远程接口调用。
  • 接口调用, 处理返回值

    function handleSyncFlag(jsonobj, p...ImgCount) {
       request.post(`${z...fil...ress}/ge...ount`).send(jsonobj).end(function (err, resp) {
    	if(err) {
    		logformat('getrea...ount.err:', err);
    		return;
    	}   
    	logformat('接口返回:'+JSON.stringify(resp.body));
    	if (resp.body.success == false) {
    		logformat('success.faluse:', JSON.stringify(resp.body)); 
    		return;
    	}   
    	let ...ImgCount = resp.body.data;
    	logformat('...数量:',...imgCount,'本地图片数量:',p...ImgCount);   
    	if(...ImgCount != p...ImgCount) {
    		updateP...SyncFlag(jsonobj);	            	
    	} 
    
      }); 
    }
    
三、抽象sql公共部分getsharesql,
  • 避免sql查询语句冗余, 并进行测试test()

    function getsharesql() {
    // var time = '2023-12-20';
     var time = moment().format('YYYY-MM-DD');
     logformat('time:', time);
     let sharesql = ` FROM registra...tu_time BETWEEN CONCAT('${time}',' 00:00:00') AND CONCAT('${time}',' 23:59:59') and st...m != '' `;
     return sharesql;
    }
    function test() {
    	setInterval(()=>{
    		var a = `'${getsharesql()}'`; 
    		var b = a + `limit ?`;
    		logformat(b);
    	}, 1000);
    }
    
四、格式化打印日志logformat
  • 获取格式化时间戳moment().format('YYYY-MM-DD HH:mm:ss')
  • 使用语法拼接sql
    `update reg...ud=2,sync_time='${moment().format('YYYY-MM-DD HH:mm:ss')}' where reg_id=${jsonobj.regId}`;
    
    function update...cFlag(jsonobj) {
        var sqlArr = new Array();
        sqlArr.push(`update reg...ud=2,sync_time='${moment().format('YYYY-MM-DD HH:mm:ss')}' where reg_id=${jsonobj.regId};`);
        var sql = sqlArr.join('');
        mysql_pool.query(sql, function (err, rows, fields) {
    	if (err) {
    	    logformat('更新...nc_cloud=2 报错. err' + err);
    	    return;
    	}
    	logformat('更新... 成功. reg_id:' + jsonobj.regId);
        })
    }
    function logformat(... msgstr) {
        var msgobj = {
            "orgCode": z...film_orgCode,
            "orgName": z...film_orgName,
            "msg": msgstr,
            "time": moment().format('YYYY-MM-DD HH:mm:ss')
        }
        console.log("msgstr:", msgstr);
    }
    
五. 核心部分。
  • 轮询等待消费完成, 再继续进行下一次循环。
    • 若条件为真则一直循环等待, 等待第二个setInterval消费完成pagequeue中的数据。
      while(pagequeue.length == 0), while(pagequeue.length > 0) 
      
  • libproc 进行标识更新进行handlepage方法的被重新调用
    async function handlepage(sum) {
    	let totalPage = Math.floor(sum/pageSize);
    	let remain = sum % pageSize;
    	if(remain > 0) {
    		totalPage += 1;
    	}
    	for(let i=1; i<=totalPage; i++) {
                    let sql = `SELECT  b.stu_se....stu_time ${sharesql}  order by a.reg_id asc limit ?, ?`;
     	        let startcount = (i-1)*pageSize;
    		logformat('startcount:',startcount,'pagesize:',pageSize)
    		mysql_pool.query(sql, [(i-1)*pageSize, pageSize], function(err, rows, fields){
    			if(err) {
    				logformat(err);
    			}
    			pagequeue = rows;
    		});
    		await sleep(1000);// 等待mysql异步查询结果
    		while(pagequeue.length == 0) {
    		   await sleep(1000);
    		}
    		while(pagequeue.length > 0) {
    		   await sleep(1000);
    		}
    	}
    	libproc = false;
    
    }
    
  • 等待第二个setInterval消费完成pagequeue中的数据
    function impl() {
    	setInterval(()=>{
    	    if(pagequeue.length > 0) { # 这里是第二个setInterval方法, 如果数组队列中有值则进行消费
    				let item = pagequeue.shift(); # 取出数组中的元素
    				logformat('patient_id:', item.patient_id, 'reg_id:', item.reg_id, 'orgCode:', ...lm_orgCode);
    				var jsonobj = {
    					regId: item.reg_id,
    					orgCode: ...lm_orgCode
    					};
    				var ...gCount  = 0;
    				var ...gFilePath  = "";
    				var sqlstr = `selec... `
    					...s.query(sqlstr, [item.reg_id], (err, result) => {
    						...
    						handleSyncFlag(jsonobj, ...ImgCount); # 处理业务逻辑
    					});
    		}
    	},2000);
    
    }
    
六、脚本中使用的nodejs依赖 package.json
```js
{
  "name": "z...-client",
  "version": "1.0.0",
  "description": "",
  "main": "main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "...",
  "license": "ISC",
  "dependencies": {
    "body-parser": "^1.19.0",
    "child_process": "^1.0.2",
    "cors": "^2.8.5",
    "dicom-parser": "^1.8.21",
    "express": "^4.17.1",
    "fs": "0.0.1-security",
    "http": "0.0.1-security",
    "iconv-lite": "^0.6.3",
    "ioredis": "^5.3.2",
    "md5": "^2.3.0",
    "moment": "^2.29.1",
    "mssql": "^6.0.1",
    "mysql": "^2.18.1",
    "node-schedule": "^2.0.0",
    "path": "^0.12.7",
    "pg": "^8.6.0",
    "redis": "^3.1.2",
    "socket.io-client": "^2.2.0",
    "superagent": "^6.1.0",
    "uuid": "^9.0.0"
  }
}

```

七、总结

脚本中使用 ... 处理了敏感信息

  1. 两次调用setInterval方法进行队列消费
  2. for循环中使用while等待
  3. 使用标识判断setInterval中的业务处理顺序
  4. 使用了moment、superagen、依赖处理时间字符串格式与远程接口调用,使用nodejs语法灵活拼接字符串。
  5. 使用数组作为消费队列,并使用shift数组方法获取最先进入队列的元素(第一个元素)
  6. 第一次使用的setInterval时长 大于 第二次的setInterval。第一次为4000ms, 第二次2000ms。这样做是因为可以加快队列的消费速度。

你可能感兴趣的:(数据库,node.js,js,json)