Sunday, October 29, 2006

Apache配置虚拟主机


本文转自 http://www.ideagrace.com/html/doc/2005/10/27/00686.html

Apache配置虚拟主机
发表于:2005-10-27 阅读:713次



虚拟主机使得一台服务器可以运行多个网站。本文介绍如何配置服务器使其支持虚拟主机,包括基于IP的虚拟主机和基于名称的虚拟主机,以及通过运行多个服务daemon实现一台机器运行多个网站的方法。

一、虚拟主机及其应用(1月15日)

   虚拟主机就是运行在同一物理服务器上的具有不同名字的多个Web网站。它的基本思想是:即使所有网站都运行在同一个服务器上,Apache也知道用户访问的是哪一个网站,而且能够正确地返回用户所请求的内容。

   虚拟主机是这样一种特殊的技术:它使得我们可以在一台机器上运行多个Web网站,支持许多不同的域名和一个域之内的多个名字,从而使得一台通过DSL接入的家用计算机看起来象是一屋子的服务器。这是一种非常巧妙的技术。

  为什么要使用虚拟主机

   或许你现在已经有了自己的应用虚拟主机的设想。下面是本人通常使用虚拟主机的情况。我在一家Web设计公司工作。为客户开发Web网站时,我们总是为该客 户配置一个专用的虚拟主机。这些虚拟主机往往是基于名称的虚拟主机,因为这设置起来比较快,而且几乎总是只在内部进行。也就是说,在内部DNS服务器上设 置一下名称,这样内部的主机就可以看到这个Web网站,而外面的浏览器只能看到我们开发服务器上公开的内容。

   这种做法有着许许多多的好处。它使得我们能够设置网站的目录结构,其设置方法又和开发完成后实际运行的网站完全一样。它使得我们能够完全地分离不同网站的 文件,不会出现搞不清某个文件属于哪个网站的混乱。此外,从开发者的角度来看,它使得我们能够在DreamWeaver中设置完全独立的网站,而不仅仅是 设置同一网站的不同子目录。

   最后,如果使用虚拟主机,不论我们同时开发的客户项目有多少,都无需分别为每个网站配置一个新的服务器。在任何时候这都是使用虚拟主机最主要的理由。

二、基于IP的虚拟主机(1月15日)

   我们可以用两种方法设置虚拟主机。这两种方法只是服务器上配置的不同,对客户来说没有什么区别——也就是说,用户无法知道他们所使用的虚拟主机属于哪一种类型,甚至不会知道自己正在使用虚拟主机。

   这两种类型分别是基于IP的(IP-based)虚拟主机和基于名称的(Name-Based)虚拟主机。简单地说,两者的区别在于:使用基于IP的虚拟 主机时,每一个虚拟主机都有一个不同的IP地址,而基于名称的虚拟主机都有相同的IP地址,但它们的名称不同。两种类型各有优点,但它们的实现方法并没有 什么大的差别。

   在基于IP的虚拟主机环境中,同一个服务器机器上运行着多个Web网站,但每一个Web网站都有其自己的IP地址。为了实现这一点,我们首先要在操作系统 上设置这些IP地址。大多数现代操作系统都允许我们为一台机器设置任意多的IP地址,具体的设置方法对每种操作系统来说都不同。本文不准备在方面作深入的 介绍。如果你不熟悉如何在自己的操作系统上进行设置,请向熟悉的专家请教。一般这种设置并不是很困难。或者,你也可以跳过这种基于IP的虚拟主机,直接转 入下面介绍的基于名称的虚拟主机。

   有些老式的操作系统要求每个NIC(network interface card,网卡)对应一个IP地址,但现在一般不再有这种限制。

   在机器上设置好多个IP地址之后,我们还要在DNS(域名服务器)中设置这些IP地址,这样DNS就能够解析我们赋予这些IP地址的名称。这里我们不准备介绍DNS记录的具体设置方法,你可以要求DNS服务器的管理员设置这些记录。

   接下来要做的工作就直接和Apache有关了。假定我们已经将所有的IP地址设置到了机器上,每一个IP地址都有相应的DNS记录,要在Apache的httpd.conf配置文件中增加的内容如下所示。

   这里我们假定有三个IP地址,对应的名称分别为name1.mydomain.com、name2.mydomain.com和 name3.mydomain.com。当然,这里指定的名称也可以属于完全不同的域,例如www.mydomain.com 和 www.myotherdomain.com。

   加入httpd.conf配置文件的内容如下所示:





DocumentRoot /usr/local/apache/name1_www

ServerName name1.mydomain.com

ErrorLog /usr/local/apache/logs/name1_logs/error_log

TransferLog /usr/local/apache/logs/name1_logs/access_log





ServerAdmin webmaster@name2.mydomain.com

DocumentRoot /usr/local/apache/name2_www

ServerName name2.mydomain.com

ServerAlias name2





DocumentRoot /usr/local/apache/name3_www

ServerName name3.mydomain.com

ScriptAlias /use/local/apache/name3_cgi







   注意我们并不需要为每个虚拟主机指定所有的配置细节。凡是没有指定的配置细节都将从服务器主配置文档继承。例如在上面的第二个虚拟主机中,我们没有指定记录日志的地方,这个虚拟主机将把日志信息写入主服务器日志文件。

   另外也请注意在第三部分中我们指定了一个CGI目录。对于其他两个部分来说,当用户访问一个包含/cgi-bin/的URL时,都默认使用主服务器配置中所指定的CGI目录,这是因为它们的虚拟主机配置中都没有指定CGI目录。

   几乎所有的配置指令都可以用于区。在服务器说明文档中,我们可以注意到每一个指令都有其合法应用场所的说明,某个指令是否可以在虚拟主机配置中使用应该已经相当清楚。

   请记住务必在重新启动服务器之前测试一下新的配置文件:



/usr/local/apache/bin/apachectl configtest





   另外一个方便的技巧是使用SeverAlias指令,它对基于名称的虚拟主机同样适用。在LAN内访问内部服务器时,我们往往希望使用比较简单的服务器名 字,例如“name2”,而不是它的全称“name2.mydomain.com”。Apache无法知道“name2”和 “name2.mydomain.com”实际上是同一个Web网站,使用ServerAlias指令可以确保我们在使用简称时不会得到意想不到的页面。

   重新启动服务器应用新的配置文件之后,Apache就能够根据URL判断用户访问的是哪一个Web网站,在此基础上提供正确的页面。即使所有网站在同一物理服务器上也绝不会出错。
三、基于名称的虚拟主机(1月15日)

   当然,许多时候我们不能奢侈到给一台机器指定多个IP地址的地步。IP地址已经越来越紧张,而且在很多时候,例如象我是通过DSL接入,我们只能占有一个IP地址。在这种情况下,基于名称的虚拟主机正好适用。

   使用基于名称的虚拟主机时,我们无需为机器设定多个IP地址,但仍旧涉及到DNS服务器的管理员,要由他们为我们的机器设置一个以上的DNS记录。这些额 外的记录称为C-record,或者CName(指向某台机器的主记录称为AName,或A-record)。我们可以让任意数量的CName指向一台特 定的机器。

   配置DNS服务器使它将所有指定的名称解析为服务器的IP地址之后,我们还要将下面的内容加入Apache服务器的配置文件:



NameVirtualHost 192.168.1.1



ServerName name1.mydomain.com

DocumentRoot /usr/local/apache/name1docs

ServerAlias name1





ServerName name2.mydomain.com

DocumentRoot /usr/local/apache/name2docs

ServerAlias name2





ServerName name3.mydomain.com

DocumentRoot /usr/local/apache/name3docs

ServerAlias name3







   正如配置基于IP的虚拟主机,我们可以在这里使用任何适用的配置指令,本例的配置应该说是相当简单的。

   下面是一些注意事项。

   首先,我们必须用NameVirtualHost指令指定哪个IP地址负责响应对虚拟主机的请求。我们可以联合运用基于IP和基于名称的虚拟主机——也就 是说,可以在同一服务器上同时地配置这两种虚拟主机,因此我们必须指定哪一个IP地址用于基于名称的虚拟主机。如果我们要在服务器的内部接口和外部接口 (即Intranet服务器和Internet服务器)上设置虚拟主机,必须同时指定这两个地址:



NameVirtualHost 192.168.1.1

NameVirtualHost 208.32.54.7





   此外,除了不同的地址之外,我们还可以在不同的端口上运行虚拟主机,当然这会使系统变得非常复杂:



NameVirtualHost 192.168.1.1:80

NameVirtualHost 192.168.1.1:90





   注意确保所有的名称都已经在DNS中登记,否则用户将不能访问到自己想要访问的页面。

   虚拟主机能够正确发挥作用的窍门之一在于浏览器告诉了服务器它正在向谁请求内容。有些仍在使用的较老的浏览器不能正确地做到这一点,它们会从机器上的“默 认”服务器读取内容。不过我们几乎无需为此担心,因为至少在最近5年来(或更早)发布的浏览器都具有这个能力。在Apache里,针对那些不能好好配合的 浏览器,我们还有相应的解决办法,不过这就留给你自己去研究。如果你对此感兴趣,请从http: //www.apache.org/docs/manual/vhosts/index.html开始。

四、运行多个daemon(1月15日)

   事实上我们还有第三种在一个服务器上运行多个Web网站的方法,这就是在同一机器上运行多个完全独立的服务daemon,每个daemon提供一个Web 网站的服务。这种方法的缺点在于每个daemon必须在不同的端口上运行,这是因为我们无法让多个服务同时监听同一端口。

   我们可以用-f命令行选项让Apache服务器使用指定的配置文件:



/usr/local/apache/bin/apache -f /usr/local/apache/conf/name2_httpd.conf





   这使得我们可以运行任意数量(只要机器能够支持)的服务daemon,每一个服务都以不同的配置运行。当然,此时应当确保每一个配置文件都有一个端口指令指向不同端口,否则自第一个以后的其他服务daemon都将不能启动并提示下列错误信息:



Could not bind to port 80: Port already in use.





   结束语:虚拟主机使得我们可以在同一服务器机器上运行多个Web网站,它有着许多好处。总而言之,它使得你可以对上司说:“我们并不需要为了运行其他18个网站去购买新的机器”。


©2005 Idea Grace

Friday, October 20, 2006

关于Delphi接口不能强制转化的补充说明

其实Delphi为了速度的原因,对于接口是采用直接解析接口在实例中的偏移来得到的.这个可以从下面的代码中看到:

var
mInt: ITestInterface;
mInt2: ITestInterface2;
begin
mInt := TTestInterface2.Create;
mInt2 := TTestInterface2.Create;
end;

其中 mInt := TTestInterface2.Create 代码为:
mov dl, $01
mov eax, [$00452e4]
call TObject.Create
mov edx, eax
jz +$03
sub edx, -$0C
lea eax, [ebp-$04]
call @IntfCopy

而 mInt2 := TTestInterface2.Create 代码为:
mov dl, $01
mov eax, [$00452e4]
call TObject.Create
mov edx, eax
jz +$03
sub edx, -$0C
lea eax, [ebp-$08]
call @IntfCopy

但对于接口之间的转化时, CSDN的halfdream(哈欠) 兄说的很对, Delphi在call @IntfCast里调用了@QueryInterface, 而这里面的代码, 其实是在System的TObject对象的GetInterface这个方法中, 但这个方法有个问题, 它需要GUID, 但我们实际可以在Delphi中使用无GUID的接口, 那么就会发现, 根本无法进行转化

比如:
INoGUIDInterface = interface
procedure NoGUID;
end;

INoGUIDInterface2 = interface
procedure NoGUID2;
end;

var
mInt: INoGUIDInterface;
mInt2: INoGUIDInterface2;
begin
mInt := TTestNoGUIDInterface2.Create;
mInt.NoGUID;

mInt2 := mInt as INoGUIDInterface2;
mInt2.NoGUID2;
end;

编译就会出错了, Operator not applicable to this operand type 根本就不支持. 所以表明接口时还是随手加上GUID的比较好

Thursday, October 19, 2006

对于delphi7的接口不能强制转化的问题

今天在发现delphi里的接口是不能强制转化的, 比如如下代码:
ITestInterface = interface(IUnknown)
['{DE8E7E63-BD1B-4BD8-B59A-43F4B2B3A3DC}']
procedure Chk;
end;

ITestInterface2 = interface(IUnknown)
['{3B47B113-4664-4CCC-9A69-E378F2D2363E}']
procedure Chk2;
end;

var
mInt: ITestInterface;
mInt2: ITestInterface2;
begin
mInt := TTestInterface2.Create;
mInt.Chk;

ITestInterface2(mInt).Chk2;

mInt2 := mInt as ITestInterface2;
mInt2.Chk2;
end;

从运行情况可以看出,在第一种情况 ITestInterface2(mInt).Chk2; 下,实际运行的还是ITestInterface下的procedure Chk;而第二种情况 mInt2 := mInt as ITestInterface2; 就能正确的运行ITestInterface2下的procedure Chk2;了.这是为什么呢?
原来这都出自Delphi的编译之手,在进行强制转化 ITestInterface2(mInt).Chk2; 时, 代码是:
mov eax, [ebp-$04]
mov edx, [eax]
call dword ptr [edx+$0c]
而进行 mInt.Chk; 调用时的代码也是这个,这说明强制转化时,编译器根本就没有把ITestInterface转化为ITestInterface2,而是理解为直接的对象强制转化,然后希望能在方法表里找到对应的方法,而且在对接口进行方法的解析时,也只是以方法的声明顺序,而不是实际的方法名来进行解析.所以在Chk为ITestInterface的第一个方法,而Chk2也为ITestInterface2的第一个方法时,就会直接的调用了ITestInterface的Chk,而不是ITestInterface2的Chk2.这个结论也可能通过下面这个实验来表明.
ITestInterface = interface(IUnknown)
['{DE8E7E63-BD1B-4BD8-B59A-43F4B2B3A3DC}']
procedure Chk;
end;

ITestInterface2 = interface(IUnknown)
['{3B47B113-4664-4CCC-9A69-E378F2D2363E}']
procedure Chk2;
procedure Chk22;
end;

var
mInt: ITestInterface;
mInt2: ITestInterface2;
begin
mInt := TTestInterface2.Create;
mInt.Chk;

ITestInterface2(mInt).Chk22;

mInt2 := mInt as ITestInterface2;
mInt2.Chk2;
end;
在这种情况下,就会报地址出错,那是因为ITestInterface根本就没有第二个方法.当然下面的代码也能说明问题
ITestInterface = interface(IUnknown)
['{DE8E7E63-BD1B-4BD8-B59A-43F4B2B3A3DC}']
procedure Chk;
procedure Chk1;
end;

ITestInterface2 = interface(IUnknown)
['{3B47B113-4664-4CCC-9A69-E378F2D2363E}']
procedure Chk2;
procedure Chk22;
end;

var
mInt: ITestInterface;
mInt2: ITestInterface2;
begin
mInt := TTestInterface2.Create;
mInt.Chk;

ITestInterface2(mInt).Chk22;

mInt2 := mInt as ITestInterface2;
mInt2.Chk2;
end;
在这种情况下,程序会调用ITestInterface的Chk1方法.

好上面说了强制转化时发生的情况,下面说用as来转化时发生的情况.

当用as来转化时,代码就变为:
lea eax, [ebp-$08]
mov edx, [ebp-$04]
mov ecx, $00452bc4
call @IntfCast
mov eax, [ebp-$08]
mov edx, [eax]
call dword prt [edx+$0c]
发现,在用as标志转化后,就会使编译器知道要在类实例中查找实际的接口ITestInterface2而不是用mInt所声明的ITestInteface.

以上说明Delphi7的编译器对接口的支持还是不够好,造成在强制转化时,不能正确的识别出要求的接口类型来.

Tuesday, October 10, 2006

Inside Microsoft .Net IL Assembly的一个错误

在这本书中, Part1的Enhancing the code的Code Retention一节中指出如果把
.class public explicit CharArray8 extends [mscorlib]System.ValueType { .size 8 }
这句放在
.field public static valuetype CharArray8 Format at FormatData
之前, 但在Even类之后的话,编译会出错, 但经过实际编译发现,这样子也是没有问题的, 看来要不是书上写错了,要不就是微软改进了它的编译器.

Monday, October 09, 2006

关于emacs比较好的站点

Emacs for Windows: http://learn.tsinghua.edu.cn:8080/2002315162/index.html
这个网站上有emacs的windows版本, 直接就是可执行文件,而不用自己再去编译了.

Emacs 中文化指南: http://man.chinaunix.net/newsoft/Emac/book.html
这个网站实在是好,站长同学把他使用emacs的经验都写在上面了.很多东东都是可以直接复制来用.

emacs在windows上使用时,它没有建立一个缺省的.emacs配置文件,可以直接在emacs上使用~/.emacs来建立配置文件.

下面这个代码可以最大化emacs
(w32-send-sys-command 61488)