1. 程式人生 > >bind9+dlz+mysql連線斷開問題

bind9+dlz+mysql連線斷開問題

前言

DLZ(Dynamically Loadable Zones)與傳統的BIND9不同,BIND的不足之處:

  1. BIND從文字檔案中獲取資料,這樣容易因為編輯錯誤出現問題。
  2. BIND需要將資料載入到記憶體中,如果域或者記錄較多,會消耗大量的記憶體。
  3. BIND啟動時解析Zone檔案,對於一個記錄較多的DNS來說,會耽誤更多的時間。
  4. 如果近修改一條記錄,那麼要重新載入或者重啟BIND 才能生效,那麼需要時間,可能會影響客戶端查詢。

而Bind-dlz 即將幫你解決這些問題,對Zone檔案操作也更方便了,直接對資料庫操作,可以很方便擴充及開發管理程式。

bind-dlz的相關配置就不說了,網上一大堆,推薦兩個:

問題描述

bind9+dlz+mysql配置好正常使用沒什麼問題,但是當使用了一段時間(都是隔夜)之後再次解析之前的域名發現解析不了,檢視日誌/var/log/messages,發現如下錯誤資訊:

localhost named[42406]: mysql driver unable to return result set for findzone query

經查詢資料得知是因為dlz在連線mysql之後,由於連線的空閒時間達到mysql的最大空閒時間,被mysql強行斷開導致無法再從mysql獲取資料。關鍵是DLZ在斷開之後不會重連,這就不得不重啟named服務重新與mysql建立連線了。

但明顯這個辦法不靠譜,想到的解決方案大概有下面幾種:

  • 1.更換成其他資料庫
  • 2.修改wait_timeout為更大值,24天左右
  • 3.重啟named服務
  • 4.保持連線的使用,定期進行查詢
  • 5.修改dlz原始碼,加入資料庫重連功能

更換資料庫暫時不考慮;修改mysql的wait_timeout也不能解決根本問題;第3個就不說了;第四個我試了下,寫了一個指令碼定時請求bind解析域名,想讓連線不超時,但是最後證明這種辦法不行,不知道是不是方式不對;最後一種是實在沒有辦法了,硬著頭皮打開了原始碼。。。

解決方案

。。。好吧,沒有想象中的那麼難,找到bind-9.6.0-P1/contrib/dlz/drivers/dlz_mysql_driver.c,大致讀了下,配合資料

C中MySQL自動重新連線(部分zz),在dlz_mysql_driver.c的mysql_create函式中加入了重連的程式碼,完整函式內容:

/*%
 * create an instance of the driver.  Remember, only 1 copy of the driver's
 * code is ever loaded, the driver has to remember which context it's
 * operating in.  This is done via use of the dbdata argument which is
 * passed into all query functions.
 */
static isc_result_t
mysql_create(const char *dlzname, unsigned int argc, char *argv[],
         void *driverarg, void **dbdata)
{
    isc_result_t result;
    dbinstance_t *dbi = NULL;
    char *tmp = NULL;
    char *dbname = NULL;
    char *host = NULL;
    char *user = NULL;
    char *pass = NULL;
    char *socket = NULL;
    int port;
    MYSQL *dbc;
    char *endp;
    int j;
    unsigned int flags = 0;
    //新增部分:定義了一個value
    char value = 1;

    UNUSED(driverarg);
    UNUSED(dlzname);

    ...

    tmp = getParameterValue(argv[1], "space=");
    if (tmp != NULL) {
        if (strcasecmp(tmp, "ignore") == 0)
            flags = flags | CLIENT_IGNORE_SPACE;
        isc_mem_free(ns_g_mctx, tmp);
    }

    //新增部分:重連的程式碼
    /* reconnect to mysql */
    result = mysql_options((MYSQL *)dbi->dbconn, MYSQL_OPT_RECONNECT, (char *)&value);
    if( result != 0 )
    {
        isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
                  DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
                  "mysql_options reconnect failed!");
        result = ISC_R_FAILURE;
        goto full_cleanup;
    }
    
    dbc = NULL;
    host = getParameterValue(argv[1], "host=");
    user = getParameterValue(argv[1], "user=");
    pass = getParameterValue(argv[1], "pass=");
    socket = getParameterValue(argv[1], "socket=");

    for (j=0; dbc == NULL && j < 4; j++)
        dbc = mysql_real_connect((MYSQL *) dbi->dbconn, host,
                     user, pass, dbname, port, socket,
                     flags);

    /* let user know if we couldn't connect. */
    if (dbc == NULL) {
        isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
                  DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
                  "mysql driver failed to create "
                  "database connection after 4 attempts");
        result = ISC_R_FAILURE;
        goto full_cleanup;
    }

    ...
}

改完之後重新編譯安裝就好了,經過測試,第二天來發現域名仍然可以正常解析。問題暫時解決,後面再觀察一下。