项目:基于gRPC进行项目的微服务架构改造

文章目录

  • 写在前面
  • 基本使用
  • 封装客户端
  • 封装服务端
  • Zookeeper

写在前面

最近学了一下gRPC进行远程调用的原理,所以把这个项目改造成了微服务分布式的架构,今天也是基本实现好了,代码已提交

在这里插入图片描述
这里补充一下文档吧,也算记录一下整个过程

基本使用

gRPC首先在安装上就非常繁琐,网络的教程也比较多,但要注意安装的版本兼容性问题,尤其是对应的Protubuf和gRPC的版本,同时要注意,在进行编译的时候要使用cmake进行编译,我最开始使用的是传统的Makefile,因为项目最开始用的就是这种,所以就直接使用了,而在进行编译链接的时候总是报错:

在这里插入图片描述
最后去查阅了官方文档,也就是gRPC的维护者,文档提示最好使用cmake进行编译:

在这里插入图片描述
https://github.com/grpc/grpc/tree/master/src/cpp

用了cmake就不会报链接的错误了,总体来说,gRPC安装确实繁琐,需要细心一点

使用的命令也比较简单:

protoc --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` message.proto
protoc --cpp_out=. message.proto

一个是生成grpc文件的,一个是生成proto文件的

下面我以一个服务为演示吧,其他的服务基本差不多,我就不再演示了

封装客户端

首先,对于客户端进行封装:

#pragma once
#include "const.h"
#include "Singleton.h"
#include "ConfigMgr.h"
#include <condition_variable>
#include <grpcpp/grpcpp.h> 
#include <queue>
#include "message.grpc.pb.h"
#include "message.pb.h"using grpc::Channel;
using grpc::Status;
using grpc::ClientContext;using message::LoginRsp;
using message::LoginReq;
using message::StatusService;/*** @brief 管理 gRPC 客户端的连接池,用于与 StatusService 通信* */
class StatusConPool 
{
public:StatusConPool(size_t poolSize, std::string host, std::string port): poolSize_(poolSize), host_(host), port_(port), b_stop_(false) {for (size_t i = 0; i < poolSize_; ++i) {std::shared_ptr<Channel> channel = grpc::CreateChannel(host + ":" + port,grpc::InsecureChannelCredentials());connections_.push(StatusService::NewStub(channel));}}~StatusConPool() {std::lock_guard<std::mutex> lock(mutex_);Close();while (!connections_.empty()) {connections_.pop();}}std::unique_ptr<StatusService::Stub> getConnection(){std::unique_lock<std::mutex> lock(mutex_);cond_.wait(lock, [this] {if (b_stop_) {return true;}return !connections_.empty();});//如果停止则直接返回空指针if (b_stop_) {return  nullptr;}auto context = std::move(connections_.front());connections_.pop();return context;}void returnConnection(std::unique_ptr<StatusService::Stub> context) {std::lock_guard<std::mutex> lock(mutex_);if (b_stop_) {return;}connections_.push(std::move(context));cond_.notify_one();}void Close(){b_stop_ = true;cond_.notify_all();}private:atomic<bool> b_stop_;size_t poolSize_;std::string host_;std::string port_;std::queue<std::unique_ptr<StatusService::Stub>> connections_;std::mutex mutex_;std::condition_variable cond_;
};/*** @brief 通过单例模式实现的 gRPC 客户端,用于向 StatusService 发送请求* */
class StatusGrpcClient :public Singleton<StatusGrpcClient>
{friend class Singleton<StatusGrpcClient>;
public:~StatusGrpcClient() {}LoginRsp Login(string username, string password){ClientContext context;LoginRsp reply;LoginReq request;request.set_username(username);request.set_password(password);auto stub = pool_->getConnection();cout << "准备进行grpc 发送了" << endl;Status status = stub->Login(&context, request, &reply);Defer defer([&stub, this]() {pool_->returnConnection(std::move(stub));});if (status.ok()) {return reply;}else {reply.set_error(ErrorCodes::RPCFailed);return reply;}}
private:StatusGrpcClient(){auto& gCfgMgr = ConfigMgr::Inst();std::string host = gCfgMgr["StatusServer"]["Host"];std::string port = gCfgMgr["StatusServer"]["Port"];cout << "host:port" << host + ":" + port << endl;pool_.reset(new StatusConPool(5, host, port));}std::unique_ptr<StatusConPool> pool_;
};

在这样进行封装了之后:

在这里插入图片描述

由于这里存在对应的接口,此时就能够进行远程调用了,然后对于远程调用回来的结果进行判断即可

封装服务端

gRPC比较优秀的一点就在于,它能够屏蔽网络的传输,使得使用者可以专注的对于业务逻辑进行处理,具体可以看下面这个:

在这里插入图片描述
这里proto会生成一个服务类,这个类是一个虚基类,只需要对于这个类进行继承后,实现对应的接口,那么在进行调用的时候就可以去调用我们实际要进行处理的逻辑,就是一个多态的思想:

#pragma once
#include <grpcpp/grpcpp.h>
#include "message.grpc.pb.h"
#include <mutex>
#include "ConfigMgr.h"
#include "MysqlMgr.h"
#include "const.h"
#include "RedisMgr.h"
#include <climits>
#include <nlohmann/json.hpp>
#include <regex>using grpc::ServerContext;
using grpc::Status;using message::LoginReq;
using message::LoginRsp;
using message::RegReq;
using message::RegRsp;
using message::StatusService;
using json = nlohmann::json;class StatusServiceImpl final : public StatusService::Service
{
public:StatusServiceImpl(){}Status Login(ServerContext* context, const LoginReq* request, LoginRsp* reply){cout << "收到了 Login" << endl;auto username = request->username();auto password = request->password();bool success = authenticate(username.c_str(), password.c_str());cout << "验证成功" << endl;if(!success){reply->set_error(ErrorCodes::PasswdErr);cout << "发送成功" << endl;return Status::OK;}reply->set_error(ErrorCodes::Success);cout << "发送成功" << endl;return Status::OK;}// 验证用户名和密码是否正确bool authenticate(const char *username, const char *password){cout << "去Redis里面看看" << endl;if(FindInRedis(username, password))return true;cout << "去Mysql里面看看" << endl;return MysqlMgr::GetInstance()->CheckPwd(username, password);}bool FindInRedis(const char* username, const char* password){string result = RedisMgr::GetInstance()->HGet("user:username:password", username);return result == password;}Status Register(ServerContext* context, const RegReq* request, RegRsp* reply){auto username = request->username();auto password = request->password();bool success = RegisterInfo(username.c_str(), password.c_str());if(!success){reply->set_error(ErrorCodes::PasswdErr);return Status::OK;}reply->set_error(ErrorCodes::Success);return Status::OK;}bool validateCredentials(const string& username, const string& password) {// 定义用户名的正则表达式regex usernamePattern("^[a-zA-Z0-9._-]{3,}$");// 定义密码的正则表达式regex passwordPattern("^[a-zA-Z0-9._-]{6,}$");// 使用regex_match进行匹配,注意这里应该是&&操作,因为两个条件都需要满足if(regex_match(username, usernamePattern) && regex_match(password, passwordPattern))return true; // 如果都匹配成功,则返回trueelsereturn false; // 否则返回false}// 尝试插入用户信息,成功返回 true,失败返回 falsebool RegisterInfo(const char *username, const char *password){if(!validateCredentials(username, password))return false;return MysqlMgr::GetInstance()->RegUser(username, password);}
};

这样,在外部服务端,就可以进行调用了:

在这里插入图片描述

Zookeeper

分布式架构当中存在一个有用的组件,Zookeeper,这个原理是进行一个类似于文件系统的架构,然后可以进行读取其中的值,并且还设置了对应的回调函数,也就是所谓的Watcher,发现有服务到达的时候,就执行对应的回调函数,那么基于这个原理,就可以去动态识别到gRPC的服务

gRPC的服务我也封装好了,其他的就看仓库里面的代码吧

#ifndef _ZOOKEEPER_H_
#define _ZOOKEEPER_H_#include <zookeeper/zookeeper.h>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/noncopyable.hpp>
#include <stdio.h>
#include <string>
#include <vector>
#include <map>
#include <sstream>
#include <typeinfo>typedef boost::function<void (const std::string &path, const std::string &value)> DataWatchCallback;
typedef boost::function<void (const std::string &path, const std::vector<std::string> &value)> ChildrenWatchCallback;
//
class ZkRet
{friend class ZooKeeper;
public:bool ok() const {return ZOK == code_; }bool nodeExist() const {return ZNODEEXISTS == code_; }bool nodeNotExist() const {return ZNONODE == code_; }operator bool() const {return ok(); }
protected:ZkRet(){code_ = ZOK; }ZkRet(int c){code_ = c; }
private:int code_;
};
// class Zookeeper, 
// thread safety: single ZooKeeper object should be used in single thread.
class ZooKeeper : public boost::noncopyable
{
public:ZooKeeper();~ZooKeeper();//ZkRet init(const std::string &connectString);ZkRet getData(const std::string &path, std::string &value);ZkRet setData(const std::string &path, const std::string &value);ZkRet getChildren(const std::string &path, std::vector<std::string> &children);ZkRet exists(const std::string &path);ZkRet createNode(const std::string &path, const std::string &value, bool recursive = true);// ephemeral node is a special node, its has the same lifetime as the session ZkRet createEphemeralNode(const std::string &path, const std::string &value, bool recursive = true);// sequence node, the created node's name is not equal to the given path, it is like "path-xx", xx is an auto-increment number ZkRet createSequenceNode(const std::string &path, const std::string &value, std::string &rpath, bool recursive = true);ZkRet createSequenceEphemeralNode(const std::string &path, const std::string &value, std::string &rpath, bool recursive = true);ZkRet watchData(const std::string &path, const DataWatchCallback &wc);ZkRet watchChildren(const std::string &path, const ChildrenWatchCallback &wc);//void setDebugLogLevel(bool open = true);//ZkRet setFileLog(const std::string &dir = "./");ZkRet setConsoleLog();//static std::string getParentPath(const std::string &path);static std::string getNodeName(const std::string &path);static std::string getParentNodeName(const std::string &path);
private:// for inner use, you should never call these functionvoid setConnected(bool connect = true){connected_ = connect; }bool connected()const{return connected_; }void restart();//// watch classclass Watch{public:Watch(ZooKeeper *zk, const std::string &path);virtual void getAndSet() const = 0;const std::string &path() const{return path_; }ZooKeeper* zk() const {return zk_; }protected:ZooKeeper *zk_;std::string path_;};typedef boost::shared_ptr<Watch> WatchPtr;class DataWatch: public Watch{public:typedef DataWatchCallback CallbackType;DataWatch(ZooKeeper *zk, const std::string &path, const CallbackType &cb);virtual void getAndSet() const;void doCallback(const std::string &data) const{ cb_ (path_, data); };private:CallbackType cb_;};class ChildrenWatch: public Watch{public:typedef ChildrenWatchCallback CallbackType;ChildrenWatch(ZooKeeper *zk, const std::string &path, const CallbackType &cb);virtual void getAndSet() const;void doCallback(const std::vector<std::string> &data) const { cb_ (path_, data); };private:CallbackType cb_;};//class WatchPool{public:template<class T>WatchPtr createWatch(ZooKeeper *zk, const std::string &path, const typename T::CallbackType &cb){std::string name = typeid(T).name() + path;WatchMap::iterator itr = watchMap_.find(name);if(watchMap_.end() == itr){WatchPtr wp(new T(zk, path, cb));watchMap_[name] = wp;return wp;}else{return itr->second;}}template<class T>WatchPtr getWatch(const std::string &path){std::string name = typeid(T).name() + path;WatchMap::iterator itr = watchMap_.find(name);if(watchMap_.end() == itr){return WatchPtr();}else{return itr->second;}}//void getAndSetAll() const{for(WatchMap::const_iterator it = watchMap_.begin(); it != watchMap_.end(); ++it){it->second->getAndSet();}}private:typedef std::map<std::string, WatchPtr> WatchMap;WatchMap watchMap_;};//static void dataCompletion(int rc, const char *value, int valueLen, const struct Stat *stat, const void *data);static void stringsCompletion(int rc, const struct String_vector *strings, const void *data);static void defaultWatcher(zhandle_t *zh, int type, int state, const char *path,void *watcherCtx);//ZkRet createTheNode(int flag, const std::string &path, const std::string &value, char *rpath, int rpathlen, bool recursive);//void miliSleep(int milisec);//zhandle_t *zhandle_;std::string connectString_;bool connected_;ZooLogLevel defaultLogLevel_;WatchPool watchPool_;//FILE *logStream_;
};#endif

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

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

相关文章

2024 年最新 windows 操作系统搭建部署 nginx 服务器应用详细教程(更新中)

nginx 服务器概述 Nginx 是一款高性能的 HTTP 和 反向代理 服务器&#xff0c;同时是一个 IMAP / POP3 / SMTP 代理服务器。Nginx 凭借其高性能、稳定性、丰富的功能集、简单的配置和低资源消耗而闻名。 浏览 nginx 官网&#xff1a;https://nginx.org/ Nginx 应用场景 静态…

Windows11安装MongoDB7.0.12详细教程

下载 地址&#xff1a;https://www.mongodb.com/try/download/community 我使用的是迅雷下载&#xff1a; 安装 选择自定义安装&#xff1a; 选择安装目录&#xff1a; 开始安装&#xff1a; 这个玩意会卡比较长的时间&#xff1a; 最后这一步如果没有科学上网&#…

甄选范文“论数据分片技术及其应用”软考高级论文,系统架构设计师论文

论文真题 数据分片就是按照一定的规则,将数据集划分成相互独立、正交的数据子集,然后将数据子集分布到不同的节点上。通过设计合理的数据分片规则,可将系统中的数据分布在不同的物理数据库中,达到提升应用系统数据处理速度的目的。 请围绕“论数据分片技术及其应用”论题…

算法日记day 24(回溯之组合问题)

一、组合总和3 题目&#xff1a; 找出所有相加之和为 n 的 k 个数的组合&#xff0c;且满足下列条件&#xff1a; 只使用数字1到9每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次&#xff0c;组合可以以任何顺序返回。 示例 1: 输…

其他:trycatch模块捕获循环错误,避免中断程序

介绍 今天有位同事问我怎么在某次循环报错后仍然可以继续程序运行&#xff0c;最后我们使用了trycatch模块。 代码解读 任务&#xff1a;在循环中&#xff0c;如果某次循环的calc出现错误则跳过这次循环并重新赋值结果 res_list <- list() # 创建一个空列表来存储结果fo…

pdf压缩文件怎么压缩最小?8款实用PDF压缩软件,你值得拥有(2024)

pdf压缩文件怎么压缩最小&#xff1f;如今&#xff0c;无论在我们日常工作还是在日常学习中&#xff0c;pdf文件都无处不在。因此&#xff0c;如何压缩pdf文件&#xff0c;将其大小降至最小&#xff0c;既保证质量又使文件更易管理成为许多人迫切关心的问题。在本文中&#xff…

php 一个极简的类例子

https://andi.cn/page/621627.html

智慧矿山,安全先行:矿山风险预警视频智能监控系统的应用解析

随着科技的飞速发展&#xff0c;矿山行业作为国民经济的重要支柱之一&#xff0c;其安全生产问题日益受到社会各界的广泛关注。为了有效降低矿山作业中的风险&#xff0c;提升安全管理水平&#xff0c;矿山风险预警视频智能监控系统应运而生。该系统集成了高清视频监控、人工智…

工具(1)—截屏和贴图工具snipaste

演示和写代码文档的时候&#xff0c;总是需要用到截图。在之前的流程里面&#xff0c;一般是打开WX或者QQ&#xff0c;找到截图工具。但是尴尬的是&#xff0c;有时候&#xff0c;微信没登录&#xff0c;而你这个时候就在写文档。为了截个图&#xff0c;还需要启动微信&#xf…

OnlyOffice在线部署

部署服务环境&#xff1a;Centos7.6 curl -sL https://rpm.nodesource.com/setup_6.x | sudo bash 安装yum-utils工具 yum install yum-utils 添加nginx.repo源(Nginx官网有最新版&#xff0c;直接copy即可) vim /etc/yum.repos.d/nginx.repo [nginx-stable] namenginx st…

ElasticSearch父子索引实战

关于父子索引 ES底层是Lucene,由于Lucene实际上是不支持嵌套类型的,所有文档都是以扁平的结构存储在Lucene中,ES对父子文档的支持,实际上也是采取了一种投机取巧的方式实现的. 父子文档均以独立的文档存入,然后添加关联关系,且父子文档必须在同一分片,由于父子类型文档并没有…

木马后门实验

实验拓扑 实验步骤 防火墙 配置防火墙—充当边界NAT路由器 边界防火墙实现内部 DHCP 分配和边界NAT需求&#xff0c;其配置如下 登录网页 编辑接口 配置e0/0 配置e0/1 编辑策略 测试&#xff1a;内部主机能获得IP&#xff0c;且能与外部kali通信 kali 接下来开启 kali 虚…

Teamcenter用本地胖客户端启动时,可以看到插件的菜单项,但是用Eclipse启动时看不到

用本地胖客户端启动时&#xff0c;可以看到定制包的插件菜单项&#xff0c;但是用Eclipse启动时&#xff0c;看不到&#xff1f; 原因&#xff1a; 是因为Eclipse启动下&#xff0c;是采用 JAVA1.8 来运行的。但是本机的胖客户端是采用JAVA 11来运行的 解决办法&#xff1a;…

表现力丰富的肖像动画框架;结合本地LLM和GraphRAG的多代理RAG超级机器人;支持向量和图路径查询RAG;

✨ 1: Follow-Your-Emoji Follow-Your-Emoji 是一个基于扩散模型的精细控制与表现力丰富的肖像动画框架。 Follow-Your-Emoji 是一种基于扩散模型的肖像动画生成框架&#xff0c;可以通过目标标志序列动画化参考肖像。其核心优势在于能够实现精细可控和富有表现力的自由风格肖…

快速幂的求解方法(位运算)

需要求解幂运算的解法&#xff0c;可以将需要运算的内容进行判别&#xff0c;众所周知&#xff0c;幂就是指数&#xff0c;就是将底数乘以自身完成n次自相乘&#xff0c;那么就可以幻化为他的幂的简化计算&#xff1b; 以二进制为例&#xff0c;你要求&#xff0c;即可以看作是…

[CISCN2019 华北赛区 Day1 Web5]CyberPunk 1

目录 题目分析功能点分析伪协议读取源码search.phpchange.phpdelete.phpconfirm.php 代码分析 解法一解法二 题目分析 功能点分析 看到查询界面&#xff0c;第一时间想到了xss&#xff0c;经过测试存在xss&#xff0c;但没用 然后想到了sql注入&#xff0c;注册的时候在地址的…

nginx实战与负载均衡

一、nginx实战 1、nginx 反向代理配置 &#xff08;1&#xff09;概述 反向代理&#xff1a;⽤户直接访问反向代理服务器就可以获得⽬标服务器&#xff08;后端服务器&#xff09;的资源。 &#xff08;2&#xff09;修改配置 [rootserver2 ~]# vim /usr/local/nginx/conf/ng…

学习日记:排序

目录 1.选择排序 2.冒泡排序 3.插入排序 4.查找 4.1 二分查找 1.选择排序 思想&#xff1a;给合适的位置选择合适的数&#xff08;用后面的数依次跟指定位置上的数比较&#xff0c;如果后面的书比指定位置的数小&#xff0c;就交换两个数&#xff0c;依次重复&#xff0c;一…

API 接口自动化测试的基本原理及实战教程

常用API接口协议介绍 HTTP协议 超文本传输协议 它是用来在Internet上传送超文本的传送协议&#xff0c;运行在TCP/IP协议族之上&#xff0c;它可以使浏览器更加高效&#xff0c;使网络传输减少。 任何服务器除了包括HTML文件以外&#xff0c;还有一个HTTP驻留程序&#xf…

(day28)leecode——有效括号

描述 数字 n 代表生成括号的对数&#xff0c;请你设计一个函数&#xff0c;用于能够生成所有可能的并且 有效的 括号组合。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;["((()))","(()())","(())()","()(())","…