IM项目:进阶版即时通讯项目---语音识别和验证码服务

本篇主要是对于该项目的语音识别和验证码服务模块的一个梳理,项目会直接使用部分封装好的内容,可以查看前面的文档或在本文档中进行查看和学习

由于这两个模块非常相似,所以我重点讲述的是语音识别服务,验证码服务基本可以照搬逻辑

语音识别

基本功能

客户端调用语音识别服务器,将语音消息转换为文字

模块划分

  1. 基于gflags框架进行配置文件的解析
  2. 基于spdlog进行日志输出
  3. 基于etcd的注册模块进行语音识别子服务的服务注册功能
  4. 基于brpc的RPC远程调用功能
  5. 基于百度云的SDK直接进行语音识别转换文字功能

流程图

在这里插入图片描述

实现逻辑

首先定义对应的proto文件:

syntax = "proto3";
package im;

option cc_generic_services = true;

message SpeechRecognitionReq {
    string request_id = 1;              //请求ID
    bytes speech_content = 2;           //语音数据
    optional string user_id = 3;        //用户ID
    optional string session_id = 4;     //登录会话ID -- 网关进行身份鉴权
}

message SpeechRecognitionRsp {
    string request_id = 1;                      //请求ID
    bool success = 2;                           //请求处理结果标志
    optional string errmsg = 3;                 //失败原因
    optional string recognition_result = 4;     //识别后的文字数据
}

// 语音识别Rpc服务及接口的定义
service SpeechService {
    rpc SpeechRecognition(SpeechRecognitionReq) returns (SpeechRecognitionRsp);
}

这里基本的调用逻辑就是,客户端传递来一个请求ID和语音数据,服务端返回给一个识别的数据或者失败原因,调用逻辑非常简单

下面生成proto文件,然后我们来看这个proto文件

解析proto文件

在生成的proto对应的pb文件中,有这样的字段

在这里插入图片描述
那在服务端,我们只需要对于这个服务来进行一个重写即可,这是pb文件为使用者创建好的模板,因此直接进行重写即可

void SpeechRecognition(google::protobuf::RpcController* controller,
                const ::im::SpeechRecognitionReq* request,
                ::im::SpeechRecognitionRsp* response,
                ::google::protobuf::Closure* done) 
{
    brpc::ClosureGuard rpc_guard(done);
    // 1. 取出请求中的语音数据
    // 2. 调用语音sdk模块进行语音识别,得到响应
    std::string err;
    std::string res = _asr_client->recognize(request->speech_content(), err);
    if (res.empty()) 
    {
        LOG_ERROR("{} 语音识别失败!", request->request_id());
        response->set_request_id(request->request_id());
        response->set_success(false);
        response->set_errmsg("语音识别失败:" + err);
        return;
    }
    // 3. 组织响应
    response->set_request_id(request->request_id());
    response->set_success(true);
    response->set_recognition_result(res);
}

将这个服务接口重写之后,作为服务端,下一个要做的事就是要搭建一个对应的RPC的服务器

只需要一句代码即可

_rpc_server->RunUntilAskedToQuit();

之后,由于当前的服务含有两个模块,不仅仅是包含有语音识别,还有服务注册和服务发现的模块,因此这里在外层又进行了一次封装,将服务注册和语音识别整合到了一起

class SpeechServerBuilder 
{
public:
    // 构造语音识别客户端对象
    void make_asr_object(const std::string &app_id,
        const std::string &api_key, const std::string &secret_key) 
    {
        _asr_client = std::make_shared<ASRClient>(app_id, api_key, secret_key);
    }

    // 用于构造服务注册客户端对象
    void make_reg_object(const std::string &reg_host, 
        const std::string &service_name,
        const std::string &access_host) 
    {
        _reg_client = std::make_shared<Registry>(reg_host);
        _reg_client->registry(service_name, access_host);
    }

    // 构造RPC服务器对象
    void make_rpc_server(uint16_t port, int32_t timeout, uint8_t num_threads) 
    {
        if (!_asr_client) 
        {
            LOG_ERROR("还未初始化语音识别模块!");
            abort();
        }
        _rpc_server = std::make_shared<brpc::Server>();
        SpeechServiceImpl *speech_service = new SpeechServiceImpl(_asr_client);
        int ret = _rpc_server->AddService(speech_service, 
            brpc::ServiceOwnership::SERVER_OWNS_SERVICE);
        if (ret == -1) 
        {
            LOG_ERROR("添加Rpc服务失败!");
            abort();
        }
        brpc::ServerOptions options;
        options.idle_timeout_sec = timeout;
        options.num_threads = num_threads;
        ret = _rpc_server->Start(port, &options);
        if (ret == -1) 
        {
            LOG_ERROR("服务启动失败!");
            abort();
        }
    }

    SpeechServer::ptr build() 
    {
        if (!_asr_client) 
        {
            LOG_ERROR("还未初始化语音识别模块!");
            abort();
        }
        if (!_reg_client) 
        {
            LOG_ERROR("还未初始化服务注册模块!");
            abort();
        }
        if (!_rpc_server) 
        {
            LOG_ERROR("还未初始化RPC服务器模块!");
            abort();
        }
        SpeechServer::ptr server = std::make_shared<SpeechServer>(_asr_client, _reg_client, _rpc_server);
        return server;
    }
    
private:
    ASRClient::ptr _asr_client;
    Registry::ptr _reg_client;
    std::shared_ptr<brpc::Server> _rpc_server;
};

所以在外部,想要进行调用的逻辑也很简单,直接进行这些服务的初始化,然后build建立连接即可

int main(int argc, char *argv[])
{
    // 初始化gflags宏
    google::ParseCommandLineFlags(&argc, &argv, true);
    im::init_logger(FLAGS_run_mode, FLAGS_log_file, FLAGS_log_level);

    // 构建语音服务器
    im::SpeechServerBuilder ssb;
    ssb.make_asr_object(FLAGS_app_id, FLAGS_api_key, FLAGS_secret_key);
    ssb.make_rpc_server(FLAGS_listen_port, FLAGS_rpc_timeout, FLAGS_rpc_threads);
    ssb.make_reg_object(FLAGS_registry_host, FLAGS_base_service + FLAGS_instance_name, FLAGS_access_host);
    
    // 创建服务器并启动
    auto server = ssb.build();
    server->start();
    return 0;
}

验证码服务

验证码服务这里主要是调用的是阿里云的服务,这个服务整体比较简单,主要是对于接口的熟悉,并对于一些代码进行一个封装就可以了

class DMSClient 
{
public:
    using ptr = std::shared_ptr<DMSClient>;
    DMSClient(const std::string &access_key_id,
        const std::string &access_key_secret) 
    {
        AlibabaCloud::InitializeSdk();
        AlibabaCloud::ClientConfiguration configuration( "cn-chengdu" );
        configuration.setConnectTimeout(1500);
        configuration.setReadTimeout(4000);
        AlibabaCloud::Credentials credential(access_key_id, access_key_secret);
        _client = std::make_unique<AlibabaCloud::CommonClient>(credential, configuration);
    }

    ~DMSClient() { AlibabaCloud::ShutdownSdk(); }

    bool send(const std::string &phone, const std::string &code) 
    {
        AlibabaCloud::CommonRequest request(AlibabaCloud::CommonRequest::RequestPattern::RpcPattern);
        request.setHttpMethod(AlibabaCloud::HttpRequest::Method::Post);
        request.setDomain("dysmsapi.aliyuncs.com");
        request.setVersion("2017-05-25");
        request.setQueryParameter("Action", "SendSms");
        request.setQueryParameter("SignName", "FreeIM");
        request.setQueryParameter("TemplateCode", "SMS_465324787");
        request.setQueryParameter("PhoneNumbers", phone);
        std::string param_code = "{\"code\":\"" + code + "\"}";
        request.setQueryParameter("TemplateParam", param_code);
        auto response = _client->commonResponse(request);
        if (!response.isSuccess()) 
        {
            LOG_ERROR("短信验证码请求失败:{}", response.error().errorMessage());
            return false;
        }
        return true;
    }
private:
    std::unique_ptr<AlibabaCloud::CommonClient> _client;
};
  • 12
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海绵宝宝de派小星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值