如何使用Docker构建微服务 - 服务
#教程 #python #node #microservices

大家好,欢迎回来,
今天,我们将介绍微服务应用程序的第二部分 - 单个服务的代码。
为了快速回忆起first part,我们今天要制造的服务是:

  • 产品:用于列出和搜索产品。
  • 订单:用于下订单。这是受验证服务器的保护
  • 通知:用于在发布新订单时通知用户。
  • auth:在下订单之前用于检查用户身份验证。

所以,没有更多的延迟,让我们开始

ps:此处的代码不完整,对于完整的代码,请查看Github repo


产品服务

这将是一个简单的节点,表达REST API。有一个GET端点可以列出所有产品。代码就像:

const express = require('express');
const app = express();

app.get('/', (req, res, next) => {
  const { ids } = req.query;
  let resultProducts = products;

  if (ids) {
    const productIds = ids.split(',');
    resultProducts = products.filter((p) => {
      return productIds.includes(p.id));
    }
  }
  res.status(200).json(resultProducts);
});

您会注意到我们有一个名为ids的查询参数。我们将使用它来获取所有产品的子集。

此外,为简单起见,完整的产品列表来自本地变量here


订单服务

这也是一个节点,即表示REST API,但不像产品那样简单。仍然只有一个POST端点,但是由于它做了多件事,让我们看一下代码。

首先,我们从请求主体中提取userIdproductIds,然后使用productIds从产品服务中获取更多产品详细信息(主要是价格和名称):

const { productIds, userId } = req.body;

const { data: orderedProducts } = await axios.get(
  `${PRODUCTS_APP_HOST}/?ids=${productIds.join(',')}`
);

然后,我们计算产品的订单和逗号分隔的名称列表:

let totalAmount = 0;
let orderedProductNames = '';

for (const product of orderedProducts) {
  totalAmount += product.price;
  orderedProductNames += orderedProductNames === '' ? product.name : `, ${product.name}`
}

最后,我们发布了nats-broker的消息(我们下一步将查看Notification Service),然后将响应发送给API请求。与产品服务类似,这里也没有数据库,因此我们只返回一个带有随机ID的普通JSON对象。

natsClient.sendMessage(
  NEW_ORDER_MESSAGING_CHANNEL,
  `Hello ${userId}, your order is confirmed. Products: ${orderedProductNames}`
);

res.status(201).json({
  productIds,
  totalAmount,
  userId,
  id: Math.floor(Math.random() * 9999)
});

通知服务

此服务是NATS订户,聆听发布给“新订单放置频道”的消息。目前,我们只是将传入的消息记录到控制台,但是在现实世界中,我们可以通过短信,电子邮件等通知用户。
该代码由2个函数组成,一个是消息的处理程序,另一个是启动订户的处理程序。

function handleMessages(err, message) {
  if (err) {
    console.log(`Error while reading messages: ${err}`);
    return;
  }
  console.log(`Message received on channel "${NEW_ORDER_MESSAGING_CHANNEL}"`);
  console.log(message);
}

async function main() {
  await natsClient.startup();
  natsClient.subscribe(NEW_ORDER_MESSAGING_CHANNEL, handleMessages);
}

main().catch(console.log);

nats-client代码

这是一个通用文件,可以由想要与Nats经纪人交谈的任何服务使用。

const { connect, StringCodec } = require('nats');

const NATS_BROKER_URL = process.env.NATS_BROKER_URL;

class NatsClient {
  #conn;
  #codec;

  constructor() {
    this.#codec = StringCodec();
  }

  async startup() {
    this.#conn = await connect({ servers: NATS_BROKER_URL });
    console.log(`Connected to nats broker on "${this.#conn.getServer()}"`);
  }

  sendMessage(channel, message) {
    this.#conn.publish(
      channel,
      this.#codec.encode(message)
    );
  }

  subscribe(channel, cb) {
    this.#conn.subscribe(channel, {
      callback: (err, payload) => {
        cb(err, this.#codec.decode(payload.data))
      }
    });
    console.log(`Subscribed to messaging channel "${channel}"`);
  }
}

当前,订单服务使用sendMessage方法,通知服务使用subscribe方法。


验证服务

这再次是REST API。但是,只是为了将其混合一点,它是使用FastAPI框架内置的。当前,只有一个端点可以验证Authorization标头,该标头检查是否有硬编码的令牌值。

@app.get("/verify")
def verify_token(authorization: Annotated[str | None, Header()] = None):
    if authorization != "secret-auth-token":
        return JSONResponse(
            status_code=status.HTTP_401_UNAUTHORIZED,
            content=None
        )

    return JSONResponse(
        status_code=status.HTTP_200_OK,
        content=None
    )

包起来

这就是单个服务代码的全部。在下一部分中,我们将查看root Level docker-compose.yml文件和nginx.conf文件以启动应用程序的所有组件并使事情正常工作。

我希望您会喜欢这个系列并学习新知识。

随时在下面的评论中发布任何问题。

欢呼!