文章目录
前言
服务注册中心是一种用于实现微服务架构的基础设施,用于管理和维护服务的注册、发现、负载均衡等功能。它可以让不同的服务通过一个中心化的方式进行管理,降低了服务之间的耦合性,提高了服务之间的可扩展性和可维护性。使用HTTP通信可以简化注册中心的实现,因为HTTP是一种开放的、标准的协议,很容易被不同的语言和框架支持。自定义Header字段可以传递更多信息,扩展性非常强,可以根据不同的需求设计不同的字段。此外,使用HTTP通信也具有较好的跨平台和跨语言特性,使得不同的服务可以在不同的服务器上运行,提高了应用的可移植性和灵活性。
使用HTTP协议实现服务注册中心具有以下优点:
通用性强:HTTP协议是广泛应用的标准协议,简单易用,可以跨平台、跨语言使用。
易于部署和管理:基于HTTP协议的服务注册中心可以很方便地部署在云端或本地服务器上,并通过监控和管理工具来维护和管理服务。
支持多种传输协议:服务注册中心可以支持多种传输协议,包括HTTP、gRPC等,从而满足不同场景下的需求。
go语言的基于HTTP 协议的服务注册中心实现已经在之前的文章实现了。
go语言实现的rpc框架
本文章用c++语言实现注册中心,利用go语言作为服务注册者,c++语言最为服务发现者,实现的功能有服务注册,服务发现,心跳机制以及负载均衡。
一、注册中心整体框架
服务注册者可以向注册中心注册服务、发送心跳、获取可用的服务地址并注销服务。其中,注册和注销操作会修改 ServerList 中的数据,而获取服务地址则只是读取该数据。同时,由于多个线程可能同时访问 ServerList,因此该程序使用了互斥锁来保证数据的安全性。HTTP服务使用httplib库来实现。
1、成员变量以及单例模式
注册中心类Registry使用单例模式来保证只有一个 Registry 对象实例存在,用于管理服务的注册和发现。因为在多个地方需要使用该类时,如果每次都创建一个新的对象,则会导致数据不一致或者资源浪费的问题。
首先,包含了三个私有成员变量 ServerList、ServerFreMap、Mu 和Timeout。其中,ServerList 是一个类似于 Map 的容器,用于存储各个服务名称对应的服务器列表;ServerFreMap 也是一个 Map 类型的变量,用于记录每个服务被调用的次数;Mu 是一个互斥锁。Timeout表示服务超时时间,也就是服务经过这么久没有发送心跳就默认该服务以及宕机了。
private:
std::chrono::duration<int, std::milli> Timeout;
std::mutex Mu;
std::map<std::string, int> ServerFreMap;
std::map<std::string, std::vector<ServerItem>> ServerList;
SelectMode selectmodel;
explicit Registry(std::chrono::duration<int, std::milli> timeout,
SelectMode model)
: Timeout(timeout),
selectmodel(model){
// std::cout << "timeout:" << std::ctime(timeout) << std::endl;
};
public:
// 获取 Registry 实例的静态方法
static Registry& instance(std::chrono::duration<int, std::milli> timeout,
SelectMode model) {
static Registry registry(timeout, model); // 静态局部变量
return registry;
}
Instance() 方法返回一个静态的 Registry 对象实例。它定义了一个静态局部变量 instance,该变量在第一次调用 Instance() 方法时被创建,并且只被创建一次,之后每次调用 Instance() 方法都返回该静态变量的引用。
由于构造函数是私有的,所以无法通过外部创建 Registry 类的对象,从而保证只有一个 Registry 实例存在。同时,使用 delete 关键字禁止了拷贝构造函数和赋值运算符的使用,防止非预期的对象复制。
因此,该程序使用单例模式来确保只有一个 Registry 对象实例存在,从而避免了数据不一致或者资源浪费的问题。
2、服务列表的维护
接下来,程序实现了三个公共方法:
PutServer() 方法:用于将一个服务注册到注册中心。在该方法中,程序首先获取该服务对应的服务器列表,如果列表中已经存在相同 IP 地址的服务器,则更新其活跃时间;否则,将新服务器添加到列表中。最后,输出一条日志表示新服务器已经成功注册。
void Registry::PutServer(const std::string& serverName,
const std::shared_ptr<ServerItem>& serverItem) {
std::lock_guard<std::mutex> lock(Mu);
auto& serverList = ServerList[serverName];
for (auto& item : serverList) {
if (item.Addr == serverItem->Addr) {
item.ActiveTime =
std::chrono::system_clock::now(); // if exists, update start time
// to keep alive
return;
}
}
serverItem->ActiveTime =
std::chrono::system_clock::now(); // if exists, update start time
// to keep alive
std::cout << "##############server Regist############## " << std::endl;
std::cout << serverItem->Name << ":" << serverItem->Addr << std::endl;
serverList.push_back(*serverItem);
}
DestructServer() 方法:用于从注册中心删除某个服务对应的服务器。在该方法中,程序首先获取该服务对应的服务器列表,然后删除相应的服务器信息。如果该服务对应的服务器列表为空,则从 ServerList 中删除该服务。
void Registry::DestructServer(const std::string& serverName,
const std::string& addr) {
std::cout << "##############server destruct############## " << std::endl;
std::cout << serverName << ":" << addr << std::endl;
auto& serverItemList = ServerList[serverName];
for (auto it = serverItemList.begin(); it != serverItemList.end();) {
if (it->Addr == addr) {
it = serverItemList.erase(it); // timeout, remove from server list
if (serverItemList.empty()) {
ServerList.erase(serverName);
}