首页

通过admin

Apache配置域名和多站点配置指引

配置Apache服务器并且设置DNS

我们通常所说的虚拟主机技术就是将一台(或者一组)服务器的资源(系统资源、网络带宽、存储空间等)按照一定的比例分割成若干台相对独立的“小主机”的技术。每一台这样的“小主机”在功能上都可以实现WWW、FTP、Mail等基本的Internet服务,就像使用独立的主机一样。phpma.com

 

目前网站服务器的虚拟主机平台使用以开放的Apache为最多,其次是微软的Windows IIS。Apache具有跨平台(FreeBSD/Linux/Windows/Solaris/Other UNIX)、易于维护与最佳安全性等优点。

Apache是率先支持基于IP虚拟主机的服务器之一。 Apache 1.1及其更新版本同时支持基于IP和基于主机名的虚拟主机,不同的虚拟主机有时会被称为基于主机(host-based) 或非IP虚拟主机(non-IP virtual hosts)。phpma.com

用Apache设置虚拟主机服务通常可以采用两种方案:基于IP地址的虚拟主机和基于主机名字的虚拟主机,下面我们分别介绍一下它们的实现方法以及优缺点。以便大家在具体的应用中能够选择最合适的实现方法。phpma.com

一、Apache实现基于IP地址的虚拟主机(每个站点拥有一个独立IP地址)

使用这种虚拟主机方式,首先要在服务器上为每个虚拟主机单独设置一个IP地址。这些IP地址可以通过增加多个网卡或者在一个网卡上设立多个IP地址来完成。有了多个IP地址后,可以采用以下两种方式之一来设置Apache。 phpma.com

1、为每个虚拟主机运行一份Apache

采用这种方式,每一份Apache程序可以以单独的用户运行,因此各个虚拟主机之间互不影响。设置这种虚拟主机时,只要为每一份Apache设置一套配置文件就可以了,唯一需要注意的是:必须使用“Listen”语句,强制每一份Apache 仅仅在属于“自己”的IP地址上接收服务请求。

优点:各个虚拟主机之间互不干扰,安全性高。

缺点:占用系统资源较多。

2、多个虚拟主机共享同一份Apache

采用这种方式,各个虚拟主机共享同一份Apache,因此各个虚拟主机之间有一定的影响,尤其是执行CGI程序时,可能会带来一些严重的安全问题。设置这种虚拟主机时,只要为每一个虚拟主机设置类似如下的信息即可:

<VirtualHost www.ghq1.com>

DocumentRoot /www/ghq1

</VirrualHost>

优点:占用系统资源比上一种方式少。

缺点:安全性低,每个虚拟主机仍然需要占用一个IP地址。

例如服务器一个网卡上绑定有两个IP地址(172.16.3.40和 172.16.3.50)分别对应域名 www.ghq1.com和www.ghq2.org的服务,配置如下

服务器配置(apache的配置文件httpd.conf)

Listen 80

<VirtualHost 172.16.3.40>

DocumentRoot /www/ghq1

ServerName www.ghq1.com

</VirtualHost>

<VirtualHost 172.16.3.50>

DocumentRoot /www/ghq2

ServerName www.ghq2.org

</VirtualHost>

配置简单说明:“Listen”默认httpd服务会监控第80号通信端口, “Listen”选项让用户自行指定apache 服务器监控的IP地址或通信端口。

“DocumentRoot”:指定apache 服务器存放网页的根目录;“ServerName”:允许用户自行设置主机名,这个名称将被送到远程连接程序,以取代安装apache 服务器主机的真实名称。<VirtualHost IP >和</VirtualHost>构成虚拟主机的语法结构,其中的IP就是我们在服务器上绑定的不同的IP地址,也可以是IP地址加上通信端口号(见下面的例子)。

如果服务器有两个IP地址(172.16.3.40和 172.16.3.50)分别对应域名 www.ghq1.com和www.ghq2.org。对每个域名,我们都希望在80端口和8080端口发布我们的网站。可以这样配置

服务器配置(apache的配置文件httpd.conf)

Listen 172.16.3.40:80

Listen 172.16.3.40:8080

Listen 172.16.3.50:80

Listen 172.16.3.50:8080

<VirtualHost 172.16.3.40:80>

DocumentRoot /www/ghq1-80

ServerName www.ghq1.com

</VirtualHost>

<VirtualHost 172.16.3.40:8080>

DocumentRoot /www/ghq1-8080

ServerName www.ghq1.com

</VirtualHost>

<VirtualHost 172.16.3.50:80>

DocumentRoot /www/ghq2-80

ServerName www.ghq1.orgphpma.com

</VirtualHost>

<VirtualHost 172.16.3.50:8080>

DocumentRoot /www/ghq2-8080

ServerName www.ghq2.org

</VirtualHost>

因此,建立虚拟主机,我们要做好不同的IP对应的域名解析工作,建立相应的目录(如/www/ghq1),将相应的主页内容存放在相应的目录中即可。

二、Apache实现基于主机名的虚拟主机服务(一个IP地址实现多个网站)

基于主机名字的虚拟主机服务,是目前虚拟主机比较常用的一种方案。因为它不需要更多的IP地址,无须什么特殊的软硬件支持。而且现在的浏览器大都支持这种虚拟主机的实现方法。基于域名的的虚拟主机是根据客户端提交的HTTP头中的关于主机名的部分决定的。使用这种技术,很多虚拟主机可以享用同一个IP地址。

基于域名的虚拟主机相对比较简单,因为我们只需要配置DNS服务器将每个主机名映射(CNAMES)到正确的IP地址,然后配置Apache HTTP服务器,令其辨识不同的主机名就可以了。基于域名的服务器也可以缓解IP地址(IPV4)不足的问题。这种方式下,各个虚拟主机共享同一份Apache,因此有CGI程序运行时,安全性也不高。

优点:只要一个IP地址就可以提供大量的虚拟主机服务。

缺点:安全性差。维护这些虚拟主机时需要更改配置文件,并且需要重新启动Apache进程才能起作用。因此不适合进行大规模的虚拟主机服务。

如果服务器只有一个IP地址,而在DNS中有很多映射到这个机器。我们想要在这个机器上运行www.ghq1.com和 www.ghq2.org两个站点。在Apache服务器的配置中创建一个虚拟主机并不会自动在DNS中对主机名做相应更新。我们必须自己在DNS中添加域名来指向我们的IP地址。否则别人是无法看到我们的web 站点。

服务器配置(apache的配置文件httpd.conf)

# Ensure that Apache listens on port 80phpma.com

Listen 80

# Listen for virtual host requests on all IP addresses

NameVirtualHost *

<VirtualHost *>

DocumentRoot /www/ghq1

ServerName www.ghq1.com

# Other directives here

</VirtualHost>

<VirtualHost *>

DocumentRoot /www/ghq2

ServerName www.ghq2.org

# Other directives here

</VirtualHost>

因为*(星号)匹配所有的地址,所以主服务器不接收任何请求。因为 www.ghq1.com首先出现在配置文件中,所以它拥有最高优先级,可以认为是默认或首要服务器。这意味着如果一个接受的请求不能与某个ServerName指令相匹配, 它将会由第一个VirtualHost所伺服。

当我们的IP地址无法确定的时候,使用*是很方便的–比如说, ISP给我们配置的是动态IP地址(如ADSL拨号上网),而我们有使用了某种动态域名解析系统时。因为*匹配任何IP 地址,所以在这样的情况下,不论IP地址如何变化,我们都不需要另外进行配置。上述配置就是我们在绝大多数情况下使用基于域名的虚拟主机时将要用到的。

本文档的涵义一言以蔽之就是:不要让Apache在解析配置文件的时候用到DNS。 如果Apache在解析配置文件时用到了DNS,您的服务器就会发生可靠性的问题(也可能根本无法启动), 或者遭致拒绝(偷窃)服务攻击(包括用户可以从其他用户那里偷窃点击)。

一个简单示例

拒绝服务

“main server”地址

避免这些问题的小技巧

附录:进一步的提示

一个简单示例

<VirtualHost www.abc.dom>

ServerAdmin webgirl@abc.dom 

DocumentRoot /www/abc 

</VirtualHost> webgirl@abc.dom 

DocumentRoot /www/abc 

</VirtualHost> webgirl@abc.dom 

DocumentRoot /www/abc 

</VirtualHost> webgirl@abc.dom 

  DocumentRoot /www/abc 

</VirtualHost> webguy@def.dom 

  DocumentRoot /www/def 

</VirtualHost> webmaster@host.foo.com

DocumentRoot /www/docs/host.foo.com

ServerName host.foo.com

ErrorLog logs/host.foo.com-error_log

TransferLog logs/host.foo.com-access_log

</VirtualHost> webmaster@host.foo.com

DocumentRoot /www/docs/host.foo.com

ServerName host.foo.com

ErrorLog logs/host.foo.com-error_log

TransferLog logs/host.foo.com-access_log

</VirtualHost>

为了让Apache功能正常,一个虚拟主机绝对需要以下两部分的信息: ServerName和与服务器对应的至少一个IP地址。 这个示例没有包括IP地址,于是Apache必须用DNS来查询www.abc.dom的地址。 如果在某些不可预料的情况下,当您的服务器解析配置文件时没有得到DNS的支持, 那么这个虚拟主机 将不会被配置。 它将不会对任何请求作出反应。(在Apache的1.2版本之前,服务器甚至无法启动)。

假设www.abc.dom的IP地址是10.0.0.1。那么看看以下这个配置片断

<VirtualHost 10.0.0.1>

ServerAdmin

现在Apache需要DNS对这个虚拟主机进行反向域名解析来确定ServerName。 如果反向解析失败,那么这将导致这个虚拟主机部分功能丧失。 (在Apache的1.2版本之前,服务器将不能启动)。如果虚拟主机是基于域名的, 它将完全不能使用,但如果它是基于IP的,那么它将很有可能工作。 然而,如果Apache不得不为一个已经包含了服务器域名的服务器产生一个完整的URL, 那么它将可能产生一个无效的URL。

以下是一个可以避免上述两个问题的配置片断.

<VirtualHost 10.0.0.1>

ServerName www.abc.dom

ServerAdmin

拒绝服务

拒绝服务主要由(至少)两种形式导致。 如果您在运行Apache 1.2以前的版本,在上述两种情况下,如果您的任何一个虚拟主机的DNS解析失败, 您都会无法启动服务。在一些情况下,DNS解析甚至不在您的控制范围之内。 比如说,如果abc.dom是您的一个客户,而且他们自己控制着DNS。 那么仅仅是因为他们删除了www.abc.dom这个记录, 都会导致您的服务器(1.2之前的版本)无法启动。

另外一种形式就更隐蔽了。比如说下面这个配置片断:

<VirtualHost www.abc.dom>

ServerAdmin

<VirtualHost www.def.dom>

ServerAdmin

假设您已经为www.abc.dom设定了10.0.0.1, 而为www.def.dom设定了10.0.0.2。 更进一步,假设def.com自己控制DNS。在这种配置下, 您已经把def.com放到了一个可以将所有指向abc.com 的所有流量据为己有的情况之下。为了达到这样的目的, 他们只需要把www.def.dom的地址解析设置成10.0.0.1就可以了。 因为他们控制着自己的DNS服务, 所以您无法阻止他们把www.def.com这个记录指向任何一个IP地址

然后,所有向10.0.0.1发出的请求 (包括用户所有类似http://www.abc.dom/任何字符的URL) 都将会为def.com这个虚拟主机所接收。 为了更好的理解着一切是怎样发生的, 您需要一个关于Apache是怎样将进入的请求分配给它的虚拟主机的深入说明。 您可以在这里发现一个完整的文档。

phpma.com

“main server”地址

在Apache 1.1中,基于域名的虚拟主机支持 需要Apache知道运行着httpd的主机的IP地址。 一般来说可以用全局变量ServerName(如果存在) 或者调用C的方法gethostname(与在命令行模式下键入hostname得到的返回值一样)。 接着它就会利用DNS来查找这个地址。目前还没有办法避免这样的查找。

如果您担心这样的查找会因为您的DNS服务器没有启动而遭到失败的结果, 您就可以在/etc/hosts中插入一条记录来确定主机名 (此文件中应该已经存在这条记录了,否则您的机器无法正常启动)。 然后,您要确认您的机器已经配置为当DNS解析失败的情况下, 它将会使用/etc/hosts根据所使用的操作系统不同, 您可能需要在/etc/resolv.conf或/etc/nsswitch.conf 两个文件中选择一个进行编辑。

如果您的服务器不必因为其他理由而使用DNS, 您也许不必在把HOSTRESORDER环境变量设置为”local”的情况下运行Apache。 这都取决于您所使用的操作系统和解析库。 如果您没有使用mod_env来控制环境变量,它还将影响到CGI。 强烈建议您参考一下您所使用的操作系统附带的man帮助或FAQ。

避免这些问题的小技巧

在VirtualHost中使用IP地址

在Listen中使用IP地址

确保所有的虚拟主机拥有显式的ServerName定义。

创建一个不包含任何服务页面的<VirtualHost_default_:*>服务器

附录:进一步的提示

涉及到DNS的情况都很让人不舒服。 在Apache 1.2 中,我们努力想让服务器在DNS解析失败的情况下至少保持能够启动, 但可能我们还是没能做到最好。在当今重编号成了必须的Internet上面, 在配置文件中显式的写明IP地址已经成为不合时宜的行为了。

上述盗窃攻击的解决办法是, 在一个正向的DNS查询结果后部署一个逆向DNS解析并将两个域名进行比较。 如果不同,就禁用相应的虚拟主机。 这个方法需要一个正确配置了的逆向域名解析服务器 (因为FTP服务器和TCP封装进行的“双重逆向”DNS处理的普遍应用,这已为大部分管理员所熟知了)。

在某些情况下,如果没有使用IP地址而DNS解析又失败了, 那么正常启动一个基于域名的虚拟主机看来是不可能的。 一些诸如禁用部分配置文件这样的权宜之计会带来比根本不能启动更遭的不可预测的结果。

随着HTTP/1.1的部署以及浏览器和代理服务器开始支持Host头, 我们完全避免使用基于IP的虚拟主机也逐渐成为可能。 这种状况下,web服务器也不必在配置时进行DNS的查询。 但在1997年3月,这些特性的采用还没有广泛到可以在重要的web服务器应用的地步。

http://www.uplinux.com/download/doc/apache/ApacheManual/dns-caveats.html#example

http://www.uplinux.com/download/doc/apache/ApacheManual/mod/core.html#virtualhost

<VirtualHost>和</VirtualHost>用于封装一组仅施用于特定虚拟主机的指令。任何在虚拟主机配置中可以使用的指令也同样可以在这里使用。当服务器接受了一个特定虚拟主机的文档请求时,它会使用封装在<VirtualHost>配置段中的指令。地址可以是

虚拟主机的IP地址;

虚拟主机IP地址对应的完整域名;

字符*,仅与NameVirtualHost *配合使用以匹配所有 的IP地址;或是

字符串_default_,与基于IP的虚拟主机联用以捕获所 有没有匹配的IP地址。

示例

<VirtualHost 10.1.2.3>

ServerAdmin

 

IPv6的地址必须放入方括号中指定,否则作为可选项的端口号将无法确定。一个IPv6的示例如下:

 

<VirtualHost [fe80::a00:20ff:fea7:ccea]>

ServerAdmin

每个虚拟主机必须对应不同的IP地址、端口号或是不同的主机名。在第一种情况下,服务器所在物理机器必须配置为可以为多个地址接受IP包。(在机器没有多个网络硬件界面的情况下,如果您的操作系统支持,您可以使用ifconfig alias命令来达到这个目的。)。

当使用基于IP的虚拟主机时,特殊的名称_default_可以在没有匹配上其它列出的虚拟主机的情况下作为匹配任何IP地址的虚拟主机。在没有进行_default_虚拟主机的设定时,在没有IP与请求匹配的情况下,将使用“主服务器”(包括所有在虚拟主机配置段之外的配置)的配置。(但请注意:任何匹配NameVirtualHost指令的IP地址既不会使用”main”服务器配置,也不会使用_default_虚拟主机的配置。 参阅基于域名的虚拟主机文档获得更多详情。)

您可以指定一个:端口来改变匹配的端口。如果没有指定,它将沿用主服务器中离它最近的那个Listen语句指定的值。您也可以指定:*来匹配那个地址上的所有端口。(当您使用_default_时,这是推荐采用的方法。)

安全提示:参阅安全提示文档获得为什么当您存储日志文件的目录对于启动服务器以外的用户来说是可写的会危及服务器安全的详细资料。

注意:<VirtualHost>的使用不会影响到Apache侦听的地址。您也许需要使用Listen来确保Apache侦听着正确的地址。

为调试程序,本机安装iis与apache,无法同时使用80端口,现给出解决方法:

方法一:

IIS5,多IP下共存,IIS为192.168.0.1,apache为192.168.0.2 原文地址

c:/Inetpub/Adminscripts

cscript adsutil.vbs set w3svc/disablesocketpooling true

该命令反馈如下disablesocketpooling : (BOOLEAN) True

重启IIS

Inetpub/AdminScripts>cscript adsutil.vbs set w3svc/disablesocketpooling true

由于 DisableSocketPooling 在 IIS 6.0 元数据库架构 (MBSchema.xml) 中被定义为有效属性,所以,您仍然可以使用 Adsutil.vbs 设置该属性,但这种设置不起作用。IIS 6.0 中的功能是新增的核心级别驱动程序 HTTP.sys 的一部分。要配置 HTTP.sys,您必须使用 Httpcfg.exe

方法二:

IIS6,多IP下共存,IIS为192.168.0.1,apache为192.168.0.2 原文地址

到2003的CD下的 support/tools/Support.cab。解压出httpcfg.exe文件,COPY到windows/system32/目录下,用法自己看帮助

命令行

绑定到某IP: httpcfg set iplisten -i 192.168.0.1

即命令使用IIS的只监听指定的IP及端口

查看绑定: httpcfg query iplisten

删除绑定: httpcfg delete iplisten -i 192.168.0.1

命令行

net stop Apache2

net stop iisadmin /y

net START Apache2

net START w3svc

保证iis下的ip设置为全局默认,Apache中httpconf设置listen 192.168.0.2:80,就应该可以两个服务同时运行,相互不冲突了。

IIS的访问地址为http://192.168.0.1,Apache访问地址为http://192.168.0.2

方法三:

网上常用的单IP共用80端口方法,不过不推荐,只是使用Apache的代理,速度有影响将apache设为使用80端口,IIS使用其它端口,比如81,然后将apache作为IIS的代理。

在httpd.conf里面,取消下面四行的注释:

LoadModule proxy_module modules/mod_proxy.so

LoadModule proxy_connect_module modules/mod_proxy_connect.so

LoadModule proxy_http_module modules/mod_proxy_http.so

LoadModule proxy_ftp_module modules/mod_proxy_ftp.so

然后建立一个虚拟主机,将该域名的所有访问转向81端口。

ServerName iloves.vicp.net

ProxyPass / http://localhost:81/

ProxyPassReverse / http://localhost:81/

这样,对外就可以只需要一个端口,即可同时使用apache和IIS的功能了

类推,使用第二种方法,你可以在IIS上配置PHP4,Apache2中配置PHP5,只需要IIS中安装PHP4,把php.ini复制到/windows目录即可,这个就不用说了吧,Apache2中,只要把PHP5的php.ini放在PHP5安装目录里面就行了

配置Apache以支持PHP5:

LoadModule php5_module “D:/PHPServer/PHP5/php5apache2.dll”

AddType application/x-httpd-php .php

DirectoryIndex index.html index.php

PHPIniDir “D:/PHPServer/PHP5”

其中最重要的一条就是 PHPIniDir,用来指明php.ini文件所在位置,即PHP5的安装目录,注意所有目录的应该改为D:/PHPServer/PHP5这种格式,而非D:/PHPServer/PHP5,IIS的访问地址为http://192.168.0.1,Apache访问地址为http://192.168.0.2

通过admin

删除域名抢注实战

现在珍贵的域名价格越来越贵,但是从另外一个方面,每天被删除的珍稀域名也不计其数。如何得到这些删除的珍稀域名,不管投资或者自己持有,需要结合两方面。第一,找到这些珍贵域名,第二,抢注这些珍贵的删除域名。

第一步,我们要找到自己想要抢注的删除域名。首先,我们要得到删除域名的列表,从这些删除域名的列表里面,我们筛选出自己认为有价值的删除域名。比较常 见的删除域名筛选查询工具可以直接从百度上搜到,笔者个人比较常用的是歪菜删除域名查询系统,里面的功能不错,覆盖了大部分笔者想要的功能。当然还有其他 的,大家直接搜过期域名抢注就能找到一堆工具了,可以选一个自己喜欢的。

笔者个人每天都查看的删除域名报表有双拼COM,三拼COM, 一些英文字母的组合,比如china,wan,youxi,you,crazy,mini之类的,具体例子就不多说了,不能把家底曝光……嘿嘿,不过 cvcv这样的,或者aabb这样的域名也是能筛选出来的,看个人喜好了,笔者比较喜欢直接有意义的域名。附一张截图。

删除域名抢注实战

最后,选定好自己喜欢的域名之后,确认这个域名的状态是不是还在删除列表之中。如果whois查询结果为pending delete,那就表明这个域名会按照显示的时间山除掉。因为删除域名筛选工具都是通过一些权威网站得到的删除域名列表,但是由于这些删除域名列表也不一 定准确,所以一定要确认是不是还在删除状态,否则一旦对方已经续费,我们就白费功夫了。另外一点,可以查查这个域名的archive历史,看看有没有被做 过非法网站,以确保自己投资安全。

第二步,选定好可以抢注的域名后,我们接下来就要用合适的抢注工具来抢注域名了。笔者个人比较经常使 用的抢注工具有pool.com,snapnames.com,godaddy.com.其实还有一家enom.com笔者一直没去用过,由于以前听过一 些大佬说enom.com的抢注方式和其他地方不一样,所以一直没去试过。抢注工具的原理都一样,这些公司会注册很多家子公司,然后申请很多的注册管道, 通过这些管道同时来在删除的下一刻进行抢注。所以公司越大,管道越多,管道越多,抢注成功率越高。下面笔者分别对这些工具作一些介绍。

pool.com是资格比较老的抢注工具,非常有实力。不过可惜的是,笔者从来没有在pool.com上面抢注成功过,可能是笔者人品太差把。pool抢 注的底价是69美元,不抢注成功不对信用卡扣款,所以,只有你抢注成功了,才有可能被扣款,最少69美元。一旦抢注成功,仅有你一人抢注这个域名的话,就 直接成功了,扣款69美元。但是如果有多人抢注,则进入3天的秘密竞价,所有抢注的人才有资格参与竞价。

snapnames.com是 笔者用得最多的一个抢注工具,极其有实力,本人所有的域名均保存在moniker.com,moniker.com和snapnames.com是一家公 司。前两个月bibi.com被删除就是被snapnames.com抢走,最后成交价20000多美元。snapnames的价格为59美元的底价,成 功后直接扣款或者进入为期三天的秘密竞价。

enom.com笔者个人不是很清楚,在这里不好发言了。但是enom.com和pool.com和snapnames.com合称为三巨头,大家有时间可以试试,比较比较。上次笔者有个在snapnames预定的域名被enom抢走了。

godaddy.com的价格是最便宜的,只要19.99美元一个抢注机会,一次可以购买很多个抢注机会。与其他抢注不同的是,godaddy是预付款 机制。买了抢注机会后才能抢注,但是如果抢注失败,还可以继续抢注,直到抢注成功为止以后,那个抢注机会才被扣除。所以笔者个人一般一次性买4个抢注机 会,这样就能用75美元25%折扣的优惠码,还能再便宜一点。多人抢注也是竞价机制。

经过笔者个人长期实践,抢注成功率是这样的 snapnames > enom > pool >goadddy.所以一旦笔者碰到可以抢也可以不抢,也就是那个域名价值不高的情况下,笔者会用godaddy,意思就是,抢到了就抢到了,没抢 到就算了。如果碰上特别好的域名,笔者会在godaddy,snapnames,pool三个地方都预订一份,一旦哪个地方抢注成功,以确保自己有竞价的 权力。虽然有些域名最后成交价相当高,但是看看竞价能了解一下当前域名市场行情也是好的。

国内域名抢注机构没有用过,因为本人所有域名均保存在国外,与其国内抢注再转移,还不如直接在国外抢注。不过大家可以去试试,只不过笔者不知道成功率有多高。

下面附上snapnames的抢注详解,其他抢注工具都类似。

第一步:注册  – 下图是第四步,必须要填信用卡信息。不过如果没有抢注成功,snapnames是不会给你扣款的。

删除域名抢注实战

第二步:提交订单  – 最低起价是59美元。

删除域名抢注实战

点ADD TO CART,然后准备下订单PLACE ORDER

删除域名抢注实战

第三步:等待结果  – PLACE ORDER之后可以在My Activity中找到这个订单,然后就是等待系统抢注了。一般com,net,org都是在晚上凌晨2点到3点左右删除。大家完全没有必要熬夜,直接睡 觉,第二天察看邮件,如果成功了,竞价去吧。如果失败了,也会给你发一封邮件通知。不过通过whois你一般不能查到是哪个公司注册,因为都是通过管道来 注册。

第四步:开始竞价  – 如果你是唯一的参与者,就直接胜利了,进入第五步。如果有很多竞价者,这就到了各方自由竞价的时间了,谁出价高,谁拿到域名,限时3天。具体操作如下图所示。

删除域名抢注实战

第五步:竞价成功 – 竞价成功后系统会自动给你开设一个账号,账号的名称和密码都是随机的,会直接发送到你的邮箱里面。你可以push进自己的主账号,也可以在60天后申请转移密码来转移到别的注册商。如下图,绿色表明你是最高出价者,就成功了。

删除域名抢注实战

到这里抢注的过程就算完了。希望大家都能找自己想要的域名。

通过admin

软文+外链,这是效果更好的站外优化方法

可能很多站长都用过乱发 软件,但是这些高手们往往是最先使用这些软件的一批人,所以效果也是很好的,当软件普及到大多数站长都在用的时候,也就往往没效了。所以在2010年的今天你还群发,你又落伍啦。还不如写几入篇软文效果好。

 

 

2、每天给网站添加一些原创内容

 

 

内容要增加多少呢?一般根据你站点内容的多少,大概文章总量的2%-5%吧,比如你用的Z-BLOG,一共有百把篇文章,那你每天加个2,3篇就可以了,如果是用CMS做的站,内容有千把篇,那每天加个 20,30篇。这个是必须的,就算你外链很强大,一段时间不更新原创的,GOOGLE一样懒得理你。每天就更新6篇文章,这样很有规律的加,蜘蛛会很青睐你的好习惯的。

 

 

3、每天给网站发布外链

 

 

有规律有步骤的加链接,那是做站的好品质,外链作用大,有规律,一步步来。一般的关键词轻松排上去了。外链,这是属于SEO站外优化的内容,网站,如果没有外部链接的导入,那么,蜘蛛程序是很难发现你的网站,有了导入链接,蜘蛛就会来我们网站。外链起到引GOOGLE蜘蛛爬行网站的作用,一个好的网站是需要大量优质外链的支持!“SEO内容为王,外链为皇”天天给网站增加优质外链(外链要纯手工发布才能把握好质量),相信网站会有不错的排名与流量。

 

 

软文+外链,这是效果更好的站外优化方法,一篇优质的软文,访问量是相当大的,回复率也高。高质量的软文就像一个外链银行,把外链稳定在优质的软文上,相信网站会有更好的排名,与流量。

 

 

4、每周写2篇软文

 

 

写软文并不难,难的是坚持写,写高质量的文章,但是如今软文是最低成本的推广方式之一,效果很明显,做为一个合格的站长,是必须学会写软文的,不熟悉的朋友可以到网上找找资料看看,另外,写软文,一定要加上你的版权声明,现在总有一些品质低下的人,转载了,连个链接都不给,有的甚至把你文章的网址改成他自己的,坚决鄙视这种人,人品不好的人做站也不会好。

 

 

5、养几个高权重博客

 

 

养博客,在新站上线的时候,随便带一下,就很容易让搜索引擎收录,有时候加的新栏目或者文章迟迟不收录的时候,也可以用这些高权重博客带一下。

 

 

6、到相关高权重论坛跟跟贴

 

 

跟贴不仅是一种美德,而且掌握了一些跟贴技巧,对网站也是很有益的,你站长类的网站,几乎发的文章一会儿就收录了,在后面多跟跟贴很容易成为反链,用好签名也能让蜘蛛天天去爬你的网站,就经常到一些高PR的会计论坛跟帖,结果得到外链。另外在与你主题相关的活跃论坛上跟跟贴,不仅仅带外链,还能为你的网站带来用户,一举两得,何乐而不为呢?

 

 

7、没事别经常大改版

 

 

生意不好整柜台,流量不高整网站,其实网站也像人一样,一个人经常改头换面,也得让你周围的人重新认识你,这需要一个过程,网站规划好了,版块定了,就不要轻易改,不得已做网站改版也尽量保持原来的路径

通过admin

WP新站之安装后必做的10件事

首先说明一下,大家不要有以为,虽然这个标题很熟悉,但却是另外一篇新的文章,我翻译的那篇文章在这里:10件安装WordPress后需要做的事。这是一个系列的新手教程,希望能通过这个系列的新手教程,帮助那些刚刚接触使用WordPress的朋友。

当你按照五分钟搭建属于你的WordPress的教程属于你自己的Blog后,肯定会觉得原来搭建一个属于自己的Blog是这么的容易的。当然,我们还需要对其进行些简单的配置,以使我们的WordPress(以下简称WP)博客更加完善。

以下的10项配置,是我列出的对于一个WP新站必做的10件事。你可以依照顺序一件一件的去完成它,当然也似相当easy的。

一、更改管理员密码并管理博客的写作者

WP在安装后,会随机设置一个相当晦涩难记的密码,所以,你要做的第一件事就是把这个难记的密码修改成一个对你来说比较容易记忆的。

你也可以在“用户”(User)面板中添加、删除、编辑博客作者的信息。

注解:这点在2.8中有了改变,使用原始密码登陆后,会有提示要修改密码,WordPress越来越智能了。

二、为日志增加永久链接(Permalinks)

WP默认的日志链接(例如:www.leemunroe.com/?p=396)形式对于用户访问和搜索引擎索引都是相当的不友好的。

当然你可以修改永久链接并在其中加入某些日志的关键词使链接更为友好。(例如:www.leemunroe.com/25-hot-female-web-designers)

1.进入“设置”(Setting) –> “永久链接”(Permalinks);

2.在“通用设置”(Common Setting)中选择“自定义结构”(Custom Structure);

3.输入 %postname%/ ;

4.为了使链接更加美观你也可以为它加上分类标签,输入 %category%/%postname%/ 即可。
10-thing-wp-01-thumb

三、上传并使用主题

1.下载或设计一个你喜欢的主题;

2.将主题解压释放至文件夹 “wp-content” –> “themes”中;

3.在“外观”(Appearance) –> “主题”(Themes)中激活它。(单击即可激活)

四、增加“分类”(Categories)并修改其默认值

WP默认的分类标签是相当不×的,名曰:“未分类”(Uncategories)。当你发表一篇新的日志却忘记了增加分类标签时,你希望WP默认给你加上一个什么标签呢?如果是我,则倾向于使用News或类似的分类标签。

1.进入“日志”(Post) –> “分类”(Categories);

2.点击“未分类”并修改它;

3.当然在此你还可以增加其他的博客分类标签。

五、使用 Akismet

Akismet是什么?如果你的Blog开放了评论系统,那么相信我,你将会收到很多烦人的垃圾评论。那么这时,Akismet就有用武之地了。

我要告诉你的是,Akismet已经整合进了WP,所以你只需激活它即可使用。

1.在管理员页面进入“插件”页面激活Akismet;

2.为了完成Akismet你需要申请一个WP的API key。当然你可以很方便的在Wordpress.com上申请到并在Profile页面获取它。
10-thing-wp-02-thumb

六、安装Google XML Sitemaps

如果你希望你的日志更容易更快的被主流搜索引擎索引,那么你就需要使用Google XML Sitemaps来为你的网站生成一个完成的XML-Sitemap。当然,它会随着你发布新日志而自动修改。

猛击此处安装Google XML Sitemaps插件

首先,你需要使用你的Google账户登入到Google Webmaster Central。在首页,会有一个验证你Blog站点的链接。依照Google的说明,一步一步的操作即可。

验证完毕后,你就可以猛击Add Sitemap将你的Blog站点的URL加入其中。格式应该为:http://www.yoursite.com/sitemap.xml
10-thing-wp-03-thumb1

七、安装WordPress数据备份插件

经常备份数据是一个好习惯。以免由于服务器的和自我的误操作的问题,而导致数据的丢失。

你可以使用WordPress Database Backup插件来定时备份你的Blog数据。插件默认是每周将备份数据发送至你的邮箱。所以当你激活插件以后,不需要你其他的任何操作就可以使用了。

猛击此处安装WordPress Database Backup插件,你还可以从Pro Blog Design上获取完整的自动备份教程。
10-thing-wp-04-thumb1

八、测试你的Blog

只有发布了一定数量的各种格式的日志,你才能彻底的了解你的Blog的状态。

你可以手动的一篇一篇的发表日志,也可以通过导入sample post collection from WP Candy来节省你测试的时间。

通过导入这些简单的日志组合(工具>导入>WordPress),将使你的Blog拥有一些简单的日志并包含评论,“父/子”分类标签和格式等。这将使你修改Blog主题更加的容易,彻底。

九、将Blog的RSS导入Feedburner

Blog的RSS的日志输出数,是否全文输出等,都可以再“设置”(Setting) –> “阅读”(Reading)中修改。

设置完成之后,你可以将你的Blog的RSS加入到Feedburner中。Feedburner将为你的RSS提供一个实时的服务状态,当你发布新日志时,它会及时的将更新上传至服务器。以供订阅者及时阅读。

当你第一次使用Feedburner时,记得要在注册后,修改你所使用主题中的RSS订阅链接。将下列代码增加至主题文件首尾head标签之间即可。

1.<link rel="alternate" type="application/rss+xml" title="Feed Title" href="YOUR FEEDBURNER URL">

10-thing-wp-05-thumb

 

十、使用站点分析

我建议使用Google Analytics来管理和统计站点的访问量。当然你也可以选择其他的站点分析服务网站比如Mint 和 StatCounter

补充项

1、设置合适的日志插图尺寸

设置一个与Blog内容区域相协调的插图尺寸。

“设置”(Setting) –> “媒体”(Media)

2、设置Blog的副标题(tagline)

你使用的主题或许不会显示副标题,但是在RSS输出中则会正常输出。

“设置”(Setting) –> “常规”(General)

原文连接:http://www.problogdesign.com/wordpress/10-things-to-do-after-installing-wordpress/

通过admin

菜鸟站长成功入门-网站推广之

1、QQ聊天。我个人感觉为第一耽误事情的活动,新手要控制上QQ时间,把精力更多投入到网站的内容推广上。能进些高手的群听些讲座是不错,切记不要花太多时间。和游戏,电影,说88,走上站长这条路,在初级阶段和那些不必要的娱乐再见吧,弄网站已经很累了,再搞那些更累,影响工作。真要聊天不如把聊天都用到交换友情链接上!

2、光想不做,眼高手低。对于新手来讲很多困难,等真正做过一次后会发现不过如此,找一个好用的CMS,改改图标,配置下IIS,不想原创的写好采集规则,放好广告生成网站,2个小时做起一个站来应该很轻松。有些站长有上千上万个网站也不足为奇。新手去花上百元左右(域名+100MASP空间不会多于150元)弄个空间开始入门之路,切不可为了这点钱到处去找什么免费空间,免费域名的,百元左右你要都舍不得花,就别入这个行业了。而且花了钱就会更认真些,呵呵。马上就行动是入门的第一步,很多人徘徊在这里很久不前进,仿佛真有多大困难。

3、努力程度。做一个专业站长,每天8小时在线时间应该是很平常的,我每天8点起床开始的工作是这样的:查看QQ,邮箱信息,看ADMIN5新闻,更新网站内容,做网站推广到晚上,然后玩玩就休息。其中内容推广是最主要的,在菜鸟阶段网站内容多少和漂亮程度重要性小于网站推广,不要把精力都花到网站花边好不好看上,酒香也怕巷子深!为什么你的网站不来人,因为别人不知道,现在都谈SEO技术,咱不会那个,咱只会人力推广,你一天发200条广告,连续发上3个月,你的网站不赚钱你可以到评论里骂我。成功就在于坚持的毅力和辛苦的劳动,我认识的一个高水平站长一个月收入5万元以上,他和他的女朋友2个人还经常工作到凌晨,那么赚钱还那么勤奋努力,是我经常鞭策自己的榜样。

4、站在巨人的肩膀上。做为新手学习高人的经验是快速提高自己的法宝,高人不教你怎么办,那就靠自己领会,去百度搜索和你网站类型一样的关键词看看人家怎么做的网站,放什么样的广告,位置是怎么放的,前段时间学人家放广告的一个小办法,每天就多收入了5美元,看来用心就有收获。

通过admin

利用XMPP实现文件共享—简析 “XMPP文件传输协议”

最近在研究XMPP协议,client端用的是开源的Gloox库,server使用gtalk server和OpenFire。昨天想实现XMPP的文件传输,于是仔细看了下XMPP的文件传输协议,结合协议和代码,算是把标准XMPP文件传输协议摸清楚了。下面是一些要点总结。

  • Sock5 Bytestreams支持点对点传输和通过Proxy中转。
  • 标准XMPP,使用Disco来发现代理服务器,所以即使发送方没有配置proxy server也没有问题(理论上哈)。不错的机制啊。
  • 点对点传输只支持Target去连接Requester,不支持双向,所以不是完全的P2P,Jingle file transfer对这一个应该做了优化。
  • Gtalk由于采用了Jingle  file transfer协议,所以没有提供用于文件传输的Proxy server,经过本人测试,下面提供解决方法:

1、发送文件的时候把自己启动一个sock5 server,以这个作为stream host。这种方式要求接受方必须是能够直连发送方,否则会出现失败。

2、指定第三方的Proxy server来实现文件传输,目前很多第三方proxy server貌似都会对ID做验证,所以最好是自己架设一个Sock5 Proxy server,实现起来应该不难。如果采用这中方式,建议不必要完全遵守XEP0065,可以对建立连接过程优化下,实现更彻底的“P2P”。(推荐)

3、使用In band bytestrreams方式。(不考虑性能的话,实现起来最简单)

4、在client端兼容Jingle协议。

1,2,3种方式只能实现第三方Client之间的文件传输,无法和官方Gtalk进行文件传输。

  • 我用的是开源的gloox库,gloox库没有支持Proxy代理服务发现机制,所以发文件放必须至少添加一个Stremhost,一般来说发文件放把自己建立一个Sock5server,并把自己和代理服务器添加到Streamhost中,主要代码:
1 m_pFt =new SIProfileFT(m_pClient,this);
2
3 m_pSock5Server =new SOCKS5BytestreamServer( m_pClient->logInstance(), 6666 );
4  if( (m_pSock5Server->listen() ) != ConnNoError )
5 {
6 g_bLisened = TRUE;
7 }
8 m_pFt->registerSOCKS5BytestreamServer(m_pSock5Server);
9 m_pFt->addStreamHost(m_pClient->jid(), “127.0.0.1”, 6666 );
10 m_pFt->addStreamHost(JID(c_szProxyServer), c_szProxyServer, 7777);

相关协议:

XEP-0096: SI File Transfer

源文档 <http://xmpp.org/extensions/xep-0096.html>

 

XEP-0065: SOCKS5 Bytestreams

源文档 <http://xmpp.org/extensions/xep-0065.html>

 

XEP-0234: Jingle File Transfer(还没研究过,不过应该差不多)

源文档 <http://xmpp.org/extensions/xep-0234.html>

通过admin

自己动手写PHP MVC框架

来自:yuansir-web.com / yuansir@live.cn

代码下载: https://github.com/yuansir/tiny-php-framework

PHP的框架众多,对于哪个框架最好,哪个框架最烂,是否应该用框架,对于这些争论在论坛里面都有人争论,这里不做评价,
个人觉得根据自己需求,选中最佳最适合自己MVC框架,并在开发中能够体现出敏捷开发的效果就OK了,作为一个PHPer要提高自己的对PHP和MVC的框架的认识,所以自己写一个MVC框架是很有必要的,
即使不是很完善,但是自己动手写一个轻量简洁的PHP MVC框架起码对MVC的思想有一定的了解,而且经过自己后期的完善会渐渐形成一个自己熟悉的一个PHP框架。

来写一个PHP MVC框架开发的简明教程,首先声明,教程里面的框架不是一个完善的框架,只是一种思路,当然每个人对MVC框架实现的方法肯定是有差异的,希望高手多提意见多指正,和我一样的菜鸟多讨论多交流,刚接触MVC的PHPer多学习。

首先,我们在项目中建立如下目录和文件:

app
|-controller    存放控制器文件
|-model        存放模型文件
|-view        存放视图文件
|-lib        存放自定义类库
|-config    存放配置文件
|–config.php   系统配置文件
|-system    系统核心目录
|-index.php    入口文件

新件的index.php为入口文件,我们这里采用单一入口,入口文件的内容很简单:

<?php
/**
* 应用入口文件
* @copyright   Copyright(c) 2011
* @author      yuansir <yuansir@live.cn/yuansir-web.com>
* @version     1.0
*/
require dirname(__FILE__).’/system/app.php’;
require dirname(__FILE__).’/config/config.php’;
Application::run($CONFIG);

入口文件主要做了2件事,第一引入系统的驱动类,第二是引入配置文件,然后运行run()方法,传入配置作为参数,具体这2个文件是什么内容,我们接下来继续看。

先看一下config/config.php文件,里面其实是一个$CONFIG变量,这个变量存放的全局的配置:

<?php
/**
* 系统配置文件
* @copyright   Copyright(c) 2011
* @author      yuansir <yuansir@live.cn/yuansir-web.com>
* @version     1.0
*/

/*数据库配置*/
$CONFIG[‘system’][‘db’] = array(
‘db_host’           =>      ‘localhost’,
‘db_user’           =>      ‘root’,
‘db_password’       =>      ”,
‘db_database’       =>      ‘app’,
‘db_table_prefix’   =>      ‘app_’,
‘db_charset’        =>      ‘urf8’,
‘db_conn’           =>      ”,             //数据库连接标识; pconn 为长久链接,默认为即时链接

);

/*自定义类库配置*/
$CONFIG[‘system’][‘lib’] = array(
‘prefix’            =>      ‘my’   //自定义类库的文件前缀
);

$CONFIG[‘system’][‘route’] = array(
‘default_controller’             =>      ‘home’,  //系统默认控制器
‘default_action’                 =>      ‘index’,  //系统默认控制器
‘url_type’                       =>      1          /*定义URL的形式 1 为普通模式    index.php?c=controller&a=action&id=2
*              2 为PATHINFO   index.php/controller/action/id/2(暂时不实现)
*/
);

/*缓存配置*/
$CONFIG[‘system’][‘cache’] = array(
‘cache_dir’                 =>      ‘cache’, //缓存路径,相对于根目录
‘cache_prefix’              =>      ‘cache_’,//缓存文件名前缀
‘cache_time’                =>      1800,    //缓存时间默认1800秒
‘cache_mode’                =>      2,       //mode 1 为serialize ,model 2为保存为可执行文件
);

我这里有意识的定义$CONFIG[‘system’]数组表示是系统的配置文件,当然你可以在里面定义$CONFIG[‘myconfig’]为表示在定义的配置,以后在程序的控制器,模型,视图中来调用,都个很自由。
具体配置值代表什么意思注视很清楚了,下面的如果程序中有详细注释的我就不解释啦,呵呵

再来看一下system/app.php文件,主要是干嘛的:

<?php
/**
* 应用驱动类
* @copyright   Copyright(c) 2011
* @author      yuansir <yuansir@live.cn/yuansir-web.com>
* @version     1.0
*/
define(‘SYSTEM_PATH’, dirname(__FILE__));
define(‘ROOT_PATH’,  substr(SYSTEM_PATH, 0,-7));
define(‘SYS_LIB_PATH’, SYSTEM_PATH.’/lib’);
define(‘APP_LIB_PATH’, ROOT_PATH.’/lib’);
define(‘SYS_CORE_PATH’, SYSTEM_PATH.’/core’);
define(‘CONTROLLER_PATH’, ROOT_PATH.’/controller’);
define(‘MODEL_PATH’, ROOT_PATH.’/model’);
define(‘VIEW_PATH’, ROOT_PATH.’/view’);
define(‘LOG_PATH’, ROOT_PATH.’/error/’);
final class Application {
public static $_lib = null;
public static $_config = null;
public static function init() {
self::setAutoLibs();
require SYS_CORE_PATH.’/model.php’;
require SYS_CORE_PATH.’/controller.php’;

}
/**
* 创建应用
* @access      public
* @param       array   $config
*/
public static function run($config){
self::$_config = $config[‘system’];
self::init();
self::autoload();
self::$_lib[‘route’]->setUrlType(self::$_config[‘route’][‘url_type’]);
$url_array = self::$_lib[‘route’]->getUrlArray();
self::routeToCm($url_array);
}
/**
* 自动加载类库
* @access      public
* @param       array   $_lib
*/
public static function autoload(){
foreach (self::$_lib as $key => $value){
require (self::$_lib[$key]);
$lib = ucfirst($key);
self::$_lib[$key] = new $lib;
}
//初始化cache
if(is_object(self::$_lib[‘cache’])){
self::$_lib[‘cache’]->init(
ROOT_PATH.’/’.self::$_config[‘cache’][‘cache_dir’],
self::$_config[‘cache’][‘cache_prefix’],
self::$_config[‘cache’][‘cache_time’],
self::$_config[‘cache’][‘cache_mode’]
);
}
}
/**
* 加载类库
* @access      public
* @param       string  $class_name 类库名称
* @return      object
*/
public static function newLib($class_name){
$app_lib = $sys_lib = ”;
$app_lib = APP_LIB_PATH.’/’.self::$_config[‘lib’][‘prefix’].’_’.$class_name.’.php’;
$sys_lib = SYS_LIB_PATH.’/lib_’.$class_name.’.php’;

if(file_exists($app_lib)){
require ($app_lib);
$class_name = ucfirst(self::$_config[‘lib’][‘prefix’]).ucfirst($class_name);
return new $class_name;
}else if(file_exists($sys_lib)){
require ($sys_lib);
return self::$_lib[‘$class_name’] = new $class_name;
}else{
trigger_error(‘加载 ‘.$class_name.’ 类库不存在’);
}
}
/**
* 自动加载的类库
* @access      public
*/
public static function setAutoLibs(){
self::$_lib = array(
‘route’              =>      SYS_LIB_PATH.’/lib_route.php’,
‘mysql’              =>      SYS_LIB_PATH.’/lib_mysql.php’,
‘template’           =>      SYS_LIB_PATH.’/lib_template.php’,
‘cache’           =>      SYS_LIB_PATH.’/lib_cache.php’,
‘thumbnail’           =>      SYS_LIB_PATH.’/lib_thumbnail.php’
);
}
/**
* 根据URL分发到Controller和Model
* @access      public
* @param       array   $url_array
*/
public static function routeToCm($url_array = array()){
$app = ”;
$controller = ”;
$action = ”;
$model = ”;
$params = ”;

if(isset($url_array[‘app’])){
$app = $url_array[‘app’];
}

if(isset($url_array[‘controller’])){
$controller = $model = $url_array[‘controller’];
if($app){
$controller_file = CONTROLLER_PATH.’/’.$app.’/’.$controller.’Controller.php’;
$model_file = MODEL_PATH.’/’.$app.’/’.$model.’Model.php’;
}else{
$controller_file = CONTROLLER_PATH.’/’.$controller.’Controller.php’;
$model_file = MODEL_PATH.’/’.$model.’Model.php’;
}
}else{
$controller = $model = self::$_config[‘route’][‘default_controller’];
if($app){
$controller_file = CONTROLLER_PATH.’/’.$app.’/’.self::$_config[‘route’][‘default_controller’].’Controller.php’;
$model_file = MODEL_PATH.’/’.$app.’/’.self::$_config[‘route’][‘default_controller’].’Model.php’;
}else{
$controller_file = CONTROLLER_PATH.’/’.self::$_config[‘route’][‘default_controller’].’Controller.php’;
$model_file = MODEL_PATH.’/’.self::$_config[‘route’][‘default_controller’].’Model.php’;
}
}
if(isset($url_array[‘action’])){
$action = $url_array[‘action’];
}else{
$action = self::$_config[‘route’][‘default_action’];
}

if(isset($url_array[‘params’])){
$params = $url_array[‘params’];
}
if(file_exists($controller_file)){
if (file_exists($model_file)) {
require $model_file;
}
require $controller_file;
$controller = $controller.’Controller’;
$controller = new $controller;
if($action){
if(method_exists($controller, $action)){
isset($params) ? $controller ->$action($params) : $controller ->$action();
}else{
die(‘控制器方法不存在’);
}
}else{
die(‘控制器方法不存在’);
}
}else{
die(‘控制器不存在’);
}
}

}

我叫它框架驱动类,也许不合适,但是我是这样理解的,它用来启动这个框架,做好一些初始化的工作,下面我来详细分析一下每个方法的功能:
1.首先时定义了一些常量,很明了,不解释了
2.setAutoLibs 这个方法其实就是设定那些是系统启动时自动加载的类库,类库文件都存放在SYS_LIB_PATH下面,以lib_开头的,当然这里你可以根据自己的规则来命名
3.autoload 这个方法就是用来引入你要自动加载的类,然后来实例化,用$_lib数组来保存类的实例,比如$lib[‘route’]是system/lib/lib_route.php中lib_route类的实例
4.newLib 这个方法是用来加载你自定义的类的,自定义类存放在根目录下的lib中,但是自定义的类的文件前缀是你自己定义的,看系统配置文件里面有,我定义的是my,这样我就可以在lib
目录下新建一个自定义的类了,比如 my_test.php
<?php
class MyTest {
function __construct() {
echo “my lib test”;
}
}
为什么类名这样命名,看下newLib方法的实现就知道,其实这些你完全可以定义自己的规则,这个方法会首先去着lib下面有没有这个类,如果有就会引入实例化,如果没有就去找系统目录下面的类,有就实例化
5.init 就是一个初始化的方法,里面其实就是加载自动加载的类,以及引入核心控制器和核心模型,这个2个核心文件过会我们再来分析
6.run 方法就是启动这个框架的了,里面的最后2步很重要,就是获取URL然后拆分成一个数组的形似,然后由routeToCm来分发到Controller和Model
7.routeToCm 很重要,根据URL分发到Controller和Model,这个我们过会来说

在run方法中
self::$_lib[‘route’]->setUrlType(self::$_config[‘route’][‘url_type’]); //设置url的类型
$url_array = self::$_lib[‘route’]->getUrlArray();                      //将url转发成数组
好吧,我们来看下route的系统类到底做了说明

<?php
/**
* URL处理类
* @copyright   Copyright(c) 2011
* @author      yuansir <yuansir@live.cn/yuansir-web.com>
* @version     1.0
*/
final class Route{
public $url_query;
public $url_type;
public $route_url = array();

public function __construct() {
$this->url_query = parse_url($_SERVER[‘REQUEST_URI’]);
}
/**
* 设置URL类型
* @access      public
*/
public function setUrlType($url_type = 2){
if($url_type > 0 && $url_type <3){
$this->url_type = $url_type;
}else{
trigger_error(“指定的URL模式不存在!”);
}
}

/**
* 获取数组形式的URL
* @access      public
*/
public function getUrlArray(){
$this->makeUrl();
return $this->route_url;
}
/**
* @access      public
*/
public function makeUrl(){
switch ($this->url_type){
case 1:
$this->querytToArray();
break;
case 2:
$this->pathinfoToArray();
break;
}
}
/**
* 将query形式的URL转化成数组
* @access      public
*/
public function querytToArray(){
$arr = !empty ($this->url_query[‘query’]) ?explode(‘&’, $this->url_query[‘query’]) :array();
$array = $tmp = array();
if (count($arr) > 0) {
foreach ($arr as $item) {
$tmp = explode(‘=’, $item);
$array[$tmp[0]] = $tmp[1];
}
if (isset($array[‘app’])) {
$this->route_url[‘app’] = $array[‘app’];
unset($array[‘app’]);
}
if (isset($array[‘controller’])) {
$this->route_url[‘controller’] = $array[‘controller’];
unset($array[‘controller’]);
}
if (isset($array[‘action’])) {
$this->route_url[‘action’] = $array[‘action’];
unset($array[‘action’]);
}
if(count($array) > 0){
$this->route_url[‘params’] = $array;
}
}else{
$this->route_url = array();
}
}
/**
* 将PATH_INFO的URL形式转化为数组
* @access      public
*/
public function pathinfoToArray(){

}
}

注意querytToArray方法,将将query形式的URL转化成数组,比如原来是localhost/myapp/index.php/app=admin&controller=index&action=edit&id=9&fid=10 这样的url就会被转发成如下的数组
array(
‘app’        =>’admin’,
‘controller’    =>’index’,
‘action’    =>’edit’,
‘id’        =>array(
‘id’    =>9,
‘fid’    =>10
)
)
这下再耐心来看下我写的笨拙的routeToCm,来通过数组参数来分发到控制器,找到控制器以后还要引用相应的模型,然后就实例化控制器和模型,呵呵,貌似有点成型了。

下面就要开始实现 控制器-模型-视图了
我们的思路是这样的,建立一个核心模型和核心控制器,在以后自己的模型和控制器中来继承核心模型和控制器,核心模型和控制器中主要可以是一些通用的方法和必须的组建的加载,下面我们先来写核心控制器,
新建system/core/controller.php
<?php
/**
* 核心控制器
* @copyright   Copyright(c) 2011
* @author      yuansir <yuansir@live.cn/yuansir-web.com>
* @version     1.0
*/
class Controller{

public function __construct() {
// header(‘Content-type:text/html;chartset=utf-8’);
}
/**
* 实例化模型
* @access      final   protected
* @param       string  $model  模型名称
*/
final protected function model($model) {
if (empty($model)) {
trigger_error(‘不能实例化空模型’);
}
$model_name = $model . ‘Model’;
return new $model_name;
}
/**
* 加载类库
* @param string $lib   类库名称
* @param Bool  $my     如果FALSE默认加载系统自动加载的类库,如果为TRUE则加载非自动加载类库
@return object
*/
final protected function load($lib,$auto = TRUE){
if(empty($lib)){
trigger_error(‘加载类库名不能为空’);
}elseif($auto === TRUE){
return Application::$_lib[$lib];
}elseif($auto === FALSE){
return  Application::newLib($lib);
}
}
/**
* 加载系统配置,默认为系统配置 $CONFIG[‘system’][$config]
* @access      final   protected
* @param       string  $config 配置名
*/
final   protected function config($config){
return Application::$_config[$config];
}
/**
* 加载模板文件
* @access      final   protect
* @param       string  $path   模板路径
* @return      string  模板字符串
*/
final protected function showTemplate($path,$data = array()){
$template =  $this->load(‘template’);
$template->init($path,$data);
$template->outPut();
}
}

注释都写的很清楚了吧,其实很简单,这里的加载模板的方法中load了一个系统自动加载的模板类,这个类我们在建立视图的时候再来讲,然后我们再来建核心模型的文件
system/core/model.php

<?php
/**
* 核心模型类
* @copyright   Copyright(c) 2011
* @author      yuansir <yuansir@live.cn/yuansir-web.com>
* @version     1.0
*/
class Model {
protected $db = null;

final public function __construct() {
header(‘Content-type:text/html;chartset=utf-8’);
$this->db = $this->load(‘mysql’);
$config_db = $this->config(‘db’);
$this->db->init(
$config_db[‘db_host’],
$config_db[‘db_user’],
$config_db[‘db_password’],
$config_db[‘db_database’],
$config_db[‘db_conn’],
$config_db[‘db_charset’]
);                                            //初始话数据库类
}
/**
* 根据表前缀获取表名
* @access      final   protected
* @param       string  $table_name    表名
*/
final protected function table($table_name){
$config_db = $this->config(‘db’);
return $config_db[‘db_table_prefix’].$table_name;
}
/**
* 加载类库
* @param string $lib   类库名称
* @param Bool  $my     如果FALSE默认加载系统自动加载的类库,如果为TRUE则加载自定义类库
@return type
*/
final protected function load($lib,$my = FALSE){
if(empty($lib)){
trigger_error(‘加载类库名不能为空’);
}elseif($my === FALSE){
return Application::$_lib[$lib];
}elseif($my === TRUE){
return  Application::newLib($lib);
}
}
/**
* 加载系统配置,默认为系统配置 $CONFIG[‘system’][$config]
* @access      final   protected
* @param       string  $config 配置名
*/
final   protected function config($config=”){
return Application::$_config[$config];
}
}

因为模型基本是处理数据库的相关内容,所以我们加载了mysql类,这个mysql类就不在这里写了,你可以自己根据习惯写自己的mysql的操作类,如果你想支持其他的数据库,完全可以自己灵活添加。

核心模型控制器已经有了,其实里面还可以添加其他你觉得必要的全局函数,这样我们开始新建一个自己的控制器和模型,来实例运用一下
新建controller/testController.php

<?php
/**
* 测试控制器
* @copyright   Copyright(c) 2011
* @author      yuansir <yuansir@live.cn/yuansir-web.com>
* @version     1.0
*/
class testController extends Controller {

public function __construct() {
parent::__construct();
}

public function index() {
echo “test”;
}

public function testDb() {
$modTest = $this->model(‘test’);        //示例化test模型
$databases = $modTest->testDatebases(); //调用test模型中 testDatebases()方法
var_dump($databases);
}
}

testController 继承我们的核心控制器,其实在以后的每个控制器中都要继承的,现在我们通过浏览器访问 http://localhost/myapp/index.php?controller=test ,哈哈,可以输出 test 字符串了
然后我们再新建一个模型model/testModel.php

<?php
/**
* 测试模型
* @copyright   Copyright(c) 2011
* @author      yuansir <yuansir@live.cn/yuansir-web.com>
* @version     1.0
*/
class testModel extends Model{

function testDatabases(){
$this->db->show_databases();
}
}

其实就是定义了一个获取所有的数据库的方法,打开浏览器访问 http://localhost/myapp/index.php?controller=test&action=testDb,不管你信不信,反正我的浏览器是输出了所有的数据库了

现在就差视图了,其实在核心控制器的controller.php文件中已经有了一个showTemplate方法,其实就是实现了加载模板类,$data就是我们要传递给模板的变量,然后输出模板
/**
* 加载模板文件
* @access      final   protect
* @param       string  $path   模板路径
* @param       array   $data   模板变量
* @return      string  模板字符串
*/
final protected function showTemplate($path,$data = array()){
$template =  $this->load(‘template’);
$template->init($path,$data);
$template->outPut();
}

下面我们来看一下template类

<?php
/**
* 模板类
* @copyright   Copyright(c) 2011
* @author      yuansir <yuansir@live.cn/yuansir-web.com>
* @version     1.0
*/
final class Template {
public $template_name = null;
public $data = array();
public $out_put = null;

public function init($template_name,$data = array()) {
$this->template_name = $template_name;
$this->data = $data;
$this->fetch();
}
/**
* 加载模板文件
* @access      public
* @param       string  $file
*/
public function fetch() {
$view_file = VIEW_PATH . ‘/’ . $this->template_name . ‘.php’;
if (file_exists($view_file)) {
extract($this->data);
ob_start();
include $view_file;
$content = ob_get_contents();
ob_end_clean();
$this->out_put =  $content;
} else {
trigger_error(‘加载 ‘ . $view_file . ‘ 模板不存在’);
}
}
/**
* 输出模板
* @access      public
* @return      string
*/
public function outPut(){
echo $this->out_put;
}
是不是简单,就是引入你的静态模版文件,放在缓冲区,然后输出,其实如果你想静态化某个模版,那个这个放在缓冲区的$this->out_put就有用了,你可以在里面添加一个静态化的方法。
好了,现在我们来在新建一个视图文件 view/test.php
<html>
<body>
这是<?php echo $test; ?>,呵呵
</body>
<html>
然后修改一些我们的testController.php中的index()
public function index() {
$data[‘test’] = “yuansir-web.com”;
$this->showTemplate(‘test’, $data);
}
再来浏览 http://localhost/myapp/index.php?controller=test ,可以输出 “这是 yuansir-web.com,呵呵”,那么显然我们的视图也完成了。

这样我们的自己写PHP的MVC的框架就完成了,再补充一下,有人可能疑惑如果我是想建立前台后台的,单一入口怎么办呢,其实你要是从头就看我的这个教程,看下代码就会发现,其实只要在 controller目录下新建
一个admin目录就可以在里面写控制器了,比如controller/admin/testController.php 模板引用也是同样的道理,建立 view/admin/test.php ,然后模板加上路径就可以了,$this->showTemplate(‘admin/test’, $data);
是不是很简单,很灵活。

好了,这样我们《自己动手写PHP MVC框架》的教程就结束了,你可以模仿自己写一个,也可以根据自己的思路来写一个,我教程中的可以自己扩增成一个完善的框架,再次申明一下,教程中的代码不完善,没有做过任何基准测试,效率神马的不考虑,便捷性神马的看个人,呵呵

通过admin

Android SQLite 事务处理

应用程序初始化时需要批量的向sqlite中插入大量数据,单独的使用for+Insert方法导致应用响应缓慢,因为 sqlite插入数据的时候默认一条语句就是一个事务,有多少条数据就有多少次磁盘操作。我的应用初始5000条记录也就是要5000次读写磁盘操作。

而且不能保证所有数据都能同时插入。(有可能部分插入成功,另外一部分失败,后续还得删除。太麻烦)

解决方法:

添加事务处理,把5000条插入作为一个事务

 

我们使用SQLite的事务进行控制:

db.beginTransaction();  //手动设置开始事务

try{

//批量处理操作

for(Collection c:colls){

insert(db, c);

}

db.setTransactionSuccessful(); //设置事务处理成功,不设置会自动回滚不提交。

//在setTransactionSuccessful和endTransaction之间不进行任何数据库操作

}catch(Exception e){

MyLog.printStackTraceString(e);

}finally{

db.endTransaction(); //处理完成

}

1.使用SQLiteDatabase的beginTransaction()方法可以开启一个事务,程序执行到endTransaction() 方法时会检查事务的标志是否为成功,如果程序执行到endTransaction()之前调用了setTransactionSuccessful() 方法设置事务的标志为成功,则所有从beginTransaction()开始的操作都会被提交,如果没有调用setTransactionSuccessful() 方法则回滚事务。

2.使用例子如下:下面两条SQL语句在同一个事务中执行。

Java代码

  1. //银行账户事务测试
  1. public void payment()
  1. {
  1.     SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
  2.     //开启事务
  1.     db.beginTransaction();
  1.     try
  1.     {
  1.         db.execSQL(“update person set amount=amount-10 where personid=?”, new Object[]{1});
  1.         db.execSQL(“update person set amount=amount+10 where personid=?”, new Object[]{2});
  1.         //设置事务标志为成功,当结束事务时就会提交事务
  1.         db.setTransactionSuccessful();
  2.     }
  1.    catch(Exception e){
  2.         throw(e);
  1.     }
  1.     finally
  2.     {
  1.         //结束事务
  1.         db.endTransaction();
  1.     }
  2. }
通过admin

sqlite的事务和锁

事务

事务定义了一组SQL命令的边界,这组命令或者作为一个整体被全部执行,或者都不执行。事务的典型实例是转帐。

事务的范围

事务由3个命令控制:BEGIN、COMMIT和ROLLBACK。BEGIN开始一个事务,之后的所有操作都可以取消。COMMIT使BEGIN后的所有命令得到确认;而ROLLBACK还原BEGIN之后的所有操作。如:

sqlite> BEGIN;

sqlite> DELETE FROM foods;

sqlite> ROLLBACK;

sqlite> SELECT COUNT(*) FROM foods;

COUNT(*)

412

上面开始了一个事务,先删除了foods表的所有行,但是又用ROLLBACK进行了回卷。再执行SELECT时发现表中没发生任何改变。

SQLite默认情况下,每条SQL语句自成事务(自动提交模式)。

冲突解决

如前所述,违反约束会导致事务的非法结束。大多数数据库(管理系统)都是简单地将前面所做的修改全部取消。

SQLite有其独特的方法来处理约束违反(或说从约束违反中恢复),被称为冲突解决。

如:

sqlite> UPDATE foods SET id=800-id;

SQL error: PRIMARY KEY must be unique

SQLite提供5种冲突解决方案:REPLACE、IGNORE、FAIL、ABORT和ROLLBACK。

REPLACE: 当发违反了唯一完整性,SQLite将造成这种违反的记录删除,替代以新插入或修改的新记录,SQL继续执行,不报错。

IGNORE

FAIL

ABORT

ROLLBACK

数据库锁

在SQLite中,锁和事务是紧密联系的。为了有效地使用事务,需要了解一些关于如何加锁的知识。

SQLite采用粗放型的锁。当一个连接要写数据库,所有其它的连接被锁住,直到写连接结束了它的事务。SQLite有一个加锁表,来帮助不同的写数据库都能够在最后一刻再加锁,以保证最大的并发性。

SQLite使用锁逐步上升机制,为了写数据库,连接需要逐级地获得排它锁。SQLite有5个不同的锁状态:未加锁(UNLOCKED)、共享(SHARED)、保留(RESERVED)、未决(PENDING)和排它(EXCLUSIVE)。每个数据库连接在同一时刻只能处于其中一个状态。每种状态(未加锁状态除外)都有一种锁与之对应。

最初的状态是未加锁状态,在此状态下,连接还没有存取数据库。当连接到了一个数据库,甚至已经用BEGIN开始了一个事务时,连接都还处于未加锁状态。

未加锁状态的下一个状态是共享状态。为了能够从数据库中读(不写)数据,连接必须首先进入共享状态,也就是说首先要获得一个共享锁。多个连接可以同时获得并保持共享锁,也就是说多个连接可以同时从同一个数据库中读数据。但哪怕只有一个共享锁还没有释放,也不允许任何连接写数据库。

如果一个连接想要写数据库,它必须首先获得一个保留锁。一个数据库上同时只能有一个保留锁。保留锁可以与共享锁共存,保留锁是写数据库的第1阶段。保留锁即不阻止其它拥有共享锁的连接继续读数据库,也不阻止其它连接获得新的共享锁。

一旦一个连接获得了保留锁,它就可以开始处理数据库修改操作了,尽管这些修改只能在缓冲区中进行,而不是实际地写到磁盘。对读出内容所做的修改保存在内存缓冲区中。

当连接想要提交修改(或事务)时,需要将保留锁提升为排它锁。为了得到排它锁,还必须首先将保留锁提升为未决锁。获得未决锁之后,其它连接就不能再获得新的共享锁了,但已经拥有共享锁的连接仍然可以继续正常读数据库。此时,拥有未决锁的连接等待其它拥有共享锁的连接完成工作并释放其共享锁。

一旦所有其它共享锁都被释放,拥有未决锁的连接就可以将其锁提升至排它锁,此时就可以自由地对数据库进行修改了。所有以前对缓冲区所做的修改都会被写到数据库文件。

死锁

为什么需要了解锁的机制呢?为了避免死锁。

考虑下面表4-7所假设的情况。两个连接——A和B——同时但完全独立地工作于同一个数据库。A执行第1条命令,B执行第2、3条,等等。

表4-7 一个死锁的假设情况

 

A连接                                     B连接

sqlite> BEGIN;

sqlite> BEGIN;

sqlite> INSERT INTO foo VALUES(‘x’);

sqlite> SELECT * FROM foo;

sqlite> COMMIT;

SQL error: database is locked

sqlite> INSERT INTO foo VALUES (‘x’);

SQL error: database is locked

 

两个连接都在死锁中结束。B首先尝试写数据库,也就拥有了一个未决锁。A再试图写,但当其INSERT语句试图将共享锁提升为保留锁时失败。

为了讨论的方便,假设连接A和B都一直等待数据库可写。那么此时,其它的连接甚至都不能够再读数据库了,因为B拥有未决锁(它能阻止其它连接获得共享锁)。那么时此,不仅A和B不能工作,其它所有进程都不能再操作此数据库了。

如果避免此情况呢?当然不能让A和B通过谈判解决,因为它们甚至不知道彼此的存在。答案是采用正确的事务类型来完成工作。

事务的种类

SQLite有三种不同的事务,使用不同的锁状态。事务可以开始于:DEFERRED、MMEDIATE或EXCLUSIVE。事务类型在BEGIN命令中指定:

BEGIN [ DEFERRED | IMMEDIATE | EXCLUSIVE ] TRANSACTION;

一个DEFERRED事务不获取任何锁(直到它需要锁的时候),BEGIN语句本身也不会做什么事情——它开始于UNLOCK状态。默认情况下就是这样的,如果仅仅用BEGIN开始一个事务,那么事务就是DEFERRED的,同时它不会获取任何锁;当对数据库进行第一次读操作时,它会获取SHARED锁;同样,当进行第一次写操作时,它会获取RESERVED锁。

由BEGIN开始的IMMEDIATE事务会尝试获取RESERVED锁。如果成功,BEGIN IMMEDIATE保证没有别的连接可以写数据库。但是,别的连接可以对数据库进行读操作;但是,RESERVED锁会阻止其它连接的BEGIN IMMEDIATE或者BEGIN EXCLUSIVE命令,当其它连接执行上述命令时,会返回SQLITE_BUSY错误。这时你就可以对数据库进行修改操作了,但是你还不能提交,当你COMMIT时,会返回SQLITE_BUSY错误,这意味着还有其它的读事务没有完成,得等它们执行完后才能提交事务。

EXCLUSIVE事务会试着获取对数据库的EXCLUSIVE锁。这与IMMEDIATE类似,但是一旦成功,EXCLUSIVE事务保证没有其它的连接,所以就可对数据库进行读写操作了。

上节那个例子的问题在于两个连接最终都想写数据库,但是它们都没有放弃各自原来的锁,最终,SHARED锁导致了问题的出现。如果两个连接都以BEGIN IMMEDIATE开始事务,那么死锁就不会发生。在这种情况下,在同一时刻只能有一个连接进入BEGIN IMMEDIATE,其它的连接就得等待。BEGIN IMMEDIATE和BEGIN EXCLUSIVE通常被写事务使用。就像同步机制一样,它防止了死锁的产生。

基本的准则是:如果你正在使用的数据库没有其它的连接,用BEGIN就足够了。但是,如果你使用的数据库有其它的连接也会对数据库进行写操作,就得使用BEGIN IMMEDIATE或BEGIN EXCLUSIVE开始你的事务。

通过admin

Eclipse add external jars导致运行出现java.lang.NoClassDefFoundError的解决方法

最近发现一个问题,有时候对一个Android项目反复的Add jar和remove jar,发现编译可以通过,但是运行起来当应用到外部jar的对象时,会抛出java.lang.NoClassDefFoundError异常。导致程序奔溃。

查看项目属性,发现java build path里比正常的项目少了Android Dependencies这一项:

通过和丢失之前的项目进行比对,发现差别在于工程根目录下的.classpath文件,<classpathentry exported=”true” kind=”con” path=”com.android.ide.eclipse.adt.LIBRARIES”/>这里的exported变成了false,于是把这里改回true,然后重新关闭和打开工程,发现就ok了。

 

 

另外如果你导入的jar包不是放在工程目录的\libs目录下,也会有这个问题,请把jar包放到\libs目录下,否则虽然能编译通过,但是因为打包的时候没有把jar打包进去,所以导致上面的那个异常。Eclipse在编译的时候会自动把\libs目录下的文件打包进去。

这个问题以前没怎么出现过,不知道是不是和Eclipse版本和ADT版本有关系。