brpc的二次封装以及brpc与etcd的联合

目的:

搭配etcd的注册中心管理能知道谁能提供什么服务,并用rpc进行服务调用

封装思想:

信道管理,将不同服务主机的通信信道管理起来

封装:

1.指定的信道管理类

一个服务通常会有多个节点,每个节点都会有自己的信道类,建立信道与服务的映射关系,服务一对多信道。

2.总体的信道管理类

管理多个服务的信道管理类管理起来

tips:我们没必要将所有的服务信道都建立起来,我们得申明我们关心的服务,不关心的就可以不管理这个服务

cpp 复制代码
#pragma once
#include <brpc/channel.h>
#include <string>
#include <vector>
#include <unordered_map>
#include <mutex>
#include <iostream>
#include "./logger.hpp"
namespace common{
class ServerChannel
{
public:
    using ChannelPtr = std::shared_ptr<brpc::Channel>;
    using Ptr = std::shared_ptr<ServerChannel>;
    ServerChannel(const std::string &servername)
        : _service_name(servername), _index(0)
    {
    }
    void append(const std::string &host)
    {
        ChannelPtr newchannel=std::make_shared<brpc::Channel>();
        brpc::ChannelOptions options;
        options.connect_timeout_ms = -1; // 尝试连接时长
         Default: 200 (milliseconds)
        options.timeout_ms = -1; // rpc调用等待时间
        // Max duration of RPC over this Channel RPC调用在信道的期间
        options.protocol = "baidu_std";
        options.max_retry = 3;
        int ret = newchannel->Init(host.c_str(), &options);
        if (ret == -1)
        {
            LOG_ERROR("初始化{}-{}信道失败!", _service_name, host);
            return;
        }
        std::unique_lock<std::mutex> lock(_mutex);
        _channels.push_back(newchannel);
        _hosts.insert({host, newchannel});
    }
    // 服务器下线了一个节点,则把这个信道删除
    void remove(const std::string &host)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto score = _hosts.find(host);
        if (score == _hosts.end())
        {
            // 没找到
            LOG_WARN("没有找到该服务中的服务为{}主机名为{}的信道", _service_name, host);
            return;
        }
        // 找到了
        ChannelPtr scoreptr = score->second;
        for (auto it = _channels.begin(); it != _channels.end(); ++it)
        {
            if (*it == scoreptr)
            {
                _channels.erase(it);
                break;
            }
        }
        _hosts.erase(host);
        LOG_INFO("服务为{}主机名为{}的信道已经删除", _service_name, host);
    }
    // 选择一个信道给这个服务
    ChannelPtr choose()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if (_channels.size() == 0)
        {
            LOG_INFO("该服务{}中暂时没有信道", _service_name);
            return ChannelPtr();
        }
        int pos = _index++ % _channels.size();
        return _channels[pos];
    }

private:
    int32_t _index;
    std::mutex _mutex;
    std::string _service_name;

    std::vector<ChannelPtr> _channels;
    // 节点与信道的位图
    std::unordered_map<std::string, ChannelPtr> _hosts;
};

class ServerManager
{
public:
    using Ptr = std::shared_ptr<ServerManager>;
    ServerManager() {}
    ServerChannel::ChannelPtr choose(const std::string &instance_name)
    {
        std::string servicename=instance_name;
        std::unique_lock<std::mutex> lock(_mutex);
 
        auto serversret = _servers.find(servicename);
        if (serversret == _servers.end())
        {
            LOG_INFO("当前服务{}没有可使用的节点!", servicename);
            return ServerChannel::ChannelPtr();
        }
        // 该服务被订阅且上线了,提供一个该服务的节点
        else
        {       
           
            auto score = _servers.find(servicename);
            ServerChannel::Ptr serverchannal = score->second;
            return serverchannal->choose();

        }
    }
    void onlinechannel(const std::string &instance_name, const std::string &host)
    {
        ServerChannel::Ptr onlineserver;
        std::string servicename=get_service_name(instance_name);
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto followret = _follows.find(servicename);
            if (followret == _follows.end())
            {
                LOG_INFO("该服务{}-{}上线了,暂时没有被订阅", servicename, host);
                return;
            }
            // 新增该服务中的一个节点
            // 当该服务未上线

            auto serversret = _servers.find(servicename);
            
           
            if (serversret == _servers.end())
            {
                // 没有找到该服务 则添加该服务
               
                onlineserver = std::make_shared<ServerChannel>(servicename);
                _servers.insert({servicename, onlineserver});
                
            }
            else
            {
            
                onlineserver = serversret->second;
            }
            if (!onlineserver)
            {
                LOG_ERROR("新增 {} 服务管理节点失败!", servicename);
                return;
            }

            // 已经存在该服务
        }
        
        onlineserver->append(host);
    
        LOG_DEBUG("{}-{} 服务上线新节点,进行添加管理!", servicename, host);
    }
    void offlinechannel(const std::string &instance_name, const std::string &host)
    {
        ServerChannel::Ptr onlineserver;
          std::string servicename=get_service_name(instance_name);
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto followret = _follows.find(servicename);
            if (followret == _follows.end())
            {
                LOG_INFO("该服务{}-{}下线了,暂时没有被订阅", servicename, host);
                return;
            }
            // 删除该服务中的一个节点
            // 当该服务未上线

            auto serversret = _servers.find(servicename);
            if (serversret == _servers.end())
            {
                // 没有找到该服务
                LOG_INFO("没有找到该服务", servicename);
                return;
            }
            else
            {
                onlineserver = serversret->second;
            }
            if (!onlineserver)
            {
                LOG_ERROR("删除 {} 服务管理节点失败!", servicename);
                return;
            }

            // 已经存在该服务
        }
        onlineserver->remove(host);
        LOG_DEBUG("{}-{} 服务下线该节点", servicename, host);
    }
    void declared(const std::string &servername)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _follows.insert(servername);
    }
    std::string get_service_name(const std::string &server_instance_name)
    {
        int pos=server_instance_name.find_last_of('/');
        if(pos==std::string::npos)
        {
            return server_instance_name;
        }
        std::string retstring = server_instance_name.substr(0,pos);
       
        return retstring;
    }

private:
    std::mutex _mutex;
    // 订阅的服务 没有被订阅的服务不提供节点使用
    std::unordered_set<std::string> _follows;
    // 服务名称 和 该服务的信道管理器
    std::unordered_map<std::string, ServerChannel::Ptr> _servers;
};
}

brpc与etcd的联合

服务端:1.构建echo服务 2.搭建RPC服务器 3.启动RPC服务器 4.在etcd上注册这个RPC服务

客户端: 1.构造RPC信道管理对象 2.再构造etcd的监控对象 3.再从RPC信道管理对象中获取提供echo服务的信道 4.发起echo业务

​​

​​

由于choose找的是servicename : /service/echo

而在新增服务时是 /service/echo/instance 所以在新增的回调函数中要采取截断的方法

​rigistry

cpp 复制代码
#include <iostream>
#include <etcd/Client.hpp>
#include <etcd/KeepAlive.hpp>
#include <etcd/Response.hpp>
#include <etcd/Value.hpp>
#include <etcd/Watcher.hpp>
#include "../common/logger.hpp"
#include "../common/etcd.hpp"
#include "../common/channel.hpp"
#include<gflags/gflags.h>
#include <functional>
#include <brpc/server.h>
#include <brpc/closure_guard.h>
#include <butil/logging.h>
#include "main.pb.cc"
DEFINE_bool(run_mode,false,"这是运行的模式默认为调试模式false");
DEFINE_string(log_file,"","发布模式下指定的文件默认为空");
DEFINE_int32(log_level,0,"调试模式下默认的等级TRACE");

DEFINE_string(Host,"http://127.0.0.1:2379","服务注册中心地址");
DEFINE_string(instance_Host,"127.0.0.1:7070","新上线服务的访问地址");
DEFINE_string(base_service,"/service","服务器监控目录");
DEFINE_string(instance_service,"/echo/instance","新上线的服务");
//一个 echo 中应该有多个 key - value ,如果定义成echo的话 只能对应一个服务的访问地址
//一个 echo 中应该有多个instance - value
DEFINE_int32(listen_post,7070,"新上线服务的访问地址");
class EchoServiceImpl:public example::EchoService               
{
    public:
    EchoServiceImpl(){}
   
    ~EchoServiceImpl() override
    {} 
    void Echo(google::protobuf::RpcController* controller,
                       const ::example::EchoRequest* request,
                       ::example::EchoResponse* response,
                       ::google::protobuf::Closure* done) override
    {
                brpc::ClosureGuard rpc_guard(done);
                // ~ClosureGuard() {
                //if (_done) {
                //_done->Run();
                //}
                std::cout<<"收到了消息"<<request->message()<<std::endl;
                response->set_message(request->message()+"好的,我知道了");

                //_done->Run();
    }

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

    //1.启动rpc服务器 并添加服务
    //2.etcd内注册该服务 以及 访问该服务的 地址
    logging::LoggingSettings logger;
    logger.logging_dest= logging :: LoggingDestination::LOG_TO_NONE;
    logging::InitLogging(logger);
    //2.创建服务器并添加业务
    brpc::Server server;
    EchoServiceImpl echosservice;
    int n=server.AddService(&echosservice,brpc::ServiceOwnership::SERVER_DOESNT_OWN_SERVICE);
    if(n<0)
    {
        std::cout<<"AddService error"<<std::endl;
        exit(0);
    }
    //函数不匹配,源码有可能设置的是指针类型,tips
    //3.设置服务器选项,并且启动服务器
    brpc::ServerOptions opt;
    opt.idle_timeout_sec=-1;//尝试连接时间 超时则退出
    opt.num_threads=1;//number of pthreads that server runs on. 单线程
   ; //number of requests processed in parallel(并行)// Default: 0 (unlimited)
   
    int m=server.Start(FLAGS_listen_post,&opt);
    if(m<0)
    {
        std::cout<<"server Start error"<<std::endl;
        exit(1);
    }
    
    Rigistry::ptr rigserver=std::make_shared<Rigistry>(FLAGS_Host);
    rigserver->registry(FLAGS_base_service+FLAGS_instance_service,FLAGS_instance_Host);
    //运行等待服务结束
    server.RunUntilAskedToQuit();//运行等待服务结束
}

discovey

cpp 复制代码
#include <iostream>
#include <etcd/Client.hpp>
#include <etcd/KeepAlive.hpp>
#include <etcd/Response.hpp>
#include <etcd/Value.hpp>
#include <etcd/Watcher.hpp>
#include "../common/logger.hpp"
#include "../common/etcd.hpp"
#include "../common/channel.hpp"
#include <gflags/gflags.h>
#include <functional>
#include <brpc/server.h>
#include <brpc/closure_guard.h>
#include <butil/logging.h>
#include "main.pb.cc"
DEFINE_bool(run_mode, false, "这是运行的模式默认为调试模式false");
DEFINE_string(log_file, "", "发布模式下指定的文件默认为空");
DEFINE_int32(log_level, 0, "调试模式下默认的等级TRACE");

DEFINE_string(Host, "http://127.0.0.1:2379", "服务注册中心地址");
DEFINE_string(instance_Host, "168.198.0.1::8080", "新上线服务的访问地址");

DEFINE_string(instance_service, "/echo", "新上线的服务");
DEFINE_string(base_dir, "/service", "新上线的服务");
DEFINE_string(call_service, "/service/echo", "新上线的服务");

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

    // 1.创建服务管理类 并申明订阅的服务

    ServerManager::Ptr server = std::make_shared<ServerManager>();
    server->declared(FLAGS_call_service);
    // 2.将两个回调函数绑定,创建发现对象

    auto onlineput = std::bind(&ServerManager::onlinechannel, (server.get()), std::placeholders::_1, std::placeholders::_2);
    auto offlineput = std::bind(&ServerManager::offlinechannel, (server.get()), std::placeholders::_1, std::placeholders::_2);
    // 3.发现对象发现该服务,创建Echo_Servicestub

    Discovery::ptr Disserver = std::make_shared<Discovery>(FLAGS_Host, FLAGS_base_dir, onlineput, offlineput);

    while (1)
    {

        auto channel = server->choose(FLAGS_call_service);
        if (!channel)
        {
            std::this_thread::sleep_for(std::chrono::seconds(1));
            return -1;
        }
        example::EchoService_Stub echoServiceStub(channel.get());
        brpc::Controller *control = new brpc::Controller();
        control->Reset();
        example::EchoRequest request1;
        request1.set_message("你好啊少年");
        example::EchoResponse *response1 = new example::EchoResponse();

        // 4.Echo_Servicestub调用Echo服务
        // rpc业务调用
        echoServiceStub.Echo(control, &request1, response1, nullptr);

        if (control->Failed())
        {
            std::cout << "rpc echo service failed" << control->ErrorText() << std::endl;
            delete control;
            delete response1;
            std::this_thread::sleep_for(std::chrono::seconds(1));
            continue;
        }

        std::cout << "client收到响应" << response1->message() << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
    return 0;
}

相关推荐
走在考研路上1 小时前
Python错误处理
开发语言·python
数据小爬虫@1 小时前
Python爬虫:如何优雅地“偷窥”商品详情
开发语言·爬虫·python
CV大法好1 小时前
刘铁猛p3 C# 控制台程序引用System.Windows.Forms报错,无法引用程序集 解决方法
开发语言·c#
工业甲酰苯胺1 小时前
C语言之输入输出
c语言·c++·算法
Days20501 小时前
uniapp小程序增加加载功能
开发语言·前端·javascript
C++忠实粉丝2 小时前
计算机网络之NAT、代理服务、内网穿透、内网打洞
网络·c++·网络协议·计算机网络·http·智能路由器
朱小勇本勇2 小时前
Qt实现控件拖曳
开发语言·数据库·qt
片片叶2 小时前
C++(十四)
开发语言·c++
dengjiayue2 小时前
golang实现简单的reids服务2
开发语言·golang
疯狂的沙粒3 小时前
JavaScript 单例模式的创建与应用
开发语言·前端·javascript·vue.js