Sunday, 14 October 2007

Integrate ZendCore with Abyss X1

ZendCore 2.5发布,个人使用的感觉上,比原来的2.0要稳定得多,速度当然很快。

我随手stop了ZendCore捆绑的Apache,在Abyss X1里面用过FastCGI连接到ZendCore,还是比较简单、顺利。只要在Scripting Parameters::Custom Environment Variables里面设置PHPRC指向php.ini所在的...\ZendCore\etc就行了。检查phpinfo(),Zend Engine v2.2.0, Zend Core v2.5.0, Zend Extension Manager v1.2.0, Zend Optimizer v3.3.1, Zend Debugger v5.2.10,一个不少。

ZendCore使用的PHP 5.2.4也存在$_SERVER["PHP_SELF"]环绕的问题,所以还是只能在Abyss X1里面,用Standard方式连接PHP,不能用PHP Style。

Monday, 8 October 2007

A Simple JSON-RPC Case in Python

前面说到了Ajax + JSON,既然有开了始,就趁着惯性,再玩了把JSON-RPC。这RPC是啥,不在网上查一下,还真不知道。先来看看Wikipedia上是怎么描述RPC的。

Remote procedure call (RPC) is a technology that allows a computer program to cause a subroutine or procedure to execute in another address space (commonly on another computer on a shared network) without the programmer explicitly coding the details for this remote interaction. That is, the programmer would write essentially the same code whether the subroutine is local to the executing program, or remote. When the software in question is written using object-oriented principles, RPC may be referred to as remote invocation or remote method invocation.
...

下面的例子虽小,JSON-RPC应用的基本流程,还是有的。

. JSON-RPC Client

试验例子中,JSON-RPC的客户端,是一个Python CGI程序。它显示一个字符串,及其长度。其中,字符串长度的计算,通过JSON-RPC,进行远程过程调用来实现。具体的代码如下:

#!/usr/bin/env python

from jsonrpc import ServiceProxy

if (__name__=='__main__'):
print "Content-Type: text/html" # HTML is following
print # blank line, end of headers

s = ServiceProxy("http://10.1.1.100/pyjsonrpc/jsonrpcServices.py")

myStr = "foobar"
myLen = s.getStringLength( myStr )
print "My string is '%s'. There are %d characters." % (myStr, myLen)

在这里,

. 导入class ServiceProxy;
. 用JSON-RPC服务的URL,来初始化一个ServiceProxy;
. 调用远端的过程。

这里用到了jsonrpc这个模块。该模块是从http://svn.json-rpc.org/trunk/python-jsonrpc上,通过SVN下载后,安装。

客户端的表面上看,JSON-RPC所调用的函数,在使用上和本地函数过程,没有特别大的差别。这里唯一需要注意的是,JSON-RPC服务的URL,即使是在本地,也必须是全路径。

. JSON-RPC Services

那么JSON-RPC,真正的服务端,又是什么样子呢?请看jsonrpcServices.py:

#!/usr/bin/env python

from jsonrpc import handleCGI, ServiceMethod

class SimpleService( object ):

@ServiceMethod
def getStringLength( self, data ):
return len( data )

if __name__ == "__main__":
service = SimpleService()
handleCGI( service )

在这里,定义了JSON-RPC Service的过程,处理CGI接口的服务请求。由此可以看到,jsonrpc模块可以自己处理CGI接口的输入和输出。并且,安装、使用jsonrpc并不需要额外的JSON解析模块,如cjson、simplejson等。jsonrpc自身内置了这项功能。在上面的例子中,除了模块名字是jsonrpc之外,看不到什么JSON的影子。没什么好奇怪的,jsonrpc模块包装了所有的技术细节。

其实,JSON只是RPC在使用过程中,用来进行数据交换的一种格式而已。如果采用XML格式,那就是XML-RPC,还有诸如PHP-RPC之类的,等等。

通常网络应用,如Ajax,在使用上,是URL + Data的模式,而在RPC中,则是URL + Action + Data的模式。尽管本质上是一样的,RPC感觉上似乎要更加面向对象一点。这一点点细微的差别,从这个简单的例子就可以看出,RPC使得应用开发得以更进一步的简化。

另外,Remote一下的好处,就是容易实现聚合,这一点比较有用。相信,RPC的应用会越来越广。

Provide JSON Service in CGI Mode by Python

上一篇中说到,Abyss Web Server提供了对Python的CGI模式的支持。具体的配置比较简单,只需指定Python解析器的位置,绑定py文件即可。详见这里

作为第一个Python Web应用,毫无疑问地,从简单的hello.py开始。

#! /usr/bin/env python

import cgi

if (__name__=='__main__'):

print "Content-Type: text/html" # HTML is following
print # blank line, end of headers

print "<TITLE>Python CGI Script</TITLE>"

print "<H1>Python CGI Script</H1>"
print "Hello, world!"

呵呵,真够简单的。接下来,该玩点流行的玩意了,于是就试了一下通过Ajax,发送和接受JSON数据。

客户端实际的页面,和Python没啥关系。采用了mootools提供的Ajax和JSON函数。源码如下:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>JSON Ajax Demo</title>
<script src='mootools.remote.js' type="text/javascript"></script>
<script language="JavaScript" type="text/javascript">
<!--
var jStatusServiceUrl = './jsonService.py';

function getLength() {
var keystring = $('keystring').value;

var uidString = Json.toString({requeststring: keystring});

new Ajax(jStatusServiceUrl, {
postBody: uidString,

onComplete: function(jsonResult) {
var jsonObj = Json.evaluate(jsonResult);

if( jsonObj.status == 'OKAY' ) {
$('answer').setHTML( jsonObj.length );
}
else {
$('answer').setHTML( "connection error." );
}

}
}).request();
}
//-->
</script>
</head>

<body>
<form method=post action="">
Type a string: 
<input type="text" name="keystring" id="keystring"><br><br>
<div>
How long is the string
<input type="button" value="?" onclick="getLength()">
</div>
<div>Answer: <span id="answer"></span></div>
</form>
</body>
</html>

这个页面的意思是,用户输入一个字符串,发送给jsonService.py,然后将返回的字符串长度,显示出来。数据传送格式,JSON;数据传送方式,Ajax POST。

在最初的试验中,是采用GET方式向jsonService.py发数据。Python的CGI module能够很好地完成任务。当尝试使用POST方式传数据的时候,发现CGI module抓不到POST数据。正常的通过FORM发送的POST数据,却是正常的。没办法,只好拿起剪刀和浆糊,自己DIY了。顺便简单地封装一下,就有了jsonCgiAdapter.py,一个Python JSON CGI适配器。其源码如下:

#!/usr/bin/env python

import sys, os
import cjson

def getJsonData():
fin = sys.stdin
env = os.environ

try:
contentLen = int( env['CONTENT_LENGTH'] )
data = fin.read( contentLen )
return cjson.decode( data )
except Exception, e:
return ""

def sendJsonData( dataObject ):
fout = sys.stdout

resultData = cjson.encode( dataObject )

response = "Content-Type: text/plain\n"
response += "Content-Length: %d\n\n" % len(resultData)
response += resultData

if sys.platform == "win32":
try:
import msvcrt
msvcrt.setmode(fout.fileno(), os.O_BINARY)
except:
pass

# put out the response
fout.write(response)
fout.flush()

jsonCgiAdapter.py使用了cjson模块,进行JSON数据解析,提供了两个函数,用于CGI模式下,完成接受、发送JSON数据。这样,jsonService.py就写成了这样:

#!/usr/bin/env python

from jsonCgiAdapter import getJsonData, sendJsonData

def getStringLength( jData ):
jStatus = { "status" : "ERROR" }

if isinstance(jData, dict) and jData.has_key( "requeststring" ) :
jStatus[ "length" ] = len( jData["requeststring"] )
jStatus[ "status" ] = "OKAY"

return jStatus

if (__name__=='__main__'):

jData = getJsonData()

currentStatus = getStringLength( jData )

sendJsonData( currentStatus )

输入abc123,返回6。Down,一个简单的Python版JSON Service程序完成。如果再添点料,加点酱油和香醋,...。

Using Abyss Web Server X1

我平常的工作基本是在笔记本电脑上进行,为了完成Web开发的一些任务,就直接拉了XAMPP来用。使用了一段时间的感觉是,Apache对于开发来讲,太“重”了,再加上调试用的Xdebug模块,笔记本的噪音就有点大。因此,一直想找一个轻量型的Web Server来用。

之前,也使用过Lighttpd的Windows版本,用FastCGI模式连接PHP Parser,一直没有成功。CGI模式的速度,调试起来不太爽。

前些日子,又查了一下,找到了这个,Abyss Web Server X1 2.5,一个同样跨平台的Web服务器软件。它的Personal Edition X1版本是免费使用的。基本上,现在流行的Script语言,如PHP,Python,Ruby,还有ASP.Net,都能够得到支持。Re-write,Virtual hosting,SSL/TLS等,也同样不少。具体的,这里有介绍。在Abyss的官方网站上,提供了简洁、有效的文档社区支持。

Abyss X1在Windows下运行,有自己的托盘式管理器,还使用一个专门的端口,提供Web模式的后台在线管理。这一点,对迅速调整服务器的配置来讲,非常之方便。提供了包括中文在内的,后台多语言包。

Abyss里,可以使用FastCGI模式,连接PHP Parser。在FastCGI模式下,PHP程序的速度很快。即使为了调试,挂载了Xdebug模块,其速度也比普通模式下、没有Xdebug的Apache + PHP快上三倍。呵呵,爽。

对于Python来讲,直接CGI支持,无需mod_python。ASP.Net,支持1.1和2.0。

URL Rewriting,Abyss不支持Apache的.htaccess文件,原有的Re-write规则作废。取而代之的,可以在后台管理介面上,设置Re-write规则,这更易于使用。

在使用Abyss X1 2.5的过程中,无巧不巧地遇到了PHP 5.2.4的一个Bug。PHP 5.2.4中,$_SERVER["PHP_SELF"]存在环绕的问题,从而导致URL地址错误。这一点,可以通过使用standard类型,而不是php style类型,来解决。

从它的更新纪录来看,之前的版本少了一些重要的特性,不推荐。推荐使用Abyss X1 v2.5,作为Web开发平台。

Monday, 1 October 2007

Install Typo3 4.x with PostgreSQL

Typo3从4.0开始,间接提供了对PostgreSQL等其它数据库的支持。这里以PostgreSQL为例,介绍如何架构Typo3,使之运行在非MySQL数据库上。

Typo3对非MySQL数据库的支持,是基于其所包含的两个扩展的基础之上,ADOdb(adodb)Database Abstraction Layer(dbal)。在Typo3 4.x的发行包里,已经包含了这两个扩展包,无需再安装。缺省配置下,未激活这两个扩展包。

. DB Server Side

首先作数据库服务器端的准备工作,建立一个空的database在DB Server上。目前,DBAL还不能直接create database,必须手动进行。还好这个过程,并不复杂,基本上就是一个指令。这里,假定建立了一个名为pgtypo3的数据库。

. Web Server Side

和通常的情况一样,在建立一个Typo3站点的时候,需要指定Aliase或虚拟主机。这里的例子,Typo3被安装到http://localhost/pgtypo3。

. Typo3 Configuration

1. 将Typo3 Dummy包或完整的Typo3包解压缩到指定的目录,比如\pgtypo3;

2. 修改\pg-typo3\typo3conf\localconf.php。修改以后的内容如下;

$TYPO3_CONF_VARS['SYS']['sitename'] = 'Pg TYPO3 site';
...
$TYPO3_CONF_VARS['EXT']['extList'] = 'tsconfig_help,...,t3skin,adodb,dbal';
...
// connect to PostgreSQL database by dbal + adodb
$TYPO3_CONF_VARS['EXTCONF']['dbal']['handlerCfg'] = array (
'_DEFAULT' => array (
'type' => 'adodb',
'config' => array( 'driver' => 'postgres8', )
)
);

这里,修改了站点名称,激活adodb和dbal扩展包,添加了dbal数据库接口定义。参考ADOdb的相关介绍,选择合适的driver。这里,将连接到PostgreSQL 8.x数据库。

3. 在浏览器中,访问http://localhost/pgtypo3。Typo3会自动进入‘1-2-3’模式的安装工具;

4. ‘1-2-3’模式的安装工具还是可以继续“借用”的。在STEP 1中,填写数据库服务器参数;

Username: typo3admin
Password: ******
Host: l92.168.0.128

5. ‘1-2-3’ STEP 2,指定Typo3使用的数据库。选项一,选择一个现存的空数据库。这里,选择前面所建立的‘pgtypo3’。不要试图去直接建立一个数据库。否则,会得到如下出错信息:

Creating a database with DBAL is not supported. Did you really read the manual?

6. ‘1-2-3’ STEP 3,不要直接按“Import database”按钮。Typo3安装工具里的初始化Query,是针对MySQL的。如果直接使用,会导致出错。因此,在这里点击“Click here to disable”,退出‘1-2-3’模式,进入正常安装模式;

7. 在正常安装模式下(http://localhost/pgtypo3/typo3/install/index.php),进入‘2: Database Analyser’。应该可以看到“Connected to SQL database successfully”,这表明前面的设置是正确的;

8. 点击Database Analyser: Menu: Update required tables的COMPARE;

9. 安装工具会列出一堆Create Table命令,点击之后的“Write to database”按钮,以初始化数据库。通过DBAL来Compare Tables,会一直出现Changing fields的列表,忽略后续出现的更新要求。其可能的原因是,将dump之后的PostgreSQL表结构,和内设的MySQL表结构相比较,在细节上很难完全一致,因此,会不断的要求更新。所以,不要管后面的内容。只要通过数据库工具,监测到数据库已经被更新就行了。也就是,建立了从be_groups到tx_rtehtmlarea_acronym等一系列库表。

10. 导入静态数据。点击Database Analyser: Menu: Dump static data的IMPORT。选择“Import the whole file 'CURRENT_STATIC' directly (ignores selections above)”,点击“Write to database”;

11. 由于回到了正常安装模式(Normale mode),需要手动建立第一个管理员账户。Database Analyser: Menu: Create "admin" user。此信息将写入数据库的‘pgtypo3.public.be_users’表中,其中的password已经经过MD5处理;

12. 在正常安装模式(Normale mode)中,剩余的工作还有:

install tool password   $TYPO3_CONF_VARS['BE']['installToolPassword']   * MD5-hashed
ImageMagick path $TYPO3_CONF_VARS['GFX']['im_path']
$TYPO3_CONF_VARS['GFX']['im_path_lzw']
backend forceCharset $TYPO3_CONF_VARS['BE']['forceCharset']

这些都可以在“1: Basic Configuration”,或“5: All Configuration”中完成。现在可以结束使用Typo3的安装模式。

13. 访问后端控制台(http://localhost/pgtypo3/typo3)。Typo3会显示一个黄色的警告信息,依此提示到Tools: DB Check中更新一下参考索引(reference index)。

至此,Typo3通过DBAL + ADOdb,使用PostgreSQL的设置工作,全部完成。后续工作,与通常无异。

参考链接

Short DBAL how-to
DBAL Manual
ADOdb Manual