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



我有一个lua脚本,运行在RHEL7主机上,它被写入以连接到MS SQL Server数据库(使用FreeTDS/unixODBC)并检索值。该脚本通常工作正常,但是,如果目标服务器脱机/不可用,它将挂起(大约 6 分 20 秒),然后最终超时并返回错误。我想要的是将这个超时减少到几秒钟......

我觉得这应该很容易做到,但我似乎无法弄清楚如何指定/强制实施与 SQL Server 的连接超时。

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

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

谁能帮我解决这个问题?我在下面包含了相关配置文件的脚本和内容。

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

这并不能解决我最初的问题,但认为值得一提作为更新......

获取值.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

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

我在树莓派3上遇到了类似的问题(使用FreeTDS 1.2.3/unixODBC)。连接工作没有问题,但我无法让超时工作(如果我强制 SQL 服务器脱机)。我跟踪了代码并意识到freetds.conf中的超时参数从未到达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

最新更新