lee的个人空间

    理论指导实践,实践验证理论

    正在浏览 资料总结 里的文章

    根据MapReduce计算的流程,在Map阶段选择好KeyValue,然后在reduce中输出计算结果,计算UV的话,最终的结果只是一个数字

    我最开始的思路是:

    map阶段选择常数1作为key,uid作为value,在reduce阶段将map输出放置到HashSet中排重,输出hashSet的size即为正确的UV

    我运行的日志如下:

    11/08/22 16:14:54 INFO mapred.JobClient:   FileSystemCounters
    11/08/22 16:14:54 INFO mapred.JobClient:     FILE_BYTES_READ=817583842
    11/08/22 16:14:54 INFO mapred.JobClient:     HDFS_BYTES_READ=11406242768
    11/08/22 16:14:54 INFO mapred.JobClient:     FILE_BYTES_WRITTEN=1459132572
    11/08/22 16:14:54 INFO mapred.JobClient:     HDFS_BYTES_WRITTEN=9
    11/08/22 16:14:54 INFO mapred.JobClient:   Map-Reduce Framework
    11/08/22 16:14:54 INFO mapred.JobClient:     Reduce input groups=1
    11/08/22 16:14:54 INFO mapred.JobClient:     Combine output records=0
    11/08/22 16:14:54 INFO mapred.JobClient:     Map input records=5097005
    11/08/22 16:14:54 INFO mapred.JobClient:     Reduce shuffle bytes=0
    11/08/22 16:14:54 INFO mapred.JobClient:     Reduce output records=1
    11/08/22 16:14:54 INFO mapred.JobClient:     Spilled Records=17676018
    11/08/22 16:14:54 INFO mapred.JobClient:     Map output bytes=61164060
    11/08/22 16:14:54 INFO mapred.JobClient:     Combine input records=0
    11/08/22 16:14:54 INFO mapred.JobClient:     Map output records=5097005
    11/08/22 16:14:54 INFO mapred.JobClient:     Reduce input records=5097005
    从以上红色标记日志可以看出处理记录数为5097005条,但reduce分组为1,reduce输出结果也为1
    这是因为key值相同,在mapreduce过程中会将相同的key归并在一起,不难理解这种情况仅仅只有一个reduce任务
    由此造成的后果是reduce任务只会在集群其中一个节点上运行,不仅没有利用好集群处理能力,反倒因为大量数据集中而导致计算效率低下,如果出现OOM异常,那也算正常(因为将所有用户都放置到一个hashset中)
    改进:
    既然reduce任务负载不均衡是因为key单一导致,那可以打散key,把reduce任务分解成N个,分配到整个集群上计算;这就可以解决问题
    我在map阶段把uid整数化后(为什么要整数化,而不直接用字符串?这个大家都应该清楚,整数相对字符串而言,整数更节省存储容量,值比较效率更高) 对1000取模充当key,value为uid; 于是就有1000个不同的key,也将产生1000个reduce任务,这样就充分利用了集群的计算能力;
    map阶段     key: uid%1000                   value: uid
    这样就会产生0-999的key,reduce部分不需要改动,输出的结果可能如下:

    uid%1000=0       XXX

    uid%1000=1        XXX

    uid%1000=2       XXXX

    ……………………….

    uid%1000=998   XXXX

    uid%1000=999   XXXX

    上面输出的结果并非我需要的,但无碍,可以在这基础上再做mapreduce一遍,reduce任务里求和就可以了

    map代码:
    public void map(Object key, Text value, Context ctx)
    throws IOException, InterruptedException {
    try {

    String[] elements = value.toString().split(“\\|”); // 按|分隔符打散

    String userid = elements[7]; // 获取用户标识

    long uidHash = NetUtil.hash(NetUtil.computeMd5(userid), 0); // 哈希转整数

    int mapKey = (int) (uidHash % 1000);//取模将key打散

    ctx.write(new IntWritable(mapKey), new LongWritable(uidHash));

    } catch (Exception e) {
    e.printStackTrace();
    return;
    }
    }

    reduce代码

    public void reduce(IntWritable key, Iterable<LongWritable> values,
    Context ctx) throws IOException, InterruptedException {

    Set<Long> uidSet = new HashSet<Long>();

    Iterator<LongWritable> iter = values.iterator();
    while (iter.hasNext()) {
    long uid = iter.next().get();
    uidSet.add(uid);
    }

    ctx.write(key, new LongWritable(uidSet.size()));
    }

    运行后的日志如下:

    11/08/22 16:08:39 INFO mapred.JobClient:   FileSystemCounters
    11/08/22 16:08:39 INFO mapred.JobClient:     FILE_BYTES_READ=817583986
    11/08/22 16:08:39 INFO mapred.JobClient:     HDFS_BYTES_READ=11406242768
    11/08/22 16:08:39 INFO mapred.JobClient:     FILE_BYTES_WRITTEN=1459132860
    11/08/22 16:08:39 INFO mapred.JobClient:     HDFS_BYTES_WRITTEN=7890
    11/08/22 16:08:39 INFO mapred.JobClient:   Map-Reduce Framework
    11/08/22 16:08:39 INFO mapred.JobClient:     Reduce input groups=1000
    11/08/22 16:08:39 INFO mapred.JobClient:     Combine output records=0
    11/08/22 16:08:39 INFO mapred.JobClient:     Map input records=5097005
    11/08/22 16:08:39 INFO mapred.JobClient:     Reduce shuffle bytes=0
    11/08/22 16:08:39 INFO mapred.JobClient:     Reduce output records=1000
    11/08/22 16:08:39 INFO mapred.JobClient:     Spilled Records=17676018
    11/08/22 16:08:39 INFO mapred.JobClient:     Map output bytes=61164060
    11/08/22 16:08:39 INFO mapred.JobClient:     Combine input records=0
    11/08/22 16:08:39 INFO mapred.JobClient:     Map output records=5097005
    11/08/22 16:08:39 INFO mapred.JobClient:     Reduce input records=5097005
    同样的输入记录数,但可以看到以分散成了1000个reduce任务,输出结果同样是1000个

    最近自己写的nio程序持续报错(client-server端借助mina实现)

    DefaultExceptionMonitor.exceptionCaught(45) 2011-09-02 09:40:57 | Unexpected exception.
    java.nio.channels.ClosedSelectorException
    at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:66)
    at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:80)
    at org.apache.mina.transport.socket.nio.NioProcessor.select(NioProcessor.java:69)
    at org.apache.mina.core.polling.AbstractPollingIoProcessor$Processor.run(AbstractPollingIoProcessor.java:961)
    at org.apache.mina.util.NamePreservingRunnable.run(NamePreservingRunnable.java:64)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:650)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:675)
    at java.lang.Thread.run(Thread.java:595)

    通过搜索引擎没有查出问题,但可能的原因是线程阻塞在Selector.select()时,调用了wakeup方法,而此时selector已经关闭

    与淘宝大牛 @dennis zhuang 对这个问题做了一次交流,他与我一致认为问题不在mina上,而在于使用错误,导致mina关闭了selector。

    因client每发送一次消息都会报一次异常,Dennis 以此推断是我用短连接但没有复用NioSocketConnector造成的,client端发送完消息关闭NioSocketConnector就可能报ClosedSelectorException问题,这涉及mina的内部实现:

    当client端关闭connector,mina要关闭NioProcessor线程,此时线程正阻塞在select上,所以先关闭selector,然后唤醒它,mina就会提示selector已经被关闭

    此外创建NioSocketConnector代价太大,所以即使用短连接也得考虑复用它,而不是每通信完一次,就将其关闭,这样就可以解决异常问题(复用connector不关闭它),同时还能提高性能。当然最好的是用mina构建client-server的长连接,实现重连机制,通信稳定且性能更优!

     

    简要需求如下(注:以下域名和ip形式纯属假象,你肯定没见过a.b.c.d的ip,这些字母代表0-255之间的数字):

    1,aaa.com———>a.b.c.d:8080/web

    2,aaa.com/admin–>a.b.c.d:8082/admin

    3,bbb.com———->a.b.c.d:8083/wap

    经过多次的尝试,始终没法把第一条和第三条映射上,nginx的简化配置如下:

    server aaa.com

    location / {

    proxy_pass http://a.b.c.d:8080/web

    }

    location /admin{

    proxy_pass http://a.b.c.d:8082/admin

    }

    server bbb.com

    location /{

    proxy_pass http://a.b.c.d:8083/wap

    }

    # service nginx reconfigure后发现除了第二条映射生效外,其他两个都始终报302 404错误,然后将第一、三条 location / 分别改成 /web   /wap

    与/admin一致后,然后通过aaa.com/web  aaa.com/wap访问就能成功,通过这规律可能是一个域名只能映射到ip:port;而到模块名就必须也得在域名后加上相应的模块名;而且要用到模块名,在location处的映射必须与应用名一致即如果配置了下面这种形式同样会出错:

    server aaa.com

    location /myAdmin{

    proxy_pass http://a.b.c.d:8082/admin;

    }

    以上红色字体部分对某些应用必须要相同,否则没法跳转。

    根据现有情况,只能改变应用模块的访问方式了,将web,wap所有的应用名去掉,保证a.b.c.d:8080就能访问到web站点,而不需要加上/web,wap同理

    改动后自己服务器上测试后通过!

     

    nginx安装:http://www.javali.org/360

    hive特性:

    数据存储在hdfs上,依托hadoop集群实现并行计算

    采用hiveQL作为查询语言,与SQL极其相似

    Hive中存储的数据无固定格式要求,可随用户自定义

    可追加数据,但不支持更改

    可扩展性强,支持大规模并行计算

     

    hive安装
    请确保 hadoop集群处在运行状态
    当前用户环境变量中有HADOOP_HOME,如果不设置,hive没法运行

    $cd /opt/soft
    $wget  http://apache.etoak.com/hive/hive-0.7.0/hive-0.7.0-bin.tar.gz
    $tar -zxvf  hive-0.7.0-bin.tar.gz
    $mv hive-0.7.0 hive
    $cd hive/conf

    $cp hive-default.xml hive-site.xml
    $cd ../bin
    $./hive   启动hive进入命令行

    操作指南
    可见官网文档:https://cwiki.apache.org/confluence/display/Hive/GettingStarted#GettingStarted-SQLOperations

    Hive提供了很多的函数,可以在命令行下show functions罗列所有的函数
    你会发现这些函数名与mysql的很相近,绝大多数相同的,
    可通过describe function functionName 查看函数使用方法

    hive支持的数据类型很简单就int,string等原子类型, 连日期时间类型也不支持,

    但通过to_date unix_timestamp date_diff date_add date_sub等函数就能完成mysql同样的时间日期复杂操作

    分区
    hive与mysql分区有些区别,mysql分区是用表结构中的字段来分区(range,list,hash等),而hive不同,他需要手工指定分区列,这个列是独立于表结构,但属于表中一列,在加载数据时手动指定分区

    试用:
    create table mytest(id int,name string ,regtime string) PARTITIONED by (ds string) row format delimited fields terminated by ‘\t’ stored as textfile ;

    创建一个数据文件
    1       sawenlee        2011-07-28 22:23:12
    2       tonylee 2011-08-03 22:24:21
    3       nick    2011-08-02 6:2:43

    执行
    LOAD DATA LOCAL INPATH ‘../data/test.txt’ OVERWRITE INTO TABLE mytest  PARTITION (ds=’2011-08-02′);

    LOAD DATA LOCAL INPATH ‘../data/test.txt’ OVERWRITE INTO TABLE mytest  PARTITION (ds=’2011-08-03′);

    再看hive文件结构

    如果执行一个查询,hive会将其转换成map reduce在hadoop上执行(select * from mytest除外)

    select * from mytest where to_date(regtime) >to_date(’2011-8-1′);

    项目地址:http://code.google.com/p/newrsslib4j/

    源码下载:svn checkout http://newrsslib4j.googlecode.com/svn/trunk/ newrsslib4j-read-only

    下载jar包:   http://code.google.com/p/newrsslib4j/downloads/list

    描述:在rsslib4j源码基础上,保持外接口不变,添加了rss字节流编码探测及内部编码转换,理论上支持所有的编码,组件使用较原版更健壮、更稳定


    change log

    #1,引入了mozilla的jchardet包,作为组件一部分,用于探测rss字节流编码,新增了一辅助类Chaset.java 提供了几个静态编码探测方法:
    String guess(URL url);
    String guess(String path)
    String guess(InputStream in)


    #2,引入UnicodeReader替换原有的InputStreamReader,不用InputStreamReader的 原因在于某些UTF-8编码开始会带有BOM (Byte Order Mark) ;导致解析xml失败:Content is not allowed in prolog. 这是jdk1.5的bug,1.6已修复;在sun的buglist里发现了这个问题,并找到了UnicodeReader


    #3,改造RSSParser解析类,新加一个类属性 private InputSource is; 替换原有的InputStream,因原有没有考虑国际化编码问题,而InputSource可以Reader构造产生,改造完如下:
    /**
    * Set rss resource by URL
    * @param ur the remote url
    * @throws RSSException
    */
    public void setXmlResource(URL ur) throws RSSException{
    try{

    URLConnection con = u.openConnection();
    con.setReadTimeout(10000);
    String charset = Charset.guess(ur);
    is = new InputSource (new UnicodeReader(con.getInputStream(),charset));
    if (con.getContentLength() == -1 && is == null){
    this.fixZeroLength();
    }
    }catch(IOException e){
    throw new RSSException(“RSSParser::setXmlResource fails: “+e.getMessage());
    }
    }


    user guide
    代码片段:
    URL url = new URL(“http://cn.engadget.com/rss.xml”);
    RSSHandler handler = new RSSHandler();   RSSParser.parseXmlFile(url, handler, false);
    RSSChannel ch = handler.getRSSChannel();
    System.out.println(ch.toString());
    List<RSSItem> lst = handler.getRSSChannel().getItems();
    for (int j = 0; j < lst.size(); j++) {
    RSSItem itm = lst.get(j);
    System.out.println(itm.toString());
    }

    准备

    1. jdk1.6及以上
    2. cygwin
    3. eclipse3.X
    4. hadoop-0.20.2.tar.gz
    5. 参见hadoop分布式集群搭建一文

    cygwin安装
    进入cygwin首页,点击setup.exe
    在线安装,选择一个镜像点直接下一步;如果安装不成功,则换一个镜像

    默认的cygwin安装是不带openssh的以及vi工具的;前者是hadoop环境必备的,后者则用来编辑配置文件

    可以重新再点击setup.exe,进入到软件列表里,在查询框分别输入openssh 、vi
    点击条目变成install,而非skip ,下一步即可实现在线安装


    配置sshd
    $ ssh-keygen   一直按回车直到执行结束

    $ cat ~/.ssh/id_rsa.pub >>authorized_keys
    执行ssh localhost还需要密码的话,就分别执行chmod 755 authorized_keys


    安装hadoop
    将hadoop-0.20.2.tar.gz拷贝到CYGWIN_HOME/home/USER_NAME/下
    $ tar -zxvf hadoop-0.20.2.tar.gz
    $ mv hadoop-0.20.2 hadoop
    $ cd hadoop/conf
    $ vi hadoop-env.sh  设置JAVA_HOME 如果不设置则无法启动hadoop

    $ vi core-site.xml
    内容修改为:
    <property>
    <name>fs.default.name</name>
    <value>hdfs://localhost:9770</value>
    </property>

    $ vi mapred-site.xml    我的cygwin_home是c:\cygwin 用户名是sawenlee
    内容修改为
    <property>
    <name>mapred.job.tracker</name>
    <value>localhost:9771</value>
    </property>
    <property>
    <name>mapred.system.dir</name>
    <value>C:\cygwin\home\sawenlee\hadoop\system\mapred.system.dir</value>
    </property>
    <property>
    <name>mapred.local.dir</name>
    <value>C:\cygwin\home\sawenlee\hadoop\data\mapred.local.dir</value>
    </property>
    <property>
    <name>mapred.child.tmp</name>
    <value>C:\cygwin\home\sawenlee\hadoop\data\temp</value>
    </property>

    $ vi hdfs-site.xml
    内容修改为
    <property>
    <name>dfs.name.dir</name>
    <value>C:\cygwin\home\sawenlee\hadoop\data\dfs.name.dir</value>
    </property>
    <property>
    <name>dfs.data.dir</name>
    <value>C:\cygwin\home\sawenlee\hadoop\data\dfs.data.dir</value>
    </property>
    <property>
    <name>dfs.permissions</name>
    <value>false</value>
    </property>
    <property>
    <name>dfs.replication</name>
    <value>1</value>
    </property>


    启动hadoop服务
    $ bin/hadoop namenode -format  #格式化namenode
    $ bin/start-all.sh  启动namenode datanode jobtracker tasktracker等


    配置eclipse插件
    将hadoop目录下的contrib/eclipse-plugin.jar拷贝到eclipse安装目录的plugins下
    重新启动eclipse即可看到右上角视图中多了一个map/reduce视角

    配置hadoop_home
    window–>preferences–>map/reduce设置hadoop安装路径,必须与服务配置一致,不然会产生意料不到的问题

    配置hdfs location
    在map/reduce视图下,new hadoop location…
    如下图配置: 注意第一个端口是在core-site.xml中配置的,第二个是在mapred-site.xml配置的,根据实际情况填写

    如果配置成功可以直接浏览到hdfs上的文件,如下图


    运行本地测试程序
    new project—>map/reduce project —>new class

    public class HdfsCat {

    /**
    * @param args
    * @throws IOException
    */
    public static void main(String[] args) throws IOException {
    String uri = “hdfs://localhost:9770/tmp/wordcount/test.txt”;
    Configuration cnf = new Configuration();
    FileSystem fs = FileSystem.get(URI.create(uri),cnf);
    InputStream in = null;
    try {
    in = fs.open(new Path(uri));
    IOUtils.copyBytes(in, System.out, 4096,false);
    }finally{
    IOUtils.closeStream(in);
    }
    }
    }

    直接run,如果没有出错,在控制台看到文件内容了,那么恭喜你,本地调试环境搭建成功了

    今天无意间发现开发服务器磁盘空间利用近100%,结合df & du查找出了罪魁祸首是mysql的数据文件和日志文件占用过多磁盘如图

    ibdata1占用了35G,mysql-bin.****占用近20G;而开发服务器是非主从架构,不存在日志复制,数据同步机制的。

    所以第一步可以把二进制文件清理掉

    以管理员身份登录mysql

    执行RESET MASTER清除所有的二进制日志

    另外还可以有针对性的删除
    PURGE MASTER LOGS TO & PURGE MASTER LOGS BEFORE
    执行PURGE MASTER LOGS TO ‘mysql-bin.******’命令,是单独删除日志文件
    执行PURGE MASTER LOGS BEFORE ‘yyyy-mm-dd hh:mm:ss’命令,是将在’yyyy-mm-dd hh:mm:ss’时间之前的所有日志进行删除

     

    如果存在主从同步,设置二进制日志文件失效时间可有效防止磁盘空间无止境的被占用

    expire_logs_days=3 这样日志文件只保留3天,如果设置0则代表日志文件永不失效

     

     

     

     

     

    ibdata1数据文件瘦身

    在网上查找了相关资料,ibdata1是存放innodb引擎数据和索引的文件,因开发服务器参数设置不当,所有数据库数据及索引都存放在一个文件,而且这个文件有个特点是:不能通过mysql命令缩小,删除或者drop数据(表)同样不会变小,但新增数据可重复利用空间;

    如果给ibdata1瘦身就只能先将库结构及数据dump备份成文件,然后drop掉所有的库,并删除掉ibdata1,利用mysql将之前备份的文件重建数据库

    如果采用独立表空间则可以避免这个问题,设置方式;此法有风险,请在备用机上测试OK后再尝试

    在my.cnf中的innodb块新增一行配置

    innodb_file_per_table

    重启server后,今后新建的innodb引擎表都会独立一个文件以table_name.ibd文件存在每个库数据文件夹下。

    准备

    • nginx-0.8.x
    • pcre-8.10
    • zlib-1.2.5

    并解压到/opt/soft目录下


    准备nginx用户,并加入到www组中:

    #追加一个www组
    groupadd -f www 

    #追加一个nginx用户
    useradd -s /sbin/nologin -g www nginx

    编译nginx:以下每个参数看着是- 实际是两个横线,要替换下

    ./configure –prefix=/usr/local/nginx \ 

    –user=nginx –group=www \

    –with-pcre=/opt/soft/pcre-8.10 \

    –with-zlib=/opt/soft/zlib-1.2.5 \

    –with-http_stub_status_module \

    –without-http_fastcgi_module \

    –without-http_memcached_module \

    –without-http_map_module \

    –without-http_geo_module \

    –without-http_autoindex_module


    make;make install

    #编译启动文件
    vi /etc/init.d/nginx

    将下述内容拷贝其中:

    #!/bin/bash
    # v.0.0.1
    # create by jackbillow at 2007.10.15
    # nginx – This shell script takes care of starting and stopping nginx.
    #
    # chkconfig: – 60 50
    # description: nginx [engine x] is light http web/proxy server
    # that answers incoming ftp service requests.
    # processname: nginx
    # config: /etc/nginx.conf
    # 目录需要替换
    nginx_path=”/usr/local/nginx”
    nginx_pid=”/usr/local/nginx/nginx.pid” 

    # Source function library.
    . /etc/rc.d/init.d/functions

    # Source networking configuration.
    . /etc/sysconfig/network

    # Check that networking is up.
    [ ${NETWORKING} = "no" ] && exit 0
    [ -x $nginx_path/sbin/nginx ] || exit 0
    RETVAL=0
    prog=”nginx”

    start() {
    # Start daemons.
    if [ -e $nginx_pid -a ! -z $nginx_pid ];then
    echo “nginx already running….”
    exit 1
    fi
    if [ -e $nginx_path/conf/nginx.conf ];then
    echo -n $”Starting $prog: ”
    $nginx_path/sbin/nginx -c $nginx_path/conf/nginx.conf &
    RETVAL=$?
    [ $RETVAL -eq 0 ] &&
    { touch /var/lock/subsys/$prog success $”$prog” }

    echo
    else
    RETVAL=1
    fi
    return $RETVAL
    }
    # Stop daemons.
    stop() {
    echo -n $”Stopping $prog: ”
    killproc -d 10 $nigx_path/sbin/nginx
    RETVAL=$?
    echo
    [ $RETVAL = 0 ] && rm -f $nginx_pid /var/lock/subsys/$prog
    }
    # See how we were called.
    case “$1″ in
    start)
    start
    ;;
    stop)
    stop
    ;;
    reconfigure)
    stop
    start
    ;;
    status)
    status $prog
    RETVAL=$?
    ;;
    *)
    echo $”Usage: $0
    {start |stop |reconfigure |status}


    exit 1
    esac
    exit $RETVAL


    #追加系统服务
    chkconfig –add /etc/init.d/nginx


    #分配执行权限
    chmod +x /etc/init.d/nginx


    #启动nginx
    service nginx start


    #停止nginx
    service nginx stop


    #重启nginx
    service nginx reconfigure


    #查看nginx状态
    service nginx status

     

    这次完全是奔着活动主题去的——当下火热的nosql,云计算平台的解决方案

    活动地点是在中关村某咖啡店,首先是签到领取t-shirt,进入会场演讲者steven早已就位,看到首页PPT投影在屏幕上时就已明白,T-shirt不是白送的,饮料也不是白喝的,演讲者Steven是来推广他家产品的。

    废话少说,直奔主题!

    couchbase有多款产品: couchbase server  &  cousebase single server & couchbase mobile

    couchbase server是这次分享的主角,毕竟是云计算的解决方案定位的主题

    couchbase server有以下特点:

    1,A distributed key-value NoSQL database——schema-less, auto-sharding, high-performance

    2,Horizontally scalable——可动态增删节点(Zero downtime topology change),通过界面操作即可,系统会自动rebalance节点数据

    3,Support hadoop integration——要做大量数据统计分析时可通过flume&sqoop将数据从couchbase迁移到hdfs

    4,Easy management and monitoring——完善友好的监控系统(可通过后台监测ops、内存、硬盘、网络等指标)

    couchbase架构

    moxi为分布式代理,通过key查找它的value位置完全由它来决定,相当于memcache客户端的分布式hash算法,couchbase 集成了memcachd充当缓存,它完全可以memcached模式运行,数据就完全存储在内存中,不会持久化到磁盘上。


    数据写入流程

    rebalance机制——如何能zero downtime topology change,仅仅是在节点扩展时,迁移需要迁移的数据,如下图node1&node2中仅仅迁移了多余的两组数据,所以速度还是非常快的

    这仅仅是本次会议总结,今后有机会再测试下它的性能;附上一张现场讨论PP

    couchbase官方网站:http://www.couchbase.com/products-and-services/overview

    培训文档点这里下载

    关于XStream

    XStream是一个将对象序列化成XML同样可从XML还原对象的简单类库

    特性.

    • 简单易用. 提供了高级封装来简化用户使用
    • 不需要任何映射 在不需要映射的情况下, 大多数对象可以序列化成xml
    • 性能好,消耗内存低
    • 生成的xml方便阅读,格式紧凑
    • 不需要对对象修改,就可以序列化内部字段,包括private & final。支持非public类和内部类,并且不需要有默认构造器
    • 定制化转换策略

    应用场景

    • 协议传输
    • 持久化
    • 配置
    • 单元测试

    已知的局限性

    Known Limitations
    If using the enhanced mode, XStream can re-instantiate classes that do not have a default constructor. However, if using a different JVM like an old JRockit version, a JDK 1.3 or you have restrictions because of a SecurityManager, a default constructor is required.
    The enhanced mode is also necessary to restore final fields for any JDK < 1.5. This implies deserialization of instances of an inner class.
    Auto-detection of annotations may cause race conditions. Preprocessing annotations is safe though

    入门资料参见: http://xstream.codehaus.org/tutorial.html

    我们做一个稍微复杂的例子,Object包含类属性,List成员

    Object2XML

    操作的对象如下:

    public class Member {
    private String name;
    private int age;
    //getters and setters are omitted

    public class Team {

    private String name;

    private List<Member> members;

    private Member leader;
    //getters and setters are omitted
    }

    我们定义一个Writer调用Xstream API直接输出文件:

    Member member1 = new Member(“lee”,28);
    Member member2 = new Member(“tae”,26);
    Member leader = new Member(“Kal”,30);
    List<Member> memberList = new ArrayList<Member>();
    memberList.add(member1);
    memberList.add(member2);
    memberList.add(leader); 

    Team team = new Team();
    team.setLeader(leader);
    team.setName(“Mobile Technology Center”);
    team.setMembers(memberList);

    // Serialize the object
    XStream xs = new XStream();
    try
    { FileOutputStream fs = new FileOutputStream( “d:/tmp/team1.txt”); xs.toXML(team, fs); }

    catch (FileNotFoundException e1)
    { e1.printStackTrace(); }

    输出文件:
    <com.sohu.xstream.mine.Team>
    <name>Mobile Technology Center</name>
    <members>
    <com.sohu.xstream.mine.Member>
    <name>lee</name>
    <age>28</age>
    </com.sohu.xstream.mine.Member>
    <com.sohu.xstream.mine.Member>
    <name>tae</name>
    <age>26</age>
    </com.sohu.xstream.mine.Member>
    <com.sohu.xstream.mine.Member>
    <name>Kal</name>
    <age>30</age>
    </com.sohu.xstream.mine.Member>
    </members>
    <leader reference=”../members/com.sohu.xstream.mine.Member3″/>
    </com.sohu.xstream.mine.Team>

    这显然不可能拿来当协议输出,至少应该把类的package去掉,另外leader节点也没有输出正常的内容,Xstream提供了良好的扩展,可以自定义输出格式,只需要注册一个Converter即可
    我们定义TeamConverter覆盖marshal方法

    @Override
    public void marshal(Object obj, HierarchicalStreamWriter writer,
    MarshallingContext ctx) {
    Team team = (Team) obj;
    Member leader = team.getLeader();
    writer.startNode(“Name”);
    writer.setValue(team.getName());
    writer.endNode();
    writer.startNode(“leader”);
    writer.setValue(leader.getName());
    writer.endNode(); 

    writer.startNode(“members”);
    for (Member member : team.getMembers())
    { writer.startNode(“Member”); // writer.setValue(member.) writer.addAttribute(“Name”, member.getName()); writer.addAttribute(“age”, member.getAge()+”"); writer.endNode(); }

    writer.endNode();
    }

    客户端调用时加上属性设置和注册转换器:
    xs.alias(Team.class.getSimpleName(),Team.class);
    xs.alias(Member.class.getSimpleName(), Member.class);
    xs.registerConverter(new TeamConverter());
    这样输出的文档就比较友好了:
    <Team>
    <Name>Mobile Technology Center</Name>
    <leader>Kal</leader>
    <members>
    <Member Name=”lee” age=”28″/>
    <Member Name=”tae” age=”26″/>
    <Member Name=”Kal” age=”30″/>
    </members>
    </Team>

    XML2Object

    在TeamConverter中覆盖unmarshal方法

    @Override
    public Object unmarshal(HierarchicalStreamReader reader,
    UnmarshallingContext ctx) { 

    Team team = new Team();
    reader.moveDown();
    team.setName(reader.getValue());
    reader.moveUp();
    reader.moveDown();
    team.setLeader(new Member(reader.getValue(), 20));
    reader.moveUp();
    reader.moveDown();
    List<Member> memberList = new ArrayList<Member>();
    while (reader.hasMoreChildren()) {
    reader.moveDown();
    Member member = new Member(reader.getAttribute(0),
    Integer.parseInt(reader.getAttribute(1)));
    System.out.println(reader.getNodeName() + reader.getValue() + “|”
    + reader.getAttributeCount());
    memberList.add(member);
    reader.moveUp();
    }
    team.setMembers(memberList);
    reader.moveUp();
    return team;
    }

    然后在客户端调用反序列类Reader

    XStream xs = new XStream(new DomDriver());
    Team t = new Team(); 

    try {
    FileInputStream fis = new FileInputStream(“d:/tmp/team.txt”);
    xs.alias(Team.class.getSimpleName(), Team.class);
    xs.alias(Member.class.getSimpleName(), Member.class);
    xs.registerConverter(new TeamConverter());
    Object obj = xs.fromXML(fis);
    t = (Team) obj;

    // print the data from the object that has been read
    System.out.println(t.getName());

    //why not work???
    // xs.fromXML(fis, t);
    // System.out.println(t.getName());
    } catch (FileNotFoundException ex) {
    ex.printStackTrace();
    }

    经过测试是成功的!

    但Reader里调用xs.fromXML(InputStream,Object)方法不起作用,暂时还未找到原因

    //it doesn’t work
    xs.fromXML(fis, t);
    System.out.println(t.getName()); 

    附上源码:xstream