2009年3月23日星期一

thrift粘合java

上篇用thrift粘合了erlang,分别写了客户端和服务器端。这篇继续些个java版本的客户端。其实通过这几篇可以发现,这些客户端和服务器端的格式是大同小异的,也再次说明了语言层面中语法的区别实在不大~

首先还是需要libthrift-java的包,还是去上上篇提到的地址那里去下载。
然后运行
thrift --gen java hello.thrift
这样就生成了gen-java的文件夹,里面就是thrift给我们生成好的文件。(Hello.java)
然后我们进入gen-java的目录,写一个客户端文件:
HelloClient.java

import org.apache.thrift.TException;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransportException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;

public class HelloClient{
public static void main(String [] args){
try{
TTransport transport = new TSocket("localhost", 9090);
TProtocol protocol = new TBinaryProtocol(transport);
Hello.Client client = new Hello.Client(protocol);

transport.open();
String result = client.say("world");
System.out.println(result);
transport.close();
}
catch(TException ex){
ex.printStackTrace();
}
}
}
配写一个方便的Makefile:

LIB_PATH=/usr/share/java/libthrift.jar

all:
javac -cp ./:${LIB_PATH} HelloClient.java
run:
java -cp ./:${LIB_PATH} HelloClient
clean:
rm *.class
make之前别忘了先启动服务器端,我们使用上次erlang版本的Server:

erl> hello_server:start()

然后执行我们的这个客户端:

make
make run

可以看到java版的这个也正常输出了Hello, world

2009年3月22日星期日

thrift粘合erlang

上篇用thrift尝试了粘合cpp和python,其实我们也可以看出来,所谓的粘合,无非是thrift通过socket的方式产生各种语言的服务器端和客户端的代码,然后可以用任意语言写服务器接口,用任意语言去调用这些服务端的接口。

ok,继续上次hello.thrift,我们先装好thrift-erlang的扩展包,还是去上篇文章里提到的地址down下来安装。然后是生成erlang的代码:
thrift -erl hello.thrift
这样就生成了gen-erl的文件夹。

接着我们用上一篇的python版本的hello跑服务器端,也就是python HelloServer.py
然后我们写个erlang版本的客户端去测试:
hello_client.erl
%% @author author

-module(hello_client).
-include("hello_thrift.hrl").
-export([test/0]).

p(X)->
io:format("~s~n", [X]),
ok.

test()->
Port = 9090,
{ok, Socket} = thrift_client:start_link("localhost",
Port,
hello_thrift),
{ok, Result} = thrift_client:call(Socket, say, ["world"]),
p(Result),
thrift_client:close(Socket),
ok.

写个Makefile:

ERL_THRIFT=/usr/lib/erlang/thrift/

all:
erlc -I ${ERL_THRIFT}/include *.erl
erl +K true -pa ${ERL_THRIFT}/ebin
clean:
rm *.beam
make之,会走到erl的命令行,运行:

erl> hello_client:test()

可以看到输出了

Hello, world
ok

说明这个客户端正常运行了。

都说erlang的高并发能力,不用erlang写服务器端有点可惜,所以来做一个erlang版本的服务器端,继续在gen-erl的文件夹下建立一个hello_server.erl:

%% @author author

-module(hello_server).
-include("hello_thrift.hrl").
-export([start/0, handle_function/2, say/1, stop/1]).

debug(Info)->
io:format("Debug info:~s~n",[Info]).

say(Name)->
Sentence = "Hello," ++ Name,
debug(Sentence),
BinSentence = list_to_binary(Sentence),
BinSentence.

start()->
start(9090).

start(Port)->
Handler = ?MODULE,
thrift_socket_server:start([{handler, Handler},
{service, hello_thrift},
{port, Port},
{name, hello_server}]).

stop(Server)->
thrift_socket_server:stop(Server).


handle_function(Function, Args) when is_atom(Function), is_tuple(Args) ->
case apply(?MODULE, Function, tuple_to_list(Args)) of
ok -> ok;
Reply -> {reply, Reply}
end.
上面的也很简单,和python版本的HelloServer本质是相同的,绑定9090的端口,实现了say的函数;其它的函数,start和stop不用多说,handle_function这个函数比较重要,注意我们erlang版本的客户端的代码调用了thrift_client:call的方法,这个方法传递了函数名和参数的tuple,以让服务器去做函数调用的分发,这一层在python版本的Server中是看不出的,而erlang的这个版本的handle_function就是实现了这个,这个函数是必须的。

第二个需要注意的地方是say函数,这个函数返回一个字符串,我本来使用直接返回Sentence结果,但是当客户端调用时总是出错,后来将这个字符串转成了二进制,也就是使用了list_to_binary的函数,这样才正确运行。我想也是thrift的内部通讯是通过2进制的,这个在cpp和python版的命名中(BinaryProtocol)可以看出来。

ok,试验我们的erlang服务器端,先停掉python版的那个服务器端(因为两个都用了9090的端口),然后Makefile不用变,make之,进入erl的控制台,运行:
erl> hello_server:start()

服务器就启动了。

运行erlang版的客户端/cpp版的客户端/python版的客户端都没有问题~ ^_^

---
Erlang版的就先到此,接下来或许是java版的。

2009年3月20日星期五

初试thrift

继上一篇在ubuntu下安装thrift包,在ubuntu下要使用某种语言,还需要安装对应的语言包,比如python,需要使用

import thrift

包的地址也是https://launchpad.net/%7Etxamqpteam/+archive/ppa,下载python-thrift的那个包即可。所以如果要用其他语言,一样要下载扩展包,那个libthrift0,libthrift-dev,java的,erlang的。。。

然后写个最简单的hello 的thrift服务,
hello.thrift:


#!/usr/bin/env thrift

service Hello{
string say(1:string name)
}
先些python版本的,运行thrift --gen py hello.thrift
将在目录下产上一个gen-py的文件夹,进入这个文件夹,可以看到有个hello的文件夹,这就是thrift生成的python代码了,不过服务部分还是需要我们自己搭建,下面来写个hello的服务:
HelloServer.py

from hello import Hello
from hello.ttypes import *

from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
from thrift.server import TServer

class HelloHandler():
def __init__(self):
pass

def say(self, name):
word = "Hello, %s"%(name)
print word
return word

handler = HelloHandler()
processor = Hello.Processor(handler)
transport = TSocket.TServerSocket(9090)
tfactory = TTransport.TBufferedTransportFactory()
pfactory = TBinaryProtocol.TBinaryProtocolFactory()

server = TServer.TSimpleServer(processor, transport, tfactory, pfactory)

print "Starting Server..."
server.serve()

上面的handler实现了say的方法:拿到name参数,增加hello后return出去。然后将handler作为处理器传给server,运行了SimpleServer。thrift默认绑定的端口是9090。
运行:python HelloServer.py 一个Hello的服务器就运行起来了。

接着试一下这个服务,运行hello文件夹里thrift生成的Hello-remote文件:
./Hello-remote say "world"
可以看到在9090端口运行的服务器就返回了 Hello, world了。

自己做个client:
刚才是使用thrift自己生成的Hello-remote的客户端,下面做一个自己的客户端:

#!/usr/bin/env thrift

from hello import Hello
from hello.ttypes import *

from thrift import Thrift
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol

try:
transport = TSocket.TSocket("localhost", 9090)
transport = TTransport.TBufferedTransport(transport)
protocol = TBinaryProtocol.TBinaryProtocol(transport)

client = Hello.Client(protocol)
transport.open()
print client.say("world")
transport.close()
except Thrift.TException, ex:
print "ex:%s"%(ex.message)

代码也很简单,打开9090的端口,通过Hello的client调用say的函数,同样返回Hello, world(就是和服务器端一样,那几个类名比较难记,和服务器通讯也要些不少代码:拿socket,缓存socket,绑定协议)

使用C++调用python服务:
既然facebook用thrift作了代码之间的通讯和粘合,我们就做个C++的client来和python的服务器的这个服务器通讯:
首先是用thrift生成cpp的代码:
thrift --gen cpp hello.thrift
这样就生成了gen-cpp的文件夹,在文件夹下生成了需要的cpp和h文件,下面是client的代码:
(注意要用C++的库,需要安装上面提到的libthrift-dev)
client_remote.cpp:

#include <string>
#include <iostream>

#include <protocol/TBinaryProtocol.h>
#include <transport/TSocket.h>
#include <transport/TTransportUtils.h>

#include "Hello.h"

using namespace std;

using namespace apache::thrift;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;

using namespace boost;

int main(int argc, char* argv[])
{
shared_ptr socket(new TSocket("localhost", 9090));
shared_ptr transport(new TBufferedTransport(socket));
shared_ptr protocol(new TBinaryProtocol(transport));

HelloClient client(protocol);
try
{
transport->open();
string input;
string result;
while (true)
{
cout << "Input your name:";
cin >> input;
if (input == "quit") break;
client.say(result, input);
cout << result;
}

transport ->close();
}
return 0;
}

注意库里cpp代码整个在apache::thrift的命名空间里,下载的thrift的20080411的版本中的sample的cpp用的是facebook::thrift的命名空间,是编译不过的。其他代码格式和python版本的client是类似的,也是创建socket,buffer它,调用协议。

Makefile:

BOOST_DIR = /usr/include/boost
THRIFT_DIR = /usr/include/thrift

LIB_DIR = /usr/lib

GEN_SRC = hello_constants.cpp hello_types.cpp Hello.cpp

client:
g++ client_remote.cpp -o HelloClient -I${THRIFT_DIR} -I${BOOST_DIR} -L${LIB_DIR} -lthrift ${GEN_SRC}

make之

运行这个版本的HelloClient,同样可以和服务器正常通信。

---
以上是实验了python和cpp版本的,接下来想尝试java和erlang版本的。

2009年3月18日星期三

Web IM

关于web im,最被关注的应该是facebook的在线聊天,这个在2008年的《程序员》07月刊有篇文章进行介绍,其中介绍了facebook就是使用erlang,和其他代码的粘合使用了facebook目前开放出来的thrift

使用erlang + mochiweb也是去年进行web开发的一个热点,2008年的n期《程序员》都进行了介绍,特别是facebook使用这部分成功构建了支持facebook百万数量级用户在线的聊天系统后,似乎这些东西更加炙手可热了。

这里有两篇blog,都介绍了使用erlang做聊天系统:
Comet web chat 这里的chat的代码比较简单,只是一个简单的“单房间”聊天室,不过入门是个好例子。
A Million-user Comet Application with Mochiweb 这个也是介绍使用erlang + mochiweb和comet支持百万用户在线的一个sample,分3个部分,由浅入深,也非常好。

关于mochiweb:
MochiWeb最大卖点是非常轻量(高并发)而灵巧(易于定制),在特殊的场景下非常有用。目前MochiWeb已知的应用:
  1. Facebook的Web Chat
  2. CouchDB
  3. Erlana:web Analytics服务

下载并编译MochiWeb

svn co http://mochiweb.googlecode.com/svn/trunk mochiweb
cd mochiweb
make

关于上面的提到的Comet Web Chat
作者写了一个chat的demo,大家可以从那篇文章那里,去down到这个chat.tgz,然后解压后,进入它的deps的目录,然后在deps的目录下,建立一个连接
ln -s $(你刚才安装的mochiweb的目录) mochiweb-src
然后make,然后执行start-dev.sh,默认是8000的端口,在你的浏览器上打开:
http://localhost:8000/就可以看到demo了。

2009年3月17日星期二

使用jsmin来minify js文件

《高性能网站建设指南》上有条是minify js文件,其实很多前端优化大家都知道,就是不去动手去做。

其实这个部分很简单,jsmin的工具可以去jsmin这里找到,此工具有个python的包,可以直接调用。

简单来说,例如要minify一个名叫base.js的文件,使用这个叫jsmin.py的包,可以这样做:

#!/usr/bin/env python

from jsmin import JavascriptMinify

TARGET_FILE = "base.js"
def minify():
fin = open(TARGET_FILE, "r")
fout = open("%s.min"%TARGET_FILE, "w")
jvm = JavascriptMinify()
jvm.minify(fin, fout)

if __name__ == "__main__":
minify()

这样就生成了一个叫base.js.min的压缩后文件。

还是那句,前台优化大家都知道,关键是动手去做。至少可以在YSlow的minify js一项能多得点分吧。

2009年3月16日星期一

ubuntu 安装thrift

thrift

安装参考地址:
http://wiki.apache.org/thrift/GettingUbuntuPackages

但是在我机器上装了半天,make的时候总报错:
说什么 eval 1: libtool_args += XXXX 乱七八糟的错误。
调试了半天也没有搞定。

后来只好从网上搜些非官方的安装deb包:
https://launchpad.net/~txamqpteam/+archive/ppa

还是这个快,装上之后,写个最简单的thrift文件:
#/usr/bin/env thrift
service Calculator{
void ping(),
i32 add(1:i32 num1, 2:i32 num2)
}
然后执行 thrift --gen py a.thrift后生成了gen-py的文件夹,下面有了python的文件,初步观察代码相当多。。。简单的两行做个rpc还真要写不少代码。

先弄到这里,下一步尝试使用python和C的互调

python调用java

Jython:

参考网页:http://www.jython.org/

用纯java做的python,项目受python基金会支持,于2009年3月发布了新beta版

缺点:jython不能调用大量外部提供的cPython包

JPype:

参考网页:http://jpype.sourceforge.net/

民间个人开发,支持语言嵌入,支持cPython包,最新版发布于2009年1月。(只是作者的blog的最新篇还是2007年的。。。)

缺点:文档似乎不是很全

JPype演示实例:

1:安装java

首先需要安装JDK 6

sudo apt-get install sun-java6-jdk

之后需要设置默认的java程序

sudo update-alternatives --config java

按照提示输入对应的选项,指定为JDK 6

下面设置

sudo vim /etc/environment

在其中添加如下两行:

CLASSPATH=/usr/lib/jvm/java-6-sun/lib
JAVA_HOME=/usr/lib/jvm/java-6-sun

如果其中已经设置了CLASSPATH和JAVA_HOME,则将其修改为上面的形式

2:安装JPype包

sudo python setup.py install

设置当前用户的JAVA_HOME环境变量,可以在.bashrc中设置:

export JAVA_HOME=/usr/lib/jvm/java-6-sun

3:测试java文件:

Test.java


  1. public class Test{

    public static void main(String[] args){

    System.out.println("hello world!");

    }

    }

编译:$ javac Test.java

运行:$ java Test

其中第一步会产生 Test.class,也是我们下一步测试要用的。


书写第二个java文件,这个文件没有main函数:

JavaHello.java

  1. class JavaHello{

    public JavaHello(){

    }

    public void speak(String msg){

    System.out.println(msg);

    }

    public static void hello(String [] args){

    System.out.println("hello world!");

    }

    }


编译:javac JavaHello.java

产生了 JavaHello.class


下面关键看使用JPype来调用了:

test.py


  1. #!/usr/bin/env python

    #-*- coding:utf-8 -*-

    from jpype import startJVM, getDefaultJVMPath, java, shutdownJVM

    from jpype import JClass, JPackage


    def test_jpype():

    startJVM(getDefaultJVMPath())

    """

    调用java函数

    """

    java.lang.System.out.println("hello world")


    """

    调用main函数

    """

    JavaTest = JClass("Test")

    JavaTest.main(["hello"])


    """

    调用辅助的class

    """

    JavaHello = JClass("JavaHello")

    JavaHello.hello(["hello"])

    j = JavaHello()

    j.speak("hello world!")

    """

    调用包

    """

    Pkg = JPackage("java.lang")

    Pkg.System.out.println("hello world")


    shutdownJVM()


    if __name__ == "__main__":

    test_jpype()

上面的例子分别实现了,调用java原生函数,调用我们自己的class,调用java的package

JCC:


参考网页:http://pypi.python.org/pypi/JCC

1:ubuntu下直接找这个jcc的debian包就好了,或者sudo apt-get install jcc

2:将上面打成的包,Test.jar扩展成python的模块:

  1. sudo python -m jcc --jar Test.jar --python JCCTest --build --install

3:测试执行:

  1. python

  2. >>> import JCCTest

  3. >>> JCCTest.initJVM(classpath=JCCTest.CLASSPATH)

  4. >>> JCCTest.Test.main([""])

遇到的问题:

1:第一次写java代码,完全不知道怎么做,到使用javac编译出了.class文件,才有点头绪(因为这些步骤和python编译出pyc是类似的),其中遇到了CLASSPATH设置不正确的问题,例如在上面执行 java Test可能会报这个错,可能就是CLASSPATH没设置正确的问题,在执行时可以手动指定CLASSPATH,例如:java -cp ./ Test ,这样就指定了CLASSPATH (也就是cp)为当前目录。

2:打包,如果打完的包需要 java -jar xxx.jar 来执行,那么需要包含mainfest信息,先写一个xxx.mf,里面的内容:

Main-Class: Test

(注意其中,Test和冒号之间要有空格,Test后要换行)

然后执行命令:

jar cvfm Test.jar xxx.mf Test.class

这样生成的Test.jar的包才可以用 java -jar Test.jar来执行

3:安装JCC完后,在python中import jcc 总说找不到这个so,找不到那个so,比如libjava.so libjvm.so libverify.so 这些so文件在/usr/lib/jvm/java-6-openjdk/jre/lib/i386(/client)里,在/usr/lib里建立到这些库文件的软连接即可。

结论:

个人建议使用JPype,能使用cPython的包是很关键的,对于以后扩展也很有好处,毕竟cPython的包比Jython的多很多。

如果能集成比较好,以后java的很多有用的工具都可以尝试一下,例如lucence(虽然有了pyLucence),hadoop(这个将来应该会非常有用)

JCC也不错,这个和JPype的最后发布日期都比较远(大概到2007年),不知道这些大虾们还会不会继续维护

其他:

1:Jython其实更多是Java程序员的福音。JPype则是为Python程序员打开了Java之门。 ---某网友

2:网上说的所谓的jpype不稳定 这个是不对的 如果出现问题第一看看自己的代码 第二 看你调用的java的代码 第三看jpype的手册 还有 千万不要在调用jpype的时候在python里使用那个关闭jvm的那个函数 让python运行完自己关闭jvm 不然 。。。 我发现死在那里动不了 呵呵
---某网友

3:JCC被Chandler这个项目使用了,Chandler就是在《梦断代码》里一直描述的那个半死不活的项目。

2009年3月15日星期日

First

yo2的blog服务实在太烂了,发表文章速度超慢,还经常程序或者页面出错,要早点知道blogger这边可以帮定域名,也不会去交那个36.5了~

但是这种不受天朝控制的blogger提供商,恐怕又会经常被GFW强暴,真是左右为难啊。


whatever~ try this blogger