前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >基于Tarsnodejs快速实现云短信模块服务

基于Tarsnodejs快速实现云短信模块服务

原创
作者头像
divewang
修改2019-09-02 11:52:45
2.8K0
修改2019-09-02 11:52:45
举报
文章被收录于专栏:tarsnodejstarsnodejs

导言

Tars 是将腾讯内部使用的微服务架构TAF(Total Application Framework)多年的实践成果总结而成的开源项目。Nodejs其js的语法对json处理的优势,可以适用于快速实现轻量级接口。

Tars-nodejs随着Tars开源之后与2018中旬一期发布,开启了Tars对于nodejs的支持。

下面以tarsnode整合腾讯云短信sdk为例,带大家掌握如何快速实现tarsnodejs的服务端与客户端。

服务端

Tars协议编写QSms.tars

代码语言:txt
复制
module QCloudSms

{

    struct HeadReq

    {

        0 require       string              requestId;//调用请求ID

    };

    //短信发送

    struct SendSmsReq

    {

        0 require       string              phoneNumber;   // 短信接收方手机号

        1 require       int                 templateId;    // 短信模板ID

        2 require       vector<string>      params;     //对应短信模板的数组值

    };

    //短信标准回包

    struct SendSmsRsp

    {

        0 require       int               result;//错误码,0 表示成功(计费依据),非 0 表示失败

        1 require       string            errmsg;//错误消息,result 非 0 时的具体错误信息

        2 require       string            ext;//用户的 session 内容,腾讯 server 回包中会原样返回

        3 require       string            sid;//本次发送标识 id,标识一次短信下发记录

        4 require       int               fee;//短信计费的条数

    };

    //请求短信验证码

    struct SendSmsCodeReq

    {

        0 require       string              phoneNumber;   // 短信接收方手机号

        1 require       int                 duration ;      // 过期时间单位分钟

    };

    //验证短信验证码

    struct SmsCodeCheckReq

    {

        0 require       string              phoneNumber;   // 短信接收方手机号

        1 require       string              vcode ;        // 验证码

    };

    //短信验证码消息回复

    struct SendSmsCodeRsp

    {

        0 require       int                  code;   // 消息码

        1 require       string               msg;    // 字符串内容

        2 require       string               data;   // 具体内容

    };

    interface SmsTars

    {

        // 发送单条基于模板的短信

        int sendSingleSmsWithTpl(HeadReq head ,SendSmsReq req, out SendSmsRsp rsp);

        //发送短信验证码

        int sendSmsCode(HeadReq head ,SendSmsCodeReq req, out SendSmsCodeRsp rsp);

        //验证短信验证码

        int checkSmsCode(HeadReq head ,SmsCodeCheckReq req, out SendSmsCodeRsp rsp);

    };

};

可以看到tars协议风格与protobuf比较类似,struct代表结构体类似pb的message,interface类似pb的service

使用tars2node将Tars IDL 定义文件转换为 JavaScript 语言所使用的版本,一般同时生成客户端及服务端,可以获得三个文件:QSms.js、QSmsImpl.js、QSmsProxy.js

QSmsImpl中生成对应的空方法

代码语言:txt
复制
QCloudSms.SmsTarsImp.prototype.sendSingleSmsWithTpl = async (current, head, req, rsp) => {

    //TODO

}

QCloudSms.SmsTarsImp.prototype.sendSmsCode = async (current, head, req, rsp) => {

    //TODO

}

QCloudSms.SmsTarsImp.prototype.checkSmsCode = async (current, head, req, rsp) => {

    //TODO

}

接下来只需要继续完善 QSmsImpl.js 实现文件中具体的函数就可以了。

代码结构

代码语言:txt
复制
app

  |--config(配置文件)

  |--tars(tars协议及实现)

  |--utils

  main.js(入口)

  package.json

在实现之前,我们先规划一下整个服务的代码结构。作为工具类微服务,我个人主张精简小巧,就像一个函数一样一目了然

QSmsImpl.js内方法实现

代码语言:txt
复制
QCloudSms.SmsTarsImp.prototype.sendSingleSmsWithTpl = async (current, head, req, rsp) => {

    const logger = new tarsLogs('TarsDate', 'sendSingleSmsWithTpl');



    const requestId = head.toObject().requestId;

    const { phoneNumber, templateId, params } = req.toObject();

    logger.debug(requestId, 'begin>>>', phoneNumber, templateId, params);

    let rst = {};



    if (!verifUtil.mobileVer(phoneNumber)) {

        rst = {

            result: 1,

            errmsg: 'invalid phoneNumber',

            ext: '',

            sid: '',

            fee: 0

        }

        rsp.readFromObject(rst);//将对象转为

        return current.sendResponse(rst.result, rsp);

    }

    try {

        rst = await smsUtils.sendSingleSms(phoneNumber, templateId, params);

        logger.debug(requestId, 'rst<<<', rst);//{"result":0,"errmsg":"OK","ext":"","sid":"8:MRasIKfs6eMBxstTmN020180910","fee":1}

        //错误码从1001~1036目前

    } catch (err) {

        logger.error(requestId, 'err<<<', err);

        rst = {

            result: -1,

            errmsg: 'tars server error see details in sendSingleSmsWithTpl.log',

            ext: '',

            sid: '',

            fee: 0

        }

    }

    rsp.readFromObject(rst);

    return current.sendResponse(rst.result, rsp);

};

函数从current之后开始,与interface中对应的参数保持一致

入参的.toObject()方法可以帮助你将内容快速转为和struct一致的json对象,清理掉其他无用的描述

出参的.readFromObject()可以帮助你将一个json内容复制进来

发送短信的具体实现被封装在了smsUtils工具类中,它主要集成了腾讯云短信nodejs版的SDK包,这里不再赘述。

main.js主入口

代码语言:txt
复制
'use strict';



const tarsLogs = require('@tars/logs');//日志

const logger = new tarsLogs('TarsDate', 'main');

const TARS = require("@tars/rpc");//RPC服务

const QCloudSms = require("./app/tars/svr/QSmsImp").QCloudSms;

const TarsConfigHepler = require('@tars/config');//加载外部配置服务

let smsConfig = require('./app/config/smsConfig');

let redisConfig = require('./app/config/redisConfig');



if (process.env.TARS\_CONFIG) {

    let tarsConfig = new TarsConfigHepler(process.env.TARS\_CONFIG);

    logger.info('tars server is starting...');

    TARS.server.getServant(process.env.TARS\_CONFIG).forEach(config => {

        let map, svr;

        map = {

            "demo.SmsSvr.SmsSvrObj": QCloudSms.SmsTarsImp,

        };

        if (map[config.servant]) {

            logger.info("Start Servant..." + config.servant);

            svr = TARS.server.createServer(map[config.servant]);

            svr.start(config);

            logger.info('tars server is started');

        } else {

            logger.info("Servant Not Exist..." + config.servant);

        }

    });



    //配置文件加载及更新

    tarsConfig.loadConfig('SmsSvr.conf', { format: tarsConfig.FORMAT.JSON }).then(data => {

        logger.info("data:", data);

        smsConfig.init = data;

    }, (err) => {

        logger.error("loadConfig SmsSvr err", err.response || err);

    });

    //配置文件加载及更新

    tarsConfig.loadConfig('redis.conf', { format: tarsConfig.FORMAT.JSON }).then(data => {

        logger.info("redis data:", data);

        redisConfig.init = data;

    }, (err) => {

        logger.error("loadConfig redis err", err.response || err);

    });



    tarsConfig.on('configPushed', (file) => {

        logger.info('configPushed', file);

        if (file === 'SmsSvr.conf') {

            tarsConfig.loadConfig(file, { format: tarsConfig.FORMAT.JSON }).then(data => {

                logger.info("SmsSvr data:", data);

                smsConfig.init = data;

            }, (err) => {

                logger.error("configPushed SmsSvr err", err.response || err);

            });

        }

        if (file === 'redis.conf') {

            tarsConfig.loadConfig(file, { format: tarsConfig.FORMAT.JSON }).then(data => {

                logger.info("redis data:", data);

                redisConfig.init = data;

            }, (err) => {

                logger.error("configPushed redis err", err.response || err);

            });

        }

    });



} else {

    logger.error('is not a online tars server');

}

作为服务端主入口,main.js判断了当前的环境是否是tars环境,并在环境变量中读取当前服务的的配置文件,尝试启动名为demo.SmsSvr.SmsSvrObj的Servant,并从当前这个服务中拉取SmsSvr.conf的服务配置,确保服务在执行过程中可以动态的替换配置参数,实现服务的灵活可配。


客户端

代码语言:txt
复制
const tarsLogs = require('@tars/logs');

const logger = new tarsLogs('TarsRotate','QcloudSmsUtils');//滚动调试日志

const Tars = require("@tars/rpc").client;

const QCloudSms = require("../tars\_proxy/QSmsProxy.js").QCloudSms;

const prx = Tars.stringToProxy(QCloudSms.SmsTarsProxy, "demo.SmsSvr.SmsSvrObj");



class QcloudSmsUtil {


    static async sendSingleSms(req, phoneNumber, templateId, params) {

        const requestId = (req && req.id) ? req.id : 'NoRequestId';

        logger.debug(requestId, 'sendSingleSms>>>', phoneNumber, templateId, params);

        try {

            let headReq = new QCloudSms.HeadReq();

            let sendSmsReq = new QCloudSms.SendSmsReq();

            headReq.requestId = requestId;

            sendSmsReq.readFromObject({

                phoneNumber: phoneNumber,

                templateId: templateId,

                params: params

            })

            let result = await prx.sendSingleSmsWithTpl(headReq, sendSmsReq);

            let rsp = result.response.arguments.rsp;

            logger.info(requestId, 'sendSingleSms<<<', "result.response.costtime:", result.response.costtime);

            logger.info(requestId, 'sendSingleSms<<<', "result.response.return:", result.response.return);

            logger.debug(requestId, 'sendSingleSms<<<', "result.response.arguments.rsp:", rsp);

            return rsp.toObject();

        } catch (err) {

            logger.debug(requestId, 'sendSingleSms>>>', phoneNumber, templateId, params);

            if (err.response) {//来自tars层面的错误

                logger.info(requestId, 'sendSingleSms<<<', "error.response.costtime:", err.response.costtime);

                logger.info(requestId, 'sendSingleSms<<<', "error.response.error.code:", err.response.error.code);

                logger.debug(requestId, 'sendSingleSms<<<', "error.response.error.message:", err.response.error.message);

                return Promise.reject(err.response.error.message);

            } else {

                logger.error(requestId, 'sendSingleSms error<<<', err.message);

                return Promise.reject(err.message);//node层面的错误

            }

        }

    }

}



module.exports = QcloudSmsUtil;

客户端声明好对应调用的proxy地址将sendSingleSmsWithTpl函数的两个入参内容传入其中,就可以实现对服务端的调用了,需要注意的是,tars调用的错误内容被存放在error.response中,故在代码里进行了一次判断,如果直接将error抛出是无法得到所想要的错误信息的,还会因为内容太多而容易撑爆磁盘。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 导言
  • 服务端
    • Tars协议编写QSms.tars
      • QSmsImpl中生成对应的空方法
        • 代码结构
          • QSmsImpl.js内方法实现
            • main.js主入口
            • 客户端
            相关产品与服务
            短信
            腾讯云短信(Short Message Service,SMS)可为广大企业级用户提供稳定可靠,安全合规的短信触达服务。用户可快速接入,调用 API / SDK 或者通过控制台即可发送,支持发送验证码、通知类短信和营销短信。国内验证短信秒级触达,99%到达率;国际/港澳台短信覆盖全球200+国家/地区,全球多服务站点,稳定可靠。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档