专栏首页tarsnodejs基于Tarsnodejs快速实现云短信模块服务
原创

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

导言

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

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

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

服务端

Tars协议编写QSms.tars

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中生成对应的空方法

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 实现文件中具体的函数就可以了。

代码结构

app

  |--config(配置文件)

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

  |--utils

  main.js(入口)

  package.json

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

QSmsImpl.js内方法实现

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主入口

'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的服务配置,确保服务在执行过程中可以动态的替换配置参数,实现服务的灵活可配。


客户端

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抛出是无法得到所想要的错误信息的,还会因为内容太多而容易撑爆磁盘。

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

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 《大话计算机》之:计算的本质、bio

    根据《大话计算机》内容你点我贴一贴中所述,冬瓜哥收集了 “大话存储” 和 ”大话计算机” 两个公众号中帖子下的留言如下(蓝色表示往期已回答,红色表示本期...

    冬瓜哥
  • 碎片化 | 第五阶段-06-BUG如何去处理-视频

    如清晰度低,可转PC网页观看高清版本: http://v.qq.com/x/page/r0500j3hk3p.html BUG如何处理 测试--将问题录入bu...

    码神联盟
  • 最强的CNN视觉识别模型Residual Networks 3篇论文的31页ppt讲解-可下载

    用户1908973
  • Python 开发web服务器,返回HTML页面

    从上一个篇章的内容中已经完成了使用TCP协议返回HTTP的请求,达到一个返回数据到访问浏览器的效果。

    Devops海洋的渔夫
  • linux环境常用分析日志的几个命令

    分析日志是定位问题的常用手段,但实际线上可能有大量日志,掌握一些常见查看、过滤和分析日志的命令能起到事半功倍的效果。下面列出工作中最常用的一些命令,可在具体使用...

    用户1215536
  • think-cell chart系列6——不等宽百分比堆积柱形图

    今天跟大家分享的是think-cell chart系列6——不等宽百分比堆积柱形图,学名也叫Mekko(%)图。 ? 当然这个图同样也是之前专门讲过excel中...

    数据小磨坊
  • 可能是最全的User-Agent列表

    User-Aagent String这个网站收集了爬虫、浏览器、主机、邮件客户端等客户端的请求头的UA字段。其中浏览器的UA头有将近10000个。但是访问这个网...

    SeanCheney
  • SqlServer 循环建表、删除表、更新表

    郑小超.
  • ASP.NET MVC是如何运行的[1]: 建立在“伪”MVC框架上的Web应用

    ASP.NET由于采用了管道式设计,具有很好的扩展性,而整个ASP.NET MVC应用框架就是通过扩展ASP.NET实现的。通过上面对ASP.NET管道设计的介...

    蒋金楠
  • 30.4. 企业级开发进阶2.4:服务端快捷开发

    我们可以看到客户端程序的编写相对容易,主需要连接服务端然后跟服务端进行数据交互就OK了。但是服务端的程序编写较为复杂,如果考虑到数据的并发处理等各种问题,就更加...

    大牧莫邪

扫码关注云+社区

领取腾讯云代金券

玩转腾讯云 有奖征文活动