为您的 dApp 构建微服务
原文:https://docs.elrond.com/developers/tutorials/your-first-microservice
让我们为你的去中心化应用建立一个微服务
重要
本指南扩展了 在 15 分钟内建立一个 dApp指南,请在遵循本指南之前遵循本指南。
我们将在 Devnet 上工作,所以你应该在这里创建和管理一个网络钱包。
该指南还提供视频格式:
https://www.youtube-nocookie.com/embed/pd-vSIiw6Us
乒乓微服务
本指南扩展了我们在之前的指南 中构建的分散式应用,在 15 分钟内构建一个 dApp。如果到目前为止你还没有做到,请现在就做。
在本指南中,我们将构建一个微服务(一个 API),它是区块链层和应用程序层之间的中间层。我们的应用程序将使用这种微服务,而不是直接在区块链上发出请求。
缓存
在我们的指南中,该微服务的目的是缓存来自区块链(例如get_time_to_pong
)的值,因此每个后续请求都将从我们的微服务中快速获得结果。
交易处理器
我们还将在 pong 交易完成时使缓存无效。这意味着微服务将监听区块链上所有将我们的智能合约地址作为接收方的pong
交易,并且一旦一个交易被确认,我们将使对应于发送方钱包地址的缓存记录无效。
微服务
我们将使用基于 nestjs 的微服务模板,缓存将使用 redis 完成,因此本指南的先决条件是:nodejs、npm 和 redis。
我们将扩展“在 15 分钟内构建一个 dApp”指南,因此让我们在现有的文件夹结构上进行构建,并将微服务创建到父项目文件夹的子文件夹中:
先决条件
在我们开始之前,我们将确保redis-server
已经安装并且正在我们的开发服务器上运行。
sudo apt install redis-server
或者,我们可以将 redis-server 后台化,这样它将在后台运行。
redis-server --daemonize yes
我们希望确保 redis 正在运行,因此如果我们运行:
ps aux | grep redis
然后,我们将会看到这样的日志行:
/usr/bin/redis-server 127.0.0.1:6379
微服务
好了,从微服务开始吧。首先,我们将克隆Elrond团队提供的模板。
git clone https://github.com/ElrondNetwork/ping-pong-microservice microservice
cd microservice
ls -l
让我们来看看应用程序的结构:config
-在这里我们将设置乒乓智能合约地址src/crons
-在这里定义交易处理器src/endpoints
-在这里我们将找到/ping-pong/time-to-pong/<address>
端点的代码
我们会找到一个配置文件,专门用于我们想要部署微服务的每个网络。在我们的指南中,我们将使用 devnet 配置,它可以在以下位置找到:
~ping-pong/microservice/config/config.devnet.yaml
首先,我们将配置 redis 服务器 url。如果我们在同一台机器上(或者在我们的开发机器上)运行 redis-server,那么我们可以保留默认值。
现在,我们将继续讨论智能合约地址。我们可以在我们的dapp
资源库中找到它(如果我们遵循之前的指南“在 15 分钟内构建一个 dApp”)。如果您没有在 devnet 上部署智能合约,那么我们建议您先遵循前面的指南,然后再回到这一步。
用智能合约地址的值设置contracts.pingPong
键,我们就完成了微服务的配置。
启动微服务
我们将使用 npm 安装依赖项
npm install
然后我们将为 devnet 启动微服务:
npm run start:devnet
现在,我们在端口 3001 上启动了微服务。让我们确定它的网址。默认的 url 是http://localhost:3001
,但是如果您在不同的机器上运行分散的应用程序,那么我们应该使用http://<ip>:3001
。
重温《你的第一次 dApp》
现在是时候告诉 dApp 使用微服务,而不是直接从区块链读取值了。首先,我们将在 dApp 配置文件src/config.devnet.tsx
中设置微服务 URL:我们将添加:
export const microserviceAddress =
"http://<ip>:3001/ping-pong/time-to-pong/";
接下来,我们希望从使用 vm 查询切换到使用我们新创建的微服务。获取 pong 时间的请求在src/pages/Dashboard/Actions/index.tsx
中完成。
我们将更改虚拟机查询代码:
React.useEffect(() => {
const query = new Query({
address: new Address(contractAddress),
func: new ContractFunction("getTimeToPong"),
args: [new AddressValue(new Address(address))],
});
dapp.proxy
.queryContract(query)
.then(({ returnData }) => {
const [encoded] = returnData;
switch (encoded) {
case undefined:
setHasPing(true);
break;
case "":
setSecondsLeft(0);
setHasPing(false);
break;
default: {
const decoded = Buffer.from(encoded, "base64").toString("hex");
setSecondsLeft(parseInt(decoded, 16));
setHasPing(false);
break;
}
}
})
.catch((err) => {
console.error("Unable to call VM query", err);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
转换成一个通用的 HTTP 请求(在我们的例子中我们使用axios
):
React.useEffect(() => {
axios
.get(`${microserviceAddress}${address}`)
.then(({ data }) => {
const { status, timeToPong } = data;
switch (status) {
case "not_yet_pinged":
setHasPing(true);
break;
case "awaiting_pong":
setSecondsLeft(timeToPong);
setHasPing(false);
break;
}
})
.catch((err) => {
console.error("Unable to call microservice", err);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
当然,不要忘记管理所需的导入(axios
和我们之前在配置文件config.devnet.tsx
中定义的微服务地址)。
import axios from "axios";
import { contractAddress, microserviceAddress } from "config";
我们现在可以保存 index.tsx,让我们再运行一次分散式应用程序。
npm run start
现在,我们可以验证在仪表板上我们仍然有倒计时,并且 Pong 按钮显示正常。我们可以多次刷新应用程序,首先应用程序将从区块链获取值(时间到pong
秒)。然后缓存该值,所有后续查询都将从缓存中读取该值。
您还可以在我们的公共存储库上的分支microservice
中找到 dApp 的完整代码:
https://github.com/ElrondNetwork/dapp-template/blob/microservice/src/pages/Dashboard/Actions/index.tsx
让我们深入研究微服务代码,并解释我们实现的 2 个基本特性。
我们希望最大限度地减少直接在区块链上完成的请求数量,因为它们会产生开销,因此我们将首先从区块链读取时间 to pong
,我们将缓存该值,所有后续读取都将从缓存中完成。这个值不会随着时间而改变。它只会在我们pong
之后重置。
缓存
所以缓存部分是在
ping-pong/microservice/src/endpoints/ping.pong/ping.pong.controller.ts
它使用
ping-pong/microservice/src/endpoints/ping.pong/ping.pong.service.ts
在ping.pong.service.ts
的第 16 行,函数getTimeToPong
返回用户可以使用pong
的秒数。
async getTimeToPong(address: Address): Promise<{ status: string, timeToPong?: number }> {
在第 17 行我们调用了this.getPongDeadline
,它将在第 33 行设置缓存中的返回值
return await this.cachingService.getOrSetCache(
`pong:${address}`,
async () => await this.getPongDeadlineRaw(address),
Constants.oneMinute() * 10,
);
函数this.getPongDeadlineRaw
将调用区块链上的唯一读取动作,然后this.cachingService.getOrSetCache
将它设置在缓存中。
交易处理器
在用户点击Pong
按钮并执行pong
交易后,我们必须使缓存无效,并且我们将使用交易处理器来识别区块链上的所有pong
交易,这些交易将接收器设置为我们的智能合约地址。
让我们在这里看一下交易处理器源文件:
~/ping-pong/microservice/src/crons/transaction.processor.cron.ts
在第 23 行,我们将实现async handleNewTransactions()
函数,它有一个有趣的事件:onTransactionsReceived
。每当在区块链上确认新的交易时,都会执行该事件,并提供一组交易作为参数。我们将在该数组中查找接收者等于我们的智能合约地址并且数据字段应该是pong
(如智能合约中所定义)的交易。
if (transaction.receiver === this.apiConfigService.getPingPongContract() && transaction.data) {
let dataDecoded = Buffer.from(transaction.data, 'base64').toString();
if (['ping', 'pong'].includes(dataDecoded)) {
await this.cachingService.deleteInCache(`pong:${transaction.sender}`);
}
}
如果我们找到一个,我们将使键pong:<wallet address>
的缓存数据无效,在那里我们先前存储了到达 pong 值的时间。为此我们将使用this.cachingService.deleteInCache
函数。
结论
这就是全部,我们创建了一个微服务以使我们的 dApp 更快和可扩展。这是一个通用的分散式应用架构,本指南中的大多数例子都是我们一些高可用性和大量使用的产品的起点。现在,我们为您提供一个起点,以便构建您的想法和项目。
下一步何去何从?
分解本指南,了解有关如何扩展微服务、dapp 和智能合约的更多信息。点击https://docs.elrond.com在Elrond官方文档网站了解更多信息。
如果你有任何问题,请在这里使用 stackoverflow 提问:https://stackoverflow.com/questions/tagged/elrond。
如果你觉得这个指南有用,请分享。