数据库连接

连接字符串

ACI连接数据库的字符串有两种写法,一种为服务名的方式,另一种为IP方式:

  • 服务名方式:如果在tnsnames.aci文件中配置了服务名,则URL直接写服务名即可,比如如果tnsnames.aci配置了stdb的一个服务名,访问时URL字符串只输入stdb即可。
  • IP方式:连接字符串的格式为:

{服务名} = {主机名/IP}:{端口号}/{数据库实例名称}?[参数名1=参数值]; [参数名2=参数值]

样例:localhost:2003/OSRDB? fetch_size = 1000;socket_connect_timeout = 600

备注:端口号可省略,默认访问2003端口,参数的设置参阅《参数配置文件》章节
  • 空串方式:当连接字符串传入空串(不是NULL)时,ACI会采用localhost:2003/OSRDB默认字符串信息进行连接

单用户连接

ACILogon 函数实现简化单会话登录,登录成功后返回一个分配好的”上下文句柄”,上下文句柄中包含一个服务句柄和会话句柄。后续SQL操作通过上下文句柄进行操作。返回的上下文句柄不需要在应用程序中释放,在执行 ACILogoff 函数中自行释放。

ACILogon2 的功能和 ACILogon 一样, ACILogon2 可以创建一个新的连接,也可以从一个已经存在的连接池中获得一个链接。

ACILogon / ACILogon2 创建的连接不再使用后,必须调用 ACILogoff 进行连接释放,否则可能会导致连接数大量闲置泄漏。

多用户连接

多用户连接需要应用去分配一个服务句柄、会话句柄和上下文句柄,并将服务句柄和会话句柄设置到上下文句柄中,步骤如下:

  1. 调用 ACIHandleAlloc 进行服务句柄、会话句柄和上下文句柄分配;
  2. 调用 ACIServerAttach 创建一个并没有进行用户认证网络连接;
  3. 将服务句柄通过属性设置到上下文句柄中
  4. 调用 ACISessionBegin 进行数据库认证;
  5. 将会话句柄通过属性设置到上下文句柄中

示例代码:

/*1)调用ACIHandleAlloc进行服务句柄、会话句柄和上下文句柄分配*/
ACIHandleAlloc( (void  *) envhp, (void  **) &svchp, (ub4)
     ACI_HTYPE_SVCCTX, 0, (void  **) 0);
ACIHandleAlloc( (void  *) envhp, (void  **) &srvhp, (ub4)
     ACI_HTYPE_SERVER, 0, (void  **) 0);
ACIHandleAlloc((void  *)envhp, (void  **)&usrhp, (ub4)
     ACI_HTYPE_SESSION, (size_t) 0, (void  **) 0);
/*2)调用ACIServerAttach创建一个并没有进行用户认证网络连接*/
ACIServerAttach( srvhp, errhp, (text *) 0, (sb4) 0, (ub4) ACI_DEFAULT);
/*3)将服务句柄通过属性设置到上下文句柄中*/
ACIAttrSet( (void  *) svchp, (ub4) ACI_HTYPE_SVCCTX, (void  *) srvhp,
     (ub4) 0, (ub4) ACI_ATTR_SERVER, (ACIError *) errhp);

/*4)调用ACISessionBegin 进行数据库认证*/
ACIAttrSet((void  *)usrhp, (ub4)ACI_HTYPE_SESSION, (void  *)"hr",
     (ub4)strlen("hr"), ACI_ATTR_USERNAME, errhp);
ACIAttrSet((void  *)usrhp, (ub4)ACI_HTYPE_SESSION, (void  *)"hr",
     (ub4)strlen("hr"), ACI_ATTR_PASSWORD, errhp);
checkerr(errhp, ACISessionBegin(svchp, errhp, usrhp, ACI_CRED_RDBMS,
     ACI_DEFAULT));

/*5)将会话句柄通过属性设置到上下文句柄中*/
ACIAttrSet((void  *)svchp, (ub4)ACI_HTYPE_SVCCTX, (void  *)usrhp,
     (ub4)0, ACI_ATTR_SESSION, errhp);
  • 多用户连接也可以从连接池中获得一个有效连接:

调用 ACIServerAttach 并设置mode为ACI_CPOOL,调用库连接

ACISessionBegin 设置mode为ACI_DEFAULT,示例如下:
//设置mode为ACI_CPOOL
ACIServerAttach(m_psrv, m_perr, poolName, poolNameLen,ACI_CPOOL);
ACIAttrSet(m_psvc, ACI_HTYPE_SVCCTX, m_psrv,sizeof(m_psrv), ACI_ATTR_SERVER, m_perr);
ACISessionBegin(m_psvc, m_perr, m_pses,ACI_CRED_RDBMS,ACI_DEFAULT);
  • 用户创建的连接不在使用的时候需要释放连接,释放顺序如下:
  1. 调用 ACISessionEnd 进行接受一个数据库认证;
  2. 调用 ACIServerDetach 释放一个网络连接;
  3. 调用 ACIHandleFree 进行服务句柄、会话句柄和上下文句柄释放

会话池

会话池是应用程序创建并维护一组到数据库的会话。这些会话根据请求提供给客户端。如果没有可用的会话,则可能会创建一个新的会话。客户端完成会话后,客户端会将其释放到池中。因此,池中的会话数可以动态增加。

池中的某些会话可能会被标记为具有某些属性。例如,用户可以请求默认会话,在其上设置某些属性,对其进行标记或标记,然后将其返回到池中。该用户或其他用户可能需要具有相同属性的会话,因此请求具有相同标签的会话。池中可能有多个具有相同标签的会话。会话上的标签可以更改或重置。

当没有可用的会话可用并且池达到其最大大小时,应用程序的行为取决于某些属性。可能会创建一个新会话或返回错误,或者线程可能会阻塞并等待会话释放。

会话池的主要好处是提升性能。与数据库建立连接是一项耗时的活动,尤其是在数据库处于远程状态时。因此,与其花时间连接到服务器,验证其凭据并随后接收有效的会话,不如让客户花时间从客户端池中选择一个。

会话池可以执行以下任务:

  • 透明地创建,维护和管理会话池;
  • 为应用程序提供一个接口,以创建一个池并指定池中会话的最小,增量和最大会话数量;
  • 为用户提供一个接口,以获取默认会话或标记会话并将其释放到池中。 带标签的会话是具有某些客户端定义的属性的会话;
  • 允许应用程序动态更改最小和最大会话数;
  • 通过关闭长时间闲置的会话并在需要时创建会话,提供一种机制来始终保持最佳数量的打开会话;
  • 允许具有身份验证的会话池。

会话池的功能

  1. 支持同构和异构会话池

会话池可以是同构的,也可以是异构的。同构会话池意味着池中的会话对于身份验证是相同的(它们具有相同的用户名,密码和权限)。 异构会话池意味着您必须提供身份验证信息,因为会话可以具有不同的安全属性和权限。

  1. 支持设置tag标签方式标记会话

标签为用户提供了一种自定义池中会话的方法。客户端可以从池中获取默认会话或未加标签的会话,可以在会话上设置某些属性(例如NLS设置),然后将会话返回到池中,并在 ACISessionRelease 调用时使用适当的标签对其进行标记。

原始用户或其他用户可以请求具有相同标签的会话来拥有具有相同属性的会话,并且可以通过在 ACISessionGet 调用中提供相同标签的会话。

Tag标签支持多属性标签模式:

多属性标签由一个或多个由分号分隔的<属性名> = <属性值>对组成,其中<属性名> = <属性值> 都是字符串。

ACISessionGet 调用期间,在taginfo参数中,最先出现的属性名称被赋予查找匹配的最高属性,而最后出现的属性名称被赋予最低优先级。 因此,在确定池中的匹配会话时,字符串中属性的顺序很重要。

多属性标记中传递的属性名称和属性值有以下限制:

  • 属性名称和属性值均区分大小写;
  • 属性名称在标签中只能出现一次,如果多次指定相同的属性名称,将引发错误;
  • 应该为属性名称和值都指定一个非空字符串;
  • 属性名称和属性值前后的制表符或空格将被忽略去除。 例如,将“PDB = PDB1”视为“PDB=PDB1”。
  • 属性名称和属性值中不应包含空格,例如,由于NLS和LANGUAGE之间存在空格,NLS <space> LANGUAGE = Chinese将导致错误。

为了更清楚的说明多属性标签的概念,假设要在数据库环境中部署的应用程序要求会话获取请求应尽可能满足来自同一可插入数据库(例如pdb1)的会话。 接下来,它还要求会话属于同一语言(例如,FRENCH),但是将与pdb1的会话赋予更高的优先级。 然后,该应用程序可以提供一个多属性标签,如下所示:

char *props  = “PDB=pdb1;LANGUAGE=FRENCH”

现在,假定池中有两个具有属性的会话,如下所示:

Session 1 = >  “PDB=pdb1;LANGUAGE=CHINESE”
Session 2 = >  “PDB=pdb2;LANGUAGE=FRENCH”

在这种情况下,会话获取请求( ACISessionGet )返回会话1,因为PDB属性通过置于LANGUAGE属性的前面隐式地具有较高的优先级。

会话池的句柄

会话池的使用涉及两个句柄:ACISPool 和 ACIAuthInfo。

ACISPool是会话池句柄,使用 ACIHandleAlloc 分配它,句柄类型为ACI_HTYPE_SPOOL。必须将其传递给 ACISessionPoolCreateACISessionPoolDestroy

ACISPool *spoolhp;
ACIHandleAlloc((void *) envhp, (void **) &spoolhp, ACI_HTYPE_SPOOL,  (size_t) 0, (void **) 0));

一个环境句柄下可以分配多个ACISPool会话池句柄。

ACIAuthInfo是身份验证信息句柄,使用 ACIHandleAlloc 分配它, 认证信息句柄的属性类型为ACI_HTYPE_AUTHINFO。 它被传递给 ACISessionGet 。 它支持用户会话句柄支持的所有属性。

ACIAuthInfo *authp;
ACIHandleAlloc((void *) envhp, (void **) &authp, ACI_HTYPE_AUTHINFO, (size_t) 0, (void **) 0));

会话池的使用

使用会话池的步骤

  1. 使用 ACIHandleAlloc 为ACISPool句柄分配会话池句柄。可以为环境句柄创建多个会话池;
  2. 使用 ACISessionPoolCreate 将模式设置为ACI_DEFAULT(对于新会话池)创建会话池;
  3. 在每个线程中使用会话池中的会话:
  • 使用 ACIHandleAlloc 分配类型为ACIAuthInfo的认证信息句柄
  • 使用 ACIAttrSet 在身份验证信息句柄中设置用户名和密码
  • 开始事务
  • 分配语句句柄
  • 准备SQL语句

注意:使用从会话池中获得的服务上下文时,需要使用 ACISessionGet (或 ACILogon2 )返回的服务上下文,而不要在这些调用之外创建其他服务上下文。随后,使用 ACIStmtPrepare2 和服务上下文获得的任何语句句柄都应仅与相同的服务上下文结合使用,而不应与不同的服务上下文结合使用。

  • 执行SQL语句
  • 提交或回滚交易
  • 结束每个线程的循环
  1. 使用 ACISessionPoolDestroy 销毁会话池。

分配会话池的句柄

会话池要求通过调用 ACIHandleAlloc 来分配会话池句柄ACI_HTYPE_SPOOL。可以为给定的环境句柄创建多个池。分配单个会话池句柄示例:

ACISPool *poolhp;
ACIHandleAlloc((void *) envhp, (void **) &poolhp, ACI_HTYPE_SPOOL, (size_t) 0, (void **) 0));

创建会话池

使用函数 ACISessionPoolCreate 创建会话池。示例如下:

ACISessionPoolCreate(envhp, errhp, poolhp, (OraText **)&poolName,
            (ub4 *)&poolNameLen, database,
            (ub4)strlen((const signed char *)database),
            sessMin, sessMax, sessIncr,
            (OraText *)appusername,
            (ub4)strlen((const signed char *)appusername),
            (OraText *)apppassword,
            (ub4)strlen((const signed char *)apppassword),
            ACI_DEFAULT);

登录数据库

使用这些调用以会话池模式登录数据库。

  1. ACILogon2

这是最简单的调用。 但是,它不向用户提供使用标记的选项。 以下是如何使用 ACILogon2 以会话池模式登录数据库的示例:

for (i = 0; i < MAXTHREADS; ++i)
{
  ACILogon2(envhp, errhp, &svchp[i], "hr", 2, "hr", 2, poolName,
            poolNameLen, ACI_LOGON2_SPOOL));
}
  1. ACISessionGet

这是推荐使用的方式。 它为用户提供了使用标记来标记池中会话的选项,这使检索特定会话更加容易。 以下是使用 ACISessionGet 的示例。

ACISessionGet(envhp, errhp, &svchp, authInfop,
           (OraText *)database,strlen(database), tag,
           strlen(tag), &retTag, &retTagLen, &found,
           ACI_SESSGET_SPOOL);

使用从会话池获取的服务上下文时,需要使用 ACISessionGet (或 ACILogon2 )返回的服务上下文,而不是在这些调用之外创建其他服务上下文。

随后,使用 ACIStmtPrepare2 和服务上下文获得的任何语句句柄都应仅与相同的服务上下文结合使用,而不应与不同的服务上下文结合使用。

登出数据库

根据登录模式对应两种退出方式:

  1. ACILogoff

如果使用 ACILogon2 建立连接,则必须调用 ACILogoff 才能注销。

  1. ACISessionRelease

如果使用 ACISessionGet 建立连接,则必须调用 ACISessionRelease 退出,此时未提交的事务会自动提交。

销毁会话池

调用 ACISessionPoolDestroy 销毁会话池,示例如下:

ACISessionPoolDestroy(poolhp, errhp, ACI_DEFAULT);

释放会话池句柄

调用 ACIHandleFree 释放会话池句柄,示例如下:

ACIHandleFree((void *)poolhp, ACI_HTYPE_SPOOL);

建议在将会话释放回池之前,提交或回滚任何未完成的事务。 如果不这样做,则在释放会话池时,数据库会自动提交任何打开的事务。

如果在使用会话池时检测到实例故障,则ACI尝试清除与该实例的会话。

开发建议

建议在将会话释放回池之前,提交或回滚任何未完成的事务。 如果不这样做,则在释放会话池时,会自动提交任何打开的事务。

连接池

连接池是指为了达到负载均衡,多次会话共用一组可重复使用的物理连接,应用程序可以避开连接管理的工作,同时也是避免频繁创建和断开数据库连接带来的开销。在使用ACI连接池时,客户端程序需按照以下步骤建立连接并开启会话:

连接池的句柄

连接池的使用涉及两个句柄:ACICPool 和 ACIAuthInfo。

ACICPool是连接池句柄,使用 ACIHandleAlloc 分配它,句柄类型为ACI_HTYPE_CPOOL。必须将其传递给 ACIConnectionPoolCreateACIConnectionPoolDestroy

ACICPool *cpoolhp;
ACIHandleAlloc((void *) envhp, (void **) &cpoolhp, ACI_HTYPE_CPOOL,  (size_t) 0, (void **) 0));

一个环境句柄下可以分配多个ACICPool连接池句柄。

ACIAuthInfo是身份验证信息句柄,使用 ACIHandleAlloc 分配它, 认证信息句柄的属性类型为ACI_HTYPE_AUTHINFO。 它被传递给 ACISessionGet 。 它支持用户会话句柄支持的所有属性。

ACIAuthInfo *authp;
ACIHandleAlloc((void *) envhp, (void **) &authp, ACI_HTYPE_AUTHINFO, (size_t) 0, (void **) 0));

连接池的使用

在使用ACI连接池时,客户端程序需按照以下步骤建立连接并开启会话:

  1. 分配连接池句柄
  2. 创建连接池
  3. 登录数据库
  4. 断开与数据库的连接
  5. 销毁连接池
  6. 释放连接池句柄

1) 分配连接池句柄

通过调用 ACIHandleAlloc 函数,指定句柄类型为ACI_HTYPE_CPOOL可以实现对连接池句柄的分配。在一个给定的环境句柄上,可以建立多个连接池。

ACICPool *poolhp;
ACIHandleAlloc((void *) envhp, (void **) &poolhp, ACI_HTYPE_CPOOL,  (size_t) 0, (void **) 0));

2) 创建连接池

通过调用 ACIConnectionPoolCreate 函数,可以建立连接池并实现连接池句柄的初始化。

函数 ACIConnectionPoolCreate 的输出参数poolName和poolNameLen所包含的信息,可用于后续的 ACIServerAttachACILogon2ACISessionGet 函数,替换原有的数据库连接串。

连接池句柄共有以下7个相关属性:

  • ACI_ATTR_CONN_MIN,建立连接池时需要打开的最小连接数。
  • ACI_ATTR_CONN_MAX,连接池中允许的最大连接数。当连接池中的连接已达到最大值,且所有连接均处于忙的状态,此时仍需要连接会持续等待直到获取一个空闲连接。
  • ACI_ATTR_CONN_INCR,连接池中现有的连接全部处于忙的状态时,后续调用仍需要新建连接时,连接每次增长的个数。该参数只在连接池中已有的连接数小于允许的最大连接数时,才会起作用。
  • ACI_ATTR_CONN_NOWWAIT,当连接池中所有的连接处于忙的状态,并且连接池中的连接数已经到最大值,该属性用来决定某一个连接是否需要复审。如果设置了该属性,当连接池中再无空闲连接时,将会抛出错误。否则,将会一直等到获取可用连接为止。
  • ACI_ATTR_CONN_TIMEOUT,连接闲置超过这个时间(以秒为单位)将会被终止,以维持打开连接的最佳数量。该属性可以动态设置。如果该属性不被设置,则连接将永远保持。
  • ACI_ATTR_CONN_BUSY_COUNT,处于busy状态的连接数。
  • ACI_ATTR_CONN_OPEN_COUNT,处于open状态的连接数。
  • 以上介绍的前五个属性可以进行动态设置,应用程序可以通过读取当前的负载,调整这些属性为合适的值。其中属性ACI_ATTR_CONN_MIN、ACI_ATTR_CONN_MAX和ACI_ATTR_CONN_INCR的动态设置,必须调用函数 ACIConnectionPoolCreate ,并且将mode置为ACI_CPOOL_REINITIALIZE,ACI_CPOOL_REINITIALIZE模式调用 ACIConnectionPoolCreate 接口前一定是先用ACI_DEFAULT模式调用了 ACIConnectionPoolCreate ,否则会报错。

使用示例:

ACIConnectionPoolCreate((ACIEnv *)envhp,
                 (ACIError *)errhp, (ACICPool *)poolhp,
                 &poolName, &poolNameLen,
                 (text *)database,strlen(database),
                 (ub4) connMin, (ub4) connMax, (ub4) connIncr,
                 (text *)poolUsername,strlen(poolUserLen),
                 (text *)poolPassword,strlen(poolPassLen),
                 ACI_DEFAULT));

3) 登录数据库

应用程序登录数据库时,有以下三种方式:

  • ACILogon2 ,这是最简单的接口。如果应用程序只需要一个简单的连接池连接,且不需要改变会话句柄的任何属性,可以用该接口。
for (i = 0; i < MAXTHREADS; ++i)
{
   ACILogon2(envhp, errhp, &svchp[i], "hr", 2, "hr", 2, poolName,poolNameLen, ACI_LOGON2_CPOOL));
}
  • ACISessionGet ,这是推荐使用的接口,它为用户提供了使用外部身份验证方法 ACISessionGet 是建议的统一函数调用,用于检索会话。
for (i = 0; i < MAXTHREADS; ++i)
{
        ACISessionGet(envhp, errhp, &svchp, authp, (OraText *) poolName,strlen(poolName), NULL, 0, NULL, NULL, NULL,ACI_SESSGET_CPOOL)
}
  • ACIServerAttachACISessionBegin ,如果应用程序必须对服务器句柄和会话句柄的某些属性进行设置,可以用此接口。这种连接方式,应用程序必须分配连接池句柄、服务器句柄、会话句柄和服务器上下文句柄,然后按照以下步骤建立连接:建立连接池,调用 ACIServerAttach 并设置mode为ACI_CPOOL,调用 ACISessionBegin 设置mode为ACI_DEFAULT。

建立连接池

调用 ACIServerAttach 并设置mode为ACI_CPOOL,

调用 ACISessionBegin 设置mode为ACI_DEFAULT。

4) 断开与数据库的连接

不同的登录方式,可以对应选择下面不同的断开方式,连接池的连接断开本质是将连接归还到连接池,连接可能并未真实销毁,有以下三种方式归还连接的方式:

5) 销毁连接池

通过调用 ACIConnectionPoolDestroy 函数,销毁连接池。

6) 释放连接池句柄

通过调用 ACIHandleFree 函数,释放连接池句柄。

示例代码:

for (i = 0; i < MAXTHREADS; ++i)
{
  checkerr(errhp, ACILogoff((void *) svchp[i], errhp));
}
checkerr(errhp, ACIConnectionPoolDestroy(poolhp, errhp, ACI_DEFAULT));
checkerr(errhp, ACIHandleFree((void *)poolhp,ACI_HTYPE_CPOOL));

连接池使用示例

/* 分配连接池句柄 */
 checkerr(errhp,ACIHandleAlloc(envhp,(void**)&poolhp,
                             ACI_HTYPE_CPOOL,0,0));
 checkerr(errhp, ACIConnectionPoolCreate (envhp,
                             errhp,poolhp,&poolname,&pnamelen,
                             (oratext*)conn_str,
                             len,min,max,incr,0,0,0,0,ACI_DEFAULT));
 /* 分配会话句柄 */
 checkerr(errhp, ACIHandleAlloc(envhp,
                 (void**)&authp, ACI_HTYPE_AUTHINFO, 0,0));
 checkerr(errhp,ACIAttrSet(authp, ACI_HTYPE_AUTHINFO,
                  username, strlen((char*)username), ACI_ATTR_USERNAME,errhp);
 checkerr(errhp,ACIAttrSet(authp, ACI_HTYPE_AUTHINFO,
                  password, strlen((char*)password), ACI_ATTR_PASSWORD,
                  errhp));
/* 从连接池中获得一连接(获得一个上下文句柄) */
 checkerr(errhp,ACISessionGet(envhp,errhp,
              &svchp,authp,poolname, pnamelen,0,0,0,0,0,
              ACI_SESSGET_CPOOL));
.....
/* 释放一个链接,归还到连接池 */
 checkerr(errhp,ACISessionRelease(svchp,errhp,NULL, 0,ACI_DEFAULT));

连接池和会话池如何选择

由于神通数据库的机制原因,连接池和会话池效果一样,会话和物理连接是一一对应,无法分开。不管是连接池还是会话池,池中的的会话或连接个数代表了与数据库实际创建的连接数。

连接超时处理机制

ACI连接数据库时,可能会访问的主机未开机,或者输入的IP不存在等原因,不同的原因,ACI会有不同的处理方式:

  1. 无法快速返回的情况

无法快速返回的情况包括主机未开机、IP不存在:

ACI老版本(V2.0.17以前但不含V2.0.17)采用的是阻塞连接模式,在这种情况下,网络SOCKET的连接超时受平台控制,在Linux下受系统参数net.ipv4.tcp_syn_retries影响,这个参数默认值为6,根据系统参数规则,访问一个不存在的连接时会重试6次,每次等待时间(单位为秒)分别为2,4,8,16.32,64,算上首次连接的1秒,合计127秒,如果想降低超时时间,可以减小操作系统net.ipv4.tcp_syn_retries参数值,比如为3,这超时时间大约为20秒左右返回;Windows下默认等待约20几秒,没有找到控制方法。

ACI新版本(V2.0.17以后含V2.0.17)连接时默认采用异步模式,通过轮询的方式判断连接是否正常,连接时间受参数配置文件中的socket_connect_timeout参数控制,默认超时时间为60秒;也可以设置环境句柄的属性:ACI_ATTR_SOCKET_CONNECT_TIMEOUT进行设置。

ACI新版本也可以用阻塞模式,与老版本兼容,通过设置socket_connway参数值为on即可,或者设置环境句柄的属性:ACI_ATTR_SOCKET_BLOCK_CONN进行设置。需要注意的是,如果修改为阻塞模式,连接的超时时间一样受操作系统控制。

  1. 可快速返回的情况

数据库未启动、客户端访问端口号错误、用户名和密码错误等情况,会快速返回,不会有等待。

设置网络连接的阻塞模式以及连接超时时间的两种方式:

(1) 设置ACI的运行配置文件,将socket_connect_timeout设置为60

(2) 通过接口设置,通过 ACIAttrSet 进行设置

sb4 sock_connect_timeout= 60;
bool socket_block_conn = TRUE;
//设置网络阻塞模式
err = ACIAttrSet ((void *) env, ACI_HTYPE_ENV, (void *) & socket_block_conn,sizeof(socket_block_conn), ACI_ATTR_SOCKET_BLOCK_CONN, (ACIError *) err);
//设置连接超时时间
err = ACIAttrSet ((void *) env, ACI_HTYPE_ENV, (void *) & sock_connect_timeout,sizeof(sock_connect_timeout),ACI_ATTR_SOCKET_CONNECT_TIMEOUT, (ACIError *) err);

自动重连机制

重连是由于连接因为网络原因断开后,需要重连过程,重连其实代表的为重连次数,每隔一秒会进行一次网络重连,每次重连的超时时间受socket_connect_timeout参数控制,比如reconnect_timeout设置为10,非阻塞连接下:socket_connect_timeout设置为20秒,总的网络重连超时时间为10*20=200秒; 阻塞模式下则是系统超时时间*10,比如默认是127*10 = 1270秒。设置重连参数的两种方式:

  1. 设置ACI的运行配置文件,将reconnect_timeout设置为10
  2. 通过接口设置,通过 ACIAttrSet 进行设置
sb4 reconnect_timeout= 10;
err = ACIAttrSet ((void *) env, ACI_HTYPE_ENV, (void *) &reconnect_timeout,
      sizeof(reconnect_timeout), ACI_ATTR_RECONNECT_TIMEOUT, (ACIError *) err);

Socket扩展属性

部分现场需要,需要在socket网络层面,添加一定的扩展属性对访问客户端进行标识,用于通信认证的一种补充,ACI的socket对扩展进行了支持,ACI成名调用fsetxattr函数设置socket的扩展属性。设置扩展属性的方式有两种:

  1. 设置ACI的运行配置文件,将sockmark设置为true,并设置sockmark_name和sockmark_aftersocket、sockmark_afterconnect相关参数。
  2. 通过接口设置,通过 ACIAttrSet 进行设置
sb4 sockmark = FALSE;
char * markname=” security.secmark”;
char * markaftersoket = "007F000001E30401000000000000 ";
char * markafterconn = "007F000001E30401000000000001";
err = ACIAttrSet ((void *) env, ACI_HTYPE_ENV, (void *) sockmark,
      sizeof(sockmark), ACI_ ACI_ATTR_SOCK_MARK, (ACIError *) err);
err = ACIAttrSet ((void *) env, ACI_HTYPE_ENV, (void *) sockmark,
      sizeof(sockmark), ACI_ ACI_ATTR_SOCK_MARK_NAME, (ACIError *) err);
err = ACIAttrSet ((void *) env, ACI_HTYPE_ENV, (void *) markaftersoket,
      strlen(markaftersoket),ACI_ATTR_SOCK_MARK_AFTERSOCKET, (ACIError *) err);
err = ACIAttrSet ((void *) env, ACI_HTYPE_ENV, (void *) markafterconn,
      strlen (markafterconn),ACI_ATTR_SOCK_MARK_AFTERCONNECT, (ACIError *) err);

SSL安全通信

从v2.0.51(含)版本开始,aci支持通过openssl库与数据库端进行安全通信,需要有以下两个步骤:

1)通过设置环境句柄的ACI_ATTR_LDAP_AUTH属性来开启是否启用安全连接:

ub2 auth_type = 1;
ACIAttrSet(env, ACI_HTYPE_ENV, &auth_type, sizeof(ub2), ACI_ATTR_LDAP_AUTH, err);

ACI_ATTR_LDAP_AUTH属性的值如果大于0,则代表开启安全通信。

2)通过设置环境句柄的ACI_ATTR_WALL_LOC属性来设置客户端密钥证书的文件路径和证书密码:

char * auth_wallet_path = "/opt/wallet/";
ACIAttrSet(env, ACI_HTYPE_ENV, auth_wallet_path, strlen(auth_wallet_path), ACI_ATTR_WALL_LOC, err);

char * auth_wallet_pwd = "szoscar55";
ACIAttrSet(env, ACI_HTYPE_ENV, auth_wallet_pwd, strlen(auth_wallet_pwd), ACI_ATTR_WALL_PWD, err);

如果指定的路径目录下没有相关证书,在调用ACISessionBegin会错误, 返回错误码:ERR_SSL_FILE_NOT_EXIST;

路径结尾必须有目录分隔符(/opt/wallet/ 正确,/opt/wallet 错误);

值得注意的是证书密码与数据库用户的密码不同,需要区别使用。

证书文件夹中必须包含以下文件:

  • SYSDBA.crt
  • SYSDBA.key
  • ca.crt

注解

  1. 用户密钥文件的名称,必须与登录数据库的用户相同,且大小写一致,比如用户名为SYSDBA,/opt/wallet/目录下必须有SYSDBA.crt和SYSDBA.key密钥文件。

  2. ACI调用ssl库和crypto库是使用时才加载的,默认会到ACI当前目录下加载,找不到则到系统目录中查找,查找文件:

    • win32 :libssl-1_1.dll 和 libcrypto-1_1.dll
    • win64 :libssl-1_1-x64.dll 和 libcrypto-1_1-x64.dll
    • Linux : libssl.so.1.1 和 libcrypto.so.1.1

3)如果需要设置指定的加密算法。可以通过设置环境句柄的ACI_ATTR_WALL_CIPHERLIST属性来设置对应的加密算法:

char * auth_wallet_cipherlist = "tls1.3,TLS_AES_128_GCM_SHA256";
ACIAttrSet(env, ACI_HTYPE_ENV, auth_wallet_cipherlist, strlen(auth_wallet_cipherlist), ACI_ATTR_WALL_CIPHERLIST, err);

指定加密协议及加密算法时,加密协议及加密算法之间使用逗号进行分隔。

支持的协议版本有:ssl3、tls1、tls1.1、tls1.2、tls1.3,使用无效的协议版本时,将会无法连接数据库。

操作系统认证(OS免密登录)

从v2.0.58(含)版本开始,aci支持本地操作系统认证与数据库端进行通信。设置操作系统认证时,不需要指定登录的用户名及密码即可完成数据库的连接。设置操作系统认证的方式有两种:

  1. 设置ACI的运行配置文件,将enable_os_auth设置为1
  2. 通过接口设置,通过 ACIAttrSet 进行设置
ub2 auth_type = 1;
ACIAttrSet(env, ACI_HTYPE_ENV, &auth_type, sizeof(ub2), ACI_ATTR_OS_AUTH, err);

ACI_ATTR_OS_AUTH属性的值为1,则代表开启操作系统认证。

注解

当用户通过本地Windows或Linux用户帐户进行连接时,会将该本地用户的操作系统身份信息将传送到数据库服务端,信息包括:域名、用户名、域用户组名。 数据库服务器获得该信息,并通过检测系统表中是否存在该用户账号或者所属的组账号信息来进行用户身份的验证。

配置步骤如下

1.数据库设置ENABLE_OPERATING_SYSTEM_AUTHENT = 1参数

2.创建一个OPS$+操作系统用户的用户名,需要添加认证方式为IDENTIFIED EXTERNALLY参数。例如:create user OPS$root IDENTIFIED EXTERNALLY