21.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对应的关系的消息IDID_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})doname=$(ls ${file} | cut -d. -f1,2)mv $file ${name}.${newsuffix}doneecho "build proto file successd!"
因为protoc会自动生成cc后缀的文件,为了方便我们Makefile的编译,所以将cc文件改成cpp的。
21.2 proto编译环境集成
现在我们将`lars-dns`的Makefile加入针对proto文件的编译
Lars/lars_dns/Makefile
TARGET= bin/lars_dnsCXX=g++CFLAGS=-g -O2 -Wall -Wno-deprecatedBASE=../baseBASE_H=$(BASE)/includePROTO = $(BASE)/protoPROTO_H = $(BASE)/protoLARS_REACTOR=../lars_reactorLARS_REACTOR_H =$(LARS_REACTOR)/includeLARS_REACTOR_LIB=$(LARS_REACTOR)/lib -llreactorMYSQL=$(BASE)/mysql-connector-cMYSQL_H=$(MYSQL)/includeMYSQL_LIB=$(MYSQL)/lib/libmysqlclient.aOTHER_LIB = -lpthread -ldl -lprotobufSRC= ./srcINC= -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: cleanclean:-rm -f src/*.o $(TARGET)
添加了两个部分一个`OBJS`增添一个`lars.pb.o`的依赖,然后再`OTHER_LIB`增加`-lprotobuf`动态库的连接。
21.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 和 cmdidint modid, cmdid;modid = req.modid();cmdid = req.cmdid();//3. 根据modid/cmdid 获取 host信息host_set hosts = Route::instance()->get_hosts(modid, cmdid);//4. 将数据打包成protobuflars::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;//组装keyuint64_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;}
21.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_test1CXX=g++CFLAGS=-g -O2 -Wall -Wno-deprecatedBASE=../../baseBASE_H=$(BASE)/includePROTO = $(BASE)/protoPROTO_H = $(BASE)/protoLARS_REACTOR=../../lars_reactorLARS_REACTOR_H =$(LARS_REACTOR)/includeLARS_REACTOR_LIB=$(LARS_REACTOR)/lib -llreactorOTHER_LIB = -lpthread -ldl -lprotobufSRC= ./srcINC= -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.oOBJS += $(PROTO)/lars.pb.o$(TARGET): $(OBJS)$(CXX) $(CFLAGS) -o $(TARGET) $(OBJS) $(INC) $(LIB)%.o: %.cpp$(CXX) $(CFLAGS) -c -o $@ $< $(INC).PHONY: cleanclean:-rm -f ./*.o $(TARGET)
我们分别启动bin/lars_dns进程,和test/lars_dns_test1进程
服务端
$ ./bin/lars_dnsmsg_router init...create 0 threadcreate 1 threadcreate 2 threadcreate 3 threadcreate 4 threadadd msg cb msgid = 1lars dns service ....begin acceptbegin accept[thread]: get new connection succ!modID = 1, cmdID = 1, ip = 3232235953, port = 7777modID = 1, cmdID = 2, ip = 3232235954, port = 7776modID = 2, cmdID = 1, ip = 3232235955, port = 7778modID = 2, cmdID = 2, ip = 3232235956, port = 7779modID = 1, cmdID = 1, ip = 3232235953, port = 7777modID = 1, cmdID = 2, ip = 3232235954, port = 7776modID = 2, cmdID = 1, ip = 3232235955, port = 7778modID = 2, cmdID = 2, ip = 3232235956, port = 7779read data from socket
客户端
$ ./lars_dns_test1 -h 127.0.0.1 -p 7778msg_router init...do_connect EINPROGRESSadd msg cb msgid = 2connect 127.0.0.1:7778 succ!modid = 1cmdid = 2host_size = 1-->ip = 3232235954-->port = 7776
