Electron 集成 Express + p-limit + SQlite WAL读写模式解决并发锁库的问题

news2024/11/15 10:48:23

背景

经过通信层面的优化后,我们不再走 Electron 提供的内置进程间通信 IPC,改为利用 Express 提供的 Http 本地服务来进行多处直达通信机制,同时利用本地 Sqlite 来保存大量数据,但 Express 提供的本地服务是支持并发请求的,而 Sqlite 是不支持行锁的机制,一旦有写入操作,Sqlite 都是直接锁库,除了采用单表单库减少锁库问题外,另外就是走队列的方式来逐个入库,避免写锁问题。

一个Sqlite3教程好文档,分享到这里:函数sqlite3VdbeHalt | SQlite源码分析

解决方案

启用 WAL 模式

WAL 模式即将写的数据暂存在 WAL 文件中,不影响主库,这样就可以避开库锁问题,同时读也可以并行操作,大大提高了 Sqlite 读写并行能力

export const userDataPath = app.getPath('userData')
const storagePath = path.join(userDataPath, '/sqlite/wa_verify.db')

// 创建 Sequelize 实例
export const sequelize = new Sequelize({
    dialect: 'sqlite',
    storage: storagePath,
    define: {
        freezeTableName: true
    },
    logging: false
})

// 启用 WAL 模式
(async () => {
    try {
        await sequelize.authenticate()
        await sequelize.query('PRAGMA journal_mode=WAL;')
        console.log('WAL mode enabled.')
    } catch (error) {
        console.error('Unable to enable WAL mode:', error)
    }
})()

Expess 层面加限流

p-limit 是个好东西,这个直接可控制请求的并发数,如果想搞成队列机制,直接设为 1 即可,省去了自己写队列的烦恼,另外我也第一次发现异步开发的优越性,写个队列也非常简单,而同步开发就没这么方便,必须分为两个进程来搞事情,一个写入队列,一个弹出队列,但是如何保证本地 http 返回结果就很难了,而异步可以一直等待着。

import express from 'express';
import pLimit from 'p-limit';

const app = express();
const port = 3000;

const limit = pLimit(1); // 限制并发请求为 1

app.get('/car', (req, res) => {
    const startTime = Date.now();

    limit(() => new Promise((resolve) => {
        setTimeout(() => {
            const endTime = Date.now();
            const processingTime = endTime - startTime;

            res.json({
                message: '车信息处理完成',
                startTime: new Date(startTime).toISOString(),
                endTime: new Date(endTime).toISOString(),
                processingTime: `${processingTime}ms`
            });

            resolve();
        }, 3000); // 模拟处理时间
    }));
});

app.listen(port, () => {
    console.log(`服务器正在监听 http://localhost:${port}`);
});

客户端测试 Express 并发脚本

import fetch from 'node-fetch';

const url = 'http://localhost:3000/car'; // 你的服务地址
const concurrentRequests = 10; // 请求数

async function sendRequest() {
    const startTime = Date.now();

    try {
        const response = await fetch(url);
        const data = await response.json();
        const endTime = Date.now();

        console.log('响应数据:', data);
        console.log(`请求开始时间: ${data.startTime}`);
        console.log(`请求结束时间: ${data.endTime}`);
        console.log(`处理时间: ${data.processingTime}`);
        console.log(`单个请求的处理时间: ${endTime - startTime}ms`);
    } catch (error) {
        console.error('发生错误:', error);
    }
}

async function testConcurrency() {
    for (let i = 0; i < concurrentRequests; i++) {
        console.log(`发起请求 ${i + 1}...`);
        sendRequest(); // 逐个发送请求,等待每个请求完成
    }
}

testConcurrency();

请求时间结果截图,明显串行执行,完美!

同一个limit可以作为多个请求限流队列来用

服务端代码

服务端代码这里要注意limit要框住整个接口的处理逻辑,这样才能保证整个接口逻辑都处理完毕后,才会处理下一个请求;

res.json({}) 返回一定要跟在limit的逻辑里面,不然就会出现客户端一请求,服务端就返回OK了, 而实际上现在的请求并没有得到处理,还好Express里的p-limit并没有像其他语言,如PHP,Python等一旦连接结束,其相关的线程都全部释放,这也许是协程调度的好处。

注意服务端语言务必都要使用async  + await 来保证代码的同步执行,如果没有同步作为基础,发生任何不可预测的BUG都有可能

import express from 'express';
import pLimit from 'p-limit';

const app = express();
const port = 3000;

// 设置并发限制的数量
const maxConcurrentRequests = 3; // 限制并发请求为 3
const limit = pLimit(maxConcurrentRequests);

function createHandler(responseMessage) {
    return (req, res) => {
        const startTime = Date.now();

        limit(() => new Promise((resolve) => {
            setTimeout(() => {
                const endTime = Date.now();
                const processingTime = endTime - startTime;

                res.json({
                    message: responseMessage,
                    startTime: new Date(startTime).toISOString(),
                    endTime: new Date(endTime).toISOString(),
                    processingTime: `${processingTime}ms`
                });

                resolve();
            }, 3000); // 模拟处理时间
        }));
    };
}

app.get('/car', createHandler('车信息处理完成'));
app.get('/bus', createHandler('公交信息处理完成'));

app.listen(port, () => {
    console.log(`服务器正在监听 http://localhost:${port}`);
});
客户端测试代码
import fetch from 'node-fetch';

const carUrl = 'http://localhost:3000/car'; // 车信息服务地址
const busUrl = 'http://localhost:3000/bus'; // 公交信息服务地址
const concurrentRequests = 10; // 请求数

async function sendRequest(url, route) {
    const startTime = Date.now();

    try {
        const response = await fetch(url);
        const data = await response.json();
        const endTime = Date.now();

        console.log(`${route} - 响应数据:`, data);
        console.log(`${route} - 请求开始时间: ${data.startTime}`);
        console.log(`${route} - 请求结束时间: ${data.endTime}`);
        console.log(`${route} - 处理时间: ${data.processingTime}`);
        console.log(`${route} - 单个请求的处理时间: ${endTime - startTime}ms`);
    } catch (error) {
        console.error(`${route} - 发生错误:`, error);
    }
}

async function testConcurrency() {
    console.log(`开始发起 ${concurrentRequests} 个请求到 /car 路由...`);
    for (let i = 0; i < concurrentRequests; i++) {
        console.log(`发起 /car 请求 ${i + 1}...`);
        sendRequest(carUrl, '/car'); // 逐个发送请求到 /car 路由
    }

    console.log(`开始发起 ${concurrentRequests} 个请求到 /bus 路由...`);
    for (let i = 0; i < concurrentRequests; i++) {
        console.log(`发起 /bus 请求 ${i + 1}...`);
        sendRequest(busUrl, '/bus'); // 逐个发送请求到 /bus 路由
    }
}

testConcurrency();

测试结果截图

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2047271.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

食品零食小吃商城管理系统-计算机毕设Java|springboot实战项目

&#x1f34a;作者&#xff1a;计算机毕设残哥 &#x1f34a;简介&#xff1a;毕业后就一直专业从事计算机软件程序开发&#xff0c;至今也有8年工作经验。擅长Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等。 擅长&#xff1a;按照需求定制化开发项目、 源…

DataX二次开发之达梦数据库插件

达梦数据库自定义插件 达梦8的依赖引入定义reader module定义writer module修改核心配置数据库类型支持打包插件测试 以mysql到dm数据库为例配置mysql2dm.json执行任务查询下结果 DataX二次开发之达梦数据库插件 DataX 是阿里巴巴集团内被广泛使用的离线数据同步工具/平台,支持…

eNSP 华为远程登录路由器

华为远程登录路由器 前提&#xff1a;主机能与路由器通信就行&#xff0c;如果不同网段就配路由协议&#xff0c;这里直接模拟直连通信 Cloud&#xff1a; R&#xff1a; <Huawei>sys [Huawei]sys R [R]int g0/0/0 [R-GigabitEthernet0/0/0] [R-GigabitEthernet0/0/0]i…

AQS 原理详解

日常开发中&#xff0c;我们经常使用锁或者其他同步器来控制并发&#xff0c;那么它们的基础框架是什么呢&#xff1f;如何实现的同步功能呢&#xff1f;本文将详细讲解构建锁和同步器的基础框架--AQS&#xff0c;并根据源码分析其原理。 一、什么是 AQS&#xff1f; (一) AQS…

Oracle+ASM+High冗余详解及空间计算

Oracle ASM&#xff08;Automatic Storage Management&#xff09;的High冗余模式是一种提供高度数据保护的策略&#xff0c;它通过创建多个数据副本来确保数据的可用性和安全性。 以下是关于Oracle ASM High冗余的详细解释&#xff1a; 一、High冗余的特点 1.数据冗余度 在Hi…

Java | Leetcode Java题解之第342题4的幂

题目: 题解&#xff1a; class Solution {public boolean isPowerOfFour(int n) {return n > 0 && (n & (n - 1)) 0 && n % 3 1;} }

【Datawhale AI夏令营第四期】 魔搭-大模型应用开发方向笔记 Task03 大咖项目分享 人话八股文Bakwaan_Buddy项目开发尝试

【Datawhale AI夏令营第四期】 魔搭-大模型应用开发方向笔记 Task03 人话八股文Bakwaan_Buddy项目开发尝试 Idea: 我们草台班子目前的想法是解决大家计算机学院毕业面临的BUG——不爱背、背不下来八股文&#xff0c;觉得枯燥、烦、工作了用不着&#xff0c;反正就是知识他不进…

Python酷库之旅-第三方库Pandas(085)

目录 一、用法精讲 356、pandas.Series.str.isnumeric方法 356-1、语法 356-2、参数 356-3、功能 356-4、返回值 356-5、说明 356-6、用法 356-6-1、数据准备 356-6-2、代码示例 356-6-3、结果输出 357、pandas.Series.str.isdecimal方法 357-1、语法 357-2、参数…

RabbitMQ的核心概念

RabbitMQ是一个消息中间件&#xff0c;也是一个生产者消费者模型&#xff0c;负责接收&#xff0c;存储和转发消息。 核心概念 Producer 生产者&#xff0c;是RabbitMQ Server的客户端&#xff0c;向RabbitMQ发送消息。 Consumer 消费者&#xff0c;是RabbitMQ Server的客…

Ps:首选项 - 单位与标尺

Ps菜单&#xff1a;编辑/首选项 Edit/Preferences 快捷键&#xff1a;Ctrl K Photoshop 首选项中的“单位与标尺” Units & Rulers选项卡允许用户根据工作需求定制 Photoshop 的测量单位和标尺显示方式。这对于保持工作的一致性和精确性&#xff0c;尤其是在跨设备或跨平台…

mybatis plus 查询部分源码分析,typehandler怎么实现的?FastjsonTypehandler 查询问题怎么解决?

我们在使用mysql的json字段的时候有时为了方便&#xff0c;最好是查询的时候直接反序列化为对象比较好&#xff0c;这时候我们就用到了typehandler这个属性 首先mybatis plus 会初始化一系列的 typeHandler,并且扫描用户设置的typeHandler路径&#xff08;mybatis-plus: type-…

Flutter-->AAPT: error: resource android:attr/lStar not found.

更新Flutter 3.24.0之后, 打包出现AAPT: error: resource android:attr/lStar not found.问题, 这里出一个我的解决方案. 更新Flutter 3.24.0之后, Android编译sdk需要使用34, 否则就会出现很多问题… 由于很多库都不可能及时更新适配到Android sdk 34, 所以可以等pub get将子…

硅谷物理服务器有哪些关键优势和特点

硅谷的物理服务器设施全球知名&#xff0c;为各类企业提供了卓越的IT基础设施支持。下面将逐一探讨硅谷物理服务器的关键优势和特点&#xff0c;rak小编为您整理发布硅谷物理服务器有哪些关键优势和特点。 1. 卓越的性能 高性能计算能力&#xff1a;硅谷的物理服务器采用最新一…

Authentik:开源身份提供商

Authentik 是一个开源身份提供商&#xff0c;旨在实现最大的灵活性和适应性。 它可轻松集成到现有环境中并支持新协议。 它是一个全面的解决方案&#xff0c;用于在您的应用程序中实现注册、帐户恢复等功能&#xff0c;无需手动管理这些任务。 Authentik 可以无缝集成到现有…

arcgis打开不同tif格式编码的栅格数据

1、如下图&#xff0c;将文件包包解压打开&#xff0c;看到【2020年GDP数据】。 2、点击进入【2020年GDP数据】文件夹如下图所示。接着去打开arcgis软件。 3、按照步骤来&#xff0c;在arcgis【目录】里面添加【文件夹】然后选中你刚刚解压的【GDP文件夹数据】&#xff0c;最…

21 注意力机制—自注意力

目录 1.自注意力和位置编码跟CNN,RNN对比位置编码(position encoding)1、和 CNN / RNN 不同,自注意力并没有记录位置信息2、为了使用序列的顺序信息,通过在输入表示中添加位置编码将位置信息注入到输入里3、P 的元素具体计算如下:位置编码矩阵绝对位置信息相对位置信息总…

Linux运维篇-yum命令报错 /lib64/libcurl.so.4相关

目录 项目场景&#xff1a;问题描述原因分析&#xff1a;解决方案&#xff1a; 项目场景&#xff1a; centos7&#xff0c;8&#xff0c;同样也适用openEuer&#xff0c;Kylin等redhat系的国产化操作系统 问题描述 在使用yum命令时报错&#xff1a; 主要报错信息为&#xff1…

诈骗未成功是否构成犯罪?

诈骗未成功不一定构成犯罪。在刑法上&#xff0c;构成诈骗罪需要满足特定的构成要件&#xff0c;包括有非法占有的目的、实施了虚构事实或隐瞒真相的行为、对方因此陷入错误认识并处分财产、行为人或第三方取得财产、被害人遭受财产损失。如果诈骗行为未能成功&#xff0c;即被…

[C#]基于winform结合photocartoon算法实现人物卡通化源码实现

【官方框架】 https://github.com/minivision-ai/photo2cartoon 简介 人像卡通风格渲染的目标是&#xff0c;在保持原图像ID信息和纹理细节的同时&#xff0c;将真实照片转换为卡通风格的非真实感图像。我们的思路是&#xff0c;从大量照片/卡通数据中习得照片到卡通画的映射…

HDRP管线下的开放世界游戏与跨平台优化,《仙剑世界》万字分享

《仙剑世界》作为仙剑 IP 系列的最新⻓篇⼒作&#xff0c;从故事和剧情上延续了仙剑的精髓。在仙剑 33 年的世界观下&#xff0c;《仙剑世界》打造出了⼀个由浪漫唯美的江南全景、磅礴恢弘的蜀⼭、神秘苗疆等区域构成的 384 平⽅公⾥完整的⽆缝开放⼤世界。以东⽅题材为起点&am…