20.1 Route类的单例模式设计

  1. 首先我们将Route类设计成单例,我们创建头文件和cpp文件.

lars_dns/include/dns_route.h

  1. #pragma once
  2. class Route
  3. {
  4. public:
  5. //创建单例的方法
  6. static void init() {
  7. _instance = new Route();
  8. }
  9. static Route *instance() {
  10. //保证init方法在这个进程执行中,只执行一次
  11. pthread_once(&_once, init);
  12. return _instance;
  13. }
  14. private:
  15. //构造函数私有化
  16. Route();
  17. Route(const Route&);
  18. const Route& operator=(const Route&);
  19. //单例
  20. static Route* _instance;
  21. //单例锁
  22. static pthread_once_t _once;
  23. /* ---- 属性 ---- */
  24. //...
  25. };
  26. lars_dns/src/dns_route.cpp
  27. #include "dns_route.h"
  28. //单例对象
  29. Route * Route::_instance = NULL;
  30. //用于保证创建单例的init方法只执行一次的锁
  31. pthread_once_t Route::_once = PTHREAD_ONCE_INIT;

20.2 Route中的map数据类型定义

  1. **这里的Route并非reactor中的router,这里的Route我们是把modid/cmdid与需要管理的远程服务器的serverip/serverport的一条对应关系叫一个Route。**<br /> 我们用map来存储这些关系,其中keymodid/cmdid的一个二进制偏移量处理,而mapvalue是一个set集合,因为一个modid/cmdid可能对应多个host主机的ip和端口。具体的表现数据结构形式如下。<br />![](https://cdn.nlark.com/yuque/0/2022/jpeg/26269664/1650026084823-95c12039-8209-4436-90ce-f8ff568e34ef.jpeg#clientId=ud5fcc95a-e218-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u28833435&margin=%5Bobject%20Object%5D&originHeight=768&originWidth=1024&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=uebf25851-125a-4a07-8b47-d59fa7a647f&title=)<br /> 接下来,我们来定义一个相关代码:

lars_dns/include/dns_route.h

  1. #pragma once
  2. #include <pthread.h>
  3. #include <ext/hash_map>
  4. #include <ext/hash_set>
  5. #include "mysql.h"
  6. using __gnu_cxx::hash_map;
  7. using __gnu_cxx::hash_set;
  8. //定义用来保存modID/cmdID与host的IP/host的port的对应的关系 数据类型
  9. typedef hash_map< uint64_t, hash_set<uint64_t> > route_map;
  10. typedef hash_map< uint64_t, hash_set<uint64_t> >::iterator route_map_it;
  11. //定义用来保存host的IP/host的port的的集合 数据类型
  12. typedef hash_set<uint64_t> host_set;
  13. typedef hash_set<uint64_t>::iterator host_set_it;
  14. class Route
  15. {
  16. public:
  17. //创建单例的方法
  18. static void init() {
  19. _instance = new Route();
  20. }
  21. static Route *instance() {
  22. //保证init方法在这个进程执行中,只执行一次
  23. pthread_once(&_once, init);
  24. return _instance;
  25. }
  26. private:
  27. //构造函数私有化
  28. Route();
  29. Route(const Route&);
  30. const Route& operator=(const Route&);
  31. //单例
  32. static Route* _instance;
  33. //单例锁
  34. static pthread_once_t _once;
  35. /* ---- 属性 ---- */
  36. //数据库
  37. MYSQL _db_conn; //mysql链接
  38. char _sql[1000]; //sql语句
  39. //modid/cmdid---ip/port 对应的route关系map
  40. route_map *_data_pointer; //指向RouterDataMap_A 当前的关系map
  41. route_map *_temp_pointer; //指向RouterDataMap_B 临时的关系map
  42. pthread_rwlock_t _map_lock;
  43. };

20.3 Route初始化

lars_dns/src/dns_route.cpp

  1. #include <string>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include "lars_reactor.h"
  5. #include "dns_route.h"
  6. #include "string.h"
  7. using namespace std;
  8. //单例对象
  9. Route * Route::_instance = NULL;
  10. //用于保证创建单例的init方法只执行一次的锁
  11. pthread_once_t Route::_once = PTHREAD_ONCE_INIT;
  12. Route::Route()
  13. {
  14. //1 初始化锁
  15. pthread_rwlock_init(&_map_lock, NULL);
  16. //2 初始化map
  17. _data_pointer = new route_map();//RouterDataMap_A
  18. _temp_pointer = new route_map();//RouterDataMap_B
  19. //3 链接数据库
  20. this->connect_db();
  21. //4 查询数据库,创建_data_pointer 与 _temp_pointer 两个map
  22. this->build_maps();
  23. }
  24. void Route::connect_db()
  25. {
  26. // --- mysql数据库配置---
  27. string db_host = config_file::instance()->GetString("mysql", "db_host", "127.0.0.1");
  28. short db_port = config_file::instance()->GetNumber("mysql", "db_port", 3306);
  29. string db_user = config_file::instance()->GetString("mysql", "db_user", "root");
  30. string db_passwd = config_file::instance()->GetString("mysql", "db_passwd", "aceld");
  31. string db_name = config_file::instance()->GetString("mysql", "db_name", "lars_dns");
  32. mysql_init(&_db_conn);
  33. //超时断开
  34. mysql_options(&_db_conn, MYSQL_OPT_CONNECT_TIMEOUT, "30");
  35. //设置mysql链接断开后自动重连
  36. my_bool reconnect = 1;
  37. mysql_options(&_db_conn, MYSQL_OPT_RECONNECT, &reconnect);
  38. 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)) {
  39. fprintf(stderr, "Failed to connect mysql\n");
  40. exit(1);
  41. }
  42. }
  43. void Route::build_maps()
  44. {
  45. int ret = 0;
  46. snprintf(_sql, 1000, "SELECT * FROM RouteData;");
  47. ret = mysql_real_query(&_db_conn, _sql, strlen(_sql));
  48. if ( ret != 0) {
  49. fprintf(stderr, "failed to find any data, error %s\n", mysql_error(&_db_conn));
  50. exit(1);
  51. }
  52. //得到结果集
  53. MYSQL_RES *result = mysql_store_result(&_db_conn);
  54. //得到行数
  55. long line_num = mysql_num_rows(result);
  56. MYSQL_ROW row;
  57. for (long i = 0; i < line_num; i++) {
  58. row = mysql_fetch_row(result);
  59. int modID = atoi(row[1]);
  60. int cmdID = atoi(row[2]);
  61. unsigned ip = atoi(row[3]);
  62. int port = atoi(row[4]);
  63. //组装map的key,有modID/cmdID组合
  64. uint64_t key = ((uint64_t)modID << 32) + cmdID;
  65. uint64_t value = ((uint64_t)ip << 32) + port;
  66. printf("modID = %d, cmdID = %d, ip = %lu, port = %d\n", modID, cmdID, ip, port);
  67. //插入到RouterDataMap_A中
  68. (*_data_pointer)[key].insert(value);
  69. }
  70. mysql_free_result(result);
  71. }

20.4 测试Route的构造及map加载

完成lars dns-service V0.1版本测试
我们在Lars/base/sql加入几个简单插入数据的sql语句,方便数据库里有一些测试数据,我们之后应该会提供一个web管理端来操作数据库。

Lars/base/sql/dns_route_insert.sql

  1. USE lars_dns;
  2. INSERT INTO RouteData(modid, cmdid, serverip, serverport) VALUES(1, 1, 3232235953, 7777);
  3. INSERT INTO RouteData(modid, cmdid, serverip, serverport) VALUES(1, 2, 3232235954, 7776);
  4. INSERT INTO RouteData(modid, cmdid, serverip, serverport) VALUES(1, 2, 3232235955, 7778);
  5. INSERT INTO RouteData(modid, cmdid, serverip, serverport) VALUES(1, 2, 3232235956, 7779);
  6. UPDATE RouteVersion SET version = UNIX_TIMESTAMP(NOW()) WHERE id = 1;
  7. Lars/base/sql/dns_route_drop.sql
  8. USE lars_dns;
  9. DELETE FROM RouteData;
  10. UPDATE RouteVersion SET version = UNIX_TIMESTAMP(NOW()) WHERE id = 1;
  1. 先将测试数据导入数据库。然后回到lars_dns下编译。执行
  1. $./bin/lars_dns
  2. msg_router init...
  3. create 0 thread
  4. create 1 thread
  5. create 2 thread
  6. create 3 thread
  7. create 4 thread
  8. modID = 1, cmdID = 1, ip = 3232235953, port = 7777
  9. modID = 1, cmdID = 2, ip = 3232235954, port = 7776
  10. modID = 1, cmdID = 2, ip = 3232235955, port = 7778
  11. modID = 1, cmdID = 2, ip = 3232235956, port = 7779
  12. lars dns service ....