|
<!--START RESERVED FOR FUTURE USE INCLUDE FILES--><!-- this content will be automatically generated across all content areas --> <!--END RESERVED FOR FUTURE USE INCLUDE FILES-->
|
级别: 初级
Teodor Zlatanov (tzz@iglou.com), 程序员, Gold Software Systems
2000 年 10 月 09 日
如果您使用手工构建的方法,那么基于文件的配置很快就会崩溃。Teodor Zlatanov 演示了 AppConfig 模块如何处理用于 Perl 程序的本地配置存储,以及如何将这些配置存储到数据库中,以便随后能从网络上的任何机器进行访问。
<!--START RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--END RESERVED FOR FUTURE USE INCLUDE FILES-->
程序(从低级的列目录程序到 Web 浏览器)的首要需求之一是:它应该是可配置的。事实证明,基于文件的可配置性和命令行选项的组合是针对可配置性需要的长期而又灵活的解决方案。Perl 程序通常采用这种方法,尽管它们往往还包括一个配置文件和命令行解析例程。
我们将在本文中使用的命令行解析有一点复杂。因此,为了避免进一步的混淆,如果您正在进行的解析等级高于简单参数,我建议您使用 Parse::RecDescent(或等价的解析模块)。有关复杂命令行解析的讨论,请参阅我关于说英语的 Perl 程序的 前一篇文章。
在开始之前,请确保您已经在系统上安装了 Perl 5.005(或更新版本)和 CPAN AppConfig 模块。您还需要 Persistent::MySQL 或适用于您的特定数据库的 Persistent 类。这些都可以在 CPAN 中获取(请参阅本文后面的 参考资料)。
简单的方法:自己动手(DIY)
理论上(并在有适当工具的情况下!)任何人都可以构建配置解析器,对吗?举例来说, Perl Cookbook展示了一个提供良好开端的快速实现。那么,如果您从此类实现开始的话,编写一个配置文件解析器有多难呢?
实际上相当困难,因为此类项目涉及如下几个比较复杂的问题:
- 配置文件中的空白行和注释
- 错误的行(象拼错的关键字)以及哪些内容不可或缺而哪些内容可以忽略的问题
- 您必须自己编写解析器的可能性,因为您可能需要多种不同数据结构(例如,布尔型、标量、数组和散列)
- 多个配置文件
- 变量缺省值
- 将命令行选项与文件配置集成并控制它们的交互方式
- 用另一种 DIY 配置文件格式培训用户(这通常有些类似于:“只要别一行中只有‘=’号,这就有效。哦,还有注释由‘#’开始,但它们必须独立于其它行。别忘了对关键字使用大写,对值用小写。回来!回来!我还没有告诉您关于强制关键字的事情呢!”)
- 重新编写或复制可能有错误的配置代码而不是重用模块
- 使配置成为具有一致接口的对象而不是通常的关键字的 DIY 随机散列
您已经害怕了?这就是我们使用 AppConfig 的原因。它可以处理所有这些问题。很清楚的一点是,您不应该使用 DIY。
求助于 AppConfig
尽管 Andy Wardley 编写的 AppConfig CPAN 模块有助于解决上面列出的所有问题,但它不是万能的。它不可能魔术般地改进您的程序。有时需要重新编写以使用 AppConfig。(也存在一点学习曲线,本文试图帮助您降低学习的难度。)
显然,如果您不确定应该使用 DIY 还是 AppConfig,那么应该根据您的经验和正在编写的内容作出决定。但我相信,AppConfig 不能做得象 DIY 一样好或更好这种情况是非常少的。
关于 AppConfig 能为您做些什么,以下将逐点进行说明(按照前一节的问题列表):
- 配置文件中的空白行和注释:AppConfig 可以识别空白行和注释,并将忽略它们。
- 错误的行(象拼错的关键字)中哪些内容是不可或缺的,哪些内容是可以忽略的:可以设置 AppConfig 的灵敏度,以忽略错误设置或异常中止程序。如果可以使用其它的拼写,则关键字可以有别名(譬如在国际化设置中)。
- 编写自己的解析器,因为您需要不同的数据结构(布尔型、标量、数组)和散列:AppConfig 处理所有这些数据结构,但它这样做时不用嵌套。如果您需要嵌套的散列或数组,则需要自己动手或帮助一下 AppConfig。
- 多个配置文件:AppConfig 将根据需要处理任意数量的配置文件,依次从每个文件中装入设置。您还可以帮助 AppConfig 重新设置数组和散列,以便插入到堆栈底部的值不必出现在堆栈顶部。
- 变量缺省值:AppConfig 提供变量缺省值。“-variable”语法将配置文件中的变量重新设置成其缺省状态。
- 控制命令行选项并将它们与文件配置集成:AppConfig 提供对 Getopt::Std 和 Getopt::Long 命令行选项解析的支持。解析可以在读取配置文件之前或之后执行。
- 用另一种 DIY 配置文件格式培训用户:AppConfig 使用标准、灵活的格式。“KEYWORD 值”和/或“KEYWORD=值”对于标量而言都是可接受的。因为数组是由元素构成的,所以“ARR=1”的后面跟着“ARR=2”将产生一个具有元素 1 和 2 的 ARR 数组。您还可以将布尔选项指定为“bool”、“nobool”、“!bool”、“bool on”、“bool off”、“bool yes”(但是“bool no”会产生错误)、“bool=1”、“bool=0”。(显然,“声名狼籍的”符号逻辑发明者 George Boole 博士 — 请参阅 参考资料 — 会觉得这些选项很亲切。)散列选项被指定为“KEYWORD PARAMETER = 值”,其中的值将成为带有关键字 PARAMETER 的散列项。
- 重新编写或复制可能有错误的配置代码,而不是重用模块:AppConfig 对这一点相当稳定,并且到该模块的接口不大可能更改。它还已经通过了数千名其他程序员的测试,所以为什么不使用它呢?
- 使配置成为具有一致接口的对象,而不是使用通常的 DIY 随机散列:一致的 API 从主程序中抽取配置处理程序,并用简化与处理程序的连接(在本例中处理程序是 AppConfig)。这种方法引入的错误也更少,因为它只使用方法,而不直接使用数据结构。
既然我们已经看到了很多选用 AppConfig 的好理由,那么,让我们看一下完整的带注释的 AppConfig 用法示例。目前,我们将省略许多比较高级的特性(将在下一节中讨论)。可以从命令行用“-varname value”设置标量、布尔型和数组变量,用“-varname key=value”设置散列变量。此处用的配置文件是 config.pl,以下是示例:
# blank lines are ignored
# set a boolean
debug
# set a scalar
name=E.T.
# set an array
hosts = dbhost
hosts = backuphost
# reset the hosts array
-hosts
# add new values to hosts
hosts = firewall
hosts = farewell
# set a hash
phone joe = 222-333-4444
phone marge = 555-666-7777
|
AppConfig 高级用法
AppConfig 可以在几个级别上进行变量扩展,这取决于 EXPAND 设置。有关更多详细信息请参阅 AppConfig 文档。
# expand all variables, globally
my $config = AppConfig->new({ GLOBAL => { EXPAND => EXPAND_ALL } });
# expand just HOME_DIR as UID, so "~username" will work as in the shell
$config->define('HOME_DIR => { ARGCOUNT => ARGCOUNT_ONE, EXPAND
=> EXPAND_UID });
|
INI 样式的节是 AppConfig 的另一个特性,您会发现它很有用。通过在配置文件中使用 [节](它本身占一行),您可以列出在文件结束前使用的所有关键字,也可以使用节名加上下划线‘_’列出本节和下一节的所有关键字。例如:
[file]
location = /tmp
type = txt
name = accounts.txt
[database]
host = wyrm
user = slayer
password = amethyst
|
等价于:
file_location = /tmp
file_type = txt
file_name = accounts.txt
database_host = wyrm
database_user = slayer
database_password = amethyst
|
可以用 varlist() 函数检查 AppConfig 配置对象。下面的代码打印了 AppConfig 对象中每个变量的内容。注:varlist() 可能有点麻烦,因为它必须采用正则表达式(空字符串是绝对无效的)。
use Data::Dumper; # for hash and array references
my %varlist = $config->varlist('.*');
foreach my $varname (keys %varlist)
{
print "Variable name $varname, value = ", Dumper $config->get($varname), "/n";
}
|
AppConfig 中有一个 Getopt::Long 接口,它允许访问 Getopt::Long 模块的所有功能。下面的代码定义了 Getopt::Long 的变量参数,调用该段代码以解析来自命令行的参数。无效值会引起错误。
$config->define("help|h|!"); # define a boolean
$config->define("code|c|=i"); # define a scalar integer
$config->define("list|l|=f@"); # define a array of floating point values only
$config->define("uids|u|=f%"); # define a hash of floating point values only
$config->getopt(); # instead of args(), to use the Getopt::Long options
|
AppConfig 中还可以进行变量验证。这意味着通过引用正则表达式(或者甚至是一段代码),变量会拒绝将其值设置为某些恶意的值或完全无意义值的尝试。
# the username validation succeeds only when it is exactly "joe"
# the password validation succeeds when it contains "joe" or "joE"
$config->define(
'USERNAME' => { ARGCOUNT => ARGCOUNT_ONE,
VALIDATE => sub # subroutine validation
{
my $varname = shift @_;
my $value = shift @_;
print "$varname = $value/n";
return ($value eq "joe");
}
},
'PASSWORD' => { ARGCOUNT => ARGCOUNT_ONE,
VALIDATE => "jo[Ee]" # regex validation
}
);
|
AppConfig 使自动触发操作成为可能,因此每次变量的值更改时,该操作就会执行。注:对 AppConfig 的引用也被传送到子例程,所以单个更改会触发其它变量更改。
$config->define(
'USERNAME' => { ARGCOUNT => ARGCOUNT_ONE,
ACTION => sub # autoaction
{
my $config = shift @_;
my $varname = shift @_;
my $value = shift @_;
print "$varname = $value/n";
}
}
);
|
AppConfig 限制
AppConfig 不处理变量中嵌入的代码。在我看来,配置文件中无论如何都不应该存在代码,并且允许用户执行任意代码是个坏主意。但是,AppConfig 不提供对变量的自动求值,尽管确实可以协调与变量相关联的验证和自动操作子例程来执行这项任务。如果您确实感到对此有强烈的需求,那么挑出有疑问的变量并自己针对它们运行 eval()(以下面所阐述的方法)。不用说,除非您完全希望将对您的程序的这一级别的控制权赋予用户,否则就 不要这样做。
foreach my $varname ('username', 'password')
{
$config->set($varname, eval $config->get($varname));
}
|
AppConfig 中 INI 样式的节不是很重要。它们定义代码节,但在使用之前必须预先知道这些节。最好先把节设计好,以便它们创建嵌套在父对象中的新 AppConfig 对象,但这是个次要问题。
在简单测试中,使用 AppConfig 似乎不影响装入和执行速度。它是个相当小的模块,其大小/速度代价通常可以忽略不计。当然,如果您的程序对时间很敏感,那么您应该针对使用和不使用 AppConfig 的情况分别对它计时,然后自己决定是否值得使用该模块。
AppConfig 的复杂程度和学习曲线之所以比您原来预期的要低很多,很大程度上是因为出色的 API。这里有使人混淆的地方(尤其对新程序员更是如此),但总的来说,对于任何原先具有 Perl 经验的人,这不是很重要的问题。
AppConfig 的解析限制在于其可用性。如果您需要命令行选项的高级解析,请参阅有关说英语的 Perl 程序的 前一篇文章。(这一限制与 AppConfig 不能进行上下文敏感的解析有特殊的关系。)
用 AppConfig 和 Persistent::DBI 将配置上载到数据库
我建议在阅读本节之前,阅读我关于 用 Persistent 模块保存数据的文章。您还应该对 Perl 引用和 SQL 数据库有一定理解。我特定的代码示例使用了 MySQL 数据库以及相应的 Persistent 模块。如果您正在使用另一种数据库(例如 Postgres 或 Oracle),则应该寻找其它 Persistent 模块。
在配置可用于数据库环境之前,必须先设计数据库模式。换句话说,在开始编写用来保存和恢复数据的代码 之前,您需要确定希望保存什么数据。这个示例将在不同表中存储布尔值、标量、数组和散列。
这未必是最佳途径。您也可以将一个表用于所有数据类型,或按用途划分表。我的示例仅是实现持久配置的许多方法之一。它肯定不是仅有的方法。
我在此处提供的模式可能对于大多数用途都足够了。它确实对于值和键长度有一些限制,但可以在代码中轻松地调整那些限制。但是,创建数组和散列元素标识的方法可能引起问题。在这些情况下没有完美的解决方案,只有解决问题的不同方法。将任意的结构化数据存储到关系数据库总是麻烦的。
我们将使用 AppConfig::State 中的 _argcount() 方法。有关这个方法的更详细信息,请阅读 AppConfig::State 的手册页。简单来说,如果我们知道变量名,该方法就可以告诉我们正在处理什么类型的变量。
在我的代码示例中,我使用了 MySQL 数据库和相应的 Persistent 模块: persistent-config.pl。
结束语
只要做少量工作,就可以使 AppConfig 和 Persistent 类很好地合作。前一节中展示的持久配置脚本可以处理大多数具有短键和变量名的配置,但还有可改进的余地。在按自己的喜好编写完脚本后,就可以在网络的 任何地方启动它,并让它从中央主机载入当前配置。至少,进行改进将教会您关于数据库配置的知识,并有助于您以网络为中心的新方式研究应用程序。
代码重用通常是模块唯一最大的好处,尤其对 AppConfig 更是这样。对于 DIY(自己动手)方法产生了错误和延迟的情况,AppConfig 提供了有效的单一解决方案,该方案很可能可以满足大多数配置需求。
“AppConfig 限制”一节所列出的限制非常少。当然,在选用 AppConfig 之前,您应该确定对于您的项目什么才是最适合的。最好牢记如何将此处提供的信息应用于您的特定项目,并研究 AppConfig 手册页。
现在干什么呢?您应该从 AppConfig 中只采用您所需要的。而不要尝试让它为您做所有的工作。将一半程序放到一个配置文件中看来挺有趣,但如果这样做,不久以后用户就会咆哮着冲进您的办公室。请使您的配置文件有逻辑性而又简单。编写程序接受的配置语法的详细说明,包括 AppConfig 提供的非常周到的命令行选项。
|
相关推荐
`OpenSSL`是一个强大的安全套接字层密码库,囊括主要的密码算法、常用的密钥和证书封装管理功能及`SSL`协议,并提供丰富的应用程序供测试或其它目的使用。`nginx`不仅支持`http`协议,还支持`https`(即在`ssl`协议...
它是经验丰富的用户手中的强大工具,但对于初次使用的用户而言,存在可用性问题。 安装过程繁琐,必须手动安装许多必备组件。 安装后,最初的用户体验令人生畏。 大多数功能是通过配置文件控制的。 该文件超过1100...
3.4.3 对memcached_functions_mysql的简单功能进行测试 3.4.4 使用memcached_functions_mysql的经验与技巧 3.5 本章小结 第2篇 数据备份恢复篇 第4章 开源网络备份软件bacula 4.1 bacula总体概述 4.1.1...
7.8 为新表配置应用程序 116 7.9 总结 116 第8章 phpMyAdmin:开放源码的 MySQL前端 117 8.1 phpMyAdmin的特性 117 8.2 安装phpMyAdmin 118 8.3 防止“网络机器人” 118 8.4 用密码保护phpMyAdmin目录 119 8.5 使用...
在性能优化方面,PHP支持 opcode 缓存(如APC、OpCache)以加速脚本执行,可通过配置调整、代码优化、使用缓存技术等手段提升应用性能。近年来,PHP持续进行性能改进与新特性的引入,如PHP 7系列版本在速度上有了...
在性能优化方面,PHP支持 opcode 缓存(如APC、OpCache)以加速脚本执行,可通过配置调整、代码优化、使用缓存技术等手段提升应用性能。近年来,PHP持续进行性能改进与新特性的引入,如PHP 7系列版本在速度上有了...
·为php、perl和c语言提供了应用程序编程接口(api)。 ·在每章的api开头部分提供了简要的指南。 ·新增复制、触发器和存储过程章节。 ·提供了许多mysql的实用示例。 ·给出了一些有用的提示,用于帮助...
·为php、perl和c语言提供了应用程序编程接口(api)。 ·在每章的api开头部分提供了简要的指南。 ·新增复制、触发器和存储过程章节。 ·提供了许多mysql的实用示例。 ·给出了一些有用的提示,用于帮助...
第二部分 CGI应用程序开发 第3章 设计CGI应用 第4章 理解基本的CGI元素 第5章 将HTML和CGI用作用户界面 第三部分 CGI编程实例和服务器配置 第6章 简单CGI脚本举例 第7章 编制脚本并设置Web服务器 第8章 修改CGI脚本 ...
1.3.1 功能丰富 3 1.3.2 多任务 4 1.4 为什么选择 Linux 6 1.4.1 何时使用 Linux 6 1.4.2 服务器与工作站 6 1.4.3 推荐的硬件 7 1.4.4 移植到 Linux工作站 7 1.5 Linux分发包 8 1.6 升级或移植前的考虑 10 1.6.1 ...
使用它,可以配置操作系统内部组件,例如用户,磁盘配额,服务或配置文件,以及修改和控制开源应用程序,例如BIND DNS服务器,Apache HTTP服务器,PHP,MySQL,。 通过安装模块可以扩展可用性,模块可以定制。 除...
在性能优化方面,PHP支持 opcode 缓存(如APC、OpCache)以加速脚本执行,可通过配置调整、代码优化、使用缓存技术等手段提升应用性能。近年来,PHP持续进行性能改进与新特性的引入,如PHP 7系列版本在速度上有了...
Of course, we could use Java (or PERL, C/C++ or what ever) code to generate HTML. There are several disadvantages to that approach: • Java programmers should develop services, not HTML. • Changes...
Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java源码...
Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java源码...
(Torvalds@kruuna.helsinki.fi)写了 Linux核心程序的 0.02 版开始的,但其后的发展却几乎都 是由互联网上的 Linux社团(Linux Community)互通交流而完成的。Linux 不属于任何一 家公司或个人,任何人都可以免费取得...