IM项目-----语音识别子服务

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录


前言

语音转换子服务,用于调用语音识别 SDK,进行语音识别,将语音转为文字后返回给网关。

  1. 语音消息的文字转换:客户端进行语音消息的文字转换。

一、搭建思想

1.参数解析 -- 基于gflags模块

rpc所需信息:

当前服务器的地址端口:用于搭建rpc服务器的监听地址信息

服务注册所需信息:

注册中心的地址端口:用于进行向服务注册中心进行服务注册

外部访问的地址端口:用于告诉注册中心的访问地址信息

语音识别平台所需信息

(app id,api key,secret key)

日志模块所需信息:

运行模式,日志文件名称,日志输出级别

2.初始化日志模块

3.搭建RPC服务器--实现语音识别业务接口功能

4.向注册中心进行服务注册

二、服务器搭建

1.继承speechService类,重写业务代码

只有一个rpc服务,就是进行语音识别,请求方需要将语音文件内容发送过来,我们在需要调用语音识别sdk,因此在成员变量中有一个ASRClient。这个变量是在make_rpc时传入进来的,在创建rpc时需要添加服务。

cpp 复制代码
//1.继承SpeechService服务类,重写业务方法
    class SpeechServiceImpl :public SpeechService
    {
    public:
        SpeechServiceImpl(const ASRClient::ptr& _asr_client)
            :_client(_asr_client)
        {

        }

        ~SpeechServiceImpl(){};

        //重写业务方法
        void SpeechRecognition(google::protobuf::RpcController* controller,
                        const ::lkm_im::SpeechRecognitionReq* request,
                        ::lkm_im::SpeechRecognitionRsp* response,
                        ::google::protobuf::Closure* done)
        {
            brpc::ClosureGuard rpc_guard(done);
            
            //解析出请求中的语音数据
            //基于语音识别sdk进行语音识别调用,获取语音转文字结果
            std::string err_msg;
            std::string resp = _client->recognize(request->speech_content(),err_msg);
            if(resp.empty()){
                //语音转文字失败
                LOG_ERROR("requestId = {} 语音识别失败.",request->request_id());
                response->set_request_id(request->request_id());
                response->set_success(false);
                response->set_errmsg(err_msg);
            }
            //构造响应返回
            response->set_request_id(request->request_id());
            response->set_success(true);
            response->set_recognition_result(resp);
        }
    private:
        ASRClient::ptr _client;     //语音识别客户端
    };

2.编写语音识别服务器类

在语音识别子服务中有三个对象,一个是服务注册对象,一个是语音识别对象,还有一个是rpc服务器。

服务注册对象的构建需要etcd服务器地址,注册的服务名称以及对应的主机地址。

语音识别对象的构建需要三个key.

rpc服务器构建需要rpc服务器监听的端口,还需要提供超时时间以及io线程数量.

构造这三个对象需要九个参数,因此我们使用建造者模式。

我们通过建造者类来构造这个对象,然后调用这个类提供的start方法,启动rpc服务器。

cpp 复制代码
   //2.封装一个语音识别子服务服务器
    class SpeechServer
    {
    public:
    using ptr = std::shared_ptr<SpeechServer>;
        SpeechServer(const Registry::ptr& registry,const ASRClient::ptr& asr_client,const std::shared_ptr<brpc::Server>& server)
            :_registry(registry),_asr_client(asr_client),_server(server)
        {

        }
        ~SpeechServer(){}

        //启动rpc服务器
        void start()
        {
            _server->RunUntilAskedToQuit();
        }
    private:
        Registry::ptr _registry;    //服务注册对象
        ASRClient::ptr _asr_client;     //语音识别客户端
        std::shared_ptr<brpc::Server> _server;        //rpc服务器
    };

3.建造者类编写

这个类提供了三个方法make_**(),需要先调用这个三个方法,来分别构造出rpc服务器,服务注册和语音识别客户端。在调用build方法,生成一个speechServer对象,通过这个对象就可以启动服务器。

在构造服务注册对象时,就会向etcd进行服务注册。

cpp 复制代码
//建造者类,具体思想是通过建造者类的build函数构造一个SpeechServer对象,通过这个对象启动rpc服务器
    class SpeechServerBuilder
    {
    public:
        void make_registry(const std::string& etcd_host,const std::string& service_name,const std::string& service_host)
        {
            _registry = std::make_shared<Registry>(etcd_host);
            //进行服务注册
            _registry->registry(service_name,service_host);
        }

        void make_asr(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_brpc(uint16_t port, int32_t timeout = -1, uint8_t num_threads = 1)
        {
            if(!_asr_client){
                LOG_ERROR("语音识别客户端未构造");
                abort();
            }

            //创建brpc服务器对象
            _server = std::make_shared<brpc::Server>();
            //添加服务
            SpeechServiceImpl *SpeechService = new SpeechServiceImpl(_asr_client);    //把这个对象的交给_server释放
            int ret = _server->AddService(SpeechService,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 = _server->Start(port,&options);
            if(ret == -1){
                LOG_ERROR("rpc服务器启动失败");
                abort();
            }
        }


        SpeechServer::ptr build()
        {
            if(!_registry){
                LOG_ERROR("服务注册客户端对象未构造");
                abort();
            }
            if(!_asr_client){
                LOG_ERROR("语音识别客户端未构造");
                abort();
            }
            if(!_server){
                LOG_ERROR("rpc服务器对象未构造");
                abort();
            }

            SpeechServer::ptr speechServer = std::make_shared<SpeechServer>(_registry,_asr_client,_server);
            return speechServer;
        }
    private:
        Registry::ptr _registry;    //服务注册对象
        ASRClient::ptr _asr_client;     //语音识别客户端
        std::shared_ptr<brpc::Server> _server;        //rpc服务器
    };

三.测试

cpp 复制代码
#include "speech_server.hpp"

DEFINE_bool(run_mode, false, "程序的运行模式,false-调试; true-发布;");
DEFINE_string(log_file, "", "发布模式下,用于指定日志的输出文件");
DEFINE_int32(log_level, 0, "发布模式下,用于指定日志输出等级");

DEFINE_string(etcd_host, "127.0.0.1:2379", "注册中心主机地址");
DEFINE_string(base_service, "/service", "服务监控根目录");
DEFINE_string(instance_name, "/speech_service/instance", "当前实例名称");
DEFINE_string(access_host, "127.0.0.1:10001", "当前实例的外部访问地址");

DEFINE_string(app_id, "115608644", "语音平台应用ID");
DEFINE_string(api_key, "GLQvgyNc4AaqhPfnDIMTRlw4", "语音平台API密钥");
DEFINE_string(secret_key, "vTcqDBswZUfAgjTcFA3GJGrc6yEWIO2w", "语音平台加密密钥");

DEFINE_int32(rpc_port,10001,"rpc服务器监听端口");   //必须和access_host端口一致

int main(int argc,char*argv[])
{
    google::ParseCommandLineFlags(&argc, &argv, true);
    lkm_im::init_logger(FLAGS_run_mode, FLAGS_log_file, FLAGS_log_level);

    lkm_im::SpeechServerBuilder ssb;
    ssb.make_registry(FLAGS_etcd_host,FLAGS_base_service + FLAGS_instance_name,FLAGS_access_host);
    ssb.make_asr(FLAGS_app_id,FLAGS_api_key,FLAGS_secret_key);
    ssb.make_brpc(FLAGS_rpc_port);
    lkm_im::SpeechServer::ptr speechServer = ssb.build();
    speechServer->start();

    return 0;
}
相关推荐
怀澈12228 分钟前
高性能服务器模型之Reactor(单线程版本)
linux·服务器·网络·c++
chnming19871 小时前
STL关联式容器之set
开发语言·c++
威桑1 小时前
MinGW 与 MSVC 的区别与联系及相关特性分析
c++·mingw·msvc
熬夜学编程的小王1 小时前
【C++篇】深度解析 C++ List 容器:底层设计与实现揭秘
开发语言·数据结构·c++·stl·list
yigan_Eins1 小时前
【数论】莫比乌斯函数及其反演
c++·经验分享·算法
Mr.131 小时前
什么是 C++ 中的初始化列表?它的作用是什么?初始化列表和在构造函数体内赋值有什么区别?
开发语言·c++
阿史大杯茶1 小时前
AtCoder Beginner Contest 381(ABCDEF 题)视频讲解
数据结构·c++·算法
C++忠实粉丝1 小时前
计算机网络socket编程(3)_UDP网络编程实现简单聊天室
linux·网络·c++·网络协议·计算机网络·udp
我们的五年2 小时前
【Linux课程学习】:进程描述---PCB(Process Control Block)
linux·运维·c++
程序猿阿伟2 小时前
《C++ 实现区块链:区块时间戳的存储与验证机制解析》
开发语言·c++·区块链