JS RPC详解

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: JS RPC详解

📡 RPC 概念与通讯原理

RPC 概念

远程过程调用(RPC,Remote Procedure Call)是一种协议,它允许程序在另一台计算机上执行过程或函数,就像是在本地计算机上执行一样。RPC 使得分布式系统中的不同计算机之间能够进行通信,从而实现资源的共享与协同工作。

在 RPC 中,客户端发起一个请求,通过网络将请求发送到服务器端。服务器端接收到请求后,执行相应的函数或过程,并将结果返回给客户端。RPC 的关键在于透明性:客户端不需要了解请求实际是如何被处理的,RPC 机制负责将请求和响应过程隐藏在网络通信的细节之下。

RPC 通讯原理

RPC 通讯包括两个主要的步骤:请求序列化和响应反序列化。以下是 RPC 通讯的基本流程:

  1. 请求序列化:客户端将调用方法的名称、参数等数据打包成消息格式,并通过网络发送给服务器。这一过程通常涉及数据的编码和转换,使其可以通过网络传输。
  2. 请求传输:客户端将序列化后的请求通过网络协议(如 HTTP、TCP 等)发送给服务器。网络层负责将数据包从客户端传输到服务器端。
  3. 服务器处理:服务器接收到请求后,对请求进行解码和解析,提取出方法名和参数。然后,服务器调用对应的方法,并将结果进行序列化。
  4. 响应传输:服务器将结果打包成响应消息,通过网络发送回客户端。
  5. 响应反序列化:客户端接收到响应后,对结果进行解码和反序列化,将其恢复成可用的格式。

示例代码

以下是一个简单的 JavaScript 示例,展示了 RPC 的基本通讯原理:

// RPC 服务端
const http = require('http');
const querystring = require('querystring');

// 处理 RPC 请求
const requestHandler = (req, res) => {
    let body = '';
    req.on('data', chunk => {
        body += chunk.toString();
    });
    req.on('end', () => {
        const requestData = querystring.parse(body);
        if (requestData.method === 'add') {
            const result = parseInt(requestData.param1) + parseInt(requestData.param2);
            res.end(JSON.stringify({ result }));
        } else {
            res.end(JSON.stringify({ error: 'Unknown method' }));
        }
    });
};

// 创建并启动 HTTP 服务器
const server = http.createServer(requestHandler);
server.listen(3000, () => {
    console.log('RPC server running at http://localhost:3000/');
});
// RPC 客户端
const http = require('http');
const querystring = require('querystring');

// 发送 RPC 请求
const postData = querystring.stringify({
    method: 'add',
    param1: '5',
    param2: '7'
});

const options = {
    hostname: 'localhost',
    port: 3000,
    path: '/',
    method: 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Content-Length': Buffer.byteLength(postData)
    }
};

const req = http.request(options, (res) => {
    let responseBody = '';
    res.on('data', (chunk) => {
        responseBody += chunk;
    });
    res.on('end', () => {
        console.log('Response:', responseBody);
    });
});

req.write(postData);
req.end();

🛠️ RPC 环境搭建与集群

环境搭建

在搭建 RPC 环境时,我们通常需要配置服务器和客户端,以便它们能够相互通信。在实践中,常用的技术栈包括 Node.js 和 Express,结合 RPC 框架,如 grpcJSON-RPC,可以简化搭建过程。以下是基于 Node.js 的 RPC 环境搭建示例:

# 安装所需的 Node.js 模块
npm install express body-parser
// RPC 服务端代码
const express = require('express');
const bodyParser = require('body-parser');

const app = express();
app.use(bodyParser.json());

// 定义 RPC 接口
app.post('/rpc', (req, res) => {
    const { method, params } = req.body;
    if (method === 'subtract') {
        const result = params[0] - params[1];
        res.json({ result });
    } else {
        res.json({ error: 'Unknown method' });
    }
});

// 启动服务器
app.listen(3000, () => {
    console.log('RPC server listening on http://localhost:3000');
});

集群部署

在生产环境中,为了提高系统的可靠性和性能,通常需要对 RPC 服务进行集群部署。集群部署可以通过负载均衡器来实现负载均衡,并通过多台服务器来提升系统的可用性。以下是一个简单的集群部署示例:

// 集群部署代码
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
    // Fork workers.
    for (let i = 0; i < numCPUs; i++) {
        cluster.fork();
    }

    cluster.on('exit', (worker, code, signal) => {
        console.log(`Worker ${worker.process.pid} died`);
    });
} else {
    // Workers can share any TCP connection
    http.createServer((req, res) => {
        res.writeHead(200, { 'Content-Type': 'text/plain' });
        res.end('Hello World\n');
    }).listen(8000);
}

🧩 RPC 调试技巧与工具

终端调试

RPC 调试通常涉及到查看网络请求和响应。终端工具如 curlPostman 可以用来手动发送 RPC 请求和查看响应。以下是使用 curl 调试 RPC 请求的示例:

curl -X POST http://localhost:3000/rpc -H "Content-Type: application/json" -d '{"method": "subtract", "params": [10, 4]}'

注入技巧

注入技巧可以用来测试 RPC 接口的安全性和稳定性。以下是一个简单的示例,展示如何使用注入技巧测试 RPC 接口:

// 注入测试代码
const axios = require('axios');

const testInjection = async () => {
    try {
        const response = await axios.post('http://localhost:3000/rpc', {
            method: 'subtract',
            params: ['10', '4; DROP TABLE Users;']
        });
        console.log('Response:', response.data);
    } catch (error) {
        console.error('Error:', error.message);
    }
};

testInjection();

🧑‍💻 油猴开发者工具使用与脚本注入

油猴开发者工具使用

油猴(Tampermonkey)是一款浏览器扩展,用于编写和管理用户脚本。用户脚本可以用来定制网页的显示和功能。以下是油猴开发者工具的基本使用方法:

  1. 安装油猴扩展:在浏览器的扩展商店中搜索并安装油猴扩展。
  2. 创建新脚本:点击油猴图标,选择“创建新脚本”来编写自己的用户脚本。
  3. 编辑脚本:在编辑器中编写 JavaScript 代码,用于修改网页行为或样式。
  4. 保存和运行:保存脚本并刷新网页,以查看效果。

油猴脚本注入原理

油猴脚本通过注入 JavaScript 代码到网页中来实现对网页的修改。以下是一个简单的油猴脚本示例,展示如何修改网页的标题:

// ==UserScript==
// @name         Modify Page Title
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  Modify the title of the page
// @match        *://*/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';
    document.title = 'New Page Title';
})();

油猴脚本编写方式

油猴脚本的编写方式可以根据需要进行调整,以下是一些常见的脚本编写方法:

  1. 修改网页内容:通过 DOM 操作修改网页的 HTML 内容和样式。
  2. 注入外部脚本:将外部 JavaScript 文件注入到网页中,以扩展功能。
  3. 拦截和修改请求:使用 XMLHttpRequestfetch API 拦截和修改网络请求。
// ==UserScript==
// @name         Inject External Script
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  Inject an external script into the page
// @match        *://*/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // 创建 script 元素
    const script = document.createElement('

script');
    script.src = 'https://example.com/external-script.js';
    script.onload = () => {
        console.log('External script loaded');
    };

    // 注入到网页
    document.head.appendChild(script);
})();

🔑 RPC 接口调用与动态传参

RPC 接口调用

RPC 接口调用可以通过不同的传参方式来实现灵活的接口设计。以下是一个示例,展示如何通过动态传参来调用 RPC 接口:

const axios = require('axios');

// 动态调用 RPC 接口
const callRpcMethod = async (methodName, params) => {
    try {
        const response = await axios.post('http://localhost:3000/rpc', {
            method: methodName,
            params: params
        });
        console.log('Response:', response.data);
    } catch (error) {
        console.error('Error:', error.message);
    }
};

// 调用加法方法
callRpcMethod('add', [5, 7]);

// 调用减法方法
callRpcMethod('subtract', [10, 4]);

动态传参示例

在实际应用中,动态传参可以根据不同的需求来调整请求参数。以下是一个示例,展示如何通过动态传参来实现不同的功能:

const axios = require('axios');

// 定义动态参数
const dynamicParams = {
    add: [3, 5],
    subtract: [10, 4],
    multiply: [2, 8]
};

// 动态调用方法
const executeRpcMethods = async () => {
    for (const [method, params] of Object.entries(dynamicParams)) {
        try {
            const response = await axios.post('http://localhost:3000/rpc', {
                method: method,
                params: params
            });
            console.log(`${method} Response:`, response.data);
        } catch (error) {
            console.error(`${method} Error:`, error.message);
        }
    }
};

executeRpcMethods();

🧩 RPC 加密算法一把梭

加盐与魔改加密算法

加盐是指在加密过程中添加额外的数据来增加安全性。以下是一个加盐和魔改的加密算法示例:

const crypto = require('crypto');

// 加盐加密函数
function encryptWithSalt(data, key, salt) {
    const cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(key), Buffer.from(salt));
    let encrypted = cipher.update(data, 'utf8', 'hex');
    encrypted += cipher.final('hex');
    return encrypted;
}

// 加盐解密函数
function decryptWithSalt(encryptedData, key, salt) {
    const decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(key), Buffer.from(salt));
    let decrypted = decipher.update(encryptedData, 'hex', 'utf8');
    decrypted += decipher.final('utf8');
    return decrypted;
}

// 示例
const key = 'thisisaverysecretkeythatshouldbe32chars!';
const salt = 'thisisaverysecretsalt';
const data = 'SensitiveData';
const encrypted = encryptWithSalt(data, key, salt);
const decrypted = decryptWithSalt(encrypted, key, salt);

console.log(`Encrypted: ${encrypted}`);
console.log(`Decrypted: ${decrypted}`);

拓展加密算法

  1. MD5 加盐
const crypto = require('crypto');

function md5WithSalt(data, salt) {
    return crypto.createHash('md5').update(data + salt).digest('hex');
}

const saltedMd5 = md5WithSalt('SensitiveData', 'randomSalt');
console.log(`MD5 with Salt: ${saltedMd5}`);
  1. SHA-256 加盐
const crypto = require('crypto');

function sha256WithSalt(data, salt) {
    return crypto.createHash('sha256').update(data + salt).digest('hex');
}

const saltedSha256 = sha256WithSalt('SensitiveData', 'randomSalt');
console.log(`SHA-256 with Salt: ${saltedSha256}`);
  1. HMAC 加密
const crypto = require('crypto');

function hmacEncrypt(data, key) {
    return crypto.createHmac('sha256', key).update(data).digest('hex');
}

const hmacKey = 'supersecretkey';
const hmacEncrypted = hmacEncrypt('SensitiveData', hmacKey);
console.log(`HMAC Encrypted: ${hmacEncrypted}`);
  1. AES-128 加密
const crypto = require('crypto');

function encryptAes128(data, key) {
    const cipher = crypto.createCipheriv('aes-128-cbc', Buffer.from(key), Buffer.alloc(16, 0));
    let encrypted = cipher.update(data, 'utf8', 'hex');
    encrypted += cipher.final('hex');
    return encrypted;
}

const aes128Key = 'thisis128bitkey!';
const aes128Encrypted = encryptAes128('SensitiveData', aes128Key);
console.log(`AES-128 Encrypted: ${aes128Encrypted}`);
  1. RSA 加密
const crypto = require('crypto');

// 生成 RSA 密钥对
const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
    modulusLength: 2048,
});

// RSA 加密
function rsaEncrypt(data, publicKey) {
    return crypto.publicEncrypt(publicKey, Buffer.from(data)).toString('hex');
}

// RSA 解密
function rsaDecrypt(encryptedData, privateKey) {
    return crypto.privateDecrypt(privateKey, Buffer.from(encryptedData, 'hex')).toString();
}

const rsaEncrypted = rsaEncrypt('SensitiveData', publicKey);
const rsaDecrypted = rsaDecrypt(rsaEncrypted, privateKey);

console.log(`RSA Encrypted: ${rsaEncrypted}`);
console.log(`RSA Decrypted: ${rsaDecrypted}`);
目录
相关文章
|
6月前
|
中间件
egg.js 24.17中间件配置
egg.js 24.17中间件配置
65 0
egg.js 24.17中间件配置
|
6月前
|
开发框架 JavaScript 中间件
中间件应用Koa.js(Node.js)
【5月更文挑战第3天】我们添加了两个中间件。第一个中间件记录请求的开始时间,并在下一个中间件执行完毕后计算并打印出请求的总时间。第二个中间件与之前的示例相同,它设置响应体为 "Hello World"
57 6
中间件应用Koa.js(Node.js)
|
2月前
|
JSON JavaScript 前端开发
js请求后端9
js请求后端9
34 2
|
6月前
|
JavaScript 前端开发 API
js是什么,作用是什么,特点是什么?
js是什么,作用是什么,特点是什么?
|
前端开发 JavaScript UED
为什么选择 Next.js 框架?
为什么选择 Next.js 框架?
272 0
|
JavaScript 前端开发 C#
js代码如何封装
js代码如何封装
95 0
|
Web App开发 消息中间件 监控
V8如何处理JS
Chromium本身就是一个浏览器 Chrome浏览器一般选择Chromium的稳定版本作为它的基础 浏览器大战,其实就是渲染引擎之争 v8是「JS虚拟机」的一种 源代码对 V8 来说只是「一堆字符串」 执行JS代码核心流程 1. 先编译 2. 后执行 V8采用「JIT」(Just In Time)技术提升效率
121 0
V8如何处理JS
|
JavaScript 前端开发 关系型数据库
JS - WebAPI 基础(上)
JS - WebAPI 基础(上)
147 0
JS - WebAPI 基础(上)
|
JavaScript 前端开发 Java
JS - WebAPI 基础(下)
JS - WebAPI 基础(下)
122 0
JS - WebAPI 基础(下)
|
JavaScript
js实现websocket实例
js实现websocket实例
247 0