当前位置: 首页 > news >正文

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信息测试

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;
}
```

相关文章:

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数…...

Linux53 百度网盘运行(下载devtoolset11后仍提示stdc++3.0.29缺失 计划用docker容器隔离运行,计划后续再看)

算了 放弃 都用到docker了 计划先看看系统服务后续再研究吧 百度网盘运行(下载devtoolset11后仍提示stdc3.0.29缺失 计划用docker容器隔离运行 但是由于系统服务未扎实&#xff0c;计划后续再看 重新下了el7的版本 刚才已启动成功 单输入xlock不启动 切换用户也不启动 …...

ASP.NET MVC​ 入门与提高指南八

45. 神经形态计算与 MVC 应用性能革新 45.1 神经形态计算概念 神经形态计算是借鉴生物神经系统的结构和工作原理来设计计算系统。它模仿人脑神经元和突触的工作方式&#xff0c;具备低功耗、高并行性和自适应学习等特性&#xff0c;能在处理复杂感知和认知任务时展现出卓越的…...

Python刷题:流程控制(下)

今天刷的是PythonTip的Python 入门挑战中的题&#xff0c;整体难度不高&#xff0c;适合小白练手以及巩固知识点。下面会进行详细讲解。 每日一句 在无人问津的角落里&#xff0c; 默默努力的人独自发光&#xff0c; 孤独是他们奋斗路上的常客&#xff0c; 却也是成就他们的…...

【Bootstrap V4系列】学习入门教程之 组件-徽章(Badge)和面包屑导航(Breadcrumb)

Bootstrap V4系列 学习入门教程之 组件-徽章&#xff08;Badge&#xff09;和面包屑导航&#xff08;Breadcrumb&#xff09; 徽章&#xff08;Badge&#xff09;一、示例二、根据情境改变外观三、胶囊式徽章&#xff08;Pill badges&#xff09;四、链接 面包屑导航&#xff0…...

结合强化学习RL和SFT各自训练优势,让模型边学边练,从而平衡Zero-RL训练中的模仿和探索!!

摘要&#xff1a;最近在大型推理模型&#xff08;LRMs&#xff09;方面的进展表明&#xff0c;通过简单的基于规则的奖励进行强化学习&#xff08;RL&#xff09;&#xff0c;可以涌现出复杂的行为&#xff0c;例如多步推理和自我反思。然而&#xff0c;现有的零强化学习&#…...

ai之paddleOCR 识别PDF python312和paddle版本冲突 GLIBCXX_3.4.30

这里写自定义目录标题 问题一**解决方案****方法 1&#xff1a;使用符号链接将系统库链接到 Conda 环境** **补充说明****验证修复结果** 问题二&#xff1a;**问题根源****解决方案****1. 确认 TensorRT 安装状态****2. 安装 TensorRT 并配置环境变量****3. 验证 TensorRT 与 …...

C++ 单例模式详解

单例模式是一种创建型设计模式&#xff0c;它确保一个类只有一个实例&#xff0c;并提供一个全局访问点来访问该实例。 概念解析 单例模式的核心思想是&#xff1a; 限制类的实例化次数&#xff0c;确保在整个应用程序中只有一个实例存在 提供对该实例的全局访问点 控制共享…...

生成器模式(Builder Pattern)

好问题&#xff01;生成器模式&#xff08;Builder Pattern&#xff09;在现实生活和程序开发中非常常见&#xff0c;它适合创建**“一步一步搭建起来的复杂对象”**。 &#x1f9e0; 一句话定义 生成器模式&#xff08;Builder Pattern&#xff09;是一种将复杂对象的构建过程…...

计算机网络八股文--day4 --传输层TCP与UDP

这是面试中最常考到的一层&#xff1a;端到端&#xff08;也就是进程之间&#xff09;的透明数据传输服务&#xff0c;差错控制和流量控制 该层呈上启下&#xff0c;像上面的资源子网提高服务&#xff0c;并使用下面通信子网的服务 端口&#xff0c;用于唯一标识主机上进程的&…...

大型语言模型个性化助手实现

大型语言模型个性化助手实现 目录 大型语言模型个性化助手实现PERSONAMEM,以及用户资料和对话模拟管道7种原位用户查询类型关于大语言模型个性化能力评估的研究大型语言模型(LLMs)已经成为用户在各种任务中的个性化助手,从提供写作支持到提供量身定制的建议或咨询。随着时间…...

步进电机中断函数解释

STM32 motor111.c 中 HAL_TIM_PeriodElapsedCallback 函数逐行解释 下面我们对 STM32 项目中 motor111.c 文件里的 HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) 函数进行逐行解析&#xff0c;帮助初学者理解每一行代码的作用。此函数是在定时器产生更新中断时被调…...

多把锁以及线程死锁问题

在 Java 中&#xff0c;每一个对象都可以作为一把锁&#xff0c;synchronized 通过获取对象头中的锁标志位来实现同步。当一个线程获取到对象的锁后&#xff0c;其他线程就无法再获取该锁&#xff0c;只能等待获取到锁的线程释放锁之后才能继续执行被 synchronized 修饰的代码块…...

Vue 3 Element Plus 浏览器使用例子

Element Plus 是一个基于 Vue 3 的流行开源 UI 库&#xff0c;提供了一系列的组件&#xff0c;帮助开发者快速构建现代化的用户界面。它的设计简洁、现代&#xff0c;包含了许多可定制的组件&#xff0c;如按钮、表格、表单、对话框等&#xff0c;适合用于开发各种 Web 应用。 …...

NoxLucky:个性化动态桌面,打造独一无二的手机体验

在数字时代&#xff0c;手机桌面的个性化设置已经成为许多人表达自我和展示个性的重要方式。今天&#xff0c;我们要介绍的 NoxLucky&#xff0c;就是这样一款功能强大的动态桌面手机应用。它不仅提供了独家的动态壁纸素材库&#xff0c;还支持将抖音、INS等平台的短视频直接设…...

如何在WORD WPS中输入英语音标 批量给英语标注音标

在我国&#xff0c;英语的学习&#xff0c;已经是贯穿小学到大学的课程&#xff0c;英语学习&#xff0c;关键是听说读写&#xff0c;而听说读&#xff0c;都离不开一个字--音&#xff0c;这就涉及到了英语的音标了。音标&#xff08;Phonetics&#xff09;是表示单词发音的一种…...

RUST变量学习笔记

1&#xff0c;作用域概念 捕获环境 2&#xff0c;所有权概念 Rust则是通过所有权和借用来保证内存安全。很多人不理解为啥说Rust是内存安全的&#xff0c;其实就是在默认情况下&#xff0c;你是写不出内存不安全的代码的。 Rust的所有权并不难理解&#xff0c;它有且只有如下…...

n8n工作流自动化平台的实操:本地化高级部署

一、本地高级部署 1.下载 docker pull docker.n8n.io/n8nio/n8n 2.运行 docker volume create n8n_data docker run -dit --name n8n -p 5678:5678 -v n8n_data:/home/node/.n8n -e N8N_SECURE_COOKIEfalse -e N8N_RUNNERS_ENABLEDtrue -e N8N_ENFORCE_SETTINGS_FIL…...

【Ansible自动化运维实战:从Playbook到负载均衡指南】

本文是「VagrantVirtualBox虚拟化环境搭建」的续篇&#xff0c;深入探索Ansible在自动化运维中的核心应用&#xff1a; ✅ Ansible核心技能&#xff1a;Playbook编写、角色&#xff08;Roles&#xff09;模块化、标签&#xff08;Tags&#xff09;精准控制 ✅ 实战场景覆盖&a…...

数据赋能(210)——质量管理——可靠性原则

概述 数据可靠性原则确保数据的准确性、完整性、一致性和可信性&#xff0c;是决策和业务活动对数据质量的基本要求。在信息化和数字化快速发展的今天&#xff0c;数据已成为企业的重要资产&#xff0c;数据可靠性直接影响到企业的决策质量和业务活动效果。数据可靠性是数据质…...

二、机器学习中Python变量基础

二、Python变量基础 像C语言和Matlab一样&#xff0c;变量名由字母、数字、下划线组成&#xff08;但不能以数字开头&#xff0c;字母区分大小写&#xff09;变量名不能与内置的函数同名。 根据变量是否可以充当容器&#xff0c;将变量类型分为基本类型和高级类型。 基本变量…...

有机玻璃材质数据采集活性炭吸附气体中二氧化硫实验装置

JGQ112Ⅱ有机玻璃材质数据采集活性炭吸附气体中二氧化硫实验装置 一.实验目的 1.熟悉活性炭吸附剂的特性和在SO2气体净化方面的应用。 2.掌握活性炭吸附法的流程和实验过程中各参数的控制方法。 3.了解主要参数变化对吸附效率的影响。 4.掌握吸附等温线概念和测定方法。 二.技术…...

Javase 基础入门 —— 07 接口

本系列为笔者学习Javase的课堂笔记&#xff0c;视频资源为B站黑马程序员出品的《黑马程序员JavaAI智能辅助编程全套视频教程&#xff0c;java零基础入门到大牛一套通关》&#xff0c;章节分布参考视频教程&#xff0c;为同样学习Javase系列课程的同学们提供参考。 01 概述 接…...

LangChain:重构大语言模型应用开发的范式革命

2022年10月22日&#xff0c;Harrison Chase在GitHub上提交了名为LangChain的开源项目的第一个代码版本。这个看似普通的代码提交&#xff0c;却悄然开启了一场重塑大语言模型&#xff08;LLM&#xff09;应用开发范式的技术革命。彼时&#xff0c;距离ChatGPT引爆全球人工智能浪…...

【现代深度学习技术】现代循环神经网络04:双向循环神经网络

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈PyTorch深度学习 ⌋ ⌋ ⌋ 深度学习 (DL, Deep Learning) 特指基于深层神经网络模型和方法的机器学习。它是在统计机器学习、人工神经网络等算法模型基础上&#xff0c;结合当代大数据和大算力的发展而发展出来的。深度学习最重…...

重塑数学边界:人工智能如何引领数学研究的新纪元

目录 一、人工智能如何重新定义数学研究的边界 &#xff08;一&#xff09;数学与AI的关系&#xff1a;从基础理论到创新思维的回馈 &#xff08;二&#xff09;AI的创造力&#xff1a;突破传统推理的局限 &#xff08;三&#xff09;AI对数学研究的潜在贡献&#xff1a;创…...

链表的回文结构题解

首先阅读题目&#xff1a; 1.要保证是回文结构 2.他的时间复杂度为O(n)、空间复杂度为O(1) 给出思路: 1.首先利用一个函数找到中间节点 2.利用一个函数逆置中间节点往后的所有节点 3.现在有两个链表&#xff0c;第一个链表取头节点一直到中间节点、第二个链表取头结点到尾…...

xLua笔记

Generate Code干了什么 肉眼可见的&#xff0c;在Asset文件夹生成了XLua/Gen文件夹&#xff0c;里面有一些脚本。然后对加了[CSharpCallLua]的变量寻找引用&#xff0c;发现它被XLua/Gen/DelegatesGensBridge引用了。也可以在这里查哪些类型加了[CSharpCallLua]。 public over…...

【Hive入门】Hive与Spark SQL深度集成:通过Spark ThriftServer高效查询Hive表

目录 引言 1 Spark ThriftServer架构解析 1.1 核心组件与工作原理 1.2 与传统HiveServer2的对比 2 Spark ThriftServer部署指南 2.1 环境准备与启动流程 2.1.1 前置条件检查 2.1.2 服务启动流程 2.2 高可用部署方案 2.2.1 基于ZooKeeper的HA架构 3 性能优化实战 3.…...

快速掌握--cursor

Cursor - The AI Code Editor 官网下载安装 详细教程&#xff1a;cursor 下载安装使用&#xff08;保姆教程&#xff09;_cursor下载-CSDN博客 不知道为啥&#xff0c;第一次给我用的是繁体回答 然后改了一下询问方式 codebase就是告诉ai可以从整个项目中找答案&#xff0…...

Linux之基础开发工具(yum,vim,gcc,g++)

目录 一、软件包管理器 1.1、什么是软件包 1.2、yum具体操作 1.2.1、查看软件包 1.2.2、安装软件 1.2.3、卸载软件 1.2.4、安装源 二、编辑器vim 2.1、vim的基本概念 2.2、vim的基本操作 2.3、vim正常模式命令集 2.4、vim末行模式命令集 2.5、替换模式 2.6、视图…...

【计算机视觉】三维重建: OpenMVS:工业级多视图立体视觉重建框架

深度解析OpenMVS&#xff1a;工业级多视图立体视觉重建框架 技术架构与核心算法1. 系统架构设计2. 核心算法解析稠密点云重建表面重建网格优化 实战全流程指南环境配置硬件要求编译安装&#xff08;Ubuntu&#xff09; 数据处理流程输入准备&#xff08;OpenMVG输出&#xff09…...

C++负载均衡远程调用学习之异步消息任务功能与连接属性

目录 1.LarV0.11-异步消息机制的event_loop增添属性分析 2.LARS 3.LarV0.11异步消息发送机制的实现及测试 4.LarV0.11异步消息任务机制bug修复和效果演示 5.LarV0.12链接参数属性的绑定 1.LarV0.11-异步消息机制的event_loop增添属性分析 ## 4) 事件触发event_loop ​ …...

内存性能测试方法

写于 2022 年 6 月 24 日 内存性能测试方法 - Wesley’s Blog dd方法测试 cat proc/meminfo console:/ # cat proc/meminfo MemTotal: 3858576 kB MemFree: 675328 kB MemAvailable: 1142452 kB Buffers: 65280 kB Cached: 992252 …...

游戏引擎学习第256天:XBox 控制器卡顿和修复 GL Blit 伽玛问题

回顾并为今天定下基调 今天的主要任务是让我们的性能分析工具正常工作&#xff0c;因为昨天已经完成了结构性工作。现在&#xff0c;剩下的工作大部分应该是调试和美化。性能分析工具现在应该已经基本可用了。昨天我们在这个方面取得了很大的进展。 接下来&#xff0c;我们将…...

4.29-4.30 Maven+单元测试

单元测试&#xff1a; BeforeAll在所有的单元测试方法运行之前&#xff0c;运行一次。 AfterAll在所有单元测试方法运行之后&#xff0c;运行一次。 BeforeEach在每个单元测试方法运行之前&#xff0c;都会运行一次 AfterEach在每个单元测试方法运行之后&#xff0c;都会运行…...

Android 端如何监控 ANR、Crash、OOM 等严重问题

在移动互联网时代&#xff0c;Android 应用已经成为我们生活中不可或缺的一部分。从社交聊天到在线购物&#xff0c;从娱乐消遣到办公学习&#xff0c;几乎每个人的手机里都装满了各式各样的应用。然而&#xff0c;作为开发者&#xff0c;咱们得面对一个残酷的现实&#xff1a;…...

Spring Boot 微服务打包为 Docker 镜像并部署到镜像仓库实战案例

案例项目素材可以拉取我github上的&#xff1a; https://github.com/AcademicTECHNERD/SpringCoudEurekaDemo 下面的案例将把我的product-service&#xff08;也就是提供者&#xff09;打包为镜像 执行maven命令&#xff1a; mvn clean package -DskipTests在根目录加一个dock…...

springAop代理责任链模式源码解析

目录 两次匹配 Bean 后置处理器中的匹配 方法调用时的匹配 Bean后置处理器中Advisor匹配流程 方法调用时的匹配 Jdk cglib 小小总结 Advisor 收集与排序 责任链执行过程 两次匹配 Bean 后置处理器中的匹配 在 Bean 初始化过程中&#xff0c;Spring 会通过 Bean 后置…...

ElasticSearch深入解析(九):Object、Nested、Flattened类型

文章目录 一、Object 类型&#xff1a;默认的嵌套对象处理方式核心原理典型场景关键限制 二、Nested 类型&#xff1a;解决嵌套数组的关联查询核心原理典型场景使用示例注意事项 三、Join 类型&#xff1a;跨文档的父子关联核心原理典型场景使用示例注意事项 四、Flattened 类型…...

list的迭代器详讲

1.list的迭代器就是封装了节点指针的类 2.迭代器失效 迭代器失效即迭代器封装的节点指针无效 。因为 list 的底层结构为带头结点的双向循环链表 &#xff0c;因此 在 list 中进行插入时是不会导致 list 的迭代 器失效的&#xff0c;只有在删除时才会失效&#xff0c;并且失效的…...

动态规划之多状态问题1

题目解析&#xff1a; 也就是给一个预约数组&#xff0c;选择一些数字&#xff0c;让其总和最大&#xff0c;但不能选择相邻的两个数字 算法原理&#xff1a; 依旧可以根据经验题目 以dp[i]位置结尾时&#xff0c;巴拉巴拉 根据题目要求补充完整&#xff0c;dp[i]&#xff…...

音视频开源项目列表

音视频开源项目列表 一、多媒体处理框架 通用音视频处理 FFmpeg - https://github.com/FFmpeg/FFmpeg 最强大的音视频处理工具库支持几乎所有格式的编解码提供命令行工具和开发库 GStreamer - https://gitlab.freedesktop.org/gstreamer/gstreamer 跨平台多媒体框架基于管道…...

论微服务架构及其应用

试题四 论微服务架构及其应用 微服务提倡将单一应用程序划分成一组小的服务&#xff0c;服务之间互相协调、互相配合&#xff0c;为用户提供最终价值。每个服务运行在其独立的进程中&#xff0c;服务与服务间采用轻量级的通信机制互相沟通。在微服务架构中&#xff0c;每个服务…...

Spring Cloud与Service Mesh集成:Istio服务网格实践

文章目录 引言一、Spring Cloud与Service Mesh概述二、Istio服务网格架构三、Spring Cloud与Istio集成的基础设施准备四、服务发现与负载均衡五、流量管理与弹性模式六、安全通信与认证授权七、可观测性集成八、配置管理集成总结 引言 微服务架构已成为现代分布式系统的主流设…...

Day109 | 灵神 | 148.排序链表 | 归并排序

Day109 | 灵神 | 148.排序链表 | 归并排序 148. 排序链表 - 力扣&#xff08;LeetCode&#xff09; 以下是灵神的题解&#xff0c;笔者认为这题只要可以看懂就好了 两种方法&#xff1a;分治和迭代 文章目录 Day109 | 灵神 | 148.排序链表 | 归并排序前置题目方法一&#x…...

[更新完毕]2025东三省C题深圳杯C题数学建模挑战赛数模思路代码文章教学: 分布式能源接入配电网的风险分析

完整内容请看文章最下面的推广群 分布式能源接入配电网的风险分析 摘要 随着可再生能源渗透率的不断提升&#xff0c;分布式光伏发电在配电网中的大规模接入给传统电力系统运行带来了新的挑战。光伏发电固有的间歇性和波动性特征&#xff0c;加之配电网拓扑结构的复杂性&…...

ActiveMQ 集群搭建与高可用方案设计(二)

五、高可用方案设计与优化 &#xff08;一&#xff09;Zookeeper 在 ActiveMQ 集群中的应用 作用&#xff1a;在 ActiveMQ 集群中&#xff0c;Zookeeper 扮演着至关重要的角色。它主要用于选举 Master 节点&#xff0c;通过其内部的选举机制&#xff0c;从众多的 ActiveMQ Br…...

多协议 Tracker 系统架构与传感融合实战 第六章 多传感器时钟同步与数据对齐

第六章 多传感器时钟同步与数据对齐 摘要 本章围绕多源传感融合系统中——尤其是 IMU 与 UWB——的时钟同步与数据对齐问题展开,系统介绍: 硬件时钟源类型及漂移特性 软件校准策略:NTP/PTP 与自定义心跳同步 多源时钟同步算法:两阶段对齐与漂移补偿 数据缓冲与双队列对齐架…...

【算法基础】插入排序算法 - JAVA

一、算法基础 1.1 什么是插入排序 插入排序是一种简单直观的排序算法&#xff0c;它的工作原理类似于我们打牌时整理手牌的过程。插入排序的核心思想是将数组分为已排序和未排序两部分&#xff0c;每次从未排序部分取出一个元素&#xff0c;插入到已排序部分的适当位置。 1.…...