IBM Support

优化Java Toolbox for i 连接策略

Technical Blog Post


Abstract

优化Java Toolbox for i 连接策略

Body

作为一种面向对象的Java编程模型,IBM Java Toolbox for iJava应用程序访问IBM i数据与资源提供了API级的通信支持。而AS400类则代表了IBM Java Toolbox for iIBM iSocket连接,任何依赖IBM Java Toolbox for i操纵IBM i数据与资源的行为都是以与IBM i的通信连接为前提。从这个意义上讲,AS400可以看成IBM i的访问入口点。本文的主要目标就是从重用IBM i连接对象AS400的角度,指导读者如何使用IBM Java Toolbox for i实现与IBM i的优化连接。有关IBM Java Toolbox for i的基础知识,请参见另一篇技术文档Toolbox for Java JTOpen

从内容上,本文主要分为5部分。第1部分主要从连接池优化的角度,介绍IBM Java Toolbox for i针对IBM i连接的优化;第2部分则主要从重用AS400对象的角度,介绍IBM Java Toolbox for i的优化策略;第3部分则是连接池与重用AS400两种优化策略的结合;第4部分介绍的是优化IBM i连接的启停策略;第5部分是总结。

IBM Java Toolbox 连接池优化

通常而言,优化通信连接的一个习惯性想法是采用连接池策略,减少每次建立服务器端连接所带来的资源与时间开销。IBM Java Toolbox for i提供了AS400ConnectionPool类,用于创建与管理连接池,如图一。

1. 连接池工作原理

 图像

 

 

 

 

 

 

 

 

 

 

 

 

 

 

AS400ConnectionPool类的工作原理如下:

  1. 创建IBM i连接池对象—AS400ConnectionPool
  2. 设置连接池相关属性,比如最大连接数。
  3. 建立一定数量的预连接,即所谓的空闲连接。
  4. 对于一个服务器访问请求,直接从连接池中得到一个连接。如果连接池对象中没有空闲的连接,且连接数没有达到最大活跃连接数,则创建一个新的IBM i连接。
  5. 存取IBM i连接。
  6. 关闭IBM i连接,并非真正关闭,而是将其放入空闲队列中。如果实际空闲连接数大于初始空闲连接数,则释放连接。
  7. 释放连接池对象。相应地,释放所有IBM i连接。 

 

作为示例,清单1演示的是如何使用AS400ConnectionPool类,维护与COMMAND主机服务器的连接池策略。

清单 1. 连接池用法示例1

// 创建与IBM i的连接池.

AS400ConnectionPool testPool = new AS400ConnectionPool();

// 设置连接池的最大连接数为128.

testPool.setMaxConnections(128);

// COMMAND主机服务器建立5个预连接.

testPool.fill(sys, usr, pwd, AS400.COMMAND, 5);

// 从连接池中获取一个IBM i连接

AS400 newConn = testPool.getConnection(sys, usr, pwd, AS400.COMMAND);

// 使用IBM i连接远程执行CL命令.

CommandCall cmd = new CommandCall(newConn);

cmd.run("CRTLIB FRED");

// 返回IBM i连接至连接池

testPool.returnConnectionToPool(newConn); 

// 释放连接池.

testPool.close();

 

接下来,我们对比一下使用连接池策略前后的性能指标,如清单2所示。它演示了以下三种情况:

  • 第一种情况: 不使用连接池,建立100条数据库连接所耗费的时间
  • 第二种情况: 使用连接池(预安装50条数据库连接),建立100次数据库连接所耗费的时间
  • 第三种情况: 使用连接池,100次反复重用1条数据库连接所耗费的时间

 

清单 2. 连接池用法示例2

// 创建与IBM i的连接池.

AS400ConnectionPool testPool = new AS400ConnectionPool();

// 设置连接池的最大连接数为128.

testPool.setMaxConnections(128);

// COMMAND主机服务器建立50个预连接.

testPool.fill(sys, usr, pwd, AS400.DATABASE, 50);

long start = 0;

long end = 0;

 

// 第一种情况: 不使用连接池,建立100条数据库连接所耗费的时间

start = System.currentTimeMillis();

for (int i = 0; i < 100; i++) {

       AS400 con = new AS400(sys, usr, pwd);

       con.connectService(AS400.DATABASE);

       con.disconnectService(AS400.DATABASE);

}

end = System.currentTimeMillis();

System.out.println("第一种情况: 不使用连接池,建立100条数据库连接所耗费的时间:\n" + (end-start)+"ms");

 

// 第二种情况: 使用连接池(预安装50条数据库连接),建立100次数据库连接所耗费的时间

start = System.currentTimeMillis();

for (int i = 0; i < 100; i++) {

       AS400 con = testPool.getConnection(sys, usr, pwd, AS400.DATABASE);

}

end = System.currentTimeMillis();

System.out.println("第二种情况: 使用连接池(预安装50条数据库连接),建立100次数据库连接所耗费的时间:\n" + (end-start)+"ms");

 

// 第三种情况: 使用连接池,100次反复重用1条数据库连接所耗费的时间

start = System.currentTimeMillis();

for (int i = 0; i < 100; i++) {

       AS400 con = testPool.getConnection(sys, usr, pwd, AS400.DATABASE);

       testPool.returnConnectionToPool(con);

}

end = System.currentTimeMillis();

System.out.println("第三种情况: 使用连接池,100次反复重用1条数据库连接所耗费的时间:\n" + (end-start)+"ms");

 

// 释放连接池.

testPool.close();

 

对应的输出结果为:

第一种情况: 不使用连接池,建立100条数据库连接所耗费的时间:

7781ms

第二种情况: 使用连接池(预安装50条数据库连接),建立100次数据库连接所耗费的时间:

3859ms

第三种情况: 使用连接池,100次反复重用1条数据库连接所耗费的时间:

16ms

 

这里需要说明的是,由于系统环境等因素的限制,我们并不保证结果的精确性,只是为了体现以上三种情况下连接性能的相对差异性。

 

通过分析测试结果,我们可以发现,针对建立100条与数据库主机服务器连接的同一需求,第一种情况实际建立了100条连接,而第二种情况实际连接数为50条(其余50条均为连接池中的预连接,无需连接开销),对应的连接时间差不多为前者的一半。第三种情况实际上并没有增加任何服务器连接的开销,因此它耗费的时间最少,相对于前两种情况,几乎可以忽略不计。

 

IBM Java Toolbox 重用IBM i连接优化

上述连接池策略是一种基于缓冲原理的服务器连接优化技术,可以有效的减少开销,提高效率。

不过,连接池策略有一定的限制:即连接池中每一个连接均为同一种类型。而对于IBM i连接而言,不同类型的主机服务器对应不同的连接类型。IBM Java Toolbox for i支持的常用主机服务器如下:

  • JDBC
  • Program call and command call
  • Integrated file system
  • Print
  • Data queue
  • Record-level access 

如此一来,当服务器的种类变得多种多样的时候,连接池策略的局限性马上就体现出来了。比如,清单1演示的是连接池对象AS400ConnectionPool只针对Command Call服务器的连接有效,它无法创建基于JDBC服务器的连接。

针对这种情况,代表IBM i连接的AS400对象采用了内置的重用服务器连接策略,可有效解决该问题。图2演示的是如何重用AS400对象,节省与服务器的连接,提高性能。对于客户端程序的6个服务器连接请求,构造了3AS400对象。

可以看到,无论是来自不同服务器的连接请求,还是同一服务器的重复请求,均可重用AS400对象。

2 重用多个AS400对象

图像

   

 

 

 

 

 

 

 

 

 

 

 

 

   

 

 

为进一步节省连接带来的资源开销,我们可以采取一种“极端”的方式,即重用同一个AS400,服务于所有的服务器连接请求,如图3所示。

3 重用一个AS400对象

图像

 

 

 

 

 

 

 

 

 

 

 

   

 

   

   

 

 

这里,我们简单分析下上述两种应用场景下服务器请求,重用AS400对象数量,服务器连接数量之间的对应关系。图2的应用场景中,对于6个服务器请求,我们重用3AS400对象,一共建立了5条连接。在图3的应用场景中,对于4个服务器请求,我们重用1AS400对象,一共建立了3条连接。

 

下面,我们从代码的角度,分别给出几个示例,详细阐述重用AS400对象的规则。

通常情况下,我们为每一个服务器请求创建一个AS400对象,建立一条与IBM i的连接。在清单3中,我们创建了2CommandCall对象,用于向Command Call服务器发送CL命令。由于2CommandCall对象分别使用了2个不同的AS400对象,因此创建了两条服务器连接。

清单3. 针对同一连接类型,未重用AS400对象

// 创建两个AS400对象.

AS400 sys1 = new AS400(system, usr, pwd);

AS400 sys2 = new AS400(system, usr, pwd);

// 分别关联AS400对象

CommandCall cmd1 = new CommandCall(sys1," CRTLIB MyLib1 ");

CommandCall cmd2 = new CommandCall(sys2," CRTLIB MyLib2 ");

// 两条连接,执行CL

cmd1.run();

cmd2.run();

换句话说,清单3的统计结果是:针对2个相同的服务器请求,各自使用1AS400对象,一共建立了2条连接。

 

针对上述同一种IBM i连接类型,再来看一下重用AS400的情况。在清单4中,我们创建了2CommandCall对象,用于向Command Call服务器发送CL命令。由于2CommandCall对象重用了同一个AS400对象,因此只创建了一条服务器连接。

清单4. 针对同一连接类型,重用1AS400对象

// 创建一个AS400对象.

AS400 sys = new AS400(system, usr, pwd);

// 重用同一个AS400对象

CommandCall cmd1 = new CommandCall(sys," CRTLIB MyLib1 ");

CommandCall cmd2 = new CommandCall(sys," CRTLIB MyLib2 ");

// 重用一条连接,执行CL

cmd1.run();

cmd2.run();

换句话说,清单4的统计结果是:针对2个相同的服务器请求,重用1AS400对象,一共建立了1条连接。

 

通过上述同一连接类型的优化策略,我们减少了连接次数,提高了程序的性能。不过,这并不意味着任何情况下都可以通过重用AS400,达到提升程序性能的目标。

在清单5中,我们创建了2个服务器请求对象。一个为CommandCall对象,用于向Command Call服务器发送CL命令,另一个为IFSFileInputStream对象,用于向Integrated file system服务器请求文件相关服务。即使两个服务器请求对象分别重用1AS400对象,但是创建了两条服务器连接,分别作用于Command Call服务器与Integrated file system服务器。

清单 5. 针对不同连接类型,重用1AS400对象

// 创建一个AS400对象.

AS400 sys = new AS400(system, usr, pwd);

// 创建CommandCall请求.

CommandCall cmd = new CommandCall(sys," CRTLIB MyLib ");

// 创建IFSFile请求

IFSFileInputStream file = new IFSFileInputStream(sys,"/myfile");

// 执行CL命令。

cmd.run();

换句话说,清单5的统计结果是:针对2个不同的服务器请求,虽然重用1AS400对象,仍然建立了2条不同的连接,服务于各自的服务器连接。

 

连接池优化与重用IBM i连接优化的结合

作为扩展,我们将基于AS400ConnectionPool的连接池策略与基于AS400的连接重用策略有机的结合起来。在清单6中,我们创建了一个AS400ConnectionPool连接池,但却没有指定任何连接池的服务器种类。在创建基于Command Call服务器的连接请求后,该连接池就自动创建并维护基于Command Call的服务器请求连接。

清单6. 从连接池获取连接

// 创建一个连接池

AS400ConnectionPool pool = new AS400ConnectionPool();

// 从连接池中获取一个连接

AS400 sys = pool.getConnection(system, usr, pwd);

// 使用该连接,执行CL命令

CommandCall cmd = new CommandCall(sys," CRTLIB MyLib ");

cmd.run();

// 连接返回给连接池

pool.returnConnectionToPool(sys);

 

在前面5个示例的铺垫之后,第6个例子应该非常容易理解。由于采用了AS400ConnectionPool连接池策略,相比于第1条初始化连接,第2条连接“轻量级”许多,实际上,它重用了第1条连接,如清单7所示。

清单7. 重用连接池中的连接

// 创建一个连接池

AS400ConnectionPool pool = new AS400ConnectionPool();

// 从连接池中获取一个连接

AS400 sys = pool.getConnection(system, usr, pwd);

// 使用该连接,执行CL命令

CommandCall cmd = new CommandCall(pool," CRTLIB MyLib ");

cmd.run();

// 连接返回给连接池

pool.returnConnectionToPool(sys);

AS400 conn = pool.getConnection(system, usr, pwd, AS400.COMMAND);

 

优化IBM i连接的启停策略

刚才,我们围绕着AS400AS400ConnectionPool两个对象,分别阐述了如何采取重用的思想,尽量减少对服务器连接负载。另一方面,服务器连接,离不开启与停两个操作。由于服务器类别的不同,服务器连接的启、停,尤其停止操作会稍显复杂。因此,这里有必要结合一些示例来理解启动与停止以AS400为中心的服务器连接。

 

首先,来看一下针对AS400的启动连接。清单8演示的是如何启动连接。相对来说逻辑比较简单。

清单8. 启动服务器连接

// 创建AS400对象.

AS400 sys = new AS400(system, usr, pwd);

// 连接Command服务器

sys.connectService(AS400.COMMAND);

 

从编程的习惯上,一旦连接被建立,应用程序有责任在合适的时候释放连接,无论采取的是显示或者隐式关闭连接的方式。

下面,我们结合图文与代码示例,阐述释放连接的几种方式。图4与图2属于同一个应用场景,要关闭最上面的一条基于IFS服务器的连接,只需要释放第一个AS400对象即可。

4 释放“独占式”AS400对象连接

图像

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

释放“独占式”AS400连接对应的代码如清单9所示:

清单 9. 释放“独占式”AS400连接

// 创建AS400对象.

AS400 sys = new AS400(system, usr, pwd);

// 使用该连接,执行CL命令

CommandCall cmd = new CommandCall(sys," CRTLIB MyLib ");

cmd.run();

// 释放连接

sys.disconnectService(AS400.COMMAND);

 

如果说,图4演示的是释放关于独占式AS400对象连接,那么图5则演示的是释放“共享式”AS400对象连接。它的特点是,一旦“共享式”AS400对象被释放连接,则所有的共享该AS40对象的服务器连接都会被中断。

5 释放“共享式”AS400对象连接

图像

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

释放“共享式”AS400连接对应的代码如清单10所示:

清单 10. 释放“共享式”AS400连接

// 创建一个AS400对象

AS400 sys = new AS400(system, usr, pwd);

// 重用同一个AS400对象

CommandCall cmd1 = new CommandCall(sys," CRTLIB MyLib1 ");

CommandCall cmd2 = new CommandCall(sys," CRTLIB MyLib2 ");

// 重用一条连接,执行CL

cmd1.run();

// 释放共享式连接

sys.disconnectService(AS400.COMMAND);

// 连接关闭,需要重连

cmd2.run();

sys.disconnectService(AS400.COMMAND);

 

我们可以看到,当释放“共享式” AS400对象时,容易“伤及无辜”,同时中断所有与该” AS400对象关联的连接。一个自然的做法是重连。这种重连是隐式的,比如在上例中,调用run()会隐式自动重新连接Command Call服务器。我们称之为自动重连。

但是并不是所有的服务器都支持自动重连功能。IFS文件服务器就是一个例外。理由是避免破外文件读写的一致性。例如,当IFS文件服务器连接被断开之后,另一程序或者操作视图改写该文件的内容,甚至是删除操作。因此需要显示的重新建立连接,如清单11所示。

清单 11. IFS服务器不支持自动重连

// 创建一个AS400对象

AS400 sys = new AS400(system, usr, pwd);

IFSFileInputStream file1 = new IFSFileInputStream(sys,"/file1");

IFSFileInputStream file2 = new IFSFileInputStream(sys,"/file2");

// 建立IFS连接,操作文件

int i1 = file1.read();

file1.close();

// 关闭连接

sys.disconnectService(AS400.FILE);

// 重连失败

int i2 = file2.read();

file2.close();

sys.disconnectService(AS400.FILE);

 

至此,我们分别从连接池策略,重用AS400策略等方面,相继介绍了IBM Java Toolbox for i针对IBM i连接优化的编程支持。

 

总结

总之,IBM i的连接优化是IBM Java Toolbox for i编程的重要话题之一。如何在各种不同的应用场合,结合连接池与AS400对象重用策略,合理利用IBM i的连接资源,是本文主要讨论的场景。读者应根据具体的使用场景,选择适合的IBM i优化连接策略。

 

参考资源

 

作者:皮光明 

 

[{"Business Unit":{"code":"BU054","label":"Systems w\/TPS"},"Product":{"code":"SWG60","label":"IBM i"},"Component":"","Platform":[{"code":"PF025","label":"Platform Independent"}],"Version":"","Edition":"","Line of Business":{"code":"LOB08","label":"Cognitive Systems"}}]

UID

ibm11144936