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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/79328.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/79328.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/79328.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

fastapi+vue中的用戶權限管理設計

數據庫設計&#xff1a;RBAC數據模型 這是一個典型的基于SQLAlchemy的RBAC權限系統數據模型實現&#xff0c;各模型分工明確&#xff0c;共同構成完整的權限管理系統。 圖解說明&#xff1a; 實體關系&#xff1a; 用戶(USER)和角色(ROLE)通過 USER_ROLE 中間表實現多對多關系…

【Python實戰】飛機大戰

開發一個飛機大戰游戲是Python學習的經典實戰項目&#xff0c;尤其適合結合面向對象編程和游戲框架&#xff08;如Pygame&#xff09;進行實踐。以下是游戲設計的核心考慮因素和模塊劃分建議&#xff1a; 一、游戲設計核心考慮因素 性能優化 Python游戲需注意幀率控制&#xff…

Flowable7.x學習筆記(十八)拾取我的待辦

前言 本文從解讀源碼到實現功能&#xff0c;完整的學習Flowable的【TaskService】-【claim】方法實現的任務拾取功能。 一、概述 當調用 TaskService.claim(taskId, userId) 時&#xff0c;Flowable 會先加載并校驗任務實體&#xff0c;再判斷該任務是否已被認領&#xff1b;若…

SQL經典實例

第1章 檢索記錄 1.1 檢索所有行和列 知識點&#xff1a;使用SELECT *快速檢索表中所有列&#xff1b;顯式列出列名&#xff08;如SELECT col1, col2&#xff09;提高可讀性和可控性&#xff0c;尤其在編程場景中更清晰。 1.2 篩選行 知識點&#xff1a;通過WHERE子句過濾符合條…

HTTPcookie與session實現

1.HTTP Cookie 定義 HTTP Cookie &#xff08;也稱為 Web Cookie 、瀏覽器 Cookie 或簡稱 Cookie &#xff09;是服務器發送到 用戶瀏覽器并保存在瀏覽器上的一小塊數據&#xff0c;它會在瀏覽器之后向同一服務器再次發 起請求時被攜帶并發送到服務器上。通常&#xff0…

【算法基礎】冒泡排序算法 - JAVA

一、算法基礎 1.1 什么是冒泡排序 冒泡排序是一種簡單直觀的比較排序算法。它重復地走訪待排序的數列&#xff0c;依次比較相鄰兩個元素&#xff0c;如果順序錯誤就交換它們&#xff0c;直到沒有元素需要交換為止。 1.2 基本思想 比較相鄰元素&#xff1a;從頭開始&#xf…

0902Redux_狀態管理-react-仿低代碼平臺項目

文章目錄 1 Redux 概述1.1 核心概念1.2 基本組成1.3 工作流程1.4 中間件&#xff08;Middleware&#xff09;1.5 適用場景1.6 優缺點1.7 Redux Toolkit&#xff08;現代推薦&#xff09;1.8 與其他工具的對比1.9 總結 2 todoList 待辦事項案例3 Redux開發者工具3.1 核心功能3.2…

《ATPL地面培訓教材13:飛行原理》——第6章:阻力

翻譯&#xff1a;Leweslyh&#xff1b;工具&#xff1a;Cursor & Claude 3.7&#xff1b;過程稿 第6章&#xff1a;阻力 目錄 引言寄生阻力誘導阻力減少誘導阻力的方法升力對寄生阻力的影響飛機總阻力飛機總重量對總阻力的影響高度對總阻力的影響構型對總阻力的影響速度穩…

C++總結01-類型相關

一、數據存儲 1.程序數據段 ? 靜態&#xff08;全局&#xff09;數據區&#xff1a;全局變量、靜態變量 ? 堆內存&#xff1a;程序員手動分配、手動釋放 ? 棧內存&#xff1a;編譯器自動分配、自動釋放 ? 常量區&#xff1a;編譯時大小、值確定不可修改 2.程序代碼段 ?…

【Hot 100】94. 二叉樹的中序遍歷

目錄 引言二叉樹的中序遍歷我的解題代碼優化更清晰的表述建議&#xff1a; &#x1f64b;?♂? 作者&#xff1a;海碼007&#x1f4dc; 專欄&#xff1a;算法專欄&#x1f4a5; 標題&#xff1a;【Hot 100】94. 二叉樹的中序遍歷?? 寄語&#xff1a;書到用時方恨少&#xff…

大語言模型(LLMs)微調技術總結

文章目錄 全面總結當前大語言模型&#xff08;LLM&#xff09;微調技術1. 引言2. 為什么需要微調&#xff1f;3. 微調技術分類概覽4. 各種微調技術詳細介紹4.1 基礎微調方法4.1.1 有監督微調&#xff08;Supervised Fine-Tuning, SFT&#xff09;4.1.2 全參數微調&#xff08;F…

解決Maven項目中報錯“java不支持版本6即更高的版本 7”

錯誤背景 當Maven項目編譯或運行時出現錯誤提示 Java不支持版本6即更高的版本7&#xff0c;通常是由于項目配置的JDK版本與當前環境或編譯器設置不一致導致的。例如&#xff1a; 項目配置的Java版本為6或7&#xff0c;但實際使用的是JDK 17。Maven或IDE的編譯器未正確指定目標…

C++筆記-多態(包含虛函數,純虛函數和虛函數表等)

1.多態的概念 多態(polymorphism)的概念:通俗來說&#xff0c;就是多種形態。多態分為編譯時多態(靜態多態)和運行時多態(動態多態)&#xff0c;這里我們重點講運行時多態&#xff0c;編譯時多態(靜態多態)和運行時多態(動態多態)。編譯時多態(靜態多態)主要就是我們前面講的函…

【Unity】MVP框架的使用例子

在提到MVP之前&#xff0c;可以先看看這篇MVC的帖子&#xff1a; 【Unity】MVC的簡單分享以及一個在UI中使用的例子 MVC的不足之處&#xff1a; 在MVC的使用中&#xff0c;會發現View層直接調用了Model層的引用&#xff0c;即這兩個層之間存在著一定的耦合性&#xff0c;而MV…

前端js學算法-實踐

1、兩數之和 const twoSum (nums, target) > {const obj {}for (let m 0; m < nums.length; m) {const cur nums[m]const diff target - curif(obj.hasOwnProperty(diff)){ // 查詢對象中是否存在目標值-當前值鍵值對console.log([obj[diff], m]) // 存在則直接獲取…

《MATLAB實戰訓練營:從入門到工業級應用》趣味入門篇-用聲音合成玩音樂:MATLAB電子琴制作(超級趣味實踐版)

《MATLAB實戰訓練營&#xff1a;從入門到工業級應用》趣味入門篇-用聲音合成玩音樂&#xff1a;MATLAB電子琴制作&#xff08;超級趣味實踐版&#xff09; 開篇&#xff1a;當MATLAB遇見音樂 - 一場數字與藝術的浪漫邂逅 想象一下&#xff0c;你正坐在一臺古老的鋼琴前&#x…

實戰探討:為什么 Redis Zset 選擇跳表?

在了解了跳表的原理和實現后&#xff0c;一個常見的問題&#xff08;尤其是在面試中&#xff09;隨之而來&#xff1a;為什么像 Redis 的有序集合 (Zset) 這樣的高性能組件會選擇使用跳表&#xff0c;而不是大家熟知的平衡樹&#xff08;如紅黑樹&#xff09;呢&#xff1f; 對…

數據結構-線性結構(鏈表、棧、隊列)實現

公共頭文件common.h #define TRUE 1 #define FALSE 0// 定義節點數據類型 #define DATA_TYPE int單鏈表C語言實現 SingleList.h #pragma once#include "common.h"typedef struct Node {DATA_TYPE data;struct Node *next; } Node;Node *initList();void headInser…

高中數學聯賽模擬試題精選學數學系列第3套幾何題

△ A B C \triangle ABC △ABC 的內切圓 ⊙ I \odot I ⊙I 分別與邊 B C BC BC, C A CA CA, A B AB AB 相切于點 D D D, E E E, F F F, D D ′ DD DD′ 為 ⊙ I \odot I ⊙I 的直徑, 過圓心 I I I 作直線 A D ′ AD AD′ 的垂線 l l l, 直線 l l l 分別與 D E DE…

使用 ossutil 上傳文件到阿里云 OSS

在處理文件存儲和傳輸時&#xff0c;阿里云的對象存儲服務&#xff08;OSS&#xff09;是一個非常方便的選擇。特別是在需要批量上傳文件或通過命令行工具進行文件管理時&#xff0c;ossutil提供了強大的功能。本文將詳細說明如何使用 ossutil 上傳文件到阿里云 OSS&#xff0c…