C++负载均衡远程调用学习之 Dns-Route关系构建

目录

1.LARS-DNS-MYSQL环境搭建

2.LARSDNS-系统整体模块的简单说明

3.Lars-Dns-功能说明

4.Lars-Dns-数据表的创建

5.Lars-Dns-整体功能说明

6.Lars-DnsV0.1-Route类的单例实现

7.Lars-DnsV0.1-Route类的链接数据库方法实现

8.Lars-DnsV0.1-定义存放RouteData关系的map数据结构

9.课前回顾

10.Lars-DnsV0.1-将Route数据加载map中

11.Lars-Dns的proto协议定义

12.Lars-DnsV0.1-实现获取route信息功能

[13.Lars-DnsV0.1-获取route hosts信息测试](#13.Lars-DnsV0.1-获取route hosts信息测试)

14.Lars-DnsV0.1-总结


1.LARS-DNS-MYSQL环境搭建

四、Lars-DNS Service开发

**1) 简介**

​ 负责接收各agent对某modid、cmdid的请求并返回该modid、cmdid下的所有节点,即为agent提供获取路由服务

1.1 架构

![3-Lars-dnsserver](./pictures/3-Lars-dnsserver.png)

**1.2 网络模块**

​ DnsService服务模型采用了one loop per thread TCP服务器,主要是基于Lars-Reactor:

  • 主线程Accepter负责接收连接(agent端连接)

  • Thread loop们负责处理连接的请求、回复;(agent端发送查询请求,期望获取结果)

2.LARSDNS-系统整体模块的简单说明

**1.3 双map模型**

​ DnsServer使用两个map存储路由数据(key = `modid<<32 + cmdid` , value = set of `ip<<32 + port`)

  • 一个`RouterDataMap_A`:主数据,查询请求在此map执行

  • 另一个`RouterDataMap_B`:后台线程周期性重加载路由到此map,作为最新数据替换掉上一个map

这两个map分别由指针`data_pointer`与`temp_pointer`指向.

3.Lars-Dns-功能说明

1.4 Backend Thread守护线程

**dns service还有个业务线程:**

1、负责周期性(default:1s)检查`RouteVersion`表版本号,如有变化,说明`RouteData`有变更,则重加载`RouteData`表内容;然后将`RouteChange`表中被变更的`modid`取出,根据订阅列表查出`modid`被哪些连接订阅后,向所有工作线程发送任务:要求订阅这些`modid`的连接推送`modid`路由到agent

2、此外,还负责周期性(default:8s)重加载`RouteData`表内容

**PS:重加载`RouteData`表内容的细节**

重加载`RouteData`表内容到`temp_pointer`指向的`RouterDataMap_B`,而后上写锁,交换指针`data_pointer`与`temp_pointer`的地址,于是完成了路由数据更新

4.Lars-Dns-数据表的创建

**主业务**

  1. 服务启动时,`RouteData`表被加载到`data_pointer`指向的`RouterDataMap_A`中, `temp_pointer`指向的`RouterDataMap_B`为空

  2. 服务启动后,agent发来Query for 请求某`modid/cmdid`,到其所在Thread Loop上,上读锁查询`data_pointer`指向的`RouterDataMap_A`,返回查询结果;

  3. 如果此`modid/cmdid`不存在,则把`agent ip+port`+`moid/cmdid`发送到Backend thread loop1的队列,让其记录到ClientMap

后台线程Backend thread每隔10s清空`temp_pointer`指向的`RouterDataMap_B`,再加载`RouteData`表内容到`temp_pointer`指向的`RouterDataMap_B`,加载成功后交换指针`data_pointer`与`temp_pointer`指针内容,于是完成了路由数据的更新.

5.Lars-Dns-整体功能说明

**2) 数据库创建**

* 表`RouteData`: 保存了所有mod路由信息.

| 字段 | 数据类型 | 是否可以为空 | 主键 | 默认 | 附加 | 说明 |

| ---------- | ---------------- | ------------ | ---- | ---- | ------ | ------------ |

| id | int(10) unsigned | No | 是 | NULL | 自增长 | 该条数据ID |

| modid | int(10) unsigned | No | | NULL | | 模块ID |

| cmdid | int(10) unsigned | No | | NULL | | 指令ID |

| serverip | int(10) unsigned | No | | NULL | | 服务器IP地址 |

| serverport | int(10) unsigned | No | | NULL | | 服务器端口 |

* 表`RouteVersion`: 当前`RouteData`路由版本号,每次管理端修改某mod的路由,`RouteVersion`表中的版本号都被更新为当前时间戳

| 字段 | 数据类型 | 是否可以为空 | 主键 | 默认 | 附加 |

| ------- | ---------------- | ------------ | ---- | ---- | ------ |

| id | int(10) unsigned | No | 是 | NULL | 自增长 |

| version | int(10) unsigned | No | | NULL | |

* 表`RouteChange`: 每次管理端修改某mod的路由,会记录本次对哪个mod进行修改(增、删、改),以便指示最新的`RouteData`路由有哪些mod变更了。

| 字段 | 数据类型 | 是否可以为空 | 主键 | 默认 | 附加 |

| ------- | ------------------- | ------------ | ---- | ---- | ------ |

| id | int(10) unsigned | No | 是 | NULL | 自增长 |

| modid | int(10) unsigned | No | | NULL | |

| cmdid | int(10) unsigned | No | | NULL | |

| version | bigint(20) unsigned | No | | NULL | |

相关创建表格的sql语句如下`lars_dns.sql`

```sql

DROP DATABASE if exists lars_dns;

CREATE DATABASE lars_dns;

USE lars_dns;

DROP TABLE IF EXISTS `RouteData`;

CREATE TABLE `RouteData` (

`id` int(10) unsigned NOT NULL AUTO_INCREMENT,

`modid` int(10) unsigned NOT NULL,

`cmdid` int(10) unsigned NOT NULL,

`serverip` int(10) unsigned NOT NULL,

`serverport` int(10) unsigned NOT NULL,

PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=116064 DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS `RouteVersion`;

CREATE TABLE RouteVersion (

`id` int(10) unsigned NOT NULL AUTO_INCREMENT,

`version` int(10) unsigned NOT NULL,

PRIMARY KEY (`id`)

);

INSERT INTO RouteVersion(version) VALUES(0);

DROP TABLE IF EXISTS `RouteChange`;

CREATE TABLE RouteChange (

`id` int(10) unsigned NOT NULL AUTO_INCREMENT,

`modid` int(10) unsigned NOT NULL,

`cmdid` int(10) unsigned NOT NULL,

`version` bigint(20) unsigned NOT NULL,

PRIMARY KEY (`id`)

);

```

​ 我们创建一个基础目录`Lars/base`来存放一些公共的工具和资源.

cd到`Lars/base`, 我们`mkdir sql`, 然后将`lars_dns.sql`拷贝到`sql/`文件夹下。

然后执行创建表格

```sql

$mysql -u root -p

Enter password:

Welcome to the MySQL monitor. Commands end with ; or \g.

Your MySQL connection id is 18

Server version: 5.7.27-0ubuntu0.18.04.1 (Ubuntu)

Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its

affiliates. Other names may be trademarks of their respective

owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> \. lars_dns.sql

Query OK, 0 rows affected, 1 warning (0.01 sec)

Query OK, 1 row affected (0.00 sec)

Database changed

Query OK, 0 rows affected, 1 warning (0.00 sec)

Query OK, 0 rows affected (0.08 sec)

Query OK, 0 rows affected, 1 warning (0.01 sec)

Query OK, 0 rows affected (0.06 sec)

Query OK, 1 row affected (0.01 sec)

Query OK, 0 rows affected, 1 warning (0.01 sec)

Query OK, 0 rows affected (0.13 sec)

```

6.Lars-DnsV0.1-Route类的单例实现

3) dns serivce模块目录构建

3.1 集成lars_reactor模块

​ 首先我们给dns模块创建一个项目文件夹,与`lars_reactor`并列,在`Lars/`下创建

```bash

$mkdir Lars/lars_dns

```

在`lars_dns`中,我们可以先创建基本的项目必须文件夹和文件,目录结构如下

```bash

lars_dns/

├── bin/

├── conf/

│ └── lars_dns.conf

├── include/

├── Makefile

└── src/

└── dns_service.cpp

```

> conf/lars_dns.conf

```ini

reactor

maxConn = 1024

threadNum = 5

ip = 127.0.0.1

port = 7778

```

> src/dns_service.cpp

```c

#include "lars_reactor.h"

int main(int argc, char **argv)

{

event_loop loop;

//加载配置文件

config_file::setPath("conf/lars_dns.conf");

std::string ip = config_file::instance()->GetString("reactor", "ip", "0.0.0.0");

short port = config_file::instance()->GetNumber("reactor", "port", 7778);

//创建tcp服务器

tcp_server *server = new tcp_server(&loop, ip.c_str(), port);

//注册路由业务

//开始事件监听

printf("lars dns service ....\n");

loop.event_process();

return 0;

}

```

> Makefile

```makefile

TARGET= bin/lars_dns

CXX=g++

CFLAGS=-g -O2 -Wall -Wno-deprecated

BASE=../base

BASE_H=$(BASE)/include

LARS_REACTOR=../lars_reactor

LARS_REACTOR_H =$(LARS_REACTOR)/include

LARS_REACTOR_LIB=$(LARS_REACTOR)/lib -llreactor

OTHER_LIB = -lpthread

SRC= ./src

INC= -I./include -I(BASE_H) -I(LARS_REACTOR_H)

LIB= -L(LARS_REACTOR_LIB) (OTHER_LIB)

OBJS = (addsuffix .o, (basename (wildcard (SRC)/*.cpp)))

(TARGET): (OBJS)

mkdir -p bin

(CXX) (CFLAGS) -o (TARGET) (OBJS) (INC) (LIB)

%.o: %.cpp

(CXX) (CFLAGS) -c -o @ < $(INC)

.PHONY: clean

clean:

-rm -f src/*.o $(TARGET)

```

这里主要注意一下`Makefile`的编写,我们需要连接libreactor库还有libpthread库等,还有一些头文件的文件目录不要写错。

接下来进行make,我们会在`bin/`得到dns的可执行程序,并且可以成功运行.

7.Lars-DnsV0.1-Route类的链接数据库方法实现

3.2 集成mysql模块

​ 我们需要使用libmysqlclient开发者第三方库,当然可以从mysql官方网站下载与你当前mysql版本匹配的so或者a文件,这里我们提供一个已经编译好的libmysqlclient.a和对应的头文件,代码参见:

<https://github.com/aceld/Lars/tree/master/base/mysql-connector-c\>

​ 我们把`mysql-connector-c`文件夹放在了`Lars/base/`下,作为公共包使用。

​ 接下来我们要重新修改一下`Makefile`

> Lars/lars_dns/Makefile

```makefile

TARGET= bin/lars_dns

CXX=g++

CFLAGS=-g -O2 -Wall -Wno-deprecated

BASE=../base

BASE_H=$(BASE)/include

LARS_REACTOR=../lars_reactor

LARS_REACTOR_H =$(LARS_REACTOR)/include

LARS_REACTOR_LIB=$(LARS_REACTOR)/lib -llreactor

MYSQL=$(BASE)/mysql-connector-c

MYSQL_H=$(MYSQL)/include

MYSQL_LIB=$(MYSQL)/lib/libmysqlclient.a

OTHER_LIB = -lpthread -ldl

SRC= ./src

INC= -I./include -I(BASE_H) -I(LARS_REACTOR_H) -I$(MYSQL_H)

LIB= (MYSQL_LIB) -L(LARS_REACTOR_LIB) $(OTHER_LIB)

OBJS = (addsuffix .o, (basename (wildcard (SRC)/*.cpp)))

(TARGET): (OBJS)

mkdir -p bin

(CXX) (CFLAGS) -o (TARGET) (OBJS) (INC) (LIB)

%.o: %.cpp

(CXX) (CFLAGS) -c -o @ < $(INC)

.PHONY: clean

clean:

-rm -f src/*.o $(TARGET)

```

​ 加上mysqlclient库的关联。注意,libmysqlclient.a依赖libdl库, 所以我们在 OTHER_LIB变量中加上`-ldl`, 然后我们尝试使用mysql库的接口。

> dns_service.cpp

```c

#include "lars_reactor.h"

#include "mysql.h"

int main(int argc, char **argv)

{

event_loop loop;

//加载配置文件

config_file::setPath("conf/lars_dns.conf");

std::string ip = config_file::instance()->GetString("reactor", "ip", "0.0.0.0");

short port = config_file::instance()->GetNumber("reactor", "port", 7778);

//创建tcp服务器

tcp_server *server = new tcp_server(&loop, ip.c_str(), port);

//注册路由业务

//测试mysql接口

MYSQL dbconn;

mysql_init(&dbconn);

//开始事件监听

printf("lars dns service ....\n");

loop.event_process();

return 0;

}

```

8.Lars-DnsV0.1-定义存放RouteData关系的map数据结构

首先我们将Route类设计成单例,我们创建头文件和cpp文件.

> lars_dns/include/dns_route.h

```c

#pragma once

class Route

{

public:

//创建单例的方法

static void init() {

_instance = new Route();

}

static Route *instance() {

//保证init方法在这个进程执行中,只执行一次

pthread_once(&_once, init);

return _instance;

}

private:

//构造函数私有化

Route();

Route(const Route&);

const Route& operator=(const Route&);

//单例

static Route* _instance;

//单例锁

static pthread_once_t _once;

/* ---- 属性 ---- */

//...

};

```

> lars_dns/src/dns_route.cpp

```c

#include "dns_route.h"

//单例对象

Route * Route::_instance = NULL;

//用于保证创建单例的init方法只执行一次的锁

pthread_once_t Route::_once = PTHREAD_ONCE_INIT;

```

9.课前回顾

4.2 Route中的map数据类型定义

​ **这里的Route并非reactor中的router,这里的Route我们是把`modid/cmdid`与需要管理的远程服务器的`serverip/serverport`的一条对应关系叫一个`Route`。**

​ 我们用map来存储这些关系,其中key是modid/cmdid的一个二进制偏移量处理,而map的value是一个set集合,因为一个modid/cmdid可能对应多个host主机的ip和端口。具体的表现数据结构形式如下。

​ ![10-dns_route_structure](./pictures/10-dns_route_structure.jpeg)

​ 接下来,我们来定义一个相关代码:

> lars_dns/include/dns_route.h

```c

#pragma once

#include <pthread.h>

#include <ext/hash_map>

#include <ext/hash_set>

#include "mysql.h"

using __gnu_cxx::hash_map;

using __gnu_cxx::hash_set;

//定义用来保存modID/cmdID与host的IP/host的port的对应的关系 数据类型

typedef hash_map< uint64_t, hash_set<uint64_t> > route_map;

typedef hash_map< uint64_t, hash_set<uint64_t> >::iterator route_map_it;

//定义用来保存host的IP/host的port的的集合 数据类型

typedef hash_set<uint64_t> host_set;

typedef hash_set<uint64_t>::iterator host_set_it;

class Route

{

public:

//创建单例的方法

static void init() {

_instance = new Route();

}

static Route *instance() {

//保证init方法在这个进程执行中,只执行一次

pthread_once(&_once, init);

return _instance;

}

private:

//构造函数私有化

Route();

Route(const Route&);

const Route& operator=(const Route&);

//单例

static Route* _instance;

//单例锁

static pthread_once_t _once;

/* ---- 属性 ---- */

//数据库

MYSQL _db_conn; //mysql链接

char _sql[1000]; //sql语句

//modid/cmdid---ip/port 对应的route关系map

route_map *_data_pointer; //指向RouterDataMap_A 当前的关系map

route_map *_temp_pointer; //指向RouterDataMap_B 临时的关系map

pthread_rwlock_t _map_lock;

};

```

10.Lars-DnsV0.1-将Route数据加载map中

4.3 Route初始化

> lars_dns/src/dns_route.cpp

```c

#include <string>

#include <stdlib.h>

#include <unistd.h>

#include "lars_reactor.h"

#include "dns_route.h"

#include "string.h"

using namespace std;

//单例对象

Route * Route::_instance = NULL;

//用于保证创建单例的init方法只执行一次的锁

pthread_once_t Route::_once = PTHREAD_ONCE_INIT;

Route::Route()

{

//1 初始化锁

pthread_rwlock_init(&_map_lock, NULL);

//2 初始化map

_data_pointer = new route_map();//RouterDataMap_A

_temp_pointer = new route_map();//RouterDataMap_B

//3 链接数据库

this->connect_db();

//4 查询数据库,创建_data_pointer 与 _temp_pointer 两个map

this->build_maps();

}

void Route::connect_db()

{

// --- mysql数据库配置---

string db_host = config_file::instance()->GetString("mysql", "db_host", "127.0.0.1");

short db_port = config_file::instance()->GetNumber("mysql", "db_port", 3306);

string db_user = config_file::instance()->GetString("mysql", "db_user", "root");

string db_passwd = config_file::instance()->GetString("mysql", "db_passwd", "aceld");

string db_name = config_file::instance()->GetString("mysql", "db_name", "lars_dns");

mysql_init(&_db_conn);

//超时断开

mysql_options(&_db_conn, MYSQL_OPT_CONNECT_TIMEOUT, "30");

//设置mysql链接断开后自动重连

my_bool reconnect = 1;

mysql_options(&_db_conn, MYSQL_OPT_RECONNECT, &reconnect);

if (!mysql_real_connect(&_db_conn, db_host.c_str(), db_user.c_str(), db_passwd.c_str(), db_name.c_str(), db_port, NULL, 0)) {

fprintf(stderr, "Failed to connect mysql\n");

exit(1);

}

}

void Route::build_maps()

{

int ret = 0;

snprintf(_sql, 1000, "SELECT * FROM RouteData;");

ret = mysql_real_query(&_db_conn, _sql, strlen(_sql));

if ( ret != 0) {

fprintf(stderr, "failed to find any data, error %s\n", mysql_error(&_db_conn));

exit(1);

}

//得到结果集

MYSQL_RES *result = mysql_store_result(&_db_conn);

//得到行数

long line_num = mysql_num_rows(result);

MYSQL_ROW row;

for (long i = 0; i < line_num; i++) {

row = mysql_fetch_row(result);

int modID = atoi(row[1]);

int cmdID = atoi(row[2]);

unsigned ip = atoi(row[3]);

int port = atoi(row[4]);

//组装map的key,有modID/cmdID组合

uint64_t key = ((uint64_t)modID << 32) + cmdID;

uint64_t value = ((uint64_t)ip << 32) + port;

printf("modID = %d, cmdID = %d, ip = %lu, port = %d\n", modID, cmdID, ip, port);

//插入到RouterDataMap_A中

(*_data_pointer)[key].insert(value);

}

mysql_free_result(result);

}

```

11.Lars-Dns的proto协议定义

4.4 测试Route的构造及map加载-V0.1

完成lars dns-service V0.1版本测试

我们在`Lars/base/sql`加入几个简单插入数据的sql语句,方便数据库里有一些测试数据,我们之后应该会提供一个web管理端来操作数据库。

> Lars/base/sql/dns_route_insert.sql

```sql

USE lars_dns;

INSERT INTO RouteData(modid, cmdid, serverip, serverport) VALUES(1, 1, 3232235953, 7777);

INSERT INTO RouteData(modid, cmdid, serverip, serverport) VALUES(1, 2, 3232235954, 7776);

INSERT INTO RouteData(modid, cmdid, serverip, serverport) VALUES(1, 2, 3232235955, 7778);

INSERT INTO RouteData(modid, cmdid, serverip, serverport) VALUES(1, 2, 3232235956, 7779);

UPDATE RouteVersion SET version = UNIX_TIMESTAMP(NOW()) WHERE id = 1;

```

> Lars/base/sql/dns_route_drop.sql

```sql

USE lars_dns;

DELETE FROM RouteData;

UPDATE RouteVersion SET version = UNIX_TIMESTAMP(NOW()) WHERE id = 1;

```

​ 先将测试数据导入数据库。然后回到`lars_dns`下编译。执行

```bash

$./bin/lars_dns

msg_router init...

create 0 thread

create 1 thread

create 2 thread

create 3 thread

create 4 thread

modID = 1, cmdID = 1, ip = 3232235953, port = 7777

modID = 1, cmdID = 2, ip = 3232235954, port = 7776

modID = 1, cmdID = 2, ip = 3232235955, port = 7778

modID = 1, cmdID = 2, ip = 3232235956, port = 7779

lars dns service ....

```

12.Lars-DnsV0.1-实现获取route信息功能

5) 获取Route信息

5.1 proto协议定义

​ 获取Route信息,根据之前的架构图,可以看出来应该是Agent来获取,这里我们并没有实现Agent,所以用一个其他简单的客户端来完成单元测试。但是无论用什么,总需要一个传递数据,需要一定的消息协议,lars-dns也需要设置不同纤细的分发消息路由机制,所以我们需要先定义一些proto协议。

​ 在`Lars/base`下创建`proto/`文件夹.

> Lars/base/proto/lars.proto

```protobuf

syntax = "proto3";

package lars;

/* Lars系统的消息ID */

enum MessageId {

ID_UNKNOW = 0; //proto3 enum第一个属性必须是0,用来占位

ID_GetRouteRequest = 1; //向DNS请求Route对应的关系的消息ID

ID_GetRouteResponse = 2; //DNS回复的Route信息的消息ID

}

//一个管理的主机的信息

message HostInfo {

int32 ip = 1;

int32 port = 2;

}

//请求lars-dns route信息的消息内容

message GetRouteRequest {

int32 modid = 1;

int32 cmdid = 2;

}

//lars-dns 回复的route信息消息内容

message GetRouteResponse {

int32 modid = 1;

int32 cmdid = 2;

repeated HostInfo host = 3;

}

```

​ 然后我们将proto文件编译成对应的C++文件, 我们还是提供一个脚本

> Lars/base/proto/build.sh

```bash

#!/bin/bash

proto编译

protoc --cpp_out=. ./*.proto

将全部的cc 文件 变成 cpp文件

oldsuffix="cc"

newsuffix="cpp"

dir=$(eval pwd)

for file in (ls dir | grep .${oldsuffix})

do

name=(ls {file} | cut -d. -f1,2)

mv file {name}.${newsuffix}

done

echo "build proto file successd!"

```

​ 因为protoc会自动生成cc后缀的文件,为了方便我们Makefile的编译,所以将cc文件改成cpp的。

13.Lars-DnsV0.1-获取route hosts信息测试

5.2 proto编译环境集成

​ 现在我们将`lars-dns`的Makefile加入针对proto文件的编译

> Lars/lars_dns/Makefile

```makefile

TARGET= bin/lars_dns

CXX=g++

CFLAGS=-g -O2 -Wall -Wno-deprecated

BASE=../base

BASE_H=$(BASE)/include

PROTO = $(BASE)/proto

PROTO_H = $(BASE)/proto

LARS_REACTOR=../lars_reactor

LARS_REACTOR_H =$(LARS_REACTOR)/include

LARS_REACTOR_LIB=$(LARS_REACTOR)/lib -llreactor

MYSQL=$(BASE)/mysql-connector-c

MYSQL_H=$(MYSQL)/include

MYSQL_LIB=$(MYSQL)/lib/libmysqlclient.a

OTHER_LIB = -lpthread -ldl -lprotobuf

SRC= ./src

INC= -I./include -I(BASE_H) -I(LARS_REACTOR_H) -I(MYSQL_H) -I(PROTO_H)

LIB= (MYSQL_LIB) -L(LARS_REACTOR_LIB) $(OTHER_LIB)

OBJS = (addsuffix .o, (basename (wildcard (SRC)/*.cpp)))

OBJS += $(PROTO)/lars.pb.o

(TARGET): (OBJS)

mkdir -p bin

(CXX) (CFLAGS) -o (TARGET) (OBJS) (INC) (LIB)

%.o: %.cpp

(CXX) (CFLAGS) -c -o @ < $(INC)

.PHONY: clean

clean:

-rm -f src/*.o $(TARGET)

```

​ 添加了两个部分一个`OBJS`增添一个`lars.pb.o`的依赖,然后再`OTHER_LIB`增加`-lprotobuf`动态库的连接。

14.Lars-DnsV0.1-总结

5.3 实现Route获取

​ 接下来我们来实现针对`ID_GetRouteRequest`消息指令的业务处理.

> lars_dns/src/dns_service.cpp

```c

#include "lars_reactor.h"

#include "dns_route.h"

#include "lars.pb.h"

void get_route(const char *data, uint32_t len, int msgid, net_connection *net_conn, void *user_data)

{

//1. 解析proto文件

lars::GetRouteRequest req;

req.ParseFromArray(data, len);

//2. 得到modid 和 cmdid

int modid, cmdid;

modid = req.modid();

cmdid = req.cmdid();

//3. 根据modid/cmdid 获取 host信息

host_set hosts = Route::instance()->get_hosts(modid, cmdid);

//4. 将数据打包成protobuf

lars::GetRouteResponse rsp;

rsp.set_modid(modid);

rsp.set_cmdid(cmdid);

for (host_set_it it = hosts.begin(); it != hosts.end(); it ++) {

uint64_t ip_port = *it;

lars::HostInfo host;

host.set_ip((uint32_t)(ip_port >> 32));

host.set_port((int)(ip_port));

rsp.add_host()->CopyFrom(host);

}

//5. 发送给客户端

std::string responseString;

rsp.SerializeToString(&responseString);

net_conn->send_message(responseString.c_str(), responseString.size(), lars::ID_GetRouteResponse) ;

}

int main(int argc, char **argv)

{

event_loop loop;

//加载配置文件

config_file::setPath("conf/lars_dns.conf");

std::string ip = config_file::instance()->GetString("reactor", "ip", "0.0.0.0");

short port = config_file::instance()->GetNumber("reactor", "port", 7778);

//创建tcp服务器

tcp_server *server = new tcp_server(&loop, ip.c_str(), port);

//注册路由业务

server->add_msg_router(lars::ID_GetRouteRequest, get_route);

//开始事件监听

printf("lars dns service ....\n");

loop.event_process();

return 0;

}

```

​ 需要给`Route`类,实现一个get_host()方法,来针对modid/cmdid取出对应的value

> lars_dns/src/dns_route.cpp

```c

//获取modid/cmdid对应的host信息

host_set Route::get_hosts(int modid, int cmdid)

{

host_set hosts;

//组装key

uint64_t key = ((uint64_t)modid << 32) + cmdid;

pthread_rwlock_rdlock(&_map_lock);

route_map_it it = _data_pointer->find(key);

if (it != _data_pointer->end()) {

//找到对应的ip + port对

hosts = it->second;

}

pthread_rwlock_unlock(&_map_lock);

return hosts;

}

```

相关推荐
green_pine_39 分钟前
Vue3学习笔记2——路由守卫
前端·vue.js·笔记·学习
小羊在奋斗1 小时前
基于C++、JsonCpp、Muduo库实现的分布式RPC通信框架
c++·分布式·rpc
wjm0410061 小时前
C++八股--three day --设计模式之单例和工厂
c++·单例模式·设计模式
广州华锐视点2 小时前
VR 汽车线束培训:探索高效学习新路径
学习·汽车·vr
杜大哥2 小时前
Linux:如何查看Linux服务器的磁盘、CPU、内存信息?
linux·运维·服务器
纪元A梦2 小时前
华为OD机试真题——告警抑制(2025A卷:100分)Java/python/JavaScript/C/C++/GO最佳实现
java·c语言·javascript·c++·python·华为od
海尔辛2 小时前
黑客学习计划
学习
mahuifa2 小时前
(34)VTK C++开发示例 ---将图片映射到平面
c++·平面·3d·vtk·cmake
DIY机器人工房4 小时前
[3-2]GPIO输入 江协科技学习笔记(17个知识点)
笔记·科技·学习
luckywuxn4 小时前
ant design pro 项目发布遇到登录页访问404
运维·服务器·网络