📡 RPC 概念与通讯原理
RPC 概念
远程过程调用(RPC,Remote Procedure Call)是一种协议,它允许程序在另一台计算机上执行过程或函数,就像是在本地计算机上执行一样。RPC 使得分布式系统中的不同计算机之间能够进行通信,从而实现资源的共享与协同工作。
在 RPC 中,客户端发起一个请求,通过网络将请求发送到服务器端。服务器端接收到请求后,执行相应的函数或过程,并将结果返回给客户端。RPC 的关键在于透明性:客户端不需要了解请求实际是如何被处理的,RPC 机制负责将请求和响应过程隐藏在网络通信的细节之下。
RPC 通讯原理
RPC 通讯包括两个主要的步骤:请求序列化和响应反序列化。以下是 RPC 通讯的基本流程:
-
请求序列化:客户端将调用方法的名称、参数等数据打包成消息格式,并通过网络发送给服务器。这一过程通常涉及数据的编码和转换,使其可以通过网络传输。
-
请求传输:客户端将序列化后的请求通过网络协议(如 HTTP、TCP 等)发送给服务器。网络层负责将数据包从客户端传输到服务器端。
-
服务器处理:服务器接收到请求后,对请求进行解码和解析,提取出方法名和参数。然后,服务器调用对应的方法,并将结果进行序列化。
-
响应传输:服务器将结果打包成响应消息,通过网络发送回客户端。
-
响应反序列化:客户端接收到响应后,对结果进行解码和反序列化,将其恢复成可用的格式。
示例代码
以下是一个简单的 JavaScript 示例,展示了 RPC 的基本通讯原理:
js
// 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/');
});
js
// 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 框架,如 grpc 或 JSON-RPC,可以简化搭建过程。以下是基于 Node.js 的 RPC 环境搭建示例:
bash
# 安装所需的 Node.js 模块
npm install express body-parser
js
// 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 服务进行集群部署。集群部署可以通过负载均衡器来实现负载均衡,并通过多台服务器来提升系统的可用性。以下是一个简单的集群部署示例:
js
// 集群部署代码
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 调试通常涉及到查看网络请求和响应。终端工具如 curl
和 Postman
可以用来手动发送 RPC 请求和查看响应。以下是使用 curl
调试 RPC 请求的示例:
bash
curl -X POST http://localhost:3000/rpc -H "Content-Type: application/json" -d '{"method": "subtract", "params": [10, 4]}'
注入技巧
注入技巧可以用来测试 RPC 接口的安全性和稳定性。以下是一个简单的示例,展示如何使用注入技巧测试 RPC 接口:
js
// 注入测试代码
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)是一款浏览器扩展,用于编写和管理用户脚本。用户脚本可以用来定制网页的显示和功能。以下是油猴开发者工具的基本使用方法:
-
安装油猴扩展:在浏览器的扩展商店中搜索并安装油猴扩展。
-
创建新脚本:点击油猴图标,选择"创建新脚本"来编写自己的用户脚本。
-
编辑脚本:在编辑器中编写 JavaScript 代码,用于修改网页行为或样式。
-
保存和运行:保存脚本并刷新网页,以查看效果。
油猴脚本注入原理
油猴脚本通过注入 JavaScript 代码到网页中来实现对网页的修改。以下是一个简单的油猴脚本示例,展示如何修改网页的标题:
js
// ==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';
})();
油猴脚本编写方式
油猴脚本的编写方式可以根据需要进行调整,以下是一些常见的脚本编写方法:
- 修改网页内容:通过 DOM 操作修改网页的 HTML 内容和样式。
- 注入外部脚本:将外部 JavaScript 文件注入到网页中,以扩展功能。
- 拦截和修改请求 :使用
XMLHttpRequest
或fetch
API 拦截和修改网络请求。
js
// ==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 接口:
js
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]);
动态传参示例
在实际应用中,动态传参可以根据不同的需求来调整请求参数。以下是一个示例,展示如何通过动态传参来实现不同的功能:
js
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 加密算法一把梭
加盐与魔改加密算法
加盐是指在加密过程中添加额外的数据来增加安全性。以下是一个加盐和魔改的加密算法示例:
js
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}`);
拓展加密算法
- MD5 加盐:
js
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}`);
- SHA-256 加盐:
js
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}`);
- HMAC 加密:
js
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}`);
- AES-128 加密:
js
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}`);
- RSA 加密:
js
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}`);