# 前言
今天我们来尝试使用腾讯云 Serverless
的事件触发器函数,并且用 Nest 来做云函数执行体.
采用 Serverless
部署项目后,由于用户每次访问才会创建程序响应,响应结束之后又自动销毁
那么如果是像诸如异步队列这类需求,就可以使用事件触发函数.
# 创建腾讯云消息队列 TDMQ
腾讯云 Serverless 目前提供了几种事件触发方式
- 定时触发
- COS 触发
- CMQ 主题订阅触发
- Ckafkac 触发
- API 网关触发
- CLS 日志触发
- MPS 触发
- CLB 触发
- TDMQ 消息队列触发
之前我们上传的项目则都是通过用户访问,即 API 网关触发的
今天我们来部署一个采用 TDMQ 消息队列触发的云函数
# 创建 Pulsar
版集群,命名空间,Topic, 订阅者
创建的东西有点多,在腾讯云后台点点鼠标就可以了,在这里可以看到价格,用量少的话基本上是免费的
调用费: 0.02 元 / 万次 (按阶梯计费)
提示:每月所有集群累计免费额度 1000 万 次
存储费: 0.0025 元 / GB / 小时
提示:每月所有集群累计免费额度 1024MB
注意的地方就是 Topic 需要创建一个 持久化
的主题
在 Topic 管理中再新增一个订阅,订阅名字可以自取
一切准备好之后还需要进入 角色管理
,针对命名空间进行授权,目的是要获取 秘钥
,将来在提交消息到队列需要用到.
# 创建新的云函数,并设置触发器为 TDMQ
# 创建云函数
我们创建一个简单的云函数,用以监听刚才创建的 Topic, 有消息来我们就发送一封邮件
首先写一个简单的,有消息过来我们就 console
打印出来
创建文件夹 sender
, 里面只有 2 个文件, index.js
和 serverless.yml
$ mkdir sender | |
$ touch ./sender/index.js | |
$ touch ./sender/serverless.yml |
index.js 内容很简单
'use strict'; | |
exports.main_handler = async (event, context) => { | |
console.log("Hello World") | |
console.log(event) | |
console.log(context) | |
return event | |
}; |
serverless.yml
app: sender | |
component: scf | |
name: sender | |
stage: dev | |
inputs: | |
description: 发送邮件 | |
eip: false | |
ignoreTriggers: true | |
handler: index.main_handler | |
initTimeout: 65 | |
memorySize: 128 | |
msgTTL: 21600 | |
name: sender | |
namespace: default | |
publicAccess: true | |
region: ap-shanghai | |
retryNum: 2 | |
runtime: Nodejs16.13 | |
src: | |
exclude: | |
- .env | |
- node_modules | |
src: ./ | |
timeout: 3 | |
type: event | |
vpcConfig: | |
subnetId: subnet-asy3z93z | |
vpcId: vpc-b5776770 |
这里先不创建触发器 ( ignoreTriggers: true
), 因为使用 Serverless.yml
创建不了 TDMQ 触发器
创建 package.json
和 .env
(之前的文章有所说明)
随后执行 npm run deploy
$ npm run deploy | |
> sender@1.0.0 deploy | |
> sls deploy | |
serverless ⚡tencent | |
Action: "deploy" - Stage: "dev" - App: "sender" - Name: "sender" | |
code: | |
bucket: sls-cloudfunction-ap-shanghai-code | |
object: /scf_component_i2594ce-1661844005.zip | |
description: 发送邮件 | |
functionName: sender | |
handler: index.main_handler | |
lastVersion: $LATEST | |
memorySize: 128 | |
namespace: default | |
runtime: Nodejs16.13 | |
sourceCodeDownloadUrl: https://sls-app-code-prod-1300862921.cos.ap-guangzhou.myqcloud.com/1252902543%2Fdev%2Fsender%2Fsender%2Fsource.zip?sign=q-sign-algorithm%3Dsha1%26q-ak%3DAKIDfBS1NUcuMcgVSTqzRhiX6lBUbY4KeLXW%26q-sign-time%3D1661844013%3B1661847613%26q-key-time%3D1661844013%3B1661847613%26q-header-list%3Dhost%26q-url-param-list%3D%26q-signature%3D1b65218fa54d616971a3ea4a1f13ce1971e6fc18 | |
traffic: 1 | |
triggers: (empty array) | |
type: event | |
应用控制台: https://serverless.cloud.tencent.com/apps/sender/sender/dev | |
13s »sender» 执行成功 |
部署成功之后需要在后台创建触发器
# 绑定触发器
进入函数服务 - 选择 sender 云函数 - 触发管理 点击创建触发器
在这里选择刚才创建的 Topic 和订阅消费者
# 测试事件触发云函数
可以先来测试一下,进入到 Topic 管理界面,点击发送消息
进入云函数日志管理可以查看相关日志
日志: | |
START RequestId: 32341b89-2836-11ed-a7ab-525400a75f9f | |
Hello World | |
{ | |
data: { | |
msgBody: '{"user":"Mr.adam","email":"1015517471@qq.com"}', | |
msgId: '\b���[\x10\x00\x18\x01 \x00', | |
subscriptionName: 'sendToMy', | |
tags: '', | |
timestamp: 1661844875242, | |
topic: 'persistent://pulsar-jm4bd43m2jab/sendmail/sendmail', | |
topicType: 2 | |
}, | |
datacontenttype: 'application/json;charset=utf-8', | |
id: '8efd64c4-09f5-4673-acfd-94224522638e', | |
region: 'ap-shanghai', | |
source: 'tdmq.cloud.tencent', | |
specversion: '1.0', | |
status: '', | |
subject: 'qcs::tdmq:ap-shanghai:uin/1015517471:subscriptionName/pulsar-jm4bd43m2jab/sendmail/sendmail/sendToMy', | |
tags: null, | |
time: 1661844875277, | |
type: 'connector:tdmq' | |
} | |
{ | |
succeed: [Function: succeed], | |
fail: [Function: fail], | |
callbackWaitsForEmptyEventLoop: [Getter/Setter], | |
getRemainingTimeInMillis: [Function: getRemainingTimeInMillis], | |
memory_limit_in_mb: 128, | |
time_limit_in_ms: 3000, | |
request_id: '32341b89-2836-11ed-a7ab-525400a75f9f', | |
environment: '{"SCF_NAMESPACE":"default"}', | |
environ: 'SCF_NAMESPACE=default;SCF_NAMESPACE=default', | |
function_version: '$LATEST', | |
function_name: 'sender', | |
namespace: 'default', | |
tencentcloud_region: 'ap-shanghai', | |
tencentcloud_appid: '1252902543', | |
tencentcloud_uin: '1015517471' | |
} |
我们刚刚发送的内容正确地被我们接收到了!
# 部署 Nest 独立应用
如果只是单纯发送一封邮件,单个 js 文件或许可以满足需求,但是往往我们的任务会很复杂,比如连接数据库或消息队列,记录日志等操作,这样如果把所有东西都放到一个文件里执行就有点麻烦了,所以需要一个框架来帮我们处理这一类问题,该框架不用监听任何端口,只需要它执行便可,执行完自动销毁
刚好 Nest 框架可以满足这部分需求,接下来修改 sender
云函数为一个 Nest 框架,并且完成发送邮件的相关模块吧
# 安装 Nest 及相关模块
安装一个新的 Nest 项目
$ nest new sender |
进入 sender 目录中,安装使用 Nest 发送邮件的库
$ npm install nestjs-mailer nodemailer -S | |
$ npm install @types/nodemailer -D |
# 创建 mailer
模块
$ nest g mo mailer | |
$ nest g s mailer |
# 修改 main.ts
目前项目是事件驱动,所以我们的项目不需要监听任何端口,执行其中的方法即可
import { NestFactory } from '@nestjs/core'; | |
import { AppModule } from './app.module'; | |
import { MailerService } from "./mailer/mailer.service"; | |
export default async function bootstrap(body:{user:string,email:string}) { | |
// 这里使用 createApplicationContext 加载 AppModule | |
const app = await NestFactory.createApplicationContext(AppModule); | |
const mailerService = app.get(MailerService); | |
await mailerService.sendMail(body); | |
// 这里进行销毁实例操作 | |
await app.close(); | |
} |
# 集成 nestjs-mailer
在 MailerModule
中集成 nestjs-mailer
配置
这里使用我的 QQ 邮箱 SMTP 来发送邮件
import { Module } from '@nestjs/common'; | |
import { MailerModule as Mailer } from 'nestjs-mailer'; | |
import { MailerService } from './mailer.service'; | |
@Module({ | |
imports:[ | |
Mailer.forRoot({ | |
config: { | |
transport: { | |
host: 'smtp.qq.com', | |
port: 465, | |
secure: true, | |
auth: { | |
user: '1015517471@qq.com', | |
pass: 'xxxxxxx', | |
} | |
}, | |
defaults: { | |
from: '"Mr.adam" <1015517471@qq.com>', | |
}, | |
}, | |
}), | |
], | |
providers: [MailerService] | |
}) | |
export class MailerModule {} |
mailer.service.ts
import { Injectable } from '@nestjs/common'; | |
import { InjectMailer, Mailer, template } from 'nestjs-mailer'; | |
@Injectable() | |
export class MailerService { | |
constructor( | |
@InjectMailer() private readonly mailer: Mailer, | |
) {} | |
// 发送邮件 | |
async sendMail(body){ | |
this.mailer.sendMail({ | |
from: '"Mr.adam" <1015517471@qq.com>', | |
to: body.email, | |
subject: 'Hello ✔', | |
text: `Hello ${body.user},This is a test mail!` | |
}).catch(e => console.log(e)); | |
console.log("发送成功"); | |
} | |
} |
index.js
文件是事件函数的入口文件,我们也要响应做出修改,让它初始化 nest 框架,并且把 event.data.msgBody
传输过去
'use strict'; | |
const bootstrap = require("./dist/main.js"); | |
exports.main_handler = async (event, context) => { | |
await bootstrap.default(JSON.parse(event.data.msgBody)); | |
return event | |
}; |
现在编写一个 test.js
测试一下
首先执行 npm run build
打包 nest 项目
//test.js | |
const {main_handler} = require("./index.js"); | |
const data = { | |
data: { | |
msgBody: '{"user":"Mr.adam","email":"1015517471@qq.com"}', | |
} | |
}; | |
main_handler(data,"context"); |
本地先执行一下测试脚本
$ node .\test.js | |
[Nest] 13404 - 2022/08/30 17:03:30 LOG [NestFactory] Starting Nest application... | |
[Nest] 13404 - 2022/08/30 17:03:30 LOG [InstanceLoader] MailerModule dependencies initialized +28ms | |
[Nest] 13404 - 2022/08/30 17:03:30 LOG [InstanceLoader] MailerCoreModule dependencies initialized +0ms | |
[Nest] 13404 - 2022/08/30 17:03:30 LOG [InstanceLoader] MailerModule dependencies initialized +0ms | |
[Nest] 13404 - 2022/08/30 17:03:30 LOG [InstanceLoader] AppModule dependencies initialized +1ms | |
object | |
发送成功 |
OK, 成功收到邮件!
# 部署云函数
# 部署 layer
之前的文章有详细讲过,这段就略过了...
### 修改 serverless.yml
app: sender | |
component: scf | |
name: sender | |
stage: dev | |
inputs: | |
description: 发送邮件 | |
eip: false | |
ignoreTriggers: true | |
handler: index.main_handler | |
initTimeout: 65 | |
memorySize: 128 | |
msgTTL: 21600 | |
name: sender | |
namespace: default | |
publicAccess: true | |
region: ap-shanghai | |
retryNum: 2 | |
runtime: Nodejs16.13 | |
layers: | |
- name: sender-layer | |
version: 1 | |
src: | |
dist: ./ | |
exclude: | |
- .env | |
- node_modules | |
- layer | |
src: ./ | |
timeout: 3 | |
type: event | |
vpcConfig: | |
subnetId: subnet-asy3z93z | |
vpcId: vpc-b5776770 |
# 执行部署
部署之前修改下 package.json
$ npm run deploy | |
serverless ⚡tencent | |
Action: "deploy" - Stage: "dev" - App: "sender" - Name: "sender" | |
code: | |
bucket: sls-cloudfunction-ap-shanghai-code | |
object: /scf_component_9syxhj-1661851211.zip | |
description: 发送邮件 | |
functionName: sender | |
handler: index.main_handler | |
lastVersion: $LATEST | |
layers: | |
- | |
name: sender-layer | |
version: 1 | |
memorySize: 128 | |
namespace: default | |
runtime: Nodejs16.13 | |
sourceCodeDownloadUrl: https://sls-app-code-prod-1300862921.cos.ap-guangzhou.myqcloud.com/1252902543%2Fdev%2Fsender%2Fsender%2Fsource.zip?sign=q-sign-algorithm%3Dsha1%26q-ak%3DAKIDfBS1NUcuMcgVSTqzRhiX6lBUbY4KeLXW%26q-sign-time%3D1661851223%3B1661854823%26q-key-time%3D1661851223%3B1661854823%26q-header-list%3Dhost%26q-url-param-list%3D%26q-signature%3D1b65289dfdde661673d9a010e54b8e90b229657c | |
traffic: 1 | |
triggers: (empty array) | |
type: event | |
应用控制台: https://serverless.cloud.tencent.com/apps/sender/sender/dev | |
15s »sender» 执行成功 |
部署完成我们可以再到 Topic 管理发送一条消息测试一下,结果也是成功收到邮件!
至此,我们使用 Nest 独立应用接收到了来自 TDMQ 的通知消息
如何通过 TDMQ 发送消息则见下篇博文!