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

news2024/9/20 10:47:08

文章目录

  • 语音识别
    • 基本功能
    • 模块划分
    • 流程图
    • 实现逻辑
    • 解析proto文件
  • 验证码服务

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

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

语音识别

基本功能

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

模块划分

  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;
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2073356.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

模型 卡茨管理模型

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。技术、人际、概念&#xff1a;管理技能三支柱。 1 卡茨管理模型的应用 1.1 产品经理能力模型&#xff1a;基于卡茨管理模型的应用 在现代企业管理中&#xff0c;产品经理的角色越来越受到重视。根据…

HTML简单了解和基础知识记录

参考视频 html的用途 超文本标记语言&#xff08;英语&#xff1a;HyperText Markup Language&#xff0c;简称&#xff1a;HTML&#xff09;&#xff0c;用来显示网页的文字和框架结构&#xff0c;可以认为是网页的骨架。 标签/元素 用于定义文字图片连接等&#xff0c;分…

超简单Java邮件发送-为你的小程序实现邮箱验证码登录

本项目基于Apache Commons Email for Jakarta开发。 超简单实现邮箱发送&#xff0c;并以此扩展出邮箱验证码功能(请读者扩展实现)。 构建工具&#xff1a;Maven 演示邮箱&#xff1a;outlook 准备 请确保你的邮箱开启了SMTP服务&#xff0c;并获取相关参数&#xff0c;类似…

51单片机——模块化编程

1、模块化编程介绍 传统方式编程&#xff1a;所有的函数均放在main.c里&#xff0c;若使用的模块比较多&#xff0c;则一个文件内会有很多的代码&#xff0c;不利于代码的组织和管理&#xff0c;而且很影响编程者的思路。 模块化编程&#xff1a;把各个模块的代码放在不同的.…

诊断知识:agedDTCLimit的使用

文章目录 前言agedDTCLimit的定义agedDTCLimit的使用图解agedDTCLimit总结 前言 ECU中的存储空间一般无法将所有的DTC同时记录故障数据&#xff0c;所以故障发生之后记录的数据&#xff0c;需要在一段时间后未出现故障则自动清除&#xff0c;以保证新的故障出现时&#xff0c;…

苍穹外卖-day05(SpringBoot+SSM的企业级Java项目实战)

苍穹外卖-day05 课程内容 Redis入门 Redis数据类型 Redis常用命令 在Java中操作Redis 店铺营业状态设置 功能实现&#xff1a;营业状态设置 效果图&#xff1a; 选择营业中&#xff0c;客户可在小程序端下单&#xff1a; 选择打烊中&#xff0c;客户无法在小程序端下单&…

【Docker】Docker学习04 | dockerfile的编写

本文首发于 ❄️慕雪的寒舍 dockerfile是构建docker镜像的基础&#xff0c;它规定了一系列语法&#xff0c;让我们可以在某个基础镜像之上&#xff0c;添加自己需要的操作&#xff0c;打包出一个自己的镜像。 1. dockerfile基本语法 下面是dockerfile的基本语法和其对应的功能…

【算法进阶2-动态规划】斐波那契数列(递归调用、动态规划)、钢条切割问题(自定而下实现、自底向上、切割方案)

1 斐波那契数 2 钢条切割问题 2.1 最优解情况 2.2 钢条切割问题之自定而下实现 2.3 钢条切割问题之自底向上实现 2.4 钢条切割问题-重构解-切割方案 1 斐波那契数 # 1 子问题的重复计算 def fibonacci(n: int) -> int:"""使用递归方式计算第 n 个斐波那契数…

计算机毕业设计选题推荐-法律援助平台-Java/Python项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

有限差分学习笔记

有限差分介绍 ​ 在数学中&#xff0c;有限差分法&#xff08;finite-difference methods&#xff0c;简称FDM&#xff09;&#xff0c;是一种微分方程数值方法&#xff0c;是通过有限差分来近似导数&#xff0c;从而寻求微分方程的近似解。 由泰勒展开式的推导 显式方…

给Go+Sciter开发的桌面客户端软件添加系统托盘图标

在桌面端软件开发中&#xff0c;系统托盘图标是提升用户体验的重要元素。托盘图标不仅能提供直观的状态反馈&#xff0c;还能让软件在后台运行时依然保持与用户的交互。通过托盘图标&#xff0c;用户可以轻松最小化软件、退出程序&#xff0c;甚至弹出通知&#xff0c;从而避免…

【海外EI 会议合集】电网系统/绿色能源/新材料主题均可

第五届电网系统与绿色能源国际学术会议&#xff08;PGSGE 2025&#xff09; 2025 5th International Conference on Power Grid Systems and Green Energy 重要信息 会议官网&#xff1a;www.pgsge.org 会议时间&#xff1a;2025年1月10-12日 会议地点&#xff1a;马来西亚…

Linux 部署 MinIO(远程服务器)

1. 下载安装 进入 Linux 内 cd /usr/local/ # 新建目录 mkdir minio # 进入目录 cd minio # 下载地址 wget https://dl.min.io/server/minio/release/linux-amd64/minio# 授权 chmod x minio 2. 自定义配置 自定义账号与登录密码&#xff0c;直接在本目录 默认登录账号和…

【吊打面试官系列-Memcached面试题】memcached 能够更有效地使用内存吗?

大家好&#xff0c;我是锋哥。今天分享关于 【memcached 能够更有效地使用内存吗&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; memcached 能够更有效地使用内存吗&#xff1f; Memcache 客户端仅根据哈希算法来决定将某个 key 存储在哪个节点上&#xff0c;而…

ES的介绍和使用

全文搜索引擎 Elastic Search 第一节 引言 当系统数据量上了10亿、100亿条的时候&#xff0c;我们用什么数据库好&#xff1f;如何解决单点故障&#xff1f;如何提升检索速度&#xff1f;如何解决统计分析问题&#xff1f; 传统数据库的应对解决方案 关系型数据库 通过主从备…

后端Web之登录校验(下篇)

目录 1.概述 ​2.过滤器Fliter 3.拦截器Interceptor 1.概述 Filter过滤器&#xff1a;在Web开发中&#xff0c;过滤器&#xff08;Filter&#xff09;是一种非常重要的组件&#xff0c;用于在请求到达目标资源&#xff08;如Servlet或静态资源&#xff09;之前或之后&#…

10、Redis高级:多级缓存、JVM进程缓存、OpenResty本地缓存、缓存同步Canal

多级缓存 0.学习目标 1.什么是多级缓存 传统的缓存策略一般是请求到达Tomcat后&#xff0c;先查询Redis&#xff0c;如果未命中则查询数据库&#xff0c;如图&#xff1a; 存在下面的问题&#xff1a; •请求要经过Tomcat处理&#xff0c;Tomcat的性能成为整个系统的瓶颈 …

Earth‘s Future | 西南大学时伟宇团队揭示长江上游径流变化对气候变化与人类活动响应的驱动机制不同

本文首发于“生态学者”微信公众号&#xff01; 径流是全球水循环的重要组成部分&#xff0c;对社会经济发展、维持农业生产和维护生态安全具有重要意义。自20世纪末&#xff0c;气候变化与人类活动双重加剧&#xff0c;长江上游径流变化对长江上游乃至长江流域具有重要影响。因…

SSRF以及CSRF

ssrf 服务端请求伪造&#xff1a;由于服务端提供了从其他服务器应用获取数据的功能&#xff0c;但又没有对目标地址做严格过滤与限制&#xff0c;导致攻击者可以传入任意的地址来让后端服务器对其发起请求&#xff0c;并返回对该目标地址请求的数据 数据流&#xff1a;攻击者…