在微服務架構中,SpringCloud,Eureka,Dubbo,ZooKeeper這些框架再熟悉不過了,其中面向接口的遠程方法調用是其主要核心功能之一,而MQ主要用來應用解耦,削峯填谷等作用;最近在RabbitMq官網上看到,竟然還支持RPC調用,處於好奇,動手用js跑了一遍。
共三個文件:RpcUtil.js, server.js,client.js
client.js
const { RpcUtil } = require('./rpcUtil');
const rpcUtil = new RpcUtil(userName, password, host, port);
rpcUtil.init().then(() => {
setInterval(() => {
for (let i = 0; i < 100; i++) {
rpcUtil.sendMsg(rpcUtil.generateUuid(), {
method,
params,
}, result => {
console.log(i + '<<>>' + JSON.stringify(result))
});
}
}, 3000);
}).catch(e => console.log(e));
server.js
class DealerMsgRpcUtil extends RpcUtil {
// @Override
async dealerMsg(msgContent) {
msgContent = JSON.parse(msgContent);
const { method, params } = msgContent;
let result;
try {
// 這裏調用各種方法
if (method === 'listMember') {
result = await foo(params);
} else {
result = { code: -1, msg: '找不到對應方法' };
}
} catch (e) {
result = { code: -1, msg: e.message };
}
return result;
}
}
const rpcUtil = new DealerMsgRpcUtil(rabbitMQAccount, rabbitMQPassword, proxy_host, port);
rpcUtil.init().then(() => {
rpcUtil.listenMsg();
}).catch(e => LOG(e));
RpcUtil.js
const amqp = require('amqplib/callback_api');
class RpcUtil {
constructor(rabbitMQAccount, rabbitMQPassword, rabbitMQHost, rabbitMQPort) {
this.connection;
this.channel;
this.rpcQueueServer = 'rpc_queue_server';
this.rpcQueueClient = 'rpc_queue_client';
this.correlationIdPool = new Map();
this.amqpUrl = 'amqp://' + rabbitMQAccount + ':' + rabbitMQPassword + '@' + rabbitMQHost + ':' + rabbitMQPort;
}
// 創建通道
async _createChannel() {
this.channel = await new Promise(resolve => {
this.connection.createChannel((err, channel) => {
resolve(channel);
});
});
}
// 發送端生成correlationId
generateUuid() {
return Math.random().toString() +
Math.random().toString() +
Math.random().toString();
}
// 初始化
async init() {
await new Promise((resolve, reject) => {
amqp.connect(this.amqpUrl, (error, conn) => {
if (error) {
reject(error);
} else {
this.connection = conn;
resolve();
}
});
});
await this._createChannel();
}
// 被調用端,子類需要重寫
async dealerMsg(msgContent) {
msgContent = JSON.parse(msgContent);
return { code: 200, msg: '', data: msgContent };
}
// 被調用端監聽消息
async listenMsg() {
this.channel.assertQueue(this.rpcQueueServer, { durable: false });
this.channel.prefetch(1);
this.channel.consume(this.rpcQueueServer, async msg => {
const msgContent = msg.content.toString();
let dealerResult;
try {
dealerResult = await this.dealerMsg(msgContent);
} catch (e) {
dealerResult = { code: -1, msg: e.message };
}
this.channel.sendToQueue(msg.properties.replyTo, Buffer.from(JSON.stringify(dealerResult)), {
correlationId: msg.properties.correlationId,
});
}, { noAck: true });
}
// 調用端發送消息
async sendMsg(correlationId, params, cb) {
const q = await new Promise(resolve => {
this.channel.assertQueue(this.rpcQueueClient, { exclusive: true }, (e, q) => {
resolve(q);
});
});
this.correlationIdPool.set(correlationId, cb);
this.channel.consume(q.queue, msg => {
if (this.correlationIdPool.has(msg.properties.correlationId)) {
const cb = this.correlationIdPool.get(msg.properties.correlationId);
cb(msg.content.toString());
this.correlationIdPool.delete(msg.properties.correlationId);
}
}, { noAck: true });
this.channel.sendToQueue(this.rpcQueueServer, Buffer.from(JSON.stringify(params)), {
correlationId,
replyTo: q.queue,
});
}
}
exports.RpcUtil = RpcUtil;
以上代碼僅學習用,生產環境不建議mq去實現rpc。