美文网首页
(22)获取Route信息(Dns部分)-【Lars-基于C++

(22)获取Route信息(Dns部分)-【Lars-基于C++

作者: 刘丹冰Aceld | 来源:发表于2019-10-16 09:25 被阅读0次

    【Lars教程目录】

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


    【Lars系统概述】
    第1章-概述
    第2章-项目目录构建


    【Lars系统之Reactor模型服务器框架模块】
    第1章-项目结构与V0.1雏形
    第2章-内存管理与Buffer封装
    第3章-事件触发EventLoop
    第4章-链接与消息封装
    第5章-Client客户端模型
    第6章-连接管理及限制
    第7章-消息业务路由分发机制
    第8章-链接创建/销毁Hook机制
    第9章-消息任务队列与线程池
    第10章-配置文件读写功能
    第11章-udp服务与客户端
    第12章-数据传输协议protocol buffer
    第13章-QPS性能测试
    第14章-异步消息任务机制
    第15章-链接属性设置功能


    【Lars系统之DNSService模块】
    第1章-Lars-dns简介
    第2章-数据库创建
    第3章-项目目录结构及环境构建
    第4章-Route结构的定义
    第5章-获取Route信息
    第6章-Route订阅模式
    第7章-Backend Thread实时监控


    【Lars系统之Report Service模块】
    第1章-项目概述-数据表及proto3协议定义
    第2章-获取report上报数据
    第3章-存储线程池及消息队列


    【Lars系统之LoadBalance Agent模块】
    第1章-项目概述及构建
    第2章-主模块业务结构搭建
    第3章-Report与Dns Client设计与实现
    第4章-负载均衡模块基础设计
    第5章-负载均衡获取Host主机信息API
    第6章-负载均衡上报Host主机信息API
    第7章-过期窗口清理与过载超时(V0.5)
    第8章-定期拉取最新路由信息(V0.6)
    第9章-负载均衡获取Route信息API(0.7)
    第10章-API初始化接口(V0.8)
    第11章-Lars Agent性能测试工具
    第12章- Lars启动工具脚本


    5) 获取Route信息

    5.1 proto协议定义

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

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

    Lars/base/proto/lars.proto

    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

    #!/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的。

    5.2 proto编译环境集成

    ​ 现在我们将lars-dns的Makefile加入针对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)
    

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

    5.3 实现Route获取

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

    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. 根据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

    //获取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;
    }
    

    5.3 lars_dns获取Route信息测试-V0.2

    ​ 下面我们写一个客户端检查测试一下该功能.

    lars_dns/test/lars_dns_test1.cpp

    #include <string.h>
    #include <unistd.h>
    #include <string>
    #include "lars_reactor.h"
    #include "lars.pb.h"
    
    //命令行参数
    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) 
    {
        //发送Route信息请求
        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)
    {
        //解包得到数据
        lars::GetRouteResponse rsp;
        rsp.ParseFromArray(data, len);
        
        //打印数据
        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());
        }
        
        //再请求
        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;
    
        //创建客户端
        client = new tcp_client(&loop, option.ip, option.port, "lars_dns_test");
        if (client == NULL) {
            fprintf(stderr, "client == NULL\n");
            exit(1);
        }
    
        //客户端成功建立连接,首先发送请求包
        client->set_conn_start(on_connection);
    
        //设置服务端回应包处理业务
        client->add_msg_router(lars::ID_GetRouteResponse, deal_get_route);
        
    
        loop.event_process(); 
    
        return 0;
    }
    

    ​ 同时提供一个Makefile对客户端进行编译。

    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)
    

    我们分别启动bin/lars_dns进程,和test/lars_dns_test1进程

    服务端

    $ ./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
    

    关于作者:

    作者:Aceld(刘丹冰)

    mail: danbing.at@gmail.com
    github: https://github.com/aceld
    原创书籍gitbook: http://legacy.gitbook.com/@aceld

    原创声明:未经作者允许请勿转载, 如果转载请注明出处

    相关文章

      网友评论

          本文标题:(22)获取Route信息(Dns部分)-【Lars-基于C++

          本文链接:https://www.haomeiwen.com/subject/zhxemctx.html