使用FreeTDS和unixODBC从lua脚本连接到MS SQL Server数据库时配置连接超时

时间:2017-05-11 04:39:57

标签: sql-server nginx lua freetds unixodbc

我有一个在RHEL7主机上运行的lua脚本,该脚本已编写为连接到MS SQL Server数据库(使用FreeTDS / unixODBC)并检索值。该脚本通常可以正常工作,但是,如果目标服务器处于脱机/不可用状态,它将暂停(大约6分20秒),最终超时并返回错误。我想要的是将这个超时时间缩短到几秒......

我觉得这应该很简单,但我似乎无法弄清楚如何指定/强制执行SQL Server的连接超时。

/etc/freetds.conf中有一个超时配置(见下文),但这似乎没有效果。

脚本通常从nginx(openresty)调用,这是我想要保持超时低的主要原因,但是观察到独立运行脚本时出现相同的挂起/超时行为。

任何人都可以帮我解决这个问题吗?我已经在下面包含了脚本和相关相关配置文件的内容。

更新: 我注意到在进一步测试期间值得一提的东西 - 如果我将测试脚本指向同一子网的未使用的IP地址OUTS,那么尝试进行SQL服务器连接时的超时时间约为6分21秒(如前所述)。如果我将它指向同一子网中未使用的IP地址,我的超时始终为~9秒。我不是网络专家,但这表明我实际看到的2次超时受到网络/传输层的影响 - 也许我的本地网络交换机只是将数据包丢弃到未知子网?

这并不能解决我原来的问题,但认为值得一提的是更新...

getvalue.lua:

local odbc = require "odbc"
local keyvalue = "some_value"
local retval = ""

CNN_DRV = {
        {Driver='{FreeTDS}'};
        {Server='10.10.60.100,1433'};
        {Database='databasename'};
        {Uid='sqlusername'};
        {Pwd='sqlpassword'};
};

dbassert = odbc.assert
env,err = odbc.environment()
local cnn, err = env:driverconnect(CNN_DRV)
stmt,err = cnn:prepare("{?= call dbo.GetRetValForKeyValue(?)}")
ret = stmt:vbind_param_ulong(1, ret, odbc.PARAM_OUTPUT)
val = stmt:vbind_param_char(2, keyvalue)
dbassert(stmt:execute())

stmt:foreach(function(f1)
        if tonumber(f1)
                then
                retval = string.format("%d", f1)
                else
                retval = ''
        end
end
)

print(retval)

/etc/odbcinst.ini:

[FreeTDS]
Description = FreeTDS TDS driver (for Sybase/MS SQL)
Driver = /usr/lib64/libtdsodbc.so.0
Setup = /usr/lib64/libtdsS.so.2

/etc/freetds.conf:

# Global settings are overridden by those in a database
# server specific section
[global]
        # TDS protocol version
;       tds version = 4.2

        # Whether to write a TDSDUMP file for diagnostic purposes
        # (setting this to /tmp is insecure on a multi-user system)
;       dump file = /tmp/freetds.log
;       debug flags = 0xffff

        # Command and connection timeouts
;       timeout = 10
;       connect timeout = 10

        # If you get out-of-memory errors, it may mean that your client
        # is trying to allocate a huge buffer for a TEXT field.
        # Try setting 'text size' to a more reasonable limit
        text size = 64512

2 个答案:

答案 0 :(得分:0)

在连接之前,您可以尝试为连接对象设置SQL_ATTR_LOGIN_TIMEOUT

  

对应于等待秒数的SQLUINTEGER值   在返回应用程序之前完成登录请求。   默认值取决于驱动程序。如果ValuePtr为0,则超时为   禁用,连接尝试将无限期等待。

local env, err = odbc.environment()
local cnn = env:connection()
cnn:setlogintimeout(10)
local ok, err = cnn:driverconnect(CNN_DRV)

<强>更新

还存在SQL_ATTR_CONNECTION_TIMEOUT属性(但对于ODBC 3.0)

  

对应于等待秒数的SQLUINTEGER值   对于连接完成的任何请求,在返回之前   应用。驱动程序应该返回SQLSTATE HYT00(超时已过期)   任何时候都可以在没有关联的情况下超时   查询执行或登录。

     

如果ValuePtr等于0(默认值),则没有超时。

我不公开它,但你可以尝试这个。 (也可以在头文件中签出SQL_ATTR_CONNECTION_TIMEOUT的值)。

local SQL_ATTR_CONNECTION_TIMEOUT = 113
cnn:setuintattr(SQL_ATTR_CONNECTION_TIMEOUT, 10)

答案 1 :(得分:0)

我在树莓派PI 3上遇到了类似的问题(使用FreeTDS 1.2.3 / unixODBC)。该连接正常工作,没有问题,但是超时无法正常工作(如果我强制SQL Server脱机)。我跟踪了代码,意识到freetds.conf中的timeout参数从未到达过freeTDS库,因此等待数据包(在packet.c中)的功能就一直挂在那里。

我的解决方案是使用以下更改来修改freeTDS库,以便在读取odbc.ini时将超时值输入到freeTDS库中。我不确定这是否是解决此问题的正确方法,或者我是否丢失了某些东西,但是我在网上找不到任何东西。

odbc.h(在odbc参数列表中添加了超时)

#define ODBC_PARAM_LIST \
    ODBC_PARAM(Servername) \
    ODBC_PARAM(Server) \
    ODBC_PARAM(DSN) \
    ODBC_PARAM(UID) \
    ODBC_PARAM(PWD) \
    ODBC_PARAM(Address) \
    ODBC_PARAM(Port) \
    ODBC_PARAM(TDS_Version) \
    ODBC_PARAM(Language) \
    ODBC_PARAM(Database) \
    ODBC_PARAM(TextSize) \
    ODBC_PARAM(PacketSize) \
    ODBC_PARAM(ClientCharset) \
    ODBC_PARAM(DumpFile) \
    ODBC_PARAM(DumpFileAppend) \
    ODBC_PARAM(DebugFlags) \
    ODBC_PARAM(Encryption) \
    ODBC_PARAM(Trusted_Connection) \
    ODBC_PARAM(APP) \
    ODBC_PARAM(WSID) \
    ODBC_PARAM(UseNTLMv2) \
    ODBC_PARAM(MARS_Connection) \
    ODBC_PARAM(REALM) \
    ODBC_PARAM(ServerSPN) \
    ODBC_PARAM(AttachDbFilename) \
    ODBC_PARAM(ApplicationIntent) \
    ODBC_PARAM(Timeout)

connectparams.c(在函数中添加了超时属性,并在获取dsn信息时将其读取

int
ODBCINSTGetProperties(HODBCINSTPROPERTY hLastProperty)
{

....

hLastProperty = definePropertyString(hLastProperty, odbc_param_Timeout, "10", "The timeout for connection and queries.");

return 1;
}

/** 
 * Read connection information from given DSN
 * @param DSN           DSN name
 * @param login    where to store connection info
 * @return 1 if success 0 otherwhise
 */
int
odbc_get_dsn_info(TDS_ERRS *errs, const char *DSN, TDSLOGIN * login)
{
  ....

  if (myGetPrivateProfileString(DSN, odbc_param_Timeout, tmp) > 0)
        tds_parse_conf_section(TDS_STR_TIMEOUT, tmp, login);

    return 1;
}

odbc.ini

[TestServer]
Driver=FreeTDS
Database=MyDatabase
Port=1433
Server=<<ip address>>
timeout=10