存档

2010年3月 的存档

Sersync 开源服务器文件实时同步工具

2010年3月30日 没有评论

        这是金山逍遥团队贡献的一个开源软件sersync,主要用于服务器同步,web镜像等功能。基于boost1.41.0,inotify api,rsync command.开发。

测试环境 CentOS,ubuntu。

源码地址:

http://code.google.com/p/sersync/
    目前使用的比较多的同步程序版本是inotify-tools,另外一个是google开源项目Openduckbill(依赖于inotify- tools),这两个都是基于脚本语言编写的,其设计思路同样是采用inotify与rsync命令。 相比较上面两个项目,本项目优点是:
1.sersync是使用c++编写,而且对linux系统文件系统产生的临时文件和重复的文件操作进行过滤(我稍后会提到),所以在结合rsync同步的时候,节省了运行时耗和网络资源。因此更快。
2.相比较上面两个项目,sersync配置起来很简单:在http://code.google.com/p/sersync/downloads /list 处下载源码(分为32版本,与64位版本),其中bin目录下已经有我编译好的2进制文件,配合bin目录下的xml文件直接使用即可。
3.另外本项目相比较其他脚本开源项目,使用多线程进行同步,尤其在同步较大文件时,能够保证多个服务器实时保持同步状态。
4.本项目自带出错处理机制,通过失败队列对出错的文件重新出错,如果仍旧失败,则每10个小时对同步失败的文件重新同步。
5.本项目自带crontab功能,只需在xml配置文件中开启,即可按您的要求,隔一段时间整体同步一次。
6.本项目自带socket与http协议扩展,满足您二次开发的需要。

基本架构:

   利用inotify与rsync对服务器进行实时同步,其中inotify用于监控文件系统事件,rsync是目前广泛使用的同步算法,其优点是只对文件不同的部分进行操作,所以其优势大大超过使用挂接文件系统的方式进行镜像同步。


  

设计简析

   如上图所示,线程组线程是等待线程队列的守护线程,当队列中有数据的时候,线程组守护线程逐个唤醒,当队列中inotify事件交多的时候就会被全部唤醒一起工作。这样设计的目的是能够同时处理多个inotify事件,重发利用服务器的并发能力(核数*2+2)。

   之所以称之为线程组线程,是因为每个线程在工作的时候,会根据服务器的数量建立子线程,子线程可以保证所有的文件与各个服务器同时同步,当要同步的文件较大的时候,这样设计可以保证各个远程服务器可以同时获得要同步的文件。

   服务线程 的作用有三个,首先是处理同步失败的文件,将这些文件再次同步,对于再次同步失败的文件会生成rsync_fail_log.sh脚本,记录失败的事件。 同时每隔10个小时执行脚本一次,同时清空脚本。服务线程的第三个作用是crontab功能,可以每隔一定时间,将所有路径整体同步一次。

   过滤队列的建立是为了过滤短时间内产生的重复的inotify信息,例如在删除文件夹得时候,inotify就会同时产生删除文件夹里的文件与删除文件夹 得事件,通过过滤队列当删除文件夹事件产生的时候,会将之前加入队列的删除文件的事件全部过滤掉,这样只产生一条事件减轻了同步的负担。同时对于修改文件 的操作的时候,会产生临时文件与重复操作。

原文:http://blog.csdn.net/zhoukunta/archive/2010/01/27/5261800.aspx

分类: 架构 标签:

UML建模之时序图(Sequence Diagram)

2010年3月26日 没有评论

我的做品^o^  



一、时序图简介(Brief introduction

      二、时序图元素(Sequence Diagram Elements

角色(Actor

对象(Object

生命线(Lifeline

控制焦点(Focus of Control

消息(Message

自关联消息(Self-Message

Combined Fragments

  三、时序图实例分析(Sequece Diagram Example Analysis

时序图场景

时序图实例

时序图实例分析

      四、总结(Summary

一、时序图简介(Brief introduction

      时序图(Sequence Diagram)是显示对象之间交互的图,这些对象是按时间顺序排列的。顺序图中显示的是参与交互的对象及其对象之间消息交互的顺序。时序图中包括的建模元素主要有:对象(Actor)、生命线(Lifeline)、控制焦点(Focus of control)、消息(Message)等等。

二、时序图元素(Sequence Diagram Elements

角色(Actor

  系统角色,可以是人、及其甚至其他的系统或者子系统。

对象(Object

对象包括三种命名方式:

第一种方式包括对象名和类名;

第二中方式只显示类名不显示对象名,即表示他是一个匿名对象;

第三种方式只显示对象名不显示类明。

生命线(Lifeline

生命线在顺序图中表示为从对象图标向下延伸的一条虚线,表示对象存在的时间,如下图

控制焦点(Focus of Control

控制焦点是顺序图中表示时间段的符号,在这个时间段内对象将执行相应的操作。用小矩形表示,如下图。

      

消息(Message

消息一般分为同步消息(Synchronous Message),异步消息(Asynchronous Message)和返回消息(Return Message.如下图所示:

同步消息=调用消息(Synchronous Message

消息的发送者把控制传递给消息的接收者,然后停止活动,等待消息的接收者放弃或者返回控制。用来表示同步的意义。

异步消息(Asynchronous Message

消息发送者通过消息把信号传递给消息的接收者,然后继续自己的活动,不等待接受者返回消息或者控制。异步消息的接收者和发送者是并发工作的。

返回消息(Return Message

返回消息表示从过程调用返回

自关联消息(Self-Message

表示方法的自身调用以及一个对象内的一个方法调用另外一个方法。

Combined Fragments

Ø        Alternative fragmentdenoted “alt” if…then…else对应

Ø        Option fragment (denoted “opt”) Switch对应

Ø        Parallel fragment (denoted “par”) 表示同时发生

Ø        Loop fragment(denoted “loop”) for 或者 Foreach对应

三、时序图实例分析(Sequece Diagram Example Analysis

时序图场景

完成课程创建功能,主要流程有:

1、请求添加课程页面,填写课程表单,点击【create】按钮

2、添加课程信息到数据库

3、向课程对象追加主题信息

4、为课程指派教师

5、完成课程创建功能

时序图实例

时序图实例分析

1、序号1.0-1.3完成页面的初始化

2、序号1.4-1.5课程管理员填充课程表单

3、序号1.6-1.7课程管理员点击【Create】按钮,并响应点击事件

4、序号1.8    Service层创建课程

5、序号1.9-1.10 添加课程到数据库,并返回课程编号CourseId

6、序号1.11-1.12 添加课程主题到数据库,并返回主题编号topicId

7、序号1.13        给课程指派教师

8、序号1.14        向界面抛创建课程成功与否的消息

四、总结(Summary

      时序图(Sequence Diagram)是显示对象之间交互的图,这些对象是按时间顺序排列的。顺序图中显示的是参与交互的对象及其对象之间消息交互的顺序。时序图中包括的建模元素主要有:对象(Actor)、生命线(Lifeline)、控制焦点(Focus of control)、消息(Message)等等。最后,以课程创建功能演示一时序图实例。

作者:灵动生活
出处:http://www.cnblogs.com/ywqu

分类: 其他 标签:

网银登陆困难 自动退出问题

2010年3月24日 没有评论

       今天总公司那边听说网络出现很奇怪的问题,所有电脑都不能正常登陆网银(工行,招行的都不行),过去看了一下,确实挺让人觉得郁闷的,按理全网都不能正常登陆,要么是路由器设置有问题,要么就是中了局域网病毒,但是仔细查看了一下状况,局域网中毒可以排除,问题应该出现在路由器上面。
推测可能是端口设置问题吧, 但找了半天也没发现端口有仍何问题。因为那个管路由器啥也不懂,一问三不知,最后只好试试恢复出厂设置吧。于是问他们要宽带账号密码,奇怪了给我整了两个账号,我还以为他们有两个网呢,后来,他告诉我是双线路路由,突然想到前几天看到的一些关于集群负载均衡共享Session问题,会不会是因为双线路造成的通讯问题?。。。太佩服我自己了,问题解决了
原来是双线路造成整个访问网银过程中出现通讯线路不一致导致的问题,后来仔细看了一下路由器有个线路均衡策略设置的。。。。

分类: 其他 标签:

常见命名方法:骆驼命名法、帕斯卡命名法、匈牙利命名法

2010年3月22日 没有评论

MyData 就是一个帕斯卡命名法
而myData是一个骆驼命名法,它第一个单词的第一个字母小写,后面的单词首字母大写,看起来像一个骆驼
而iMyData是一个匈牙利命名法,它的小写的i说明了它的形态,后面的和帕斯卡命名相同,指示了该变量的用途.

一、匈牙利命名法:广泛应用于象Microsoft Windows这样的环境中。

      Windows 编程中用到的变量(还包括宏)的命名规则匈牙利命名法,这种命名技术是由一位能干的 Microsoft 程序员查尔斯・西蒙尼(Charles Simonyi) 提出的。

匈牙利命名法通过在变量名前面加上相应的小写字母的符号标识作为前缀,标识出变量的作用域,类型等。这些符号可以多个同时使用,顺序是先m_(成员变量),再指针,再简单数据类型,再其他。例如:m_lpszStr, 表示指向一个以0字符结尾的字符串的长指针成员变量。

    匈牙利命名法关键是:标识符的名字以一个或者多个小写字母开头作为前缀;前缀之后的是首字母大写的一个单词或多个单词组合,该单词要指明变量的用途。

匈牙利命名法中常用的小写字母的前缀:

前 缀       类  型
a               数组 (Array)
b               布尔值 (Boolean)
by             字节 (Byte)
c              有符号字符 (Char)
cb            无符号字符 (Char Byte,没有多少人用)
cr             颜色参考值 (ColorRef)
cx,cy         坐标差(长度 ShortInt)
dw           Double Word
fn              函数
h                Handle(句柄)
i                整型
l              长整型 (Long Int)
lp             Long Pointer
m_          类的成员
n            短整型 (Short Int)
np          Near Pointer
p            Pointer
s           字符串型
sz         以null做结尾的字符串型 (String with Zero End)
w        Word

二、骆驼命名法:

        骆驼式命令法,正如它的名称所表示的那样,是指混合使用大小写字母来构成变量和函数的名字。例如,下面是分别用骆驼式命名法和下划线法命名的同一个函数:

   printEmployeePaychecks();

    print_employee_paychecks();

     第一个函数名使用了骆驼式命名法――函数名中的每一个逻辑断点都有一个大写字母来标记;第二个函数名使用了下划线法—-函数名中的每一个逻辑断点都有一个下划线来标记。

    骆驼式命名法近年来越来越流行了,在许多新的函数库和Microsoft
Windows这样的环境中,它使用得当相多。另一方面,下划线法是c出现后开始流行起来的,在许多旧的程序和UNIX这样的环境中,它的使用非常普遍。

三、帕斯卡(pascal)命名法:

       与骆驼命名法类似。只不过骆驼命名法是首字母小写,而帕斯卡命名法是首字母大写

       如:public void
DisplayInfo();

              string UserName;

              二者都是采用了帕斯卡命名法.

在C#中,以帕斯卡命名法和骆驼命名法居多。

原文:http://www.zouyang.net/post/220.html

分类: 其他 标签:

高性能PHP框架DooPHP

2010年3月21日 没有评论

       DooPHP 是一个 PHP 敏捷开发框架,同样采用 MVC 设计模式和对象关系映射(ORM)技术,可以有效的提高开发效率。

       DooPHP 本身的设计更注重的是其核心性能,而不是花哨的功能,它更适合那些不想花太多时间学习一种框架或者一门新的语言的人。

如果你不喜欢某些框架产生的那些后期难以维护和修改的设计(frameworks that generates frontend/backend design which are usually hard to modify and maintain later on),那么 DooPHP 一定适合你。DooPHP 给你干净的视图文件,而不是难以理解的思想。

如果下面这个介绍列表让你欣喜,那么你一定会喜欢上 DooPHP。

1. 高性能
2. 较低的学习曲线
3. 高度灵活性
4. 轻量级,全部文件加起来不超过1M

具体来说:

1. 它不会改变你使用 PHP 的习惯。依然支持$_GET。
2. 它具有很高的兼容性。It works with shared hosting accounts and various environment.
3. 它只需要三个配置文件。 即只需要设置你的项目路径。
4. 它不需要使用命令行,SSH也不是必须的。
5. 它不是另一个版本的RoR克隆,没有特别的编码规范约束。
6. 它具有清晰、完整的文档。
7. 它具有松耦合特性,可用于其他外部项目。
8. 它可以很轻松的结合第三方类。
9. 如果使用Netbeans开发,还会有代码提示支持。
10. 它具有完整的API,能够很好的结合AJAX 或者 Flash/Flex进行混合开发。
11. 它的 URL 被设计成对搜索引擎和人类友好。
12. 它本身非常灵活,并且具有可扩展的模版引擎。
13. 它针对common settings, routes, DB configs, DB relationship进行了几种控制。

中文手册:http://www.peehp.com/doo/index.php

分类: PHP 标签:

利用"二进制"来控制权限

2010年3月18日 没有评论

一、利用位与
1.介绍:

这里我介绍一种很常用,也比较Professor的权限控制思路。
这里我用java语言描述,其实都差不多的。自己转一下就可以了。
为了方便,我们这里定义a^b为:a的b次方
这里,我们为每一个操作设定一个唯一的整数值,比如:

删除A---0
修改A---1
添加A---2

删除B---3
修改B---4
添加B---5
。。。

理论上可以有N个操作
这取决于你用于储存用户权限值的数据类型了。

这样,如果用户有权限:添加A---2;删除B---3;修改B---4
那用户的权限值 purview =2^2+2^3+2^4=28,也就是2的权的和了(之前打错了)。
化成二进制可以表示为11100
这样,如果要验证用户是否有删除B的权限,就可以通过位与运算来实现。
在Java里,位与运算运算符号为&
即是:int value = purview &((int)Math.pow(2,3));
你会发现,当用户有操作权限时,运算出来的结果都会等于这个操作需要的权限值!
原理:
位与运算,顾名思义就是对位进行与运算:
以上面的式子为例:purview & 2^3 也就是 28&8
将它们化成二进制有
   11100
&  01000
——————-
   01000 == 8(十进制) == 2^3
同理,如果要验证是否有删除A---0的权限
可以用:purview &((int)Math.pow(2,0));
即:
   11100
  & 00001
————————
    00000 ==  0(十进制)  != 2^0

这种算法的一个优点是速度快。可以同时处理N个权限
如果想验证是否同时有删除A---0和删除B---3的权限
可以用purview&(2^0+2^3)==(2^0+2^3)?true:false;
设置多角色用户。根据权限值判断用户的角色。。。

下面提供一个java的单操作权限判断的代码:
//userPurview是用户具有的总权限
//optPurview是一个操作要求的权限为一个整数(没有经过权的!)
public static boolean checkPower(int userPurview, int optPurview)
{
      int purviewValue = (int)Math.pow(2, optPurview);
      return (userPurview & purviewValue) == purviewValue;
}

当然,多权限的验证只要扩展一下就可以了。

2.原理:
其实
添加A---2  2^2 ==  4 ==  00100
删除B---3  2^3 ==  8 ==  01000
修改B---4  2^4 ==  16 ==  10000
三个值相加就是11100B == 28(D)了

也就是说,让每一个操作权限值都是只有一个1和N个零组成的唯一排列
这样,几个权限相加时就不会出现进位的问题了。保证了每个操作权限都是独立的。
权限值只是一个代号,与大小无关。

3.注意:
(1)一个系统可能有很多的操作,因此,请建立数据字典,以便查阅,修改时使用。
(2)如果用数据库储存用户权限,请注意数值的有效范围。操作权限值请用唯一的整数!
(3)java里一个int是2^32大小,所以不会将权限都分在一个组里,都会分成几个模块,对一个模块用int类型有31个权限分配,一般都可以满足了。建议分组或用大一点的long型。

二、直接用1010……..表示
直接用字符串:010101111010011010000………
判断:0就是没权限,1就是有权限。

原文:http://huangy82.blog.163.com/blog/static/490698272009102310216497/

分类: PHP 标签:

TOMCATE 安装

2010年3月16日 没有评论

      安装Tomcat之前要先安装JDK,可从http://java.sun.com上下载最新版本的JDK。Tomcat可从Apache Jakarta Project站点(http://jakarta.apache.org/site/binindex.cgi)上下载,本书使用的Tomcat版本是5.5.7,它需要安装J2SE 5.0(JDK 1.5)以上的版本才能运行。对于Windows操作系统,Tomcat 5.5.7提供了两种安装文件,一种是jakarta-tomcat-5.5.7.exe,一种是jakarta-tomcat-5.5.7.zip(如果读者使用的是Linux系统,请下载jakarta-tomcat-5.5.7.tar.gz)。jakarta-tomcat-5.5.7.exe是可执行的安装程序,读者只需要双击这个文件,就可以开始安装Tomcat了。在安装过程中,安装程序会自动搜寻JDK和JRE的位置。安装完成后,在Windows系统的“开始”->“程序”菜单下会添加Apache Tomcat 5.5菜单组。jakarta-tomcat-5.5.7.zip是一个压缩包,只需要将它解压到硬盘上就可以了。在这里,我建议读者下载jakarta-tomcat-5.5.7.zip压缩包,通过解压缩的方式安装Tomcat,因为解压缩的方式也适用于其他的操作系统,例如Linux系统。下面我们主要介绍jakarta-tomcat-5.5.7.zip的安装与Tomcat运行环境的设置。

安装Tomcat

使用WinZip或WinRAR等解压缩工具将jakarta-tomcat-5.5.7.zip解压到指定的驱动器和目录中。笔者是在D盘上直接解压,产生了目录jakarta-tomcat-5.5.7,解压后的文件存放于D:\ jakarta-tomcat-5.5.7下。

Tomcat安装后的目录层次结构如图5-2所示。

图5-2 Tomcat 5.5.7目录层次结构

各目录的用途如表5-1所示。

表5-1 Tomcat的目录结构及其用途

/bin

存放启动和关闭Tomcat的脚本文件

/common/lib

存放Tomcat服务器及所有Web应用程序都可以访问的JAR文件

/conf

存放Tomcat服务器的各种配置文件,其中包括server.xmlTomcat的主要配置文件)、tomcat-users.xmlweb.xml等配置文件

/logs

存放Tomcat的日志文件

/server/lib

存放Tomcat服务器运行所需的各种JAR文件

/server/webapps

存放Tomcat的两个Web应用程序:admin应用程序和manager应用程序

/shared/lib

存放所有Web应用程序都可以访问的JAR文件

/temp

存放Tomcat运行时产生的临时文件

/webapps

当发布Web应用程序时,通常把Web应用程序的目录及文件放到这个目录下

/work

TomcatJSP生成的Servlet源文件和字节码文件放到这个目录下

从表5-1中可以看到,/common/lib目录、/server/lib和/shared/lib目录下都可以存放JAR文件,它们的区别在于:

— 在/server/lib目录下的JAR文件只能被Tomcat服务器访问;

— 在/shared/lib目录下的JAR文件可以被所有的Web应用程序访问,但不能被Tomcat服务器访问;

— 在/common/lib目录下的JAR文件可以被Tomcat服务器和所有的Web应用程序访问。

此外,对于后面将要介绍的Java Web应用程序,在它的WEB-INF目录下,也可以建立lib子目录,在lib子目录下可以存放各种JAR文件,这些JAR文件只能被当前Web应用程序所访问。

运行Tomcat

在Tomcat安装目录下的bin子目录中,有一些批处理文件(以.bat作为后缀名的文件),其中的startup.bat就是启动Tomcat的脚本文件,用鼠标双击这个文件,将会看到如图5-3所示的画面。

图5-3 运行Tomcat提示出错信息

笔者以前碰到过很多学员,在初次运行Tomcat时,看到如图5-3所示的信息就不知所措了。有的学员以前还配置过Tomcat,但是再次使用的时候,由于忘记了上次是如何配置的,同样感觉无从下手。

我们在学习软件开发时,一定要养成查看错误提示信息,进而根据错误提示解决问题的良好习惯。笔者第一次配置Tomcat时,就是根据错误提示信息一步一步配置成功的。很多人一看见错误信息,立即单击“确定”按钮,这样就错过了提示信息。当看到错误信息时,首先不要慌张和无所适从,仔细看清楚错误提示,不要着急单击按钮。

查看图5-3中的错误提示信息,可以看到这样一句话“The JAVA_HOME environment variable is not defined”,从画面中可以看到,在执行到“Using JAVA_HOME”这句时出现了错误,由此,我们可以想到,出错的原因可能是因为没有设置JAVA_HOME环境变量。那么JAVA_HOME环境变量的值应该是什么呢?很容易就能想到应该是JDK所在的目录,在笔者的机器上,JDK所在的目录是D:\Java\jdk1.5.0_01。

在Windows 2000操作系统下设置环境变量的步骤如下。

① 在桌面“我的电脑”上单击右键,选择“属性”,出现如图5-4所示的画面。

图5-4 “我的电脑”属性

② 单击“高级”选项卡,选择“环境变量(E)…”,如图5-5和图5-6所示。

图5-5 “高级”选项卡 图5-6 “环境变量”对话框

③ 在“系统变量”下方单击“新建”按钮。在“变量名”中输入“JAVA_HOME”,在变量值中输入JDK所在的目录“D:\Java\jdk1.5.0_01”,然后单击“确定”按钮,如图5-7所示。

图5-7 新建JAVA_HOME环境变量

④ 最后在“环境变量”对话框上单击“确定”按钮,结束JAVA_HOME环境变量的设置。

我们再一次转到D:\ jakarta-tomcat-5.5.7\bin目录下,用鼠标双击startup.bat文件,可以看到如图5-8所示的启动信息。

图5-8 Tomcat启动信息

然后,打开浏览器,在地址栏中输入http://localhost:8080/(localhost表示本地机器,8080是Tomcat默认监听的端口号),将出现如图5-9所示的Tomcat页面。

图5-9 Tomcat的默认主页

注意图5-9中鼠标(小手形状)指向的链接——Tomcat Documentation,单击将进入Tomcat的文档页面,有关Tomcat的帮助信息可以在文档页面中找到;读者也可以直接访问Tomcat的文档,文档首页的位置是Tomcat安装目录下的webapps\tomcat-docs\index.html。如果要关闭Tomcat服务器,可以用鼠标双击D:\ jakarta-tomcat-5.5.7\bin目录下的shutdown.bat文件。

如果你机器上的Tomcat启动失败,有可能是因为TCP的8080端口被其他应用程序所占用,如果你知道是哪一个应用程序占用了8080端口,那么先关闭此程序。如果你不知道或者不想关闭占用8080端口的应用程序,你可以修改Tomcat默认监听的端口号。

前面介绍了,Tomcat安装目录下的conf子目录用于存放Tomcat服务器的各种配置文件,其中的server.xml是Tomcat的主要配置文件,这是一个格式良好的XML文档,在这个文件中可以修改Tomcat默认监听的端口号。用UltraEdit(你可以用记事本程序或其他的文本编辑工具)打开server.xml,找到修改8080端口的地方。读者也许要问了,“这个配置文件,我都不熟悉,怎么知道在哪里修改端口号呢?”对于初次接触server.xml的读者,确实不了解这个文件的结构,但是我们应该有一种开放的思路,既然Tomcat的监听端口号是在server.xml中配置,那么只要我们在这个文件中查找“8080”这些数字字符序列,不就能找到修改端口号的地方了吗!在UltraEdit中,同时按下键盘上的“Ctrl”和“F”键,出现如图5-10所示的查找对话框。

图5-10 UltraEdit查找对话框

然后在“查找内容”中输入“8080”,单击“查找下一个”按钮。重复这个过程,直到找到如图5-11所示的在server.xml中配置端口号位置。

图5-11 server.xml中配置端口号的位置

找到后,如果我们不能确定此处就是修改端口号的地方,也没有关系,可以先尝试着修改一下端口号,然后启动Tomcat,如果启动成功,也就证明了我们修改的地方是正确的。学习时,我们应该养成这种探索并不断实验的精神。在这里,我们可以修改端口号为8000(读者可以根据自己机器的配置选择一个端口号),然后保存。再次启动Tomcat,在Tomcat启动完毕后,打开浏览器,在地址栏中输入http://localhost:8000/(读者根据自己设置的端口号做相应的修改),就可以看到Tomcat的默认主页了。关闭Tomcat服务器时,执行bin目录下的shutdown.bat文件。

Tomcat启动分析

在本节中我们将通过对Tomcat启动过程的分析,来帮助读者更好地理解和掌握Tomcat。

用文本编辑工具打开用于启动Tomcat的批处理文件startup.bat,仔细阅读,可以发现,在这个文件中,首先判断CATALINA_HOME环境变量是否为空,如果为空,就将当前目录设为CATALINA_HOME的值,接着判断当前目录下是否存在bin\catalina.bat,如果文件不存在,将当前目录的父目录设为CATALINA_HOME的值,根据笔者机器上Tomcat安装目录的层次结构,最后CATALINA_HOME的值被设为Tomcat的安装目录。如果环境变量CATALINA_HOME已经存在,则通过这个环境变量调用bin目录下的“catalina.bat start”命令。通过这段分析,我们了解到两个信息,一是Tomcat启动时,需要查找CATALINA_HOME这个环境变量,如果在当前目录下调用startup.bat,Tomcat会自动设置CATALINA_HOME;二是执行startup.bat命令,实际上执行的是“catalina.bat start”命令。

如果我们不是在bin目录作为当前目录时调用startup.bat,就会出现如图5-12所示的错误信息(在bin目录的父目录下调用除外)。

图5-12 在其他目录下启动Tomcat出错

要在其他目录下也能启动Tomcat,就需要设置CATALINA_HOME环境变量,你可以将CATALINA_HOME添加到Windows 2000系统的环境变量中,其值就是Tomcat的安装目录,在笔者的机器上安装目录是D:\jakarta-tomcat-5.5.7,添加环境变量的过程和前述添加JAVA_HOME环境变量的过程是一样的。如果你不想在系统的环境变量中添加,也可以直接在startup.bat文件中进行设置。下面是在startup.bat文件中设置CATALINA_HOME后的文件片段:

……

rem $Id: shutdown.bat,v 1.5 2004/05/27 15:05:01 yoavs Exp $

rem —————————————————————————

set CATALINA_HOME=D:\jakarta-tomcat-5.5.7

rem Guess CATALINA_HOME if not defined

set CURRENT_DIR=%cd%

if not "%CATALINA_HOME%" == "" goto gotHome

set CATALINA_HOME=%CURRENT_DIR%

……

注意以粗体显示的这句话的作用就是设置CATALINA_HOME环境变量。在它的下面就可以判断CATALINA_HOME是否为空了。如果你找不准位置,干脆将设置CATALINA_HOME环境变量的这句话放置到文件的第一行。JAVA_HOME环境变量也可以采用同样的方式进行设置。不过,如果你要在其他目录下,利用shutdown.bat来关闭Tomcat服务器,你也需要在shutdown.bat文件中设置CATALINA_HOME和JAVA_HOME这两个环境变量,设置变量的位置和startup.bat文件一样,都是在判断CATALINA_HOME是否为空之前。当然,为了一劳永逸,避免重装Tomcat后还要进行设置(需要是同一版本的Tomcat安装在同一位置),我们最好还是将CATALINA_HOME和JAVA_HOME这两个环境变量添加到Windows 2000系统的环境变量中。

有的读者可能会对设置Tomcat安装目录的环境变量的名字是CATALINA_HOME而感到奇怪,按照以前设置的环境变量来看,JAVA_HOME表示JDK的安装目录,那么应该用TOMCAT_HOME来表示Tomcat的安装目录,可为什么要使用CATALINA_HOME呢?实际上,在Tomcat 4以前,用的就是TOMCAT_HOME来表示Tomcat的安装目录,在Tomcat 4以后,采用了新的Servlet容器Catalina,所以环境变量的名字也改为了CATALINA_HOME。

提示:在Windows系统下环境变量的名字是与大小写无关的,也就是说JAVA_HOME和java_home是相同的。

了解了startup.bat文件以后,我们再来看看真正负责启动Tomcat服务器的catalina.bat文件。通过分析catalina.bat文件,我们发现它还调用了一个文件setclasspath.bat。在setclasspath.bat文件中,它检查JAVA_HOME环境变量是否存在,并通过设置的环境变量JAVA_HOME,找到java.exe,用于启动Tomcat。在这个文件中,还设置了其他的一些变量,分别表示JDK中的一些工具,有兴趣的读者可以自行分析一下这个文件。在执行完setclasspath.bat之后,catalina.bat剩下的部分就开始了Tomcat服务器的启动进程。

直接执行catalina.bat时,需要带上命令行的参数。读者可以在命令提示符窗口下,执行catalina.bat,就会打印出catalina.bat命令的各种参数及其含义,如图5-13所示。

图5-13 catalina.bat的各参数信息

其中常用的参数是start、run和stop,参数start表示在一个单独的窗口中启动Tomcat服务器,参数run表示在当前窗口中启动Tomcat服务器,参数stop表示关闭Tomcat服务器。我们执行startup.bat,实际上执行的就是“catalina.bat start”命令;执行shutdown.bat,实际上执行的是“catalina.bat stop”命令。“catalina.bat run”命令有时候是非常有用的,特别是当我们需要查看Tomcat的出错信息时。我们在开发JSP程序时,经常会碰到自己机器上的8080端口号被别的应用程序占用,或者在配置server.xml时出现错误,当通过startup.bat(相当于执行“catalina.bat start”)启动Tomcat服务器时,会导致启动失败,因为是在单独的窗口中启动Tomcat服务器,所以一旦启动失败,命令提示符窗口就自动关闭了,程序运行中输出的出错信息也随之消失,而且没有任何的日志信息,这就使得我们没有办法找出错误原因。当出现错误时,我们可以换成“catalina.bat run”命令再次启动,一旦启动失败,仅仅是Tomcat服务器异常终止,但是在当前的命令提示符窗口下仍然保留了启动时的出错信息,这样我们就可以查找启动失败的原因了

Tomcat的体系结构

Tomcat服务器是由一系列可配置的组件构成的,其中核心组件是Catalina Servlet容器,它是所有其他Tomcat组件的顶层容器。Tomcat各组件之间的层次关系如图5-14所示。

图5-14 Tomcat组件之间的层次结构

我们下面简单介绍一下各组件在Tomcat服务器中的作用。

(1)Server

Server表示整个的Catalina Servlet容器。Tomcat提供了Server接口的一个默认实现,这通常不需要用户自己去实现。在Server容器中,可以包含一个或多个Service组件。

(2)Service

Service是存活在Server中的内部组件,它将一个或多个连接器(Connector)组件绑定到一个单独的引擎(Engine)上。在Server中,可以包含一个或多个Service组件。Service也很少由用户定制,Tomcat提供了Service接口的默认实现,而这种实现既简单又能满足应用。

(3)Connector

连接器(Connector)处理与客户端的通信,它负责接收客户请求,以及向客户返回响应结果。在Tomcat中,有多个连接器可以使用。

(4)Engine

在Tomcat中,每个Service只能包含一个Servlet引擎(Engine)。引擎表示一个特定的Service的请求处理流水线。作为一个Service可以有多个连接器,引擎从连接器接收和处理所有的请求,将响应返回给适合的连接器,通过连接器传输给用户。用户可以通过实现Engine接口提供自定义的引擎,但通常不需要这么做。

(5)Host

Host表示一个虚拟主机,一个引擎可以包含多个Host。用户通常不需要创建自定义的Host,因为Tomcat给出的Host接口的实现(类StandardHost)提供了重要的附加功能。

(6)Context

一个Contex表示了一个Web应用程序,运行在特定的虚拟主机中。什么是Web应用程序呢?在Sun公司发布的Java Servlet规范中,对Web应用程序做出了如下的定义:“一个Web应用程序是由一组Servlet、HTML页面、类,以及其他的资源组成的运行在Web服务器上的完整的应用程序。它可以在多个供应商提供的实现了Servlet规范的Web容器中运行”。一个Host可以包含多个Context(代表Web应用程序),每一个Context都有一个惟一的路径。用户通常不需要创建自定义的Context,因为Tomcat给出的Context接口的实现(类StandardContext)提供了重要的附加功能。

下面我们通过图5-15来帮助读者更好地理解Tomcat服务器中各组件的工作流程。

图5-15 Tomcat各组件的工作流程

要了解这些组件的其他信息,可以看下面的页面:

%CATALINA_HOME%\webapps\tomcat-docs\architecture\index.html

我们可以在conf目录下的server.xml文件中对这些组件进行配置,读者打开server.xml文件,就可以看到元素名和元素之间的嵌套关系,与Tomcat服务器的组件是一一对应的,server.xml文件的根元素就是server。关于server.xml配置文件中的各元素及其属性的含义,请参见附录C。

在Tomcat中,提供了各组件的接口及其实现类,如果你要替代Tomcat中的某个组件,只需要根据该组件的接口或类的说明,重写该组件,并进行配置即可。图5-16是Tomcat各组件的类图。

在类图的接口名或类名下面是该接口或该类所在的包,这些接口和类都在%CATALINA_HOME%\ server\lib\catalina.jar文件中。对Tomcat服务器的实现感兴趣的读者,可以从http://tomcat.apache.org/上下载Tomcat的源代码。

提示:由于Apache软件基金会并不是一个商业性的组织,所以文档更新的速度有时候跟不上版本更新的速度。在Tomcat 5.5.7中,就可以发现文档与其源码实现有不一致的地方。在Tomcat 5.5.x中,去掉了org.apache.catalina.Connector接口及其相关的实现类,而直接以org.apache.catalina.connector.Connector类来代替。我们在看Tomcat的文档时,最好结合其API文档一起看,这样才能保证了解的信息是完整的和准确的。

Tomcat提供了两个管理程序:admin和manager。其中admin用于管理和配置Tomcat服务器,manager用于管理部署到Tomcat服务器中的Web应用程序。

admin Web应用程序

admin Web应用程序需要单独下载,与Tomcat在同一个下载页面,链接名是Admin zip,下载后的文件名是jakarta-tomcat-5.5.7-admin.zip,解压缩后,覆盖Tomcat安装目录下的同名目录。admin Web应用程序位于%CATALINA_HOME%\server\webapps\admin目录下。

要访问admin Web应用程序,需要添加具有管理员权限的账号,编辑%CATALINA_HOME%\ conf\tomcat-users.xml文件,在<tomcat-users>元素中添加如下内容:

<user username="admin" password="12345678" roles="admin"/>

其中用户名和密码可以根据自己的喜好设置。

启动Tomcat服务器,打开浏览器,在地址栏中输入:

http://localhost:8080/admin/

将出现如图5-17所示的页面。

图5-17 admin Web应用程序的登录界面

也可以在Tomcat的默认主页的左上方单击“Tomcat Administration”链接,进入admin登录页面。输入用户名admin,密码12345678,单击“Login”按钮,将看到如图5-18所示的页面。

图5-18 admin Web应用程序的主页面

在这个页面中,可以进行Tomcat服务器的各项配置。

5.6.2 manager Web应用程序

manager Web应用程序包含在Tomcat的安装包中。和admin程序一样,需要添加访问manager Web应用程序的管理员账号,编辑%CATALINA_HOME%\conf\tomcat-users.xml文件,在<tomcat-users>元素中添加如下内容:

<user username="manager" password="12345678" roles="manager"/>

其中用户名和密码可以根据自己的喜好设置。

启动Tomcat服务器,打开浏览器,在地址栏中输入:

http://localhost:8080/manager/html/

将出现如图5-19所示的页面。

图5-19 manager Web应用程序的登录界面

也可以在Tomcat的默认主页的左上方单击“Tomcat Manager”链接,访问manager程序。输入用户名manager,密码12345678,单击“确定”按钮,将看到如图5-20所示的页面。

图5-20 manager Web应用程序的主页面

在这个页面中,你可以部署、启动、停止、重新加载、卸载Web应用程序。注意在两个圆角矩形框中的路径“/jsp-examples”和“/servlets-examples”,单击这两个路径,将看到Tomcat提供的JSP和Servlet的例子程序,这些程序可以作为学习JSP和Servlet的参考。不过在这两个路径下,只列出了部分的例子程序,完整的JSP和Servlet例子程序位于下面的两个目录中:

%CATALINA_HOME%\webapps\jsp-examples

%CATALINA_HOME%\webapps\servlets-examples

原文:http://hi.baidu.com/shenlvjing/blog/item/46f39c4a7994ab2408f7ef6e.html

分类: 其他 标签:

一致性 hash 算法( consistent hashing )

2010年3月16日 没有评论

     consistent hashing 算法早在 1997 年就在论文 Consistent hashing and random trees 中被提出,目前在 cache 系统中应用越来越广泛;

1 基本场景

比如你有 N 个 cache 服务器(后面简称 cache ),那么如何将一个对象 object 映射到 N 个 cache 上呢,你很可能会采用类似下面的通用方法计算 object 的 hash 值,然后均匀的映射到到 N 个 cache ;

hash(object)%N

一切都运行正常,再考虑如下的两种情况;

1 一个 cache 服务器 m down 掉了(在实际应用中必须要考虑这种情况),这样所有映射到 cache m 的对象都会失效,怎么办,需要把 cache m 从 cache 中移除,这时候 cache 是 N-1 台,映射公式变成了 hash(object)%(N-1) ;

2 由于访问加重,需要添加 cache ,这时候 cache 是 N+1 台,映射公式变成了 hash(object)%(N+1) ;

1 和 2 意味着什么?这意味着突然之间几乎所有的 cache 都失效了。对于服务器而言,这是一场灾难,洪水般的访问都会直接冲向后台服务器;

再来考虑第三个问题,由于硬件能力越来越强,你可能想让后面添加的节点多做点活,显然上面的 hash 算法也做不到。

有什么方法可以改变这个状况呢,这就是 consistent hashing…

2 hash 算法和单调性

Hash 算法的一个衡量指标是单调性( Monotonicity ),定义如下:

单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲加入到系统中。哈希的结果应能够保证原有已分配的内容可以被映射到新的缓冲中去,而不会被映射到旧的缓冲集合中的其他缓冲区。

容易看到,上面的简单 hash 算法 hash(object)%N 难以满足单调性要求。

3 consistent hashing 算法的原理

consistent hashing 是一种 hash 算法,简单的说,在移除 / 添加一个 cache 时,它能够尽可能小的改变已存在 key 映射关系,尽可能的满足单调性的要求。

下面就来按照 5 个步骤简单讲讲 consistent hashing 算法的基本原理。

3.1 环形hash 空间

考虑通常的 hash 算法都是将 value 映射到一个 32 为的 key 值,也即是 0~2^32-1 次方的数值空间;我们可以将这个空间想象成一个首( 0)尾( 2^32-1 )相接的圆环,如下面图 1 所示的那样。

circle space

图 1 环形 hash 空间

3.2 把对象映射到hash 空间

接下来考虑 4 个对象 object1~object4 ,通过 hash 函数计算出的 hash 值 key 在环上的分布如图 2 所示。

hash(object1) = key1;

… …

hash(object4) = key4;

object

图 2 4 个对象的 key 值分布

3.3 把cache 映射到hash 空间

Consistent hashing 的基本思想就是将对象和 cache 都映射到同一个 hash 数值空间中,并且使用相同的 hash 算法。

假设当前有 A,B 和 C 共 3 台 cache ,那么其映射结果将如图 3 所示,他们在 hash 空间中,以对应的 hash 值排列。

hash(cache A) = key A;

… …

hash(cache C) = key C;

cache

图 3 cache 和对象的 key 值分布

说到这里,顺便提一下 cache 的 hash 计算,一般的方法可以使用 cache 机器的 IP 地址或者机器名作为 hash 输入。

3.4 把对象映射到cache

现在 cache 和对象都已经通过同一个 hash 算法映射到 hash 数值空间中了,接下来要考虑的就是如何将对象映射到 cache 上面了。

在这个环形空间中,如果沿着顺时针方向从对象的 key 值出发,直到遇见一个 cache ,那么就将该对象存储在这个 cache 上,因为对象和 cache 的 hash 值是固定的,因此这个 cache 必然是唯一和确定的。这样不就找到了对象和 cache 的映射方法了吗?!

依然继续上面的例子(参见图 3 ),那么根据上面的方法,对象 object1 将被存储到 cache A 上; object2 和 object3 对应到 cache C ; object4 对应到 cache B ;

3.5 考察cache 的变动

前面讲过,通过 hash 然后求余的方法带来的最大问题就在于不能满足单调性,当 cache 有所变动时, cache 会失效,进而对后台服务器造成巨大的冲击,现在就来分析分析 consistent hashing 算法。

3.5.1 移除 cache

考虑假设 cache B 挂掉了,根据上面讲到的映射方法,这时受影响的将仅是那些沿 cache B 逆时针遍历直到下一个 cache ( cache C )之间的对象,也即是本来映射到 cache B 上的那些对象。

因此这里仅需要变动对象 object4 ,将其重新映射到 cache C 上即可;参见图 4 。

remove

图 4 Cache B 被移除后的 cache 映射

3.5.2 添加 cache

再考虑添加一台新的 cache D 的情况,假设在这个环形 hash 空间中, cache D 被映射在对象 object2 和 object3 之间。这时受影响的将仅是那些沿 cache D 逆时针遍历直到下一个 cache ( cache B )之间的对象(它们是也本来映射到 cache C 上对象的一部分),将这些对象重新映射到 cache D 上即可。

因此这里仅需要变动对象 object2 ,将其重新映射到 cache D 上;参见图 5 。

add

图 5 添加 cache D 后的映射关系

4 虚拟节点

考量 Hash 算法的另一个指标是平衡性 (Balance) ,定义如下:

平衡性

平衡性是指哈希的结果能够尽可能分布到所有的缓冲中去,这样可以使得所有的缓冲空间都得到利用。

hash 算法并不是保证绝对的平衡,如果 cache 较少的话,对象并不能被均匀的映射到 cache 上,比如在上面的例子中,仅部署 cache A 和 cache C 的情况下,在 4 个对象中, cache A 仅存储了 object1 ,而 cache C 则存储了 object2 、 object3 和 object4 ;分布是很不均衡的。

为了解决这种情况, consistent hashing 引入了“虚拟节点”的概念,它可以如下定义:

“虚拟节点”( virtual node )是实际节点在 hash 空间的复制品( replica ),一实际个节点对应了若干个“虚拟节点”,这个对应个数也成为“复制个数”,“虚拟节点”在 hash 空间中以 hash 值排列。

仍以仅部署 cache A 和 cache C 的情况为例,在图 4 中我们已经看到, cache 分布并不均匀。现在我们引入虚拟节点,并设置“复制个数”为2 ,这就意味着一共会存在 4 个“虚拟节点”, cache A1, cache A2 代表了 cache A ; cache C1, cache C2 代表了 cache C ;假设一种比较理想的情况,参见图 6 。

virtual nodes

图 6 引入“虚拟节点”后的映射关系

此时,对象到“虚拟节点”的映射关系为:

objec1->cache A2 ; objec2->cache A1 ; objec3->cache C1 ; objec4->cache C2 ;

因此对象 object1 和 object2 都被映射到了 cache A 上,而 object3 和 object4 映射到了 cache C 上;平衡性有了很大提高。

引入“虚拟节点”后,映射关系就从 { 对象 -> 节点 } 转换到了 { 对象 -> 虚拟节点 } 。查询物体所在 cache 时的映射关系如图 7 所示。

map

图 7 查询对象所在 cache

“虚拟节点”的 hash 计算可以采用对应节点的 IP 地址加数字后缀的方式。例如假设 cache A 的 IP 地址为 202.168.14.241 。

引入“虚拟节点”前,计算 cache A 的 hash 值:

Hash(“202.168.14.241”);

引入“虚拟节点”后,计算“虚拟节”点 cache A1 和 cache A2 的 hash 值:

Hash(“202.168.14.241#1”); // cache A1

Hash(“202.168.14.241#2”); // cache A2

5 小结

Consistent hashing 的基本原理就是这些,具体的分布性等理论分析应该是很复杂的,不过一般也用不到。

http://weblogs.java.net/blog/2007/11/27/consistent-hashing 上面有一个 java 版本的例子,可以参考。

http://blog.csdn.net/mayongzhan/archive/2009/06/25/4298834.aspx 转载了一个 PHP 版的实现代码。

http://www.codeproject.com/KB/recipes/lib-conhash.aspx C语言版本

一些参考资料地址:

http://portal.acm.org/citation.cfm?id=258660

http://en.wikipedia.org/wiki/Consistent_hashing

http://www.spiteful.com/2008/03/17/programmers-toolbox-part-3-consistent-hashing/

http://weblogs.java.net/blog/2007/11/27/consistent-hashing

http://tech.idv2.com/2008/07/24/memcached-004/

http://blog.csdn.net/mayongzhan/archive/2009/06/25/4298834.aspx

分类: 架构 标签:

一致性哈希算法的PHP实现

2010年3月16日 没有评论

       发现了段优秀的代码,读了一下,顺手写了一写注释,防止以后再看的时候忘记。
主要实现了一致性哈希的算法,php界相当优秀的代码。读完心旷神怡。

view plaincopy to clipboardprint?
<?php  
/**
* Flexihash – A simple consistent hashing implementation for PHP.
*  
* The MIT License
*  
* Copyright (c) 2008 Paul Annesley
*  
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*  
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*  
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*  
* @author Paul Annesley
* @link http://paul.annesley.cc/
* @copyright Paul Annesley, 2008
* @comment by MyZ (http://blog.csdn.net/mayongzhan)
*/

/**
* A simple consistent hashing implementation with pluggable hash algorithms.
*
* @author Paul Annesley
* @package Flexihash
* @licence http://www.opensource.org/licenses/mit-license.php
*/
class Flexihash  
{  

   /**
    * The number of positions to hash each target to.
    *
    * @var int
    * @comment 虚拟节点数,解决节点分布不均的问题
    */
   private $_replicas = 64;  

   /**
    * The hash algorithm, encapsulated in a Flexihash_Hasher implementation.
    * @var object Flexihash_Hasher
    * @comment 使用的hash方法 : md5,crc32
    */
   private $_hasher;  

   /**
    * Internal counter for current number of targets.
    * @var int
    * @comment 节点记数器
    */
   private $_targetCount = 0;  

   /**
    * Internal map of positions (hash outputs) to targets
    * @var array { position => target, … }
    * @comment 位置对应节点,用于lookup中根据位置确定要访问的节点
    */
   private $_positionToTarget = array();  

   /**
    * Internal map of targets to lists of positions that target is hashed to.
    * @var array { target => [ position, position, … ], … }
    * @comment 节点对应位置,用于删除节点
    */
   private $_targetToPositions = array();  

   /**
    * Whether the internal map of positions to targets is already sorted.
    * @var boolean
    * @comment 是否已排序
    */
   private $_positionToTargetSorted = false;  

   /**
    * Constructor
    * @param object $hasher Flexihash_Hasher
    * @param int $replicas Amount of positions to hash each target to.
    * @comment 构造函数,确定要使用的hash方法和需拟节点数,虚拟节点数越多,分布越均匀,但程序的分布式运算越慢
    */
   public function __construct(Flexihash_Hasher $hasher = null, $replicas = null)  
   {  
       $this->_hasher = $hasher ? $hasher : new Flexihash_Crc32Hasher();  
       if (!emptyempty($replicas)) $this->_replicas = $replicas;  
   }  

   /**
    * Add a target.
    * @param string $target
    * @chainable
    * @comment 添加节点,根据虚拟节点数,将节点分布到多个虚拟位置上
    */
   public function addTarget($target)  
   {  
       if (isset($this->_targetToPositions[$target]))  
       {  
           throw new Flexihash_Exception("Target ‘$target’ already exists.");  
       }  

       $this->_targetToPositions[$target] = array();  

       // hash the target into multiple positions  
       for ($i = 0; $i < $this->_replicas; $i++)  
       {  
           $position = $this->_hasher->hash($target . $i);  
           $this->_positionToTarget[$position] = $target; // lookup  
           $this->_targetToPositions[$target] []= $position; // target removal  
       }  

       $this->_positionToTargetSorted = false;  
       $this->_targetCount++;  

       return $this;  
   }  

   /**
    * Add a list of targets.
    * @param array $targets
    * @chainable
    */
   public function addTargets($targets)  
   {  
       foreach ($targets as $target)  
       {  
           $this->addTarget($target);  
       }  

       return $this;  
   }  

   /**
    * Remove a target.
    * @param string $target
    * @chainable
    */
   public function removeTarget($target)  
   {  
       if (!isset($this->_targetToPositions[$target]))  
       {  
           throw new Flexihash_Exception("Target ‘$target’ does not exist.");  
       }  

       foreach ($this->_targetToPositions[$target] as $position)  
       {  
           unset($this->_positionToTarget[$position]);  
       }  

       unset($this->_targetToPositions[$target]);  

       $this->_targetCount–;  

       return $this;  
   }  

   /**
    * A list of all potential targets
    * @return array
    */
   public function getAllTargets()  
   {  
       return array_keys($this->_targetToPositions);  
   }  

   /**
    * Looks up the target for the given resource.
    * @param string $resource
    * @return string
    */
   public function lookup($resource)  
   {  
       $targets = $this->lookupList($resource, 1);  
       if (emptyempty($targets)) throw new Flexihash_Exception(‘No targets exist’);  
       return $targets[0];  
   }  

   /**
    * Get a list of targets for the resource, in order of precedence.
    * Up to $requestedCount targets are returned, less if there are fewer in total.
    *
    * @param string $resource
    * @param int $requestedCount The length of the list to return
    * @return array List of targets
    * @comment 查找当前的资源对应的节点,
    *         节点为空则返回空,节点只有一个则返回该节点,
    *         对当前资源进行hash,对所有的位置进行排序,在有序的位置列上寻找当前资源的位置
    *         当全部没有找到的时候,将资源的位置确定为有序位置的第一个(形成一个环)
    *         返回所找到的节点
    */
   public function lookupList($resource, $requestedCount)  
   {  
       if (!$requestedCount)  
           throw new Flexihash_Exception(‘Invalid count requested’);  

       // handle no targets  
       if (emptyempty($this->_positionToTarget))  
           return array();  

       // optimize single target  
       if ($this->_targetCount == 1)  
           return array_unique(array_values($this->_positionToTarget));  

       // hash resource to a position  
       $resourcePosition = $this->_hasher->hash($resource);  

       $results = array();  
       $collect = false;  

       $this->_sortPositionTargets();  

       // search values above the resourcePosition  
       foreach ($this->_positionToTarget as $key => $value)  
       {  
           // start collecting targets after passing resource position  
           if (!$collect && $key > $resourcePosition)  
           {  
               $collect = true;  
           }  

           // only collect the first instance of any target  
           if ($collect && !in_array($value, $results))  
           {  
               $results []= $value;  
           }  

           // return when enough results, or list exhausted  
           if (count($results) == $requestedCount || count($results) == $this->_targetCount)  
           {  
               return $results;  
           }  
       }  

       // loop to start – search values below the resourcePosition  
       foreach ($this->_positionToTarget as $key => $value)  
       {  
           if (!in_array($value, $results))  
           {  
               $results []= $value;  
           }  

           // return when enough results, or list exhausted  
           if (count($results) == $requestedCount || count($results) == $this->_targetCount)  
           {  
               return $results;  
           }  
       }  

       // return results after iterating through both "parts"  
       return $results;  
   }  

   public function __toString()  
   {  
       return sprintf(  
           ‘%s{targets:[%s]}’,  
           get_class($this),  
           implode(‘,’, $this->getAllTargets())  
       );  
   }  

   // —————————————-  
   // private methods  

   /**
    * Sorts the internal mapping (positions to targets) by position
    */
   private function _sortPositionTargets()  
   {  
       // sort by key (position) if not already  
       if (!$this->_positionToTargetSorted)  
       {  
           ksort($this->_positionToTarget, SORT_REGULAR);  
           $this->_positionToTargetSorted = true;  
       }  
   }  

}  

/**
* Hashes given values into a sortable fixed size address space.
*
* @author Paul Annesley
* @package Flexihash
* @licence http://www.opensource.org/licenses/mit-license.php
*/
interface Flexihash_Hasher  
{  

   /**
    * Hashes the given string into a 32bit address space.
    *
    * Note that the output may be more than 32bits of raw data, for example
    * hexidecimal characters representing a 32bit value.
    *
    * The data must have 0xFFFFFFFF possible values, and be sortable by
    * PHP sort functions using SORT_REGULAR.
    *
    * @param string
    * @return mixed A sortable format with 0xFFFFFFFF possible values
    */
   public function hash($string);  

}  

/**
* Uses CRC32 to hash a value into a signed 32bit int address space.
* Under 32bit PHP this (safely) overflows into negatives ints.
*
* @author Paul Annesley
* @package Flexihash
* @licence http://www.opensource.org/licenses/mit-license.php
*/
class Flexihash_Crc32Hasher  
   implements Flexihash_Hasher  
{  

   /* (non-phpdoc)
    * @see Flexihash_Hasher::hash()
    */
   public function hash($string)  
   {  
       return crc32($string);  
   }  

}  

/**
* Uses CRC32 to hash a value into a 32bit binary string data address space.
*
* @author Paul Annesley
* @package Flexihash
* @licence http://www.opensource.org/licenses/mit-license.php
*/
class Flexihash_Md5Hasher  
   implements Flexihash_Hasher  
{  

   /* (non-phpdoc)
    * @see Flexihash_Hasher::hash()
    */
   public function hash($string)  
   {  
       return substr(md5($string), 0, 8); // 8 hexits = 32bit  

       // 4 bytes of binary md5 data could also be used, but  
       // performance seems to be the same.  
   }  

}  

/**
* An exception thrown by Flexihash.
*
* @author Paul Annesley
* @package Flexihash
* @licence http://www.opensource.org/licenses/mit-license.php
*/
class Flexihash_Exception extends Exception  
{  
}
<?php
/**
* Flexihash – A simple consistent hashing implementation for PHP.
*
* The MIT License
*
* Copyright (c) 2008 Paul Annesley
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author Paul Annesley
* @link http://paul.annesley.cc/
* @copyright Paul Annesley, 2008
* @comment by MyZ (http://blog.csdn.net/mayongzhan)
*/

/**
* A simple consistent hashing implementation with pluggable hash algorithms.
*
* @author Paul Annesley
* @package Flexihash
* @licence http://www.opensource.org/licenses/mit-license.php
*/
class Flexihash
{

/**
* The number of positions to hash each target to.
*
* @var int
* @comment 虚拟节点数,解决节点分布不均的问题
*/
private $_replicas = 64;

/**
* The hash algorithm, encapsulated in a Flexihash_Hasher implementation.
* @var object Flexihash_Hasher
* @comment 使用的hash方法 : md5,crc32
*/
private $_hasher;

/**
* Internal counter for current number of targets.
* @var int
* @comment 节点记数器
*/
private $_targetCount = 0;

/**
* Internal map of positions (hash outputs) to targets
* @var array { position => target, … }
* @comment 位置对应节点,用于lookup中根据位置确定要访问的节点
*/
private $_positionToTarget = array();

/**
* Internal map of targets to lists of positions that target is hashed to.
* @var array { target => [ position, position, … ], … }
* @comment 节点对应位置,用于删除节点
*/
private $_targetToPositions = array();

/**
* Whether the internal map of positions to targets is already sorted.
* @var boolean
* @comment 是否已排序
*/
private $_positionToTargetSorted = false;

/**
* Constructor
* @param object $hasher Flexihash_Hasher
* @param int $replicas Amount of positions to hash each target to.
* @comment 构造函数,确定要使用的hash方法和需拟节点数,虚拟节点数越多,分布越均匀,但程序的分布式运算越慢
*/
public function __construct(Flexihash_Hasher $hasher = null, $replicas = null)
{
   $this->_hasher = $hasher ? $hasher : new Flexihash_Crc32Hasher();
   if (!empty($replicas)) $this->_replicas = $replicas;
}

/**
* Add a target.
* @param string $target
* @chainable
* @comment 添加节点,根据虚拟节点数,将节点分布到多个虚拟位置上
*/
public function addTarget($target)
{
   if (isset($this->_targetToPositions[$target]))
   {
    throw new Flexihash_Exception("Target ‘$target’ already exists.");
   }

   $this->_targetToPositions[$target] = array();

   // hash the target into multiple positions
   for ($i = 0; $i < $this->_replicas; $i++)
   {
    $position = $this->_hasher->hash($target . $i);
    $this->_positionToTarget[$position] = $target; // lookup
    $this->_targetToPositions[$target] []= $position; // target removal
   }

   $this->_positionToTargetSorted = false;
   $this->_targetCount++;

   return $this;
}

/**
* Add a list of targets.
* @param array $targets
* @chainable
*/
public function addTargets($targets)
{
   foreach ($targets as $target)
   {
    $this->addTarget($target);
   }

   return $this;
}

/**
* Remove a target.
* @param string $target
* @chainable
*/
public function removeTarget($target)
{
   if (!isset($this->_targetToPositions[$target]))
   {
    throw new Flexihash_Exception("Target ‘$target’ does not exist.");
   }

   foreach ($this->_targetToPositions[$target] as $position)
   {
    unset($this->_positionToTarget[$position]);
   }

   unset($this->_targetToPositions[$target]);

   $this->_targetCount–;

   return $this;
}

/**
* A list of all potential targets
* @return array
*/
public function getAllTargets()
{
   return array_keys($this->_targetToPositions);
}

/**
* Looks up the target for the given resource.
* @param string $resource
* @return string
*/
public function lookup($resource)
{
   $targets = $this->lookupList($resource, 1);
   if (empty($targets)) throw new Flexihash_Exception(‘No targets exist’);
   return $targets[0];
}

/**
* Get a list of targets for the resource, in order of precedence.
* Up to $requestedCount targets are returned, less if there are fewer in total.
*
* @param string $resource
* @param int $requestedCount The length of the list to return
* @return array List of targets
* @comment 查找当前的资源对应的节点,
*         节点为空则返回空,节点只有一个则返回该节点,
*         对当前资源进行hash,对所有的位置进行排序,在有序的位置列上寻找当前资源的位置
*         当全部没有找到的时候,将资源的位置确定为有序位置的第一个(形成一个环)
*         返回所找到的节点
*/
public function lookupList($resource, $requestedCount)
{
   if (!$requestedCount)
    throw new Flexihash_Exception(‘Invalid count requested’);

   // handle no targets
   if (empty($this->_positionToTarget))
    return array();

   // optimize single target
   if ($this->_targetCount == 1)
    return array_unique(array_values($this->_positionToTarget));

   // hash resource to a position
   $resourcePosition = $this->_hasher->hash($resource);

   $results = array();
   $collect = false;

   $this->_sortPositionTargets();

   // search values above the resourcePosition
   foreach ($this->_positionToTarget as $key => $value)
   {
    // start collecting targets after passing resource position
    if (!$collect && $key > $resourcePosition)
    {
     $collect = true;
    }

    // only collect the first instance of any target
    if ($collect && !in_array($value, $results))
    {
     $results []= $value;
    }

    // return when enough results, or list exhausted
    if (count($results) == $requestedCount || count($results) == $this->_targetCount)
    {
     return $results;
    }
   }

   // loop to start – search values below the resourcePosition
   foreach ($this->_positionToTarget as $key => $value)
   {
    if (!in_array($value, $results))
    {
     $results []= $value;
    }

    // return when enough results, or list exhausted
    if (count($results) == $requestedCount || count($results) == $this->_targetCount)
    {
     return $results;
    }
   }

   // return results after iterating through both "parts"
   return $results;
}

public function __toString()
{
   return sprintf(
    ‘%s{targets:[%s]}’,
    get_class($this),
    implode(‘,’, $this->getAllTargets())
   );
}

// —————————————-
// private methods

/**
* Sorts the internal mapping (positions to targets) by position
*/
private function _sortPositionTargets()
{
   // sort by key (position) if not already
   if (!$this->_positionToTargetSorted)
   {
    ksort($this->_positionToTarget, SORT_REGULAR);
    $this->_positionToTargetSorted = true;
   }
}

}

/**
* Hashes given values into a sortable fixed size address space.
*
* @author Paul Annesley
* @package Flexihash
* @licence http://www.opensource.org/licenses/mit-license.php
*/
interface Flexihash_Hasher
{

/**
* Hashes the given string into a 32bit address space.
*
* Note that the output may be more than 32bits of raw data, for example
* hexidecimal characters representing a 32bit value.
*
* The data must have 0xFFFFFFFF possible values, and be sortable by
* PHP sort functions using SORT_REGULAR.
*
* @param string
* @return mixed A sortable format with 0xFFFFFFFF possible values
*/
public function hash($string);

}

/**
* Uses CRC32 to hash a value into a signed 32bit int address space.
* Under 32bit PHP this (safely) overflows into negatives ints.
*
* @author Paul Annesley
* @package Flexihash
* @licence http://www.opensource.org/licenses/mit-license.php
*/
class Flexihash_Crc32Hasher
implements Flexihash_Hasher
{

/* (non-phpdoc)
* @see Flexihash_Hasher::hash()
*/
public function hash($string)
{
   return crc32($string);
}

}

/**
* Uses CRC32 to hash a value into a 32bit binary string data address space.
*
* @author Paul Annesley
* @package Flexihash
* @licence http://www.opensource.org/licenses/mit-license.php
*/
class Flexihash_Md5Hasher
implements Flexihash_Hasher
{

/* (non-phpdoc)
* @see Flexihash_Hasher::hash()
*/
public function hash($string)
{
   return substr(md5($string), 0, 8); // 8 hexits = 32bit

   // 4 bytes of binary md5 data could also be used, but
   // performance seems to be the same.
}

}

/**
* An exception thrown by Flexihash.
*
* @author Paul Annesley
* @package Flexihash
* @licence http://www.opensource.org/licenses/mit-license.php
*/
class Flexihash_Exception extends Exception
{
}

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/mayongzhan/archive/2009/06/25/4298834.aspx

分类: PHP 标签:

生产环境下的服务器的Crontab写法

2010年3月15日 没有评论

      虽然关于 Crontab 的介绍到处都是,详细读了一遍这个词条,收获还是有的。Crontab 这个名字来自“chronos”,一个古希腊语, “时间”的意思(以下用法 在生产环境下的服务器非常有用,抚琴煮酒强烈推荐)
常见陷阱

每个SA、DBA 或者是普通的 Unix 用户,在第一次使用 Crontab 的时候都会遇到问题. 运行Crontab 的常见错误包括如下几种:1) 出于测试目的新创建了一条 Cron JOB, 时间间隔必须超过两分钟,否则 JOB将调度不到。如果必须忽略这两分钟的载入配置时间差,可以通过重新启动 Cron Daemon 做到。

2) 从 Crontab 中启动 X Window 程序需要注意的事项:所以要么在程序前初始化“DISPLAY=:0.0″, 要么在应用程序后面追加参数 Cdisplay :0.0

3) 命令中的 % 必须做转义处理: \%.我个人的意见是不要在命令行里带这个参数,干脆写到脚本里,然后调度该脚本即可。

其实我倒是认为使用 Crontab最常见的一个问题往往是因为环境变量不对。经常会看到论坛里有人问:为什么我的 Crontab 创建了不执行? 准备创建一条 Cron JOB的时候,很多人都喜欢在命令行下运行一遍,因为这个时候环境变量是随着 Shell 自动带进来,在 Crontab中则可能因为找不到正确的环境变量,JOB 就不能执行。这个小问题就像出天花,一次教训之后就都记得了。
必须使用的一则技巧

每条 JOB执行完毕之后,系统会自动将输出发送邮件给当前系统用户。日积月累,非常的多,甚至会撑爆整个系统。所以每条 JOB命令后面进行重定向处理是非常必要的: >>/dev/null 2>&1 。前提是对 Job中的命令需要正常输出已经作了一定的处理, 比如追加到某个特定日志文件。附: Crontab 的格式说明如下:

* 逗号(’,’) 指定列表值。如: “1,3,4,7,8″
* 中横线(’-‘) 指定范围值 如 “1-6″, 代表 “1,2,3,4,5,6″
* 星号 (’*’) 代表所有可能的值

Linux(开源系统似乎都可以)下还有个 “/” 可以用. 在 Minute 字段上,*/15 表示每 15分钟执行一次. 而这个特性在商业 Unix ,比如 AIX 上就没有.

## Use the hash sign to prefix a comment
# +—————- minute (0 – 59)
# | +————- hour (0 – 23)
# | | +———- day of month (1 – 31)
# | | | +——- month (1 – 12)
# | | | | +—- day of week (0 – 7) (Sunday=0 or 7)
# | | | | |
# * * * * * command to be executed
推 荐记忆为分 时 日 月 星期


cron来源于希腊单词chronos(意为“时间”),是linux系统下一个自动执行指定任务的程序。例如,你想在每晚睡觉期间创建某些文件或文件夹 的备份,就可以用cron来自动执行。

服务的启动和停止
cron服务是linux的内置服务,但它不会开机自动启动。可以用以下命令启动和停止服务:

/sbin/service crond start
/sbin/service crond stop
/sbin/service crond restart
/sbin/service crond reload
以上1-4行分别为启动、停止、重启服务和重新加载配置。

要把cron设为在开机的时候自动启动,在 /etc/rc.d/rc.local 脚本中加入/sbin/service crond start 即可。

查看、编辑和删除
cron把命令行保存在crontab(cron table)文件里,这个文件通常在 /etc目录下。每个系统用户都可以有自己的crontab(在 /var/spool/cron/ 下)。要查看当前用户的crontab,输入crontab -l;要编辑crontab,输入 crontab -e;要删除crontab,输入 crontab-r。如当前是root身份,要查看/编辑/删除/某用户的crontab,只需在相应的命令后加上 -u USERNAME(如 crontab -e-u USERNAME)即可。crontab文件的默认编辑器是vi,可以输入 export VISUAL=’editor’ 更改默认编辑器。

cron服务每分钟不仅要读一次 /var/spool/cron 目录内的所有文件,还需要读一次/etc/crontab 文件。配置这个文件也能让cron执行任务。使用crontab命令是对用户级任务的配置,而编辑 /etc/crontab文件是对系统级任务的配置。

语法说明
以下是两个cron语句的例子(在 /etc/crontab 文件里)。前者用来晚间备份 /etc目录,后者运行Analog程序处理服务器的统计信息。

12 3 * * * root tar czf/usr/local/backups/daily/etc.tar.gz /etc >> /dev/null 2>&1
52 5 * * * root /usr/local/src/analog-5.32-lh/analog >> /dev/null2>&1
以下是cron语句中的字段与字段说明:

字段 说明
1 分钟(0-59)(有没有办法精确到秒呢?*/60?,知道的告诉我一声呵~~)
2 小时(2-24)
3 日期(1-31)
4 月份(1-12;或英文缩写Jan、Feb等)
5 周几(0-6,0为周日;或单词缩写Sun、Mon等)
6 用户名(执行命令时以此用户的身份)
7 要执行的命令(路径)

现在来看第一行:

12 3 * * * root tar czf/usr/local/backups/daily/etc.tar.gz /etc >> /dev/null 2>&1
这条语句将在每天的凌晨3点12分(03:12)运行 tar czf /usr/local/backups/daily/etc.tar.gz/etc 命令。>> /dev/null 2>&1 表示把所有标准输出发送到/dev/null(linux的回收站),把标准错误输出(2)发送到和标准输出(1)同样的地方(即/dev/null)。运行这行命令将不会产生任何输出。

这条语句可以变得稍微复杂一点:

30 15 13 6 1 * root tar czf/usr/local/backups/daily/etc.tar.gz /etc >> /dev/null 2>&1
它将在6月13日周一的15:30运行 tar czf /usr/local/backups/daily/etc.tar.gz /etc 命令。

以下语句可以达到同样的效果:

30 15 13 Jun Mon * root tar czf/usr/local/backups/daily/etc.tar.gz /etc >> /dev/null 2>&1
如果你想以用户joey的身份每小时的第15分钟运行某个程序,可以使用:

15 * * * * joey /usr/bin/somecommand >>/dev/null 2>&1
其中的星号(*)是通配符,表示cron将忽略这个字段。

如果你想每两小时就运行某个程序,可以在小时字段里使用*/2。它将会在2点,4点,6点……22点,24点运行。具体语句如下:

0 */2 * * * joey /usr/bin/somecommand >>/dev/null 2>&1
cron语句中还可以使用逗号(,)来指定多个时间。例如你想在每小时的15分和30分运行某个程序,可以在分钟字段使用 15,30:

15,30 * * * * joey /usr/bin/somecommand >>/dev/null 2>&1
如果你想在每月的第一周(即1号到7号)每天的指定时间运行某个程序,可以在日期字段使用 1-7:

15,30 */2 1-7 * * joey /usr/bin/somecommand >>/dev/null 2>&1
这条语句将在每月的第1-7日每两小时的15分和30分(02:15,02:30……22: 15,22:30等)运行/usr/bin/somecommand 命令。

如果你想在每天的16:18执行一个脚本集合,可以把所有要执行的脚本放到一个目录中(如/home/username/cron),可以使用:

18 16 * * * root run-parts /home/username/cron>> /dev/null 2>&1
如果你想保存某个程序的输出结果, 可以把 >> /dev/null 2>&1 替换为 >>/home/user/somecommand.log 2>&1 。

总结
查看当前用户的cron配置,使用 crontab -l
编辑当前用户的cron配置,使用 crontab -e
删除当前用户的cron配置,使用 crontab -r
以root身份查看/编辑/删除某用户的cron配置,在命令后加上 -u USERNAME
配置系统级的任务,编辑 /etc/crontab 文件

大多数cron的实现都支持通过 MAIL 环境变量定义邮件接收地址,如果不想要邮件,在cron job file开始 MAIL=”” 就可以了,不过这是个很糟的习惯。(MAILTO或MAIL)


原文:http://hi.baidu.com/yuhongchun027/blog/item/d81bb4a73711f09dd143580f.html

分类: Linux 标签: