按月归档: 11月 2007

[转]开发大型高负载类网站应用的几个要点

作者:nightsailer
来源:http://www.phpchina.com/bbs/thread-15484-1-1.html

看了一些人的所谓大型项目的方法,我感觉都是没有说到点子上,有点难受。
我也说说自己的看法.我个人认为,很难衡量所谓项目是否大型,
即便很简单的应用在高负载和高增长情况下都是一个挑战.因此,按照我的想法,姑且说是高负载
高并发或者高增长情况下,需要考虑的问题.这些问题,很多是和程序开发无关,而是和整个系统的
架构密切相关的.

  • 数据库

没错,首先是数据库,这是大多数应用所面临的首个SPOF。尤其是Web2.0的应用,数据库的响应是首先要解决的。
一般来说MySQL是最常用的,可能最初是一个mysql主机,当数据增加到100万以上,
那么,MySQL的效能急剧下降。常用的优化措施是M-S(主-从)方式进行同步复制,将查询和操作和分别在不同的
服务器上进行操作。我推荐的是M-M-Slaves方式,2个主Mysql,多个Slaves,需要注意的是,虽然有2个Master,
但是同时只有1个是Active,我们可以在一定时候切换。之所以用2个M,是保证M不会又成为系统的SPOF。
Slaves可以进一步负载均衡,可以结合LVS,从而将select操作适当的平衡到不同的slaves上。

以上架构可以抗衡到一定量的负载,但是随着用户进一步增加,你的用户表数据超过1千万,这时那个M变成了
SPOF。你不能任意扩充Slaves,否则复制同步的开销将直线上升,怎么办?我的方法是表分区,
从业务层面上进行分区。最简单的,以用户数据为例。根据一定的切分方式,比如id,切分到不同的数据库集群去。
全局数据库用于meta数据的查询。缺点是每次查询,会增加一次,比如你要查一个用户nightsailer,你首先要到
全局数据库群找到nightsailer对应的cluster id,然后再到指定的cluster找到nightsailer的实际数据。
每个cluster可以用m-m方式,或者m-m-slaves方式。
这是一个可以扩展的结构,随着负载的增加,你可以简单的增加新的mysql cluster进去。

需要注意的是:
1、禁用全部auto_increment的字段
2、id需要采用通用的算法集中分配
3、要具有比较好的方法来监控mysql主机的负载和服务的运行状态。如果你有30台以上的mysql数据库在跑就明白我的意思了。
4、不要使用持久性链接(不要用pconnect),相反,使用sqlrelay这种第三方的数据库链接池,或者干脆自己做,因为php4中mysql的
链接池经常出问题。

  • 缓存

缓存是另一个大问题,我一般用memcached来做缓存集群,一般来说部署10台左右就差不多(10g内存池)。需要注意一点,千万不能用使用
swap,最好关闭linux的swap。

  • 负载均衡/加速

可能上面说缓存的时候,有人第一想的是页面静态化,所谓的静态html,我认为这是常识,不属于要点了。页面的静态化随之带来的是静态服务的
负载均衡和加速。我认为Lighttped+Squid是最好的方式了。
LVS <——->lighttped====>squid(s) ====lighttpd

上面是我经常用的。注意,我没有用apache,除非特定的需求,否则我不部署apache,因为我一般用php-fastcgi配合lighttpd,
性能比apache+mod_php要强很多。

squid的使用可以解决文件的同步等等问题,但是需要注意,你要很好的监控缓存的命中率,尽可能的提高的90%以上。
squid和lighttped也有很多的话题要讨论,这里不赘述。

  • 存储

存储也是一个大问题,一种是小文件的存储,比如图片这类。另一种是大文件的存储,比如搜索引擎的索引,一般单文件都超过2g以上。
小文件的存储最简单的方法是结合lighttpd来进行分布。或者干脆使用Redhat的GFS,优点是应用透明,缺点是费用较高。我是指
你购买盘阵的问题。我的项目中,存储量是2-10Tb,我采用了分布式存储。这里要解决文件的复制和冗余。
这样每个文件有不同的冗余,这方面可以参考google的gfs的论文。
大文件的存储,可以参考nutch的方案,现在已经独立为hadoop子项目。(你可以google it)

其他:
此外,passport等也是考虑的,不过都属于比较简单的了。

吃饭了,不写了,抛砖引玉而已。

【回复】

9tmd :
说了关键的几个部分,还有一些比如squid群、LVS或者VIP(四层交换)之类的必须考虑,数据库逻辑分表不需要master里面查id,可以定期缓存或者程序逻辑上进行控制。
跟大家分享一下我的经验: http://www.toplee.com/blog/archives/337.html (欢迎讨论)
nightsailer
楼上说的很好.
我 再说一下关于为何要在主表查询,最主要的因素是考虑到复制和维护的问题。假设按照程序逻辑,用户nightsailer应该在s1集群,但是由于种种原 因,我须要将nightsailer的数据从s1集群转移到s5集群或者某些时候,我需要将某几个集群的数据合并,此时,我维护的时候只需要更新一下主数 据库中nightsailer的cluster id从1变成5,,维护的工作可以独立进行,无需考虑更新应用程序的逻辑。也许程序的id分配逻辑可以考虑到这种情况,但是这样一来,你的这个逻辑会发散 到各个应用中,产生的代码的耦合是很高的。相反,采用查表这种方式,只需要在最初的时候进行初始分配,那么其他的应用是无需考虑这些算法和逻辑的。
当然,我最初提到的增加这次查询并不是说每次查询都需要找主数据库,缓存策略是必定要考虑的。

至于说为什么要禁用auto_increment,我想也清楚了,数据的合并和分隔,肯定是不能用auto_increment的。

在闲扯一下,PHP的优化可以有很多,主要的措施:
1、使用FCGI方式,配合lighttpd,Zeus.
我个人比较喜欢Zeus,简单可靠。不过,需要¥¥¥。
lighty也不错,配置文件也很简单,比较清爽。最新的1.5,虽然不稳定,但是配合linux的aio,性能的提升
非常明显。即便现在的稳定版,使用2.6的epoll可以得到的性能是非常高。当然,lighty比zeus缺点是对于smp
的支持很有限,所以可以采用多服务器负载,或者干脆起不同的进程服务监听不同的端口。
2、专门的PHP FCGI服务器。
好处多多,在这个服务器上,就跑php的fcgi服务,你可以把一些缓存加上,比如xcache,我个人喜欢这个。
还有别的,套用大腕的话,把能装的都装上,呵呵。
另外,最主要的是,你可以只维护一个php的环境,这个环境能够被apache,zeus,lighttpd同时share,
前提是这些都使用php的fcgi模式,而且,xcache可以充分发挥!
3、apache+mod_fastcgi
apache并非无用,有时候也需要。比如我用php做了一个web_dav的服务器,在其他有问题,只能跑apache.
那么,apache安装一下mod_fastcgi,通过使用externl server,使用2配置的php fastcgi。
4、优化编译
ICC是我的首选,就是intel的编译器啦,用icc重新编译php,mysql,lighty,能编的都编,会有不小的收获的。尤其是你用
intel的cpu的话。
5、php4的64位需要patch
好像没有人在linux x86_64上编译过php4吧,我曾经googleit
,别说国内了,连老外都很少用。
这里就做个提醒把,如果用php官方下载的(包括最新的php-4.4.4),统统无法编译通过。问题是出在autoconf上,需要
手工修改config.m4,一般是在mysql,gd,ldap等一些关键的extension上,还有phpize的脚本。把/usr/lib64加入到
config.m4中相关搜索的path中。
不过我估计很少人像我这样死用php4不防,呵呵。php5就没有问题。
我也考虑正在迁移到php5.2,写代码太方便了,一直忍着呢。
QUOTE:
原帖由 wuexp 于 2007-1-3 17:01 发表
分表会使操作数据(更改,删除,查询)边的很复杂,特别是遇到排序的时候就更麻烦了.
曾经考虑根据用户id哈希一下,插入到相应的分表里

明白你的意思。

不过我们可能讨论的不完全一样,呵呵。
我所说的分表要依据不同的业务情况来划分的,

1、可以是垂直划分,
比如依据业务实体切分,比如用户a的blog贴子,用户的tag,用户的评论都在a数据库u,甚者是完整的一套数据结构(这种情况下应该说是分数据库)

2、也可以水平划分,
一个表的数据分在不同的数据库上。
比如message表,你可能分为daily_message,history_message,
dialy_meesage可能是hot对象,week_message是warm,2个月以前的帖子
可能属于cold对象了。这些对象依据访问频度不同会划分到不同的数据库群上。

3、二者结合

不过,不论如何,更改、删除并不复杂,和未分区的表没有区别。

至于查询和排序,不可能仅仅是通过select,order吧?
而是应该产生类似摘要表,索引表,参考表。。。
另外,要根据业务具体分析减少垃圾数据,有些时候,只需要最初的1万条记录,那么所有表
数据的排序就不需要了。很多传统的业务,比如零售,流水表很大,但是报表的数据
并非实时生成的,扎报表应该不陌生。

也可以参考很多网站的做法,比如technorati啊,flickr之类的。

所谓的麻烦是你设计系统的结构的时候要考虑到,在设计数据库的时候更要注意,
因此只要项目的framework最初设计比较完备,那么可以说大部分对开发人员是透明的。
前提是,你一定要设计好,而不是让程序员边写代码边设计,那会是噩梦。

我写这么多废话,并非仅仅是对程序员来说,也许对设计者更有用。

9tmd
程序逻辑上控制表拆分只需要维护一个数据库访问的配置文件即可,对于开发来说,完全透明,可以不用关心访问的是哪里,而只需要调用通用的接口即可,曾经做过的系统里面,这样的应用经常遇到,尤其在全网passport、社区帖子等方面的处理上应用最多。

原来在yahoo工作和后来mop工作都使用了这样的架构,整体感觉来说还是值得信赖的,单表毕竟存在面对极限数据量的风险。

9tmd
前 面老是有人问auto_increment的问题,其实这是MySQL官方专门针对M/S的Replication做过的说明,因为MySQL的同步是依 靠同步MySQL的SQL日志来实现的,事实上单向的Master->Slave使用auto_increment是没有问题的,而双向的M/M模 式就会存在问题了,稍微一思考就知道怎么回事了。官方文档:
http://dev.mysql.com/tech-resour … ql-replication.html
http://dev.mysql.com/doc/refman/ … auto-increment.html

另 外,在使用MySQL的同步时,需要注意在自己的代码里面,写SQL的时候不要使用MySQL自己提供的类似 NOW()之类的函数,而应该使用php程序里面计算的时间带入SQL语句里面,否则同步的时候也可能导致值不相等,这个道理可以牵涉出另外一些类似的问 题,大家可以考虑一下。

Read: 949

CSS Framework(CSS框架)

转自:http://dev.collin.cn/detail/46.html

对于如今的软件开发人员来说,各类五花八门的框架是满天飞,尤其是web开发领域,RoR、Spring、Struts、Zend等等,但要说到CSS框架,知道的人并不多,CSS也有框架吗?有,而且对web开发人员非常有帮助。

随 着Web UI Designer慢慢抛弃传统的table架构,转而纷纷采用xhtml+div+css模式,css的重要性也是与日俱增,往往会占去一个web项目中 一部分较大的工作量。而且时常会遇到各种浏览器兼容的问题,在ie上看起来很美观的页面,放到firefox下浏览却会出现错位等等这类问题。如何有效的 提高开发人员的效率,同时尽可能的解决浏览器兼容问题,于是有了CSS Framework。

什么是CSS框架

  • 框架是个概念性很强的名词,对于CSS来说,所谓的框架就是一些预先定义了全局style重置、一致style设置、form style设置的css文件,这样当启动一个新项目时,可以直接拿来就用,在这些文件的基础上做修改就可以了。
  • CSS框架没必要太复杂和庞大,一般就是以下这几个文件差不多:
    • typography.css 字体修饰
    • grid.css 表格修饰
    • layout.css页面布局修饰
    • form.css 表单修饰
    • general.css 一些常用修饰,如reset修饰等等

使用CSS框架的好处

  • 提高了生产效率并可以避免一些常见的错误。
  • 规范化CSS和HTML代码,如在类似的项目HTML元素都使用相同的IDs和class names。
  • 更加规范了项目流程,如果CSS框架整理了相应的文档,在UI上面一个Team里面的沟通会更加容易。
  • 提高了浏览器兼容性。
  • 你的代码将显得更加清晰、简单、完整以及合理的结构和可读性。

使用CSS框架的坏处

  • 你需要理解整个框架
  • 可能也会引入这个框架本身自带的bug和错误
  • 太依赖于框架,以致对CSS的本质反而不是很了解
  • 源代码相对会比较庞大,因为框架中很多代码可能是你用不到的

CSS框架推荐

Read: 576

[转]磨练构建正则表达式模式的技能

转自:http://www.ibm.com/developerworks/cn/aix/library/au-expressions.html


用于系统管理的正则表达式

级别: 中级

Michael Stutz (stutz@dsl.org), 作者, 顾问

2006 年 8 月 14 日

通过本文的学习,您可以增加一些有用的设计实际正则表达式 (regexp) 的技能。构建正则表达式是任何管理员日常工作中的一部分。为了构造返回所需条件的成功正则表达式,需要学习以模式匹配的角度进行思考,而这种技能需要花大量的时间进行练习。

引言

UNIX® 管理员每天都需要构建和使用正则表达式 (regexp) 进行文本模式匹配。大多数语言都支持正则表达式的某种实现。有的应用程序(如 EMACS)具有正则表达式搜索功能,并且您可以通过各种命令行工具使用正则表达式。无论什么应用程序,构建正确的正则表达式的关键之处在于,识别仅满足 需要匹配的数据的模式,以便在输入中排除其他不必要的内容。

出于这个目的,本文将逐步介绍几种正则表达式模式构建技巧,并介绍它们如何帮助您完成各种常规任务。

使用正则表达式 (regexp)

除非特别说明,否则本文中使用的示例都是扩展可移植操作系统接口(扩展 POSIX)的正则表达式。如果通过命令行(如使用 egrep 实用工具)使用它们,您应该根据需要引用各种正则表达式。请记住,不同的正则表达式实现之间存在一些区别,您可能不得不适应所使用的特定的工具、应用程序或语言中的具体实现。

匹配整行内容

^ 元字符匹配行首,而 $ 匹配行尾,如果将它们组合在一起(如 ^$),它们将匹配空行。(这个表达式的镜像,即 $^,是不可能匹配成功的,它将永远 都无法匹配到有效行。)这个基本的正则表达式是许多复杂正则表达式的基础,如果您还不习惯使用这个基本的正则表达式,那么您应该逐步养成使用它的习惯。使用它来构建匹配整行内容 的模式。

在用户字典文件 (/usr/dict/words) 中搜索是一个很好的基本模式。(有些版本的 UNIX 将用户字典放在 /usr/share/dict/words 中。)

例如,假设您忘记了如何拼写单词 fuchsia。其中是否包含 shcs 呢?您所知道的只是,它以 fu 开头并以 ia 结尾。

尝试使用这个模式进行搜索:

$ egrep -i '^fu.*ia$' /usr/dict/words

-i 标志表示在搜索过程中不区分大小写。在这个示例中,因为 fuchsia 拼写正确,所以在返回的单词中包括这个单词。


回页首

根据长度匹配行

使用大括号元字符 ({ }) 指定前面的正则表达式匹配多少次,如表 1 所示。当您将它们添加到刚才介绍的整行搜索中时,您可以指定行的长度。

表 1. 大括号元字符的含义

示例 描述
{X} 这个字符对前面的正则表达式匹配 X 次。
{X,} 这个字符对前面的正则表达式匹配 X 或更多 次。
{X,Y} 这个字符对前面的正则表达式匹配至少 X 而不超过 Y 次。

并不是所有扩展正则表达式的实现都支持大括号。此外,根据具体的实现,您可能需要先使用反斜杠对其进行转义。

您可以使用这个正则表达式得到字典中以单词长度为顺序的报告。所获得结果的数目取决于本地系统的字典文件中单词的数目,然而,它应该与清单 1 所示类似。在这个示例中,最常见的单词长度是 9 个字母,该字典中有 32,380 个匹配单词。该字典中不包括 25 个字母或更长的单词,并且最长的单词并不是您认为的 21 个字母长的 disestablishmentarian(有 81 个同样长度的单词,包括 superincomprehensiblephoneticohieroglyphic),这个 UNIX 字典中最长的单词有 5 个,包括 pathologicopsychological

清单 1. 计算字典中 X 个字母的单词的个数

$ for i in `seq 1 32`
> {
> echo "There are" `egrep '^.{'$i'}$' /usr/dict/words
| wc -l` "$i-letter words in the dictionary."
> }
There are 52 1-letter words in the dictionary.
There are 155 2-letter words in the dictionary.
There are 1351 3-letter words in the dictionary.
There are 5110 4-letter words in the dictionary.
There are 9987 5-letter words in the dictionary.
There are 17477 6-letter words in the dictionary.
There are 23734 7-letter words in the dictionary.
There are 29926 8-letter words in the dictionary.
There are 32380 9-letter words in the dictionary.
There are 30867 10-letter words in the dictionary.
There are 26011 11-letter words in the dictionary.
There are 20460 12-letter words in the dictionary.
There are 14938 13-letter words in the dictionary.
There are 9762 14-letter words in the dictionary.
There are 5924 15-letter words in the dictionary.
There are 3377 16-letter words in the dictionary.
There are 1813 17-letter words in the dictionary.
There are 842 18-letter words in the dictionary.
There are 428 19-letter words in the dictionary.
There are 198 20-letter words in the dictionary.
There are 82 21-letter words in the dictionary.
There are 41 22-letter words in the dictionary.
There are 17 23-letter words in the dictionary.
There are 5 24-letter words in the dictionary.
There are 0 25-letter words in the dictionary.
There are 0 26-letter words in the dictionary.
There are 0 27-letter words in the dictionary.
There are 0 28-letter words in the dictionary.
There are 0 29-letter words in the dictionary.
There are 0 30-letter words in the dictionary.
There are 0 31-letter words in the dictionary.
There are 0 32-letter words in the dictionary.
$

回页首

匹配单词

围绕字符 <> 是非常有用的模式构造器:它们将要匹配的整个单词 括起来,这表示,它们不会匹配带括号的模式,除非该模式本身就是一个单词。单词 定义为两侧由非单词字符描述的、任意数目组成单词的字符(数字、字母和下划线字符)。非单词字符包括下面的所有字符:

  • 行首
  • 空白字符
  • 标点符号
  • 行尾
  • 任何除字母、数字或下划线以外的字符

这些围绕字符可以节省大量的时间,但是它们常常没有被充分地利用,可能是因为并非所有的正则表达式实现都支持它们。如果您的正则表达式实现支持它们,那么您应该逐步养成使用它们的习惯。

将需要单独匹配的单词括起来,如下所示:

<system>

这个示例中的正则表达式不会匹配单词 ecosystemsystemicsystem/70,也不会匹配模式 system 出现在行中任意位置的那些行,它将仅仅 匹配 system 作为独立的单词出现的那些行。

围绕字符与圆括号中的分组结合在一起,可以用来匹配部分 单词。

要匹配包含以 pre 开头 的单词的那些行,可以使用:

<(pre).*>

前面的示例将匹配包含单词 prefacepreposterous 的行,但不会匹配 spreadDupre


回页首

匹配重复单词

这里介绍一种使用单词围绕字符匹配重复单词的快速方法,重复单词表示一个单词在空格之后再次出现。您还可以使用逆向引用,这是大多数流行的正则表达式实现中的一种递归特性,它可以匹配模式本身的某一部分。(将模式中需要引用的部分使用圆括号括起来,然后使用反斜杠加上需要进行引用的围绕字符编号来调用逆向引用:1 表示第一个圆括号分组,2 表示第二个圆括号分组,依此类推。)

要查找重复的单词,搜索在任意数目的空格之后再次出现该单词的情况,可以通过对第一个使用圆括号的部分进行逆向引用来实现:

(<.*>)( )+1

这个示例匹配缩写形式和任何类型的单词,但是它不会匹配由标点符号分隔的重复单词,如 It’s been a long, long time

要匹配所有的重复单词,包括由空格 任意标点符号分隔的重复单词,可以使用下面的表达式:

(<.*>).?( )+1

如果需要对这些正则表达式使用 grep,则务必使用 -i 标志,以便在搜索中不区分大小写。


回页首

匹配小时

让我们再来看另外一类常见的问题:时间和日期。这里介绍了一些设计匹配正确模式的正则表达式所需要考虑的事项。

您无法搜索任何两位的数字来匹配分钟和秒,因为它们仅仅是从 0 到 59,要匹配它们,您需要使用方括号将表示十位和个位的范围括起来:

  • 要匹配标准的 12 或 24 小时格式的小时,可以使用下面的表达式:
    (([0-1]?[0-9])|([2][0-3])):([0-5][0-9])(:[0-5][0-9])?
  • 要匹配 12 小时 AM/PM 格式、带或不带秒数的时间,甚至匹配大写或小写、不带后缀 AM 或 PM 标识符的时间,可以使用下面的表达式:
    ([^0-9])([0-1]?[0-9]){1}(((:([0-5]){1}([0-9]){1}){1,2})|(( )?([AP]M)|([ap]m)))?

如果在上一个示例中没有开始的否定语句,它将匹配不带冒号的时间,这将取决于输入数据,可能会匹配中波广播电台(在美国称为调幅 AM 电台),如 1450 AM


回页首

匹配月份

匹配 12 个月中的任何月份需要一个使用 | 操作符进行分隔的列表,但有时会使用不同的方式对日期进行缩写:

  • 要查找完整拼写或三字母缩写的 12 个月份,可以使用下面的表达式(位于一行):
    Jan(uary)?|Feb(uary)?|Mar(ch)?|Apr(il)?|May|Jun(e)?|Jul(y)?|
    Aug(ust)?|Sep(tember)?|Oct(ober)?|Nov(ember)?|Dec(ember)?
  • 您可以加以想象并搜索完整拼写或三字母缩写的变形,即仅当后面紧跟着一个空格或点号的情况,可以使用下面的表达式(位于一行):
    Jan(uary| |.)|Feb(uary| |.)|Mar(ch| |.)|Apr(il| |.)|May( |.)|Jun(e| |.)|Jul(y| |
    .)|Aug(ust| |.)|Sep(tember| |.)|Oct(ober| |.)|Nov(ember| |.)|Dec(ember| |.)

请注意,在上面的这两个示例中,May 是一个特殊的例外。在所有的月份中,它是唯一的完整拼写与三字母缩写相同的月份,所以成功的匹配必须包含这两种变形中的任何一种 作为其缩写,因此像“Mayflower”这样的单词不会导致误报。

当匹配模式前面的字符不是空格或行首时,这些示例还是会失败(返回误报的结果)。这不太可能会出现在英语散文中,但是可能出现在程序源代码中,因为其中可能使用了像 NumOct 这样的变量名。

要修复这些问题,可以执行下面的操作:

  • 使用圆括号将整个正则表达式括起来,并在它的前面加上另一个限定符,用于匹配行首或者空格字符,如下所示(位于一行):
    (^| )(Jan(uary| |.)|Feb(uary| |.)|Mar(ch| |.)|Apr(il| |.)|
    May( |.)|Jun(e| |.)|Jul(y| |.)|Aug(ust| |.)|Sep(tember| |
    .)|Oct(ober| |.)|Nov(ember| |.)|Dec(ember| |.))
  • 另一种完成这个任务的方法是,在该正则表达式的前面加上一个限定符,以匹配非文字数字的字符,如下所示(位于一行):
    ([^A-Za-z0-9])(Jan(uary| |.)|Feb(uary| |.)|Mar(ch| |.)|
    Apr(il| |.)|May( |.)|Jun(e| |.)|Jul(y| |.)|Aug(ust| |.)|
    Sep(tember| |.)|Oct(ober| |.)|Nov(ember| |.)|Dec(ember| |.))

但是仍然存在潜在的问题,对于搜索整篇英文散文,这些示例并不可靠,因为它们可能返回错误的匹配结果,如“Janelle”或“Augury”这样的单词。要修复这个问题,您必须使用单词围绕字符将每个月份括起来。

本文开头提到,正确的正则表达式应该仅返回需要匹配的数据,以便在输入中排除其他不必要的内容。这种措词是经过仔细考虑的,因为对于构建正则表达式来说,这与上下文有关。对于有些情况,前面的示例非常适合,无需添加额外的单词围绕字符。在其他的情况下,可以对其进行相当程度的简化,例如,如果您正在搜索仅包含大写的日期数值数据的日志文件,那么只需要使用像 [A-S] 这样的正则表达式来匹配包含月份名称的行。


回页首

匹配日期

您可以结合一些表 1 所示的数量匹配来匹配日期。

要匹配“month, day, years”,可以使用下面的正则表达式(因为撇号字符是该正则表达式的一部分,所以必须使用双引号将它括起来,如下所示):

"[A-Za-z]{3,10}.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2})"

这个正则表达式匹配 9 种不同的日期格式:

  1. MONTH [D]D, YY
  2. MONTH [D]D, 'YY
  3. MONTH [D]D, YYYY
  4. MON. [D]D, YY
  5. MON. [D]D, 'YY
  6. MON. [D]D, YYYY
  7. MON [D]D, YY
  8. MON [D]D, 'YY
  9. MON [D]D, YYYY

这个正则表达式的误报包括“Order 99, 99”,要消除这些误报,可以将这个正则表达式与用于月份的正则表达式结合起来,如上所述,以便能够仅匹配实际的月份名称。另外,更改数值范围以避免错误的匹配,并且通过使逗号成为可选项,重复了 18 种可能的格式。

这将得到一个很长的正则表达式。尝试下面的表达式:

"([^A-Za-z0-9])(Jan(uary| |.)|Feb(uary| |.)|Mar(ch| |.)|
Apr(il| |.)|May( |.)|Jun(e| |.)|Jul(y| |.)|Aug(ust| |.)|
Sep(tember| |.)|Oct(ober| |.)|Nov(ember| |.)|
Dec(ember| |.)) [0-3]?[0-9]{1}(,)? ([0-9]{4}|'?[0-9]{2})"

同样,根据您的需要仔细设计正则表达式。匹配模式通常比较容易,这是因为它存在于特定输入的上下文中,而不是因为它可能独立于数据集而存在。后代人将会发现,前面那个很长的正则表达式中仍然存在 Y10K 错误,因为它能匹配的最大可能的年份为 9999。


回页首

匹配整数

正如您在前几个示例中看到的,使用方括号中的范围可以很好地匹配数值。

要匹配任意长度的整数,可以在数值范围后面加上 +;要包括负值,可以在它的前面加上可选的负号(连字号)匹配:

-?[0-9]+

前面的例子可以匹配 0,因为 0 是指定范围中可选的字符。

对于数值匹配,使用圆括号将某些部分括起来也非常有效。要匹配任意的十进制数值,可以使用包含小数点加上一个或多个数值的可选围绕字符,以此对前面的正则表达式进行扩展:

-?[0-9]+(.[0-9]+)?

可以使用方括号指定十进制数值的小数位数。例如,要匹配小数位数为 5 或更多小数位数的正数值,可以使用下面的表达式:

[^-][0-9]+.([0-9]){5,}

回页首

更多实际的匹配

范围加上使用括号括起来的元字符,在查找符合任何特定格式的数值时非常有用。将前面介绍的一些技术结合起来,可以构建匹配各种数据的正则表达式:

  • 要匹配美国的电话号码,可以使用:
    ((([2-9][0-9]{2}))? ?|[2-9][0-9]{2}(?:-?| ?))[2-9][0-9]{2}[- ]?[0-9]{4}

    这个正则表达式可以匹配美国 15 种格式的电话号码:

    1. (NPA) PRE-SUFF
    2. (NPA) PRE SUFF
    3. (NPA) PRESUFF
    4. (NPA)PRE-SUFF
    5. (NPA)PRE SUFF
    6. (NPA)PRESUFF
    7. NPA PRE-SUFF
    8. NPA PRE SUFF
    9. NPA PRESUFF
    10. NPAPRE-SUFF
    11. NPAPRE SUFF
    12. NPAPRESUFF
    13. PRE-SUFF
    14. PRE SUFF
    15. PRESUFF

    它还可以匹配美国免费 WATS 号码,尽管 1-800 的“1-”前缀或其他的免费号码不是匹配的一部分,但它本身可以匹配 10 位的数值。对于以 1 或 1+ 开头的美国号码和任意数目的空格,也完全一样,长途电话拨号前缀本身无法匹配,但是只要它后面跟着实际的号码,这个正则表达式就能够将其找出来。

  • 要匹配两或三位域的电子邮件地址,可以尝试下面的表达式:
    <[^@]+>@[a-zA-Z_.]+?.[a-zA-Z]{2,3}
  • 要匹配如今所有流行的 URL,可以使用下面的正则表达式:
    (((http(s)?|ftp|telnet|news)://|mailto:)[^()[:space:]]+)

    这个表达式可以正常运行,但是匹配 URL 并不像您想象的那么简单。匹配任何可能的 URL 的正则表达式,如 RFC 1738 中的定义,发表在“Regexp for URLs”(请参见参考资料部分)一文中,它非常巨大并且看起来令人生畏。现在应该将它合并为一个 [:url:] 类(如果有用于处理类似数据种类的各种新的类,如 [:email:],那就好了)。


回页首

结束语

本文涉及到一些用于编写正则表达式的模式构建技术,以及如何使用它们来完成管理员时常碰到的特定类型数据匹配的工作。在此过程中,向您介绍了大量有价值的实际正则表达式,您可以将它们添加到自己的管理工具库中。

参考资料

学习

Read: 1007

[转]澄清Java(接口与继承)

计算机学院研二的兄弟与我讨论Java,一见面,几个问题全是关于接口,接口有什么用?为什么要用接口?什么时候该使用接口?很庆幸他们不是问我 Java如何连接SQL Server,或者是如何开发J2EE应用,这类问题有杀伤力,避之则吉。今年计算机学院本科有个毕业设计课题是做J2ME,选这个题目的学生在5月末都还在苦着脸研究java.util.*这个包,这个这个……唉。

大多数人认为,接口的意义在于顶替多重继承。众所周知Java没有c 那样多重继承的机制,但是却能够实作多个接口。其实这样做是很牵强的,接口和继承是完全不同的东西,接口没有能力代替多重继承,也没有这个义务。接口的作用,一言以蔽之,就是标志类的类别(type of class)。把不同类型的类归于不同的接口,可以更好的管理他们。OO的精髓,我以为,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计模式都只针对具备了抽象能力的语言(比如c 、java、c#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。(cowboy的名言是“抽象就是抽去像的部分”,看似调侃,实乃至理)。

设计模式中最基础的是工厂模式(Factory),在我最近的一个很简单的应用中,我想尽量的让我的程序能够在多个数据库间移植,当然,这涉及很多问题,单是如何兼容不同DBMS的SQL就让人头痛。我们不妨先把问题简单化,只考虑如何连接不同的数据库。

假设我有很多个类,分别是Mysql.java、SQLServer.java、Oracle.java、DB2.java,他们分别连接不同的数据库,统一返回一个Connection对象,并且都有一个close方法,用于关闭连接。只需要针对你的DBMS,选择不同的类,就可以用了,但是我的用户他会使用什么数据库?我不知道,我希望的是尽量少的修改代码,就能满足他的需要。我可以抽象如下接口:
package org.bromon.test;
public interface DB
{
java.sql.Connection openDB(String url,String user,String password);
void close();
}

这个接口只定义两个方法,没有任何有实际意义的代码,具体的代码由实作这个接口的类来给出,比如Mysql.java:

Package org.bromon.test;
import java.sql.*;
public class Mysql implements DB
{
private String url=”jdbc:mysql:localhost:3306/test”;
private String user=”root”;
private String password=””;
private Connection conn;
public Connection openDB(url,user,password)
{
//连接数据库的代码
}

public void close()
{
//关闭数据库
}
}

类似的当然还有Oracle.java等等,接口DB给这些类归了个类,在应用程序中我们这样定义对象:

org.bromon.test.DB myDB;

使用myDB来操作数据库,就可以不用管实际上我所使用的是哪个类,这就是所谓的“开-闭”原则。但是问题在于接口是不能实例化的, myDB=new DB(),这样的代码是绝对错误的,我们只能myDB=new Mysql()或者myDB=new oracle()。麻烦了,我还是需要指定具体实例化的是哪个类,用了接口跟没用一样。所以我们需要一个工厂:

package org.bromon.test;
public class DBFactory
{
public static DB Connection getConn()
{
Return(new Mysql());
}
}

所以实例化的代码变成:myDB=DBFactory.getConn();
这就是23种模式中最基础的普通工厂(Factory),工厂类负责具体实例化哪个类,而其他的程序逻辑都是针对DB这个接口进行操作,这就是“针对接口编程”。责任都被推卸给工厂类了,当然你也可以继续定义工厂接口,继续把责任上抛,这就演变成抽象工厂(Abstract Factory)。

整个过程中接口不负责任何具体操作,其他的程序要连接数据库的话,只需要构造一个DB对象就OK,而不管工厂类如何变化。这就是接口的意义—-抽象。

继承的概念不用多说,很好理解。为什么要继承呢?因为你想重用代码?这绝对不是理由,继承的意义也在于抽象,而不是代码重用。如果对象 A有一个run()方法,对象B也想有这个方法,所以有人就Class B extends A。这是不经大脑的做法。如果在B中实例化一个A,调用A的Run()方法,是不是可以达到同样的目的?如下:
Class B
{
A a=new A();
a.run();
}

这就是利用类的聚合来重用代码,是委派模式的雏形,是GoF一贯倡导的做法。

那么继承的意义何在?其实这是历史原因造成的,最开始的OO语言只有继承,没有接口,所以只能以继承来实现抽象,请一定注意,继承的本意在于抽象,而非代码重用(虽然继承也有这个作用),这是很多Java烂书最严重的错误之一,它们所造成的阴影,我至今还没有完全摆脱,坏书害人啊,尤其是入门类的,流毒太大。什么时候应该使用继承?只在抽象类中使用,其他情况下尽量不使用。抽象类也是不能实例化的,它仅仅提供一个模版而已,这就很能说明问题。

软件开发的万恶之源,一是重复代码而不是重用代码,二是烂用继承,尤以c 程序员为甚。Java中取缔多重继承,目的就是制止烂用继承,实是非常明智的做法,不过很多人都不理解。Java能够更好的体现设计,这是让我入迷的原因之一。

Read: 31

php中的MVC模式运用

首先我来举个例子:
一个简单的文章显示系统
简单期间,我们假定这个文章系统是只读的,也就是说这个例子将不涉及文章的发布,现在开始了。
由于只涉及数据库的读取,所以我定义了两个interface

Interface DataOperation
{
    public function select($info);
    public function selectNum($info);
}
上面这interface定义了读取数据的接口,select方法将返回所需要的文章。selectNum方法返回文章的总数,这是分页显示时用到的。$info是一个数组,用来存放查询条件
Interface DataSource
{
    public static function getInstance();
}
这里我们假定我们操作的是数据库,DataSource定义一个接口,所有实现该接口的实例类将得到一个静态对象
Interface Controller
{
    public function pop();
    public function push();
    public function execute();
}
Interface View
{
    public function display();
}
好了,我们来实现.
下面定义一个类来实现DataSource接口,这个类运用了单例模式
class DataBaseSource implements DataSource
{
    public static $instance = null;
    public static function getInstance()
    {
        if(self::$instance == null)
        {
            self::$instance == new PDO("mysql:host=localhost;dbname=article","root","123456");
        }
        return self::$instance;
    }
}
定义一个抽象类来实现DataOperation,我们要共享一个数据库连接,所以我在抽象类中将这个数据库对象初始化,这样,所有的子类都能共享这个对象
abstract class DataBaseOperation implements DataOperation
{
    protected $db = null;
    public function __construct()
    {
        $this->db = DataBaseSource::getInstance();
    }
    public function select($info);
    public function select($info);
}
下面我来写一个业务子类来实现抽象类DataBaseOperation
class Tech extends DataBaseOperation
{
    public function select($info)
    {
        //在这里实现你的代码
    }
    public function selectNum($info)
    {
        //在这里实现你的代码
    }
}
业务逻辑层我们实现了,下面是控制层
class ViewController implements Controller
{
    private $mod = array();
    public function push($key,$value);
    {
        //实现你的代码,将类注册进$this->mod;
    }
    public function pop($key)
    {       
        //实现你的代码,将$this->mod[$key]值为null;
    }
    public function execute($key)
    {
        //在这里实现你的代码,生成实例.注意利用php5新的特性,异常的处理
    }
}
好了,下面是表现层,这里将实现Interface View
abstract ArticleView implements View
{
    protected $smarty = null;
    public function __construct()
    {
        $this->smarty = new Smarty();
        ///下面你可以定义smarty的一些属性值
    }
}
具体的页面,例如科技文章的显示页面
class TechArticleView extends ArticleView
{
    public function display()
    {
        //实现你的代码,调用Tech类和更多的DataBaseOperation子类
    }
}
好了,下面是总入口 index.php
try
{
    $viewController = new ViewController();
    $viewController->push("tech",TechArticleView);
//持续的增加
    $mod = $_GET["mod"]:$_GET["mod"]:$_POST["mod"];
    //最后
    $viewController->execute($key);
}
catch(Exception $e)
{
        //如何处理异常就是你的事了
}

Read: 873