(22)獲取Route信息(Dns部分)-【Lars-基于C++負(fù)載均衡遠(yuǎn)程服務(wù)器調(diào)度系統(tǒng)教程】

【Lars教程目錄】

Lars源代碼
https://github.com/aceld/Lars


【Lars系統(tǒng)概述】
第1章-概述
第2章-項(xiàng)目目錄構(gòu)建


【Lars系統(tǒng)之Reactor模型服務(wù)器框架模塊】
第1章-項(xiàng)目結(jié)構(gòu)與V0.1雛形
第2章-內(nèi)存管理與Buffer封裝
第3章-事件觸發(fā)EventLoop
第4章-鏈接與消息封裝
第5章-Client客戶端模型
第6章-連接管理及限制
第7章-消息業(yè)務(wù)路由分發(fā)機(jī)制
第8章-鏈接創(chuàng)建/銷(xiāo)毀Hook機(jī)制
第9章-消息任務(wù)隊(duì)列與線程池
第10章-配置文件讀寫(xiě)功能
第11章-udp服務(wù)與客戶端
第12章-數(shù)據(jù)傳輸協(xié)議protocol buffer
第13章-QPS性能測(cè)試
第14章-異步消息任務(wù)機(jī)制
第15章-鏈接屬性設(shè)置功能


【Lars系統(tǒng)之DNSService模塊】
第1章-Lars-dns簡(jiǎn)介
第2章-數(shù)據(jù)庫(kù)創(chuàng)建
第3章-項(xiàng)目目錄結(jié)構(gòu)及環(huán)境構(gòu)建
第4章-Route結(jié)構(gòu)的定義
第5章-獲取Route信息
第6章-Route訂閱模式
第7章-Backend Thread實(shí)時(shí)監(jiān)控


【Lars系統(tǒng)之Report Service模塊】
第1章-項(xiàng)目概述-數(shù)據(jù)表及proto3協(xié)議定義
第2章-獲取report上報(bào)數(shù)據(jù)
第3章-存儲(chǔ)線程池及消息隊(duì)列


【Lars系統(tǒng)之LoadBalance Agent模塊】
第1章-項(xiàng)目概述及構(gòu)建
第2章-主模塊業(yè)務(wù)結(jié)構(gòu)搭建
第3章-Report與Dns Client設(shè)計(jì)與實(shí)現(xiàn)
第4章-負(fù)載均衡模塊基礎(chǔ)設(shè)計(jì)
第5章-負(fù)載均衡獲取Host主機(jī)信息API
第6章-負(fù)載均衡上報(bào)Host主機(jī)信息API
第7章-過(guò)期窗口清理與過(guò)載超時(shí)(V0.5)
第8章-定期拉取最新路由信息(V0.6)
第9章-負(fù)載均衡獲取Route信息API(0.7)
第10章-API初始化接口(V0.8)
第11章-Lars Agent性能測(cè)試工具
第12章- Lars啟動(dòng)工具腳本


5) 獲取Route信息

5.1 proto協(xié)議定義

? 獲取Route信息,根據(jù)之前的架構(gòu)圖,可以看出來(lái)應(yīng)該是Agent來(lái)獲取,這里我們并沒(méi)有實(shí)現(xiàn)Agent,所以用一個(gè)其他簡(jiǎn)單的客戶端來(lái)完成單元測(cè)試。但是無(wú)論用什么,總需要一個(gè)傳遞數(shù)據(jù),需要一定的消息協(xié)議,lars-dns也需要設(shè)置不同纖細(xì)的分發(fā)消息路由機(jī)制,所以我們需要先定義一些proto協(xié)議。

? 在Lars/base下創(chuàng)建proto/文件夾.

Lars/base/proto/lars.proto

syntax = "proto3";

package lars;

/* Lars系統(tǒng)的消息ID */
enum MessageId {
    ID_UNKNOW                = 0;  //proto3 enum第一個(gè)屬性必須是0,用來(lái)占位
    ID_GetRouteRequest       = 1;  //向DNS請(qǐng)求Route對(duì)應(yīng)的關(guān)系的消息ID
    ID_GetRouteResponse      = 2;  //DNS回復(fù)的Route信息的消息ID
}

//一個(gè)管理的主機(jī)的信息
message HostInfo {
    int32 ip = 1;
    int32 port = 2;
}

//請(qǐng)求lars-dns route信息的消息內(nèi)容
message GetRouteRequest {
    int32 modid = 1; 
    int32 cmdid = 2;
}

//lars-dns 回復(fù)的route信息消息內(nèi)容
message GetRouteResponse {
    int32 modid = 1;    
    int32 cmdid = 2;
    repeated HostInfo host = 3;
}

? 然后我們將proto文件編譯成對(duì)應(yīng)的C++文件, 我們還是提供一個(gè)腳本

Lars/base/proto/build.sh

#!/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!"

? 因?yàn)閜rotoc會(huì)自動(dòng)生成cc后綴的文件,為了方便我們Makefile的編譯,所以將cc文件改成cpp的。

5.2 proto編譯環(huán)境集成

? 現(xiàn)在我們將lars-dns的Makefile加入針對(duì)proto文件的編譯

Lars/lars_dns/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)

? 添加了兩個(gè)部分一個(gè)OBJS增添一個(gè)lars.pb.o的依賴,然后再OTHER_LIB增加-lprotobuf動(dòng)態(tài)庫(kù)的連接。

5.3 實(shí)現(xiàn)Route獲取

? 接下來(lái)我們來(lái)實(shí)現(xiàn)針對(duì)ID_GetRouteRequest消息指令的業(yè)務(wù)處理.

lars_dns/src/dns_service.cpp

#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. 根據(jù)modid/cmdid 獲取 host信息
    host_set hosts = Route::instance()->get_hosts(modid, cmdid);

    //4. 將數(shù)據(jù)打包成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. 發(fā)送給客戶端
    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);


    //創(chuàng)建tcp服務(wù)器
    tcp_server *server = new tcp_server(&loop, ip.c_str(), port);

    //注冊(cè)路由業(yè)務(wù)
    server->add_msg_router(lars::ID_GetRouteRequest, get_route);

    //開(kāi)始事件監(jiān)聽(tīng)    
    printf("lars dns service ....\n");
    loop.event_process();

    return 0;
}

? 需要給Route類,實(shí)現(xiàn)一個(gè)get_host()方法,來(lái)針對(duì)modid/cmdid取出對(duì)應(yīng)的value

lars_dns/src/dns_route.cpp

//獲取modid/cmdid對(duì)應(yīng)的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()) {
        //找到對(duì)應(yīng)的ip + port對(duì)
        hosts = it->second;
    }
    pthread_rwlock_unlock(&_map_lock);

    return hosts;
}

5.3 lars_dns獲取Route信息測(cè)試-V0.2

? 下面我們寫(xiě)一個(gè)客戶端檢查測(cè)試一下該功能.

lars_dns/test/lars_dns_test1.cpp

#include <string.h>
#include <unistd.h>
#include <string>
#include "lars_reactor.h"
#include "lars.pb.h"

//命令行參數(shù)
struct Option
{
    Option():ip(NULL),port(0) {}

    char *ip;
    short port;
};


Option option;

void Usage() {
    printf("Usage: ./lars_dns_test -h ip -p port\n");
}

//解析命令行
void parse_option(int argc, char **argv)
{
    for (int i = 0; i < argc; i++) {
        if (strcmp(argv[i], "-h") == 0) {
            option.ip = argv[i + 1];
        }
        else if (strcmp(argv[i], "-p") == 0) {
            option.port = atoi(argv[i + 1]);
        }
    }

    if ( !option.ip || !option.port ) {
        Usage();
        exit(1);
    }

}

//typedef void (*conn_callback)(net_connection *conn, void *args);
void on_connection(net_connection *conn, void *args) 
{
    //發(fā)送Route信息請(qǐng)求
    lars::GetRouteRequest req;

    req.set_modid(1);
    req.set_cmdid(2);

    std::string requestString;

    req.SerializeToString(&requestString);
    conn->send_message(requestString.c_str(), requestString.size(), lars::ID_GetRouteRequest);
}



void deal_get_route(const char *data, uint32_t len, int msgid, net_connection *net_conn, void *user_data)
{
    //解包得到數(shù)據(jù)
    lars::GetRouteResponse rsp;
    rsp.ParseFromArray(data, len);
    
    //打印數(shù)據(jù)
    printf("modid = %d\n", rsp.modid());
    printf("cmdid = %d\n", rsp.cmdid());
    printf("host_size = %d\n", rsp.host_size());

    for (int i = 0; i < rsp.host_size(); i++) {
        printf("-->ip = %u\n", rsp.host(i).ip());
        printf("-->port = %d\n", rsp.host(i).port());
    }
    
    //再請(qǐng)求
    lars::GetRouteRequest req;
    req.set_modid(rsp.modid());
    req.set_cmdid(rsp.cmdid());
    std::string requestString;
    req.SerializeToString(&requestString);
    net_conn->send_message(requestString.c_str(), requestString.size(), lars::ID_GetRouteRequest);
}


int main(int argc, char **argv)
{
    parse_option(argc, argv);

    event_loop loop;
    tcp_client *client;

    //創(chuàng)建客戶端
    client = new tcp_client(&loop, option.ip, option.port, "lars_dns_test");
    if (client == NULL) {
        fprintf(stderr, "client == NULL\n");
        exit(1);
    }

    //客戶端成功建立連接,首先發(fā)送請(qǐng)求包
    client->set_conn_start(on_connection);

    //設(shè)置服務(wù)端回應(yīng)包處理業(yè)務(wù)
    client->add_msg_router(lars::ID_GetRouteResponse, deal_get_route);
    

    loop.event_process(); 

    return 0;
}

?

? 同時(shí)提供一個(gè)Makefile對(duì)客戶端進(jìn)行編譯。

lars_dns/test/Makefile

TARGET= lars_dns_test1
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

OTHER_LIB = -lpthread -ldl -lprotobuf
SRC= ./src
INC= -I./include -I$(BASE_H) -I$(LARS_REACTOR_H) -I$(PROTO_H)

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


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

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

%.o: %.cpp
        $(CXX) $(CFLAGS) -c -o $@ $< $(INC) 

.PHONY: clean

clean:
        -rm -f ./*.o $(TARGET)

我們分別啟動(dòng)bin/lars_dns進(jìn)程,和test/lars_dns_test1進(jìn)程

服務(wù)端

$ ./bin/lars_dns 
msg_router init...
create 0 thread
create 1 thread
create 2 thread
create 3 thread
create 4 thread
add msg cb msgid = 1
lars dns service ....
begin accept
begin accept
[thread]: get new connection succ!
modID = 1, cmdID = 1, ip = 3232235953, port = 7777
modID = 1, cmdID = 2, ip = 3232235954, port = 7776
modID = 2, cmdID = 1, ip = 3232235955, port = 7778
modID = 2, cmdID = 2, ip = 3232235956, port = 7779
modID = 1, cmdID = 1, ip = 3232235953, port = 7777
modID = 1, cmdID = 2, ip = 3232235954, port = 7776
modID = 2, cmdID = 1, ip = 3232235955, port = 7778
modID = 2, cmdID = 2, ip = 3232235956, port = 7779
read data from socket

客戶端

$ ./lars_dns_test1 -h 127.0.0.1 -p 7778
msg_router init...
do_connect EINPROGRESS
add msg cb msgid = 2
connect 127.0.0.1:7778 succ!
modid = 1
cmdid = 2
host_size = 1
-->ip = 3232235954
-->port = 7776

關(guān)于作者:

作者:Aceld(劉丹冰)

mail: danbing.at@gmail.com
github: https://github.com/aceld
原創(chuàng)書(shū)籍gitbook: http://legacy.gitbook.com/@aceld

原創(chuàng)聲明:未經(jīng)作者允許請(qǐng)勿轉(zhuǎn)載, 如果轉(zhuǎn)載請(qǐng)注明出處

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容