在 Lua 脚本中使用 FreeTDS 和 unixODBC 连接 MS SQL Server 数据库时配置连接超时

我有一个在运行 RHEL7 主机上的 Lua 脚本,该脚本已编写以使用 FreeTDS / unixODBC 连接到 MS SQL Server 数据库并检索值。脚本通常运行良好,但是如果目标服务器离线/不可用,它将会挂起(大约 6 分钟 20 秒),直到某些东西最终超时并返回错误。我想要的是将此超时时间缩短到几秒钟...

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

在 /etc/freetds.conf 中有一个超时配置(如下所示),但它似乎没有影响。

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

有人能帮我解决这个问题吗?我已经在下面包括了脚本和相关配置文件的内容。

UPDATE: 在进一步测试过程中,我注意到了值得一提的事情-如果我将我的测试脚本指向同一子网之外的未使用 IP 地址,则在尝试进行 SQL 服务器连接时的超时时间为 6 分钟 21 秒(如先前提到的)。但是,如果我将其指向同一子网中未使用的 IP 地址,则我的超时时间始终为 ~9 秒钟。我不是网络专家,但这表明我实际上在这里看到的两个超时受到了网络/传输层的影响-也许我的本地网络交换机只是将未知子网的数据包丢弃了?

这解决不了我的原始问题,但是认为这是一个值得提及的更新...

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
点赞
用户2328287
用户2328287

在连接之前可以尝试为连接对象设置 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)

更新

同样存在 ODBC 3.0 的 SQL_ATTR_CONNECTION_TIMEOUT 属性。

一个 SQLUINTEGER 值,对应等待连接上的任何请求完成返回应用程序之前的秒数。无论何时驱动程序在不涉及查询执行或登录的情况下可能超时,都应该返回 SQLSTATE HYT00(超时)。

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

我没有公开它,但您可以尝试这个。 (在您的头文件中也可以查看 SQL_ATTR_CONNECTION_TIMEOUT 的值)。

local SQL_ATTR_CONNECTION_TIMEOUT = 113
cnn:setuintattr(SQL_ATTR_CONNECTION_TIMEOUT, 10)
2017-05-11 09:47:02
用户11767519
用户11767519

我在树莓派3上使用FreeTDS 1.2.3 / unixODBC遇到了类似的问题。连接没有问题,但是如果我让SQL服务器下线,就无法设置超时。我追踪了代码,并意识到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
2020-08-10 03:09:54