Quantcast
Channel: IT社区推荐资讯 - ITIndex.net
Viewing all 15843 articles
Browse latest View live

Hibernate延迟加载机制

$
0
0

转自:http://blog.163.com/xi_zh_qi/blog/static/8501594200812695053939/

延迟加载:

   延迟加载机制是为了避免一些无谓的性能开销而提出来的,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作。在Hibernate中提供了对实体对象的延迟加载以及对集合的延迟加载,另外在Hibernate3中还提供了对属性的延迟加载。下面我们就分别介绍这些种类的延迟加载的细节。

A、实体对象的延迟加载:

如果想对实体对象使用延迟加载,必须要在实体的映射配置文件中进行相应的配置,如下所示:

<hibernate-mapping>

<class name=”com.neusoft.entity.User” table=”user” lazy=”true”>

    ……

</class>

</hibernate-mapping>

通过将class的lazy属性设置为true,来开启实体的延迟加载特性。如果我们运行下面的代码:

User user=(User)session.load(User.class,”1”);(1)

System.out.println(user.getName());(2)

当运行到(1)处时,Hibernate并没有发起对数据的查询,如果我们此时通过一些调试工具(比如JBuilder2005的Debug工具),观察此时user对象的内存快照,我们会惊奇的发现,此时返回的可能是User$EnhancerByCGLIB$$bede8986类型的对象,而且其属性为null,这是怎么回事?还记得前面我曾讲过session.load()方法,会返回实体对象的代理类对象,这里所返回的对象类型就是User对象的代理类对象。在Hibernate中通过使用CGLIB,来实现动态构造一个目标对象的代理类对象,并且在代理类对象中包含目标对象的所有属性和方法,而且所有属性均被赋值为null。通过调试器显示的内存快照,我们可以看出此时真正的User对象,是包含在代理对象的CGLIB$CALBACK_0.target属性中,当代码运行到(2)处时,此时调用user.getName()方法,这时通过CGLIB赋予的回调机制,实际上调用CGLIB$CALBACK_0.getName()方法,当调用该方法时,Hibernate会首先检查CGLIB$CALBACK_0.target属性是否为null,如果不为空,则调用目标对象的getName方法,如果为空,则会发起数据库查询,生成类似这样的SQL语句:select * from user where id=’1’;来查询数据,并构造目标对象,并且将它赋值到CGLIB$CALBACK_0.target属性中。

    这样,通过一个中间代理对象,Hibernate实现了实体的延迟加载,只有当用户真正发起获得实体对象属性的动作时,才真正会发起数据库查询操作。所以实体的延迟加载是用通过中间代理类完成的,所以只有session.load()方法才会利用实体延迟加载,因为只有session.load()方法才会返回实体类的代理类对象。

B、        集合类型的延迟加载:

在Hibernate的延迟加载机制中,针对集合类型的应用,意义是最为重大的,因为这有可能使性能得到大幅度的提高,为此Hibernate进行了大量的努力,其中包括对JDK Collection的独立实现,我们在一对多关联中,定义的用来容纳关联对象的Set集合,并不是java.util.Set类型或其子类型,而是net.sf.hibernate.collection.Set类型,通过使用自定义集合类的实现,Hibernate实现了集合类型的延迟加载。为了对集合类型使用延迟加载,我们必须如下配置我们的实体类的关于关联的部分:

<hibernate-mapping>

    <class name=”com.neusoft.entity.User” table=”user”>

…..

<set name=”addresses” table=”address” lazy=”true” inverse=”true”>

<key column=”user_id”/>

<one-to-many class=”com.neusoft.entity.Arrderss”/>

</set>

    </class>

</hibernate-mapping>

通过将<set>元素的lazy属性设置为true来开启集合类型的延迟加载特性。我们看下面的代码:

User user=(User)session.load(User.class,”1”);

Collection addset=user.getAddresses();       (1)

Iterator it=addset.iterator();                (2)

while(it.hasNext()){

Address address=(Address)it.next();

System.out.println(address.getAddress());

}

当程序执行到(1)处时,这时并不会发起对关联数据的查询来加载关联数据,只有运行到(2)处时,真正的数据读取操作才会开始,这时Hibernate会根据缓存中符合条件的数据索引,来查找符合条件的实体对象。

这里我们引入了一个全新的概念——数据索引,下面我们首先将接一下什么是数据索引。在Hibernate中对集合类型进行缓存时,是分两部分进行缓存的,首先缓存集合中所有实体的id列表,然后缓存实体对象,这些实体对象的id列表,就是所谓的数据索引。当查找数据索引时,如果没有找到对应的数据索引,这时就会一条select SQL的执行,获得符合条件的数据,并构造实体对象集合和数据索引,然后返回实体对象的集合,并且将实体对象和数据索引纳入Hibernate的缓存之中。另一方面,如果找到对应的数据索引,则从数据索引中取出id列表,然后根据id在缓存中查找对应的实体,如果找到就从缓存中返回,如果没有找到,在发起select SQL查询。在这里我们看出了另外一个问题,这个问题可能会对性能产生影响,这就是集合类型的缓存策略。如果我们如下配置集合类型:

<hibernate-mapping>

    <class name=”com.neusoft.entity.User” table=”user”>

…..

<set name=”addresses” table=”address” lazy=”true” inverse=”true”>

<cache usage=”read-only”/>

<key column=”user_id”/>

<one-to-many class=”com.neusoft.entity.Arrderss”/>

</set>

    </class>

</hibernate-mapping>

这里我们应用了<cache usage=”read-only”/>配置,如果采用这种策略来配置集合类型,Hibernate将只会对数据索引进行缓存,而不会对集合中的实体对象进行缓存。如上配置我们运行下面的代码:

User user=(User)session.load(User.class,”1”);

Collection addset=user.getAddresses();      

Iterator it=addset.iterator();               

while(it.hasNext()){

Address address=(Address)it.next();

System.out.println(address.getAddress());

}

System.out.println(“Second query……”);

User user2=(User)session.load(User.class,”1”);

Collection it2=user2.getAddresses();

while(it2.hasNext()){

Address address2=(Address)it2.next();

System.out.println(address2.getAddress());

}

运行这段代码,会得到类似下面的输出:

Select * from user where id=’1’;

Select * from address where user_id=’1’;

Tianjin

Dalian

Second query……

Select * from address where id=’1’;

Select * from address where id=’2’;

Tianjin

Dalian

我们看到,当第二次执行查询时,执行了两条对address表的查询操作,为什么会这样?这是因为当第一次加载实体后,根据集合类型缓存策略的配置,只对集合数据索引进行了缓存,而并没有对集合中的实体对象进行缓存,所以在第二次再次加载实体时,Hibernate找到了对应实体的数据索引,但是根据数据索引,却无法在缓存中找到对应的实体,所以Hibernate根据找到的数据索引发起了两条select SQL的查询操作,这里造成了对性能的浪费,怎样才能避免这种情况呢?我们必须对集合类型中的实体也指定缓存策略,所以我们要如下对集合类型进行配置:

<hibernate-mapping>

    <class name=”com.neusoft.entity.User” table=”user”>

…..

<set name=”addresses” table=”address” lazy=”true” inverse=”true”>

<cache usage=”read-write”/>

<key column=”user_id”/>

<one-to-many class=”com.neusoft.entity.Arrderss”/>

</set>

    </class>

</hibernate-mapping>

此时Hibernate会对集合类型中的实体也进行缓存,如果根据这个配置再次运行上面的代码,将会得到类似如下的输出:

Select * from user where id=’1’;

Select * from address where user_id=’1’;

Tianjin

Dalian

Second query……

Tianjin

Dalian

这时将不会再有根据数据索引进行查询的SQL语句,因为此时可以直接从缓存中获得集合类型中存放的实体对象。

C、       属性延迟加载:

   在Hibernate3中,引入了一种新的特性——属性的延迟加载,这个机制又为获取高性能查询提供了有力的工具。在前面我们讲大数据对象读取时,在User对象中有一个resume字段,该字段是一个java.sql.Clob类型,包含了用户的简历信息,当我们加载该对象时,我们不得不每一次都要加载这个字段,而不论我们是否真的需要它,而且这种大数据对象的读取本身会带来很大的性能开销。在Hibernate2中,我们只有通过我们前面讲过的面性能的粒度细分,来分解User类,来解决这个问题(请参照那一节的论述),但是在Hibernate3中,我们可以通过属性延迟加载机制,来使我们获得只有当我们真正需要操作这个字段时,才去读取这个字段数据的能力,为此我们必须如下配置我们的实体类:

<hibernate-mapping>

<class name=”com.neusoft.entity.User” table=”user”>

……

<property name=”resume” type=”java.sql.Clob” column=”resume” lazy=”true”/>

    </class>

</hibernate-mapping>

通过对<property>元素的lazy属性设置true来开启属性的延迟加载,在Hibernate3中为了实现属性的延迟加载,使用了类增强器来对实体类的Class文件进行强化处理,通过增强器的增强,将CGLIB的回调机制逻辑,加入实体类,这里我们可以看出属性的延迟加载,还是通过CGLIB来实现的。CGLIB是Apache的一个开源工程,这个类库可以操纵java类的字节码,根据字节码来动态构造符合要求的类对象。根据上面的配置我们运行下面的代码:

String sql=”from User user where user.name=’zx’ ”;

Query query=session.createQuery(sql);    (1)

List list=query.list();

for(int i=0;i<list.size();i++){

User user=(User)list.get(i);

System.out.println(user.getName());

System.out.println(user.getResume());    (2)

}

当执行到(1)处时,会生成类似如下的SQL语句:

Select id,age,name from user where name=’zx’;

这时Hibernate会检索User实体中所有非延迟加载属性对应的字段数据,当执行到(2)处时,会生成类似如下的SQL语句:

Select resume from user where id=’1’;

这时会发起对resume字段数据真正的读取操作。



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐




ECharts 2.0.1 发布,JavaScript 图表库

$
0
0

ECharts 2.0.1 发布,此版本是 bug 修复版本,更新内容如下:

  • 【#】 移动设备事件失效

  • [#] [gauge]min max非默认值时计算错误

  • [#] [map]图例开关无数据时图表没更新

  • [#] [map]选择切换图表类型时错误

  • [#] [markLine]过渡更新错误fix #427 »

  • [#] 无option时resize报错

  • [#] symbole无法显示0值数据,fix #442 »

  • [#] [timeline]起始index不为0时错误,fix #403 »

  • [#] 原数组长度为1时addData错误,fix #394 »

  • [#] loading动画bubble、dymnline刷新问题,fix #406 »

  • [?] 依赖升级, ZRender 2.0.1+

ECharts开源来自百度商业前端数据可视化团队,基于html5 Canvas,是一个纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据可视化图表。创新的拖拽重计算、数据视图、值域漫游等特 性大大增强了用户体验,赋予了用户对数据进行挖掘、整合的能力。

ECharts (Enterprise Charts 商业产品图表库)

提供商业产品常用图表,底层基于 ZRender(一个全新的轻量级canvas类库),创建了坐标系,图例,提示,工具箱等基础组件,并在此上构建出折线图(区域图)、柱状图(条状图)、散点图(气泡图)、饼图(环形图)、K线图、地图、力导向布局图以及和弦图,同时支持任意维度的堆积和多图表混合展现。


HBase 写优化之 BulkLoad 实现数据快速入库

$
0
0

1、为何要 BulkLoad 导入?传统的 HTableOutputFormat 写 HBase 有什么问题?

我们先看下 HBase 的写流程:

通常 MapReduce 在写HBase时使用的是 TableOutputFormat 方式,在reduce中直接生成put对象写入HBase,该方式在大数据量写入时效率低下(HBase会block写入,频繁进行flush,split,compact等大量IO操作),并对HBase节点的稳定性造成一定的影响(GC时间过长,响应变慢,导致节点超时退出,并引起一系列连锁反应),而HBase支持 bulk load 的入库方式,它是利用hbase的数据信息按照特定格式存储在hdfs内这一原理,直接在HDFS中生成持久化的HFile数据格式文件,然后上传至合适位置,即完成巨量数据快速入库的办法。配合mapreduce完成,高效便捷,而且不占用region资源,增添负载,在大数据量写入时能极大的提高写入效率,并降低对HBase节点的写入压力。
通过使用先生成HFile,然后再BulkLoad到Hbase的方式来替代之前直接调用HTableOutputFormat的方法有如下的好处:
(1)消除了对HBase集群的插入压力
(2)提高了Job的运行速度,降低了Job的执行时间
目前此种方式仅仅适用于只有一个列族的情况,在新版 HBase 中,单列族的限制会消除。

2、bulkload 流程与实践

bulkload 方式需要两个Job配合完成: 
(1)第一个Job还是运行原来业务处理逻辑,处理的结果不直接调用HTableOutputFormat写入到HBase,而是先写入到HDFS上的一个中间目录下(如 middata) 
(2)第二个Job以第一个Job的输出(middata)做为输入,然后将其格式化HBase的底层存储文件HFile 
(3)调用BulkLoad将第二个Job生成的HFile导入到对应的HBase表中

下面给出相应的范例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
importjava.io.IOException;
 
importorg.apache.hadoop.conf.Configuration;
importorg.apache.hadoop.fs.Path;
importorg.apache.hadoop.hbase.HBaseConfiguration;
importorg.apache.hadoop.hbase.KeyValue;
importorg.apache.hadoop.hbase.client.HTable;
importorg.apache.hadoop.hbase.client.Put;
importorg.apache.hadoop.hbase.io.ImmutableBytesWritable;
importorg.apache.hadoop.hbase.mapreduce.HFileOutputFormat;
importorg.apache.hadoop.hbase.mapreduce.KeyValueSortReducer;
importorg.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles;
importorg.apache.hadoop.hbase.util.Bytes;
importorg.apache.hadoop.io.IntWritable;
importorg.apache.hadoop.io.LongWritable;
importorg.apache.hadoop.io.Text;
importorg.apache.hadoop.mapreduce.Job;
importorg.apache.hadoop.mapreduce.Mapper;
importorg.apache.hadoop.mapreduce.Reducer;
importorg.apache.hadoop.mapreduce.lib.input.FileInputFormat;
importorg.apache.hadoop.mapreduce.lib.input.TextInputFormat;
importorg.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
importorg.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
importorg.apache.hadoop.util.GenericOptionsParser;
 
publicclassGeneratePutHFileAndBulkLoadToHBase {
 
    publicstaticclassWordCountMapper extendsMapper<LongWritable, Text, Text, IntWritable>
    {
 
        privateText wordText=newText();
        privateIntWritable one=newIntWritable(1);
        @Override
        protectedvoidmap(LongWritable key, Text value, Context context)
                throwsIOException, InterruptedException {
            // TODO Auto-generated method stub
            String line=value.toString();
            String[] wordArray=line.split(" ");
            for(String word:wordArray)
            {
                wordText.set(word);
                context.write(wordText, one);
            }
             
        }
    }
     
    publicstaticclassWordCountReducer extendsReducer<Text, IntWritable, Text, IntWritable>
    {
 
        privateIntWritable result=newIntWritable();
        protectedvoidreduce(Text key, Iterable<IntWritable> valueList,
                Context context)
                throwsIOException, InterruptedException {
            // TODO Auto-generated method stub
            intsum=0;
            for(IntWritable value:valueList)
            {
                sum+=value.get();
            }
            result.set(sum);
            context.write(key, result);
        }
         
    }
     
    publicstaticclassConvertWordCountOutToHFileMapper extendsMapper<LongWritable, Text, ImmutableBytesWritable, Put>
    {
 
        @Override
        protectedvoidmap(LongWritable key, Text value, Context context)
                throwsIOException, InterruptedException {
            // TODO Auto-generated method stub
            String wordCountStr=value.toString();
            String[] wordCountArray=wordCountStr.split("\t");
            String word=wordCountArray[0];
            intcount=Integer.valueOf(wordCountArray[1]);
             
            //创建HBase中的RowKey
            byte[] rowKey=Bytes.toBytes(word);
            ImmutableBytesWritable rowKeyWritable=newImmutableBytesWritable(rowKey);
            byte[] family=Bytes.toBytes("cf");
            byte[] qualifier=Bytes.toBytes("count");
            byte[] hbaseValue=Bytes.toBytes(count);
            // Put 用于列簇下的多列提交,若只有一个列,则可以使用 KeyValue 格式
            // KeyValue keyValue = new KeyValue(rowKey, family, qualifier, hbaseValue);
            Put put=newPut(rowKey);
            put.add(family, qualifier, hbaseValue);
            context.write(rowKeyWritable, put);
             
        }
         
    }
     
    publicstaticvoidmain(String[] args) throwsException {
        // TODO Auto-generated method stub
        Configuration hadoopConfiguration=newConfiguration();
        String[] dfsArgs = newGenericOptionsParser(hadoopConfiguration, args).getRemainingArgs();
         
        //第一个Job就是普通MR,输出到指定的目录
        Job job=newJob(hadoopConfiguration, "wordCountJob");
        job.setJarByClass(GeneratePutHFileAndBulkLoadToHBase.class);
        job.setMapperClass(WordCountMapper.class);
        job.setReducerClass(WordCountReducer.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);
        FileInputFormat.setInputPaths(job, newPath(dfsArgs[0]));
        FileOutputFormat.setOutputPath(job, newPath(dfsArgs[1]));
        //提交第一个Job
        intwordCountJobResult=job.waitForCompletion(true)?0:1;
         
        //第二个Job以第一个Job的输出做为输入,只需要编写Mapper类,在Mapper类中对一个job的输出进行分析,并转换为HBase需要的KeyValue的方式。
        Job convertWordCountJobOutputToHFileJob=newJob(hadoopConfiguration, "wordCount_bulkload");
         
        convertWordCountJobOutputToHFileJob.setJarByClass(GeneratePutHFileAndBulkLoadToHBase.class);
        convertWordCountJobOutputToHFileJob.setMapperClass(ConvertWordCountOutToHFileMapper.class);
        //ReducerClass 无需指定,框架会自行根据 MapOutputValueClass 来决定是使用 KeyValueSortReducer 还是 PutSortReducer
        //convertWordCountJobOutputToHFileJob.setReducerClass(KeyValueSortReducer.class);
        convertWordCountJobOutputToHFileJob.setMapOutputKeyClass(ImmutableBytesWritable.class);
        convertWordCountJobOutputToHFileJob.setMapOutputValueClass(Put.class);
         
        //以第一个Job的输出做为第二个Job的输入
        FileInputFormat.addInputPath(convertWordCountJobOutputToHFileJob, newPath(dfsArgs[1]));
        FileOutputFormat.setOutputPath(convertWordCountJobOutputToHFileJob, newPath(dfsArgs[2]));
        //创建HBase的配置对象
        Configuration hbaseConfiguration=HBaseConfiguration.create();
        //创建目标表对象
        HTable wordCountTable =newHTable(hbaseConfiguration, "word_count");
        HFileOutputFormat.configureIncrementalLoad(convertWordCountJobOutputToHFileJob,wordCountTable);
        
        //提交第二个job
        intconvertWordCountJobOutputToHFileJobResult=convertWordCountJobOutputToHFileJob.waitForCompletion(true)?0:1;
         
        //当第二个job结束之后,调用BulkLoad方式来将MR结果批量入库
        LoadIncrementalHFiles loader = newLoadIncrementalHFiles(hbaseConfiguration);
        //第一个参数为第二个Job的输出目录即保存HFile的目录,第二个参数为目标表
        loader.doBulkLoad(newPath(dfsArgs[2]), wordCountTable);
         
        //最后调用System.exit进行退出
        System.exit(convertWordCountJobOutputToHFileJobResult);
         
    }
 
}

比如原始的输入数据的目录为:/rawdata/test/wordcount/20131212 

中间结果数据保存的目录为:/middata/test/wordcount/20131212 
最终生成的HFile保存的目录为:/resultdata/test/wordcount/20131212 
运行上面的Job的方式如下: 
hadoop jar test.jar /rawdata/test/wordcount/20131212 /middata/test/wordcount/20131212 /resultdata/test/wordcount/20131212 

3、说明与注意事项:

(1)HFile方式在所有的加载方案里面是最快的,不过有个前提——数据是第一次导入,表是空的。如果表中已经有了数据。HFile再导入到hbase的表中会触发split操作。

(2)最终输出结果,无论是map还是reduce,输出部分key和value的类型必须是: < ImmutableBytesWritable, KeyValue>或者< ImmutableBytesWritable, Put>。
否则报这样的错误:

1
2
3
java.lang.IllegalArgumentException: Can't read partitions file
...
Caused by: java.io.IOException: wrong key class: org.apache.hadoop.io.*** is not classorg.apache.hadoop.hbase.io.ImmutableBytesWritable
(3)最终输出部分,Value类型是KeyValue 或Put,对应的Sorter分别是KeyValueSortReducer或PutSortReducer,这个 SorterReducer 可以不指定,因为源码中已经做了判断:
1
2
3
4
5
6
7
if(KeyValue.class.equals(job.getMapOutputValueClass())) {
    job.setReducerClass(KeyValueSortReducer.class);
} elseif(Put.class.equals(job.getMapOutputValueClass())) {
    job.setReducerClass(PutSortReducer.class);
} else{
    LOG.warn("Unknown map output value type:"+ job.getMapOutputValueClass());
}
(4) MR例子中job.setOutputFormatClass(HFileOutputFormat.class); HFileOutputFormat只适合一次对单列族组织成HFile文件,多列簇需要起多个 job,不过新版本的 Hbase 已经解决了这个限制。 

(5) MR例子中最后生成HFile存储在HDFS上,输出路径下的子目录是各个列族。如果对HFile进行入库HBase,相当于move HFile到HBase的Region中,HFile子目录的列族内容没有了。

(6)最后一个 Reduce 没有 setNumReduceTasks 是因为,该设置由框架根据region个数自动配置的。

(7)下边配置部分,注释掉的其实写不写都无所谓,因为看源码就知道configureIncrementalLoad方法已经把固定的配置全配置完了,不固定的部分才需要手动配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
publicclassHFileOutput {
        //job 配置
    publicstaticJob configureJob(Configuration conf) throwsIOException {
        Job job = newJob(configuration, "countUnite1");
        job.setJarByClass(HFileOutput.class);
                //job.setNumReduceTasks(2); 
        //job.setOutputKeyClass(ImmutableBytesWritable.class);
        //job.setOutputValueClass(KeyValue.class);
        //job.setOutputFormatClass(HFileOutputFormat.class);
  
        Scan scan = newScan();
        scan.setCaching(10);
        scan.addFamily(INPUT_FAMILY);
        TableMapReduceUtil.initTableMapperJob(inputTable, scan,
                HFileOutputMapper.class, ImmutableBytesWritable.class, LongWritable.class, job);
        //这里如果不定义reducer部分,会自动识别定义成KeyValueSortReducer.class 和PutSortReducer.class
                job.setReducerClass(HFileOutputRedcuer.class);
        //job.setOutputFormatClass(HFileOutputFormat.class);
        HFileOutputFormat.configureIncrementalLoad(job, newHTable(
                configuration, outputTable));
        HFileOutputFormat.setOutputPath(job, newPath());
                //FileOutputFormat.setOutputPath(job, new Path()); //等同上句
        returnjob;
    }
  
    publicstaticclassHFileOutputMapper extends
            TableMapper<ImmutableBytesWritable, LongWritable> {
        publicvoidmap(ImmutableBytesWritable key, Result values,
                Context context) throwsIOException, InterruptedException {
            //mapper逻辑部分
            context.write(newImmutableBytesWritable(Bytes()), LongWritable());
        }
    }
  
    publicstaticclassHFileOutputRedcuer extends
            Reducer<ImmutableBytesWritable, LongWritable, ImmutableBytesWritable, KeyValue> {
        publicvoidreduce(ImmutableBytesWritable key, Iterable<LongWritable> values,
                Context context) throwsIOException, InterruptedException {
                        //reducer逻辑部分
            KeyValue kv = newKeyValue(row, OUTPUT_FAMILY, tmp[1].getBytes(),
                    Bytes.toBytes(count));
            context.write(key, kv);
        }
    }
}

4、Refer:

1、Hbase几种数据入库(load)方式比较

http://blog.csdn.net/kirayuan/article/details/6371635

2、MapReduce生成HFile入库到HBase及源码分析

http://blog.pureisle.net/archives/1950.html

3、MapReduce生成HFile入库到HBase

http://shitouer.cn/2013/02/hbase-hfile-bulk-load/



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



Js删除节点

$
0
0

Js删除节点

 方式一:传this参数调用方法:

1.jsp页面代码:

<p><input id="uploadImage" value="" type="file" name="uploadImage" size="50" /><a href="javascript:void(0);" onclick="deleteNode(this)" ><img src="$!{request.contextPath}/images/lend/close.png" class="closea"></a></p>

 

2.js脚本代码:

function deleteNode(obj) {
    var p= obj.parentNode;
     if(p){
         p.remove();
     }
}

 

 

 方式二:js方法中通过选择器获取节点:

1.jsp页面代码:

<p><input id="uploadImage" value="" type="file" name="uploadImage" size="50" /><a id="deleteId" href="javascript:void(0);" onclick="deleteNode()" ><img src="$!{request.contextPath}/images/lend/close.png" class="closea"></a></p>

 

2.js脚本代码:

function deleteNode() {
    var a= doucment.getElementById("deleteId");
    a.parentNode.removeChild(div);
    //或者
    a.remove()
 
    //此处删除的是a节点
}

 

 

方式三:通过jQuery方式获取节点:(尚未测试,有待测试。。。)

 

此处a标签传this到js中,js通过this(即a节点)取parent(即p节点)

那么:

(1)p.remove();可直接删除整个p节点

(2)p.parentNode.removeChild(p);也可以直接删除整个p节点

(3)p.removeChild(obj);可以删除整个a节点

(4)obj.parentNode.removeChild(obj);也可以删除整个a节点



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



itext对水印图片的旋转、放大、缩小等操作的示例

$
0
0
package test1;

import java.awt.Color;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;

import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Font;
import com.lowagie.text.HeaderFooter;
import com.lowagie.text.Image;
import com.lowagie.text.PageSize;
import com.lowagie.text.Paragraph;
import com.lowagie.text.Phrase;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfGState;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfStamper;
import com.lowagie.text.pdf.PdfWriter;

public class PdfConvertor {
 

   // txt原始文件的路径
    private static final String txtFilePath = "c:/test/01.txt";
    // 生成的pdf文件路径
    private static final String pdfFilePath = "c:/test/01.pdf";
    // 添加水印图片路径
    private static final String imageFilePath = "c:/test/images/psue.jpg";
    // 生成临时文件前缀
    private static final String prefix = "tempFile";
    // 所有者密码
    private static final String OWNERPASSWORD = "123456";

    
    public static void generatePDFWithTxt(String txtFile, String pdfFile,
            String userPassWord, String waterMarkName, int permission) {
        try {
            // 生成临时文件
            File file = File.createTempFile(prefix, ".pdf");
            // 创建pdf文件到临时文件
            if (createPDFFile(txtFile, file)) {
                // 增加水印和加密
                waterMark(file.getPath(), pdfFile, userPassWord, OWNERPASSWORD,
                        waterMarkName, permission);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    
    private static boolean createPDFFile(String txtFilePath, File file) {
        // 设置纸张
        Rectangle rect = new Rectangle(PageSize.A4);
        // 设置页码
        HeaderFooter footer = new HeaderFooter(new Phrase("页码:", PdfConvertor
                .setChineseFont()), true);
        footer.setBorder(Rectangle.NO_BORDER);
        // step1
        Document doc = new Document(rect, 50, 50, 50, 50);
        doc.setFooter(footer);
        try {
            FileReader fileRead = new FileReader(txtFilePath);
            BufferedReader read = new BufferedReader(fileRead);
            // 设置pdf文件生成路径 step2
            PdfWriter.getInstance(doc, new FileOutputStream(file));
            // 打开pdf文件 step3
            doc.open();
            // 实例化Paragraph 获取写入pdf文件的内容,调用支持中文的方法. step4
            while (read.ready()) {
                // 添加内容到pdf(这里将会按照txt文件的原始样式输出)
                doc.add(new Paragraph(read.readLine(), PdfConvertor
                        .setChineseFont()));
            }
            // 关闭pdf文件 step5
            doc.close();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    
    private static void waterMark(String inputFile, String outputFile,
            String userPassWord, String ownerPassWord, String waterMarkName,
            int permission) {
        try {
            PdfReader reader = new PdfReader(inputFile);
            PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(
                    outputFile));
            // 设置密码
            //stamper.setEncryption(userPassWord.getBytes(), ownerPassWord.getBytes(), permission, false);
            BaseFont base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H",
                    BaseFont.NOT_EMBEDDED);
            int total = reader.getNumberOfPages() + 1;
            Image image = Image.getInstance(imageFilePath);
            image.setAbsolutePosition(50, 400);//坐标
            image.setRotation(-20);//旋转 弧度
            image.setRotationDegrees(-45);//旋转 角度
//            image.scaleAbsolute(200,100);//自定义大小
            image.scalePercent(50);//依照比例缩放
            PdfContentByte under;
            int j = waterMarkName.length();
            char c = 0;
            int rise = 0;
            for (int i = 1; i < total; i++) {
                rise = 500;
                under = stamper.getUnderContent(i);
                // 添加图片
                under.addImage(image);
                PdfGState gs = new PdfGState();
                gs.setFillOpacity(0.2f);// 设置透明度为0.2
                under.setGState(gs);
                under.beginText();
                under.setColorFill(Color.CYAN);
                under.setFontAndSize(base, 30);
                // 设置水印文字字体倾斜 开始
                if (j >= 15) {
                    under.setTextMatrix(200, 120);
                    for (int k = 0; k < j; k++) {
                        under.setTextRise(rise);
                        c = waterMarkName.charAt(k);
                        under.showText(c + "");
                        rise -= 20;
                    }
                } else {
                    under.setTextMatrix(180, 100);
                    for (int k = 0; k < j; k++) {
                        under.setTextRise(rise);
                        c = waterMarkName.charAt(k);
                        under.showText(c + "");
                        rise -= 18;
                    }
                }
                // 字体设置结束
                under.endText();
                // 画一个圆
                // under.ellipse(250, 450, 350, 550);
                // under.setLineWidth(1f);
                // under.stroke();
            }
            stamper.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    
    private static Font setChineseFont() {
        BaseFont base = null;
        Font fontChinese = null;
        try {
            base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H",
                    BaseFont.EMBEDDED);
            fontChinese = new Font(base, 12, Font.NORMAL);
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return fontChinese;
    }

    public static void main(String[] args) {
        generatePDFWithTxt(txtFilePath, pdfFilePath, "123", "", 16);
    }
}

 



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



[翻译]十条有用的 Go 技术

$
0
0

原文 在此,实用总结。
————翻译分隔线————

十条有用的 Go 技术

这里是我过去几年中编写的大量 Go 代码的经验总结而来的自己的最佳实践。我相信它们具有弹性的。这里的弹性是指:
某个应用需要适配一个灵活的环境。你不希望每过 3 到 4 个月就不得不将它们全部重构一遍。添加新的特性应当很容易。许多人参与开发该应用,它应当可以被理解,且维护简单。许多人使用该应用,bug 应该容易被发现并且可以快速的修复。我用了很长的时间学到了这些事情。其中的一些很微小,但对于许多事情都会有影响。所有这些都仅仅是建议,具体情况具体对待,并且如果有帮助的话务必告诉我。随时留言:)

1. 使用单一的 GOPATH

多个 GOPATH 的情况并不具有弹性。GOPATH 本身就是高度自我完备的(通过导入路径)。有多个 GOPATH 会导致某些副作用,例如可能使用了给定的库的不同的版本。你可能在某个地方升级了它,但是其他地方却没有升级。而且,我还没遇到过任何一个需要使用多个 GOPATH 的情况。所以只使用单一的 GOPATH,这会提升你 Go 的开发进度。

许多人不同意这一观点,接下来我会做一些澄清。像 etcdcamlistore这样的大项目使用了像 godep这样的工具,将所有依赖保存到某个目录中。也就是说,这些项目自身有一个单一的 GOPATH。它们只能在这个目录里找到对应的版本。除非你的项目很大并且极为重要,否则不要为每个项目使用不同的 GOPAHT。如果你认为项目需要一个自己的 GOPATH 目录,那么就创建它,否则不要尝试使用多个 GOPATH。它只会拖慢你的进度。

2. 将 for-select 封装到函数中

如果在某个条件下,你需要从 for-select 中退出,就需要使用标签。例如:

func main() {

L:
    for {
        select {
        case <-time.After(time.Second):
            fmt.Println("hello")
        default:
            break L
        }
    }

    fmt.Println("ending")
}

如你所见,需要联合 break使用标签。这有其用途,不过我不喜欢。这个例子中的 for 循环看起来很小,但是通常它们会更大,而判断 break的条件也更为冗长。

如果需要退出循环,我会将 for-select 封装到函数中:

func main() {
    foo()
    fmt.Println("ending")
}

func foo() {
    for {
        select {
        case <-time.After(time.Second):
            fmt.Println("hello")
        default:
            return
        }
    }
}

你还可以返回一个错误(或任何其他值),也是同样漂亮的,只需要:

// 阻塞
if err := foo(); err != nil {
    // 处理 err
}

3. 在初始化结构体时使用带有标签的语法

这是一个无标签语法的例子:

type T struct {
    Foo string
    Bar int
}

func main() {
    t := T{"example", 123} // 无标签语法
    fmt.Printf("t %+v\n", t)
}

那么如果你添加一个新的字段到 T结构体,代码会编译失败:

type T struct {
    Foo string
    Bar int
    Qux string
}

func main() {
    t := T{"example", 123} // 无法编译
    fmt.Printf("t %+v\n", t)
}

如果使用了标签语法,Go 的兼容性规则( http://golang.org/doc/go1compat)会处理代码。例如在向 net包的类型添加叫做 Zone的字段,参见: http://golang.org/doc/go1.1#library。回到我们的例子,使用标签语法:

type T struct {
    Foo string
    Bar int
    Qux string
}

func main() {
    t := T{Foo: "example", Qux: 123}
    fmt.Printf("t %+v\n", t)
}

这个编译起来没问题,而且弹性也好。不论你如何添加其他字段到 T结构体。你的代码总是能编译,并且在以后的 Go 的版本也可以保证这一点。只要在代码集中执行 go vet,就可以发现所有的无标签的语法。

4. 将结构体的初始化拆分到多行

如果有两个以上的字段,那么就用多行。它会让你的代码更加容易阅读,也就是说不要:

T{Foo: "example", Bar:someLongVariable, Qux:anotherLongVariable, B: forgetToAddThisToo}

而是:

T{
    Foo: "example",
    Bar: someLongVariable,
    Qux: anotherLongVariable,
    B: forgetToAddThisToo,
}

这有许多好处,首先它容易阅读,其次它使得允许或屏蔽字段初始化变得容易(只要注释或删除它们),最后添加其他字段也更容易(只要添加一行)。

5. 为整数常量添加 String() 方法

如果你利用 iota 来使用自定义的整数枚举类型,务必要为其添加 String() 方法。例如,像这样:

type State int

const (
    Running State = iota 
    Stopped
    Rebooting
    Terminated
)

如果你创建了这个类型的一个变量,然后输出,会得到一个整数( http://play.golang.org/p/V5VVFB05HB):

func main() {
    state := Running

    // print: "state 0"
    fmt.Println("state ", state)
}

除非你回顾常量定义,否则这里的 0看起来毫无意义。只需要为 State类型添加 String()方法就可以修复这个问题( http://play.golang.org/p/ewMKl6K302):

func (s State) String() string {
    switch s {
    case Running:
        return "Running"
    case Stopped:
        return "Stopped"
    case Rebooting:
        return "Rebooting"
    case Terminated:
        return "Terminated"
    default:
        return "Unknown"
    }
}

新的输出是: state: Running。显然现在看起来可读性好了很多。在你调试程序的时候,这会带来更多的便利。同时还可以在实现 MarshalJSON()、UnmarshalJSON() 这类方法的时候使用同样的手段。

6. 让 iota 从 a +1 开始增量

在前面的例子中同时也产生了一个我已经遇到过许多次的 bug。假设你有一个新的结构体,有一个 State字段:

type T struct {
    Name  string
    Port  int
    State State
}

现在如果基于 T 创建一个新的变量,然后输出,你会得到奇怪的结果( http://play.golang.org/p/LPG2RF3y39):

func main() {
    t := T{Name: "example", Port: 6666}

    // prints: "t {Name:example Port:6666 State:Running}"
    fmt.Printf("t %+v\n", t)
}

看到 bug 了吗? State字段没有初始化,Go 默认使用对应类型的零值进行填充。由于 State是一个整数,零值也就是 0,但在我们的例子中它表示 Running

那么如何知道 State 被初始化了?还是它真得是在 Running模式?没有办法区分它们,那么这就会产生未知的、不可预测的 bug。不过,修复这个很容易,只要让 iota 从 +1 开始( http://play.golang.org/p/VyAq-3OItv):

const (
    Running State = iota + 1
    Stopped
    Rebooting
    Terminated
)

现在 t变量将默认输出 Unknown,不是吗? :)

func main() {
    t := T{Name: "example", Port: 6666}

    // 输出: "t {Name:example Port:6666 State:Unknown}"
    fmt.Printf("t %+v\n", t)
}

不过让 iota 从零值开始也是一种解决办法。例如,你可以引入一个新的状态叫做 Unknown,将其修改为:

const (
    Unknown State = iota 
    Running
    Stopped
    Rebooting
    Terminated
)

7. 返回函数调用

我已经看过很多代码例如( http://play.golang.org/p/8Rz1EJwFTZ):

func bar() (string, error) {
    v, err := foo()
    if err != nil {
        return "", err
    }

    return v, nil
}

然而,你只需要:

func bar() (string, error) {
    return foo()
}

更简单也更容易阅读(当然,除非你要对某些内部的值做一些记录)。

8. 把 slice、map 等定义为自定义类型

将 slice 或 map 定义成自定义类型可以让代码维护起来更加容易。假设有一个 Server类型和一个返回服务器列表的函数:

type Server struct {
    Name string
}

func ListServers() []Server {
    return []Server{
        {Name: "Server1"},
        {Name: "Server2"},
        {Name: "Foo1"},
        {Name: "Foo2"},
    }
}

现在假设需要获取某些特定名字的服务器。需要对 ListServers() 做一些改动,增加筛选条件:

// ListServers 返回服务器列表。只会返回包含 name 的服务器。空的 name 将会返回所有服务器。
func ListServers(name string) []Server {
    servers := []Server{
        {Name: "Server1"},
        {Name: "Server2"},
        {Name: "Foo1"},
        {Name: "Foo2"},
    }

    // 返回所有服务器
    if name == "" {
        return servers
    }

    // 返回过滤后的结果
    filtered := make([]Server, 0)

    for _, server := range servers {
        if strings.Contains(server.Name, name) {
            filtered = append(filtered, server)
        }
    }

    return filtered
}

现在可以用这个来筛选有字符串 Foo的服务器:

func main() {
    servers := ListServers("Foo")

    // 输出:“servers [{Name:Foo1} {Name:Foo2}]”
    fmt.Printf("servers %+v\n", servers)
}

显然这个函数能够正常工作。不过它的弹性并不好。如果你想对服务器集合引入其他逻辑的话会如何呢?例如检查所有服务器的状态,为每个服务器创建一个数据库记录,用其他字段进行筛选等等……

现在引入一个叫做 Servers的新类型,并且修改原始版本的 ListServers() 返回这个新类型:

type Servers []Server

// ListServers 返回服务器列表
func ListServers() Servers {
    return []Server{
        {Name: "Server1"},
        {Name: "Server2"},
        {Name: "Foo1"},
        {Name: "Foo2"},
    }
}

现在需要做的是只要为 Servers类型添加一个新的 Filter()方法:

// Filter 返回包含 name 的服务器。空的 name 将会返回所有服务器。
func (s Servers) Filter(name string) Servers {
    filtered := make(Servers, 0)

    for _, server := range s {
        if strings.Contains(server.Name, name) {
            filtered = append(filtered, server)
        }

    }

    return filtered
}

现在可以针对字符串 Foo筛选服务器:

func main() {
    servers := ListServers()
    servers = servers.Filter("Foo")
    fmt.Printf("servers %+v\n", servers)
}

哈!看到你的代码是多么的简单了吗?还想对服务器的状态进行检查?或者为每个服务器添加一条数据库记录?没问题,添加以下新方法即可:

func (s Servers) Check() 
func (s Servers) AddRecord() 
func (s Servers) Len()
...

9. withContext 封装函数

有时对于函数会有一些重复劳动,例如锁/解锁,初始化一个新的局部上下文,准备初始化变量等等……这里有一个例子:

func foo() {
    mu.Lock()
    defer mu.Unlock()

    // foo 相关的工作
}

func bar() {
    mu.Lock()
    defer mu.Unlock()

    // bar 相关的工作
}

func qux() {
    mu.Lock()
    defer mu.Unlock()

    // qux 相关的工作
}

如果你想要修改某个内容,你需要对所有的都进行修改。如果它是一个常见的任务,那么最好创建一个叫做 withContext的函数。这个函数的输入参数是另一个函数,并用调用者提供的上下文来调用它:

func withLockContext(fn func()) {
    mu.Lock
    defer mu.Unlock()

    fn()
}

只需要将之前的函数用这个进行封装:

func foo() {
    withLockContext(func() {
        // foo 相关工作
    })
}

func bar() {
    withLockContext(func() {
        // bar 相关工作
    })
}

func qux() {
    withLockContext(func() {
        // qux 相关工作
    })
}

不要光想着加锁的情形。对此来说最好的用例是数据库链接。现在对 withContext 函数作一些小小的改动:

func withDBContext(fn func(db DB)) error {
    // 从连接池获取一个数据库连接
    dbConn := NewDB()

    return fn(dbConn)
}

如你所见,它获取一个连接,然后传递给提供的参数,并且在调用函数的时候返回错误。你需要做的只是:

func foo() {
    withDBContext(func(db *DB) error {
        // foo 相关工作
    })
}

func bar() {
    withDBContext(func(db *DB) error {
        // bar 相关工作
    })
}

func qux() {
    withDBContext(func(db *DB) error {
        // qux 相关工作
    })
}

你在考虑一个不同的场景,例如作一些预初始化?没问题,只需要将它们加到 withDBContext就可以了。这对于测试也同样有效。

这个方法有个缺陷,它增加了缩进并且更难阅读。再次提示,永远寻找最简单的解决方案。

10. 为访问 map 增加 setter,getters

如果你重度使用 map 读写数据,那么就为其添加 getter 和 setter 吧。通过 getter 和 setter 你可以将逻辑封分别装到函数里。这里最常见的错误就是并发访问。如果你在某个 goroutein 里有这样的代码:

m["foo"] = bar

还有这个:

delete(m, "foo")

会发生什么?你们中的大多数应当已经非常熟悉这样的竞态了。简单来说这个竞态是由于 map 默认并非线程安全。不过你可以用互斥量来保护它们:

mu.Lock()
m["foo"] = "bar"
mu.Unlock()

以及:

mu.Lock()
delete(m, "foo")
mu.Unlock()

假设你在其他地方也使用这个 map。你必须把互斥量放得到处都是!然而通过 getter 和 setter 函数就可以很容易的避免这个问题:

func Put(key, value string) {
    mu.Lock()
    m[key] = value
    mu.Unlock()
}
func Delete(key string) {
    mu.Lock()
    delete(m, key)
    mu.Unlock()
}

使用接口可以对这一过程做进一步的改进。你可以将实现完全隐藏起来。只使用一个简单的、设计良好的接口,然后让包的用户使用它们:

type Storage interface {
    Delete(key string)
    Get(key string) string
    Put(key, value string)
}

这只是个例子,不过你应该能体会到。对于底层的实现使用什么都没关系。不光是使用接口本身很简单,而且还解决了暴露内部数据结构带来的大量的问题。

但是得承认,有时只是为了同时对若干个变量加锁就使用接口会有些过分。理解你的程序,并且在你需要的时候使用这些改进。

总结

抽象永远都不是容易的事情。有时,最简单的就是你已经实现的方法。要知道,不要让你的代码看起来很聪明。Go 天生就是个简单的语言,在大多数情况下只会有一种方法来作某事。简单是力量的源泉,也是为什么在人的层面它表现的如此有弹性。

如果必要的话,使用这些基数。例如将 []Server转化为 Servers是另一种抽象,仅在你有一个合理的理由的情况下这么做。不过有一些技术,如 iota 从 1 开始计数总是有用的。再次提醒,永远保持简单。

特别感谢 Cihangir Savas、Andrew Gerrand、Ben Johnson 和 Damian Gryski 提供的极具价值的反馈和建议。

为设计师、产品经理准备的原型制作指南

$
0
0

今天和大家分享个免费PDF电子书,该书适合设计总监、项目经理、产品经理以及设计师阅读,书内容是介绍UI设计原型线框图的知识,十分详细。

为设计师、产品经验准备的原型制作指南

共有100+彩页,虽然是英文版,但是使用了很多配图,简单易懂,对于英文不太好的用户来说也可以看。

为设计师、产品经验准备的原型制作指南

书中还介绍了几个很不错的线框图展示网站,你可以学习其它人是如何绘制原型:

Wireframe Showcase
web.without.words
Wireframes Magazine
MOObileFrames

下载:

微盘下载: http://vdisk.weibo.com/s/axnLmnhc9htSQ(PDF版)

官方地址: http://uxpin.com/guide-to-wireframing.html (需要输入邮箱,有PDF和ZIP版)


Copyright ©2010-2014 ¦ RSS订阅¦ 新浪微博¦ 本文链接¦ 添加评论
交流:UI设计交流群:59300679,与500名设计师交流设计,分享素材。

个性化推荐十大挑战

$
0
0

个性化推荐很多读者都知道,但其中不乏认识上的误区。有的人觉得个性化推荐就是细分市场和精准营销,实际上细分市场和精准营销往往是把潜在的用户分成很多群体,这固然相比基于全体的统计有了长足的进步,但是距离“给每一个用户量身定做的信息服务”还有很大的差距,所以,只能说个性化推荐是细分市场的极致!实际上,信息服务经历了两次理念上的变革,第一次是从总体到群体,第二次是从群体到个体。第二次变革正在进行中,所要用到的核心技术就是这篇文章要讨论的个性化推荐技术。

还有读者觉得个性化推荐就等同于协同过滤,这可能是因为协同过滤应用比较广泛并且比较容易为大众理解。实际上协同过滤只是个性化推荐技术中的一个成员。它与很多更先进技术相比,就好像流行歌曲和高雅音乐,前者广受欢迎,而且一般人也可以拿个麦克风吼两声,但是说到艺术高度,流行歌曲还是要差一些。当然,流行歌曲经济价值可能更大,这也是事实。总的来说,协同过滤只是个性化推荐技术中的一款轻武器,远远不等于个性化推荐技术本身。

image

图1:信息服务的两次变革:从总体到群体,从群体到个体。

有些读者可能不是很了解个性化推荐,我先推荐一些阅读的材料。中文的综述可以看我们2009年在《自然科学进展》上的综述[1]。这篇文章质量不能说很好,但是可以比较快得到很多信息,了解个性化推荐研究的概貌。有了这个基础,如果想要了解突出应用的算法和技术,我推荐项亮和陈义合著的《推荐系统实践》[2]。百分点科技出版过一本名为《个性化:商业的未来》的小册子[3],应用场景和商业模式介绍得比较细致,技术上涉及很少,附录里面介绍了一些主流算法和可能的缺陷,或许能够稍有启发。国外的专著建议关注最近出版的两本[4,5],其中[4]实际上是很多文章的汇总,因为写这些文章的都是达人,所以质量上佳。Adomavicius和Tuzhilin的大型综述特别有影响力,不仅系统回顾了推荐系统研究的全貌,还提出了一些有趣的开放性问题[6]——尽管我个人不是很喜欢他们对于推荐系统的分类方法。我们今年发表了一篇大综述,应该是目前最全面的综述,所强调的不仅仅是算法,还有很多现象和思路[7]——大家有兴趣不妨看看。

有些读者认为个性化推荐技术的研究已经进入了很成熟的阶段,没有什么特别激动人心的问题和成果。恰恰相反,现在个性化推荐技术面临很大的挑战,这也是本文力图让大家认识的。接下来进入正题!我将列出十个挑战(仅代表个人观点),其中有一些是很多年前就认识到但是没有得到解决的长期问题,有一些事实上不可能完全解决,只能提出改良方案,还有一些是最近的一些研究提出来的焦点问题。特别要提醒读者注意的是,这十个挑战并不是孤立的,极有可能一个方向上的突破能够对若干重大挑战都带来进展。

挑战一:数据稀疏性问题

现在待处理的推荐系统规模越来越大,用户和商品(也包括其他物品,譬如音乐、网页、文献……)数目动辄百千万计,两个用户之间选择的重叠非常少。如果用用户和商品之间已有的选择关系占所有可能存在的选择关系的比例来衡量系统的稀疏性,那么我们平时研究最多的MovieLens数据集的稀疏度是4.5%,Netflix是1.2%。这些其实都是非常密的数据了,Bibsonomy是0.35%,Delicious是0.046%。想想淘宝上号称有近10亿商品,平均而言一个用户能浏览1000件吗,估计不能,所以稀疏度应该在百万分之一或以下的量级。数据非常稀疏,使得绝大部分基于关联分析的算法(譬如协同过滤)效果都不好。这种情况下,通过珍贵的选择数据让用户和用户,商品和商品之间产生关联的重要性,往往要比用户之间对商品打分的相关性还重要[8]。举个例子来说,你注意到一个用户看了一部鬼片,这就很大程度上暴露了用户的兴趣,并且使其和很多其他看过同样片子的用户关联起来了——至于他给这个片子评价高还是低,反而不那么重要了。事实上,我们最近的分析显示,稀疏数据情况下给同一个商品分别打负分(低评价)和打正分的两个用户要看做正相关的而非负相关的,就是说负分扮演了“正面的角色”[9]——我们需要很严肃地重新审查负分的作用,有的时候负分甚至作用大于正分[10]。

这个问题本质上是无法完全克服的,但是有很多办法,可以在相当程度上缓解这个问题。首先可以通过扩散的算法,从原来的一阶关联(两个用户有多少相似打分或者共同购买的商品)到二阶甚至更高阶的关联[11-13],甚至通过迭代寻优的方法,考虑全局信息导致的关联[14-15]。这些方法共同的缺点是建立在相似性本身可以传播的假设上,并且计算量往往比较大。其次在分辨率非常高的精度下,例如考虑单品,数据可能非常稀疏。但是如果把这些商品信息粗粒化,譬如只考虑一个个的品类,数据就会立刻变得稠密。如果能够计算品类之间的相似性,就可以帮助进行基于品类的推荐(图2是品类树的示意图)。在语义树方面有过一些这方面的尝试[16],但是很不成熟,要应用到商品推荐上还需要理论和技术上的进步。另外,还可以通过添加一些缺省的打分或选择,提高相似性的分辨率,从而提高算法的精确度[17]。这种添加既可以是随机的,也可能来自于特定的预测算法[18]。

随机的缺省分或随机选择为什么会起到正面的作用呢,仅仅是因为提高了数据的密度吗?我认为仔细的思考会否决这个结论。对于局部热传导的算法[19],添加随机连接能够整体把度最小的一些节点的度提高,从而降低小度节点之间度差异的比例(原来度为1的节点和度为3的节点度值相差2倍,现在都加上2,度为3的节点和度为5的节点度值相差还不到1倍),这在某种程度上可以克服局部热传导过度倾向于推荐最小度节点的缺陷。类似地,随机链接可以克服协同过滤或局部能量扩散算法[20]过度倾向于推荐最大度节点的缺陷。总之,如果拉小度的比例差异能够在某种程度上克服算法的缺陷,那么使用随机缺省打分就能起到提高精确度的作用。

image

图2:品类树的示意图。

挑战二:冷启动问题

新用户因为罕有可以利用的行为信息,很难给出精确的推荐。反过来,新商品由于被选择次数很少,也难以找到合适的办法推荐给用户——这就是所谓的冷启动问题。如果我们能够获得商品充分的文本信息并据此计算商品之间的相似性,就可以很好解决冷启动的问题[21],譬如我们一般不担心图书或者论文推荐会遇到冷启动的问题。不幸的是,大部分商品不同于图书和文章本身就是丰富的内容,在这种情况下通过人工或者自动搜索爬取的方法商品相应的描述,也会有一定的效果。与之相似,通过注册以及询问得知一些用户的属性信息,譬如年龄、居住城市、受教育程度、性别、职业等等,能够得到用户之间属性的相似度,从而提高冷启动时候推荐的精确度[22,23]。

最近标签系统(tagging systems)的广泛应用提供了解决冷启动问题的可能方案[24]。因为标签既可以看作是商品内容的萃取,同时也反映了用户的个性化喜好——譬如对《桃姐》这部电影,有的人打上标签“伦理”,有的人打上标签“刘德华”,两个人看的电影一样,但是兴趣点可能不尽相同。当然,利用标签也只能是提高有少量行为的用户的推荐准确性,对于纯粹的冷启动用户,是没有帮助的,因为这些人还没有打过任何标签。系统也可以给商品打上标签,但是这里面没有个性化的因素,效果会打一个折扣。从这个意义上讲,利用标签进行推荐、激励用户打标签以及引导用户选择合适的标签,都非常重要[25]。

要缓解冷启动的问题,一种有效的办法是尽可能快地了解用户的特点和需求,所以如何设计问卷调查本身以及如何利用其中的信息也是一门大学问[26]。与之相对应,对于一个新商品,怎么样让用户,特别是有影响力的用户,对其给出高质量的评价,对于解决冷启动问题也有重大价值[27]。如何在保证一定推荐精度的情况下,让新用户和新商品的特性尽快暴露,是一个很有意义也很困难的研究难题[28]。

最近一个有趣的研究显示,新用户更容易选择特别流行的商品[29]——这无论如何是一个好消息,说明使用热销榜也能获得不错的结果。冷启动问题还可以通过多维数据的交叉推荐和社会推荐的方法部分解决,其精确度和多样性又远胜于热销榜,这一点我们在后面会进一步介绍。

挑战三:大数据处理与增量计算问题

尽管数据很稀疏,大部分数据都包含百千万计的用户和商品,与此同时,新商品也不断加入系统,新用户不停进入系统,用户和商品之间还不停产生新的连接。数据量不仅大,而且数据本身还时时动态变化,如何快速高效处理这些数据成为迫在眉睫的问题。在这个大前提下,算法时间和空间的复杂性,尤其是前者,获得了空前重视。一般而言,一个高效的算法,要么自身复杂性很低,要么能够很好并行化,要么两者兼具。

提高算法的效率,有很多途径。大致上可以分为两类,一是精确算法,二是近似算法。需要注意的是,精确算法中“精确”这次词,并不是指算法的推荐精确度有多大,而是相对于近似算法而言,强调这个算法并不是以牺牲算法中某些步骤的精确性而提高效率的。譬如说计算 n的阶乘,可以有不同的高精度算法,凡是得出最后精确值的就是精确算法,而如果利用斯特林公式进行计算,就属于近似算法了。一般而言,近似算法的效率会明显高于精确算法。

通过巧妙的方法,可以设计出效率很高的精确算法。譬如Porteous等人设计了一种可以用于潜层狄利克雷分配(Latent Dirichlet Allocation, LDA)算法的新的采样方法,比传统吉布斯采样算法快8倍[30]。Cacheda等人设计了一种预测算法,只考虑一个用户与其他用户打分的差异以及一个商品与其他商品得分的差异,这个算法远远快于协同过滤算法,却能够得到比标准的基于用户的协同过滤算法更精确的预测效果,其预测精度有时候甚至可以和SVD分解的方法媲美[31]。提高精确算法的另外一条途径就是并行化——很多算法的并行化,一点都不简单。谷歌中国成功将LDA算法并行化并应用于Orkut的推荐中,取得了很好的效果[32]。最近Gemulla等人提出了一种随机梯度下降法,可以并行分解百万行列的矩阵,该方法可以应用在包括推荐在内的若干场景下[33]。

近似算法往往基于增量计算,也就是说当产生新用户,新商品以及新的连接关系时,算法的结果不需要在整个数据集上重新进行计算得到,而只需要考虑所增加节点和连边局部的信息,对原有的结果进行微扰,快速得到新结果[34]。一般而言,这种算法随着加入的信息量的增多,其误差会积累变大,最终每过一段时间还是需要利用全局数据重新进行计算。更先进但也更苦难的办法,是设计出一种算法,能够保证其误差不会累积,也就是说其结果与利用全部数据重新计算的结果之间的差异不会单调上升。我们不妨把这种算法叫做自适应算法,它是增量算法的一个加强版本[35],其设计要求和难度更高。增量算法已经在业界有了应用,譬如百分点推荐引擎中的若干算法都采用了增量技术,使得用户每次新浏览、收藏或者购买商品后其推荐列表立刻得到更新。但是自适应算法目前还只是在比较特殊的算法上面
才能实现,更勿谈工业界应用了。

image

图3:兼顾精确性和多样性的混合扩散推荐算法示意图。

挑战四:多样性与精确性的两难困境

如果要给用户推荐他喜欢的商品,最“保险”的方式就是给他特别流行的商品,因为这些商品有更大的可能性被喜欢(否则也不会那么流行),往坏了说,也很难特别被讨厌(不要举凤姐的例子)。但是,这样的推荐产生的用户体验并不一定好,因为用户很可能已经知道这些热销流行的产品,所以得到的信息量很少,并且用户不会认同这是一种“个性化的”推荐。Mcnee等人已经警告大家,盲目崇拜精确性指标可能会伤害推荐系统——因为这样可能会导致用户得到一些信息量为0的“精准推荐”并且视野变得越来越狭窄[36]。事实上,让用户视野变得狭窄也是协同过滤算法存在的一个比较主要的缺陷。已经有一些实证研究显示,多样性、新颖性、偶然性这些从未获得过如精确性一般重要地位的因素,对于用户体验都十分重要——譬如用户希望音乐推荐更多样更偶然[37]。与此同时,应用个性化推荐技术的商家,也希望推荐中有更多的品类出现,从而激发用户新的购物需求。多样性和新颖性的要求在大多数情况下具有一致性,一些商家更喜欢引导用户关注一些销量一般的长尾商品(这些商品的利润往往更多),这种新颖性的要求往往和多样性的要求一致。还有一些特别的需求非常强调多样性和新颖性,譬如类似于“唯品会”这样的限时抢购模式或者最近非常流行的团购模式,广受欢迎的热销商品很快就抢购/团购一空,推荐引擎能够发挥作用的只能是推荐那些不太被主流消费者关注的小众产品。对于新浪微博这类的社会网络,相当部分新用户很快就不活跃了,很大程度上是因为得不到其他人关注。类似地,世纪佳缘和百合网这类的网站中,一个用户如果很长时间没有机会得到任何异性的青睐,也会失去动力。在这种情况下,我们要考虑的不仅仅是向某些用户推荐,而是如何把一些至少目前还不是特别受欢迎的用户推荐出去——这时候,在多样性和新颖性上表现出色的算法意义更大。最近Ugander等人的工作显示,一个用户要向其他若干用户推广某种互联网活动,在同等推广力度下(用推荐的朋友数目衡量),其所选择的推荐对象的结构多样性越大,效果往往越好[38]。

保证推荐的多样性很有价值,但是,推荐多样的商品和新颖的商品与推荐的精确性之间存在矛盾,因为前者风险很大——一个没什么人看过或者打分较低的东西推荐出手,很可能被用户憎恶,从而效果更差。很多时候,这是一个两难的问题,只能通过牺牲多样性来提高精确性,或者牺牲精确性来提高多样性。一种可行之策是直接对算法得到的推荐列表进行处理,从而提升其多样性[39-41]。Hurley和Zhang就是在推荐算法得到的前 N个商品中进行一次组合优化,找出 L个商品( L< N),使得这 L个商品两两之间平均相似度最小[41]。目前百分点推荐引擎所使用的方法也是类似的。这种方法固然在应用上是有效的,但是没有任何理论的基础和优美性可言,只能算一种野蛮而实用的招数。更好的办法是在设计算法的时候就同时考虑推荐的多样性和精确性,这可以通过精巧混合能量扩散和热传导算法[19],或者利用有偏的能量扩散[42]和或有偏的热传导来实现[43]。图3是能量扩散与热传导混合算法的示意图。这个算法认为目标用户选择过的商品具有一定的“推荐能力”,在能量扩散过程中它们被赋予初始能量1,而在热传导的过程中它们被认为是初始热源,具有温度1。能量扩散是一个守恒的过程,每一个时间步节点上的能量都均匀分配给所有邻居(图3上半部分);与之相对的,热传导过程中每一个节点下一个时间步的温度等于其所有邻居温度的平均值(图3下半部分)。前者倾向于推荐热门商品,后者倾向于挖掘冷门商品,两相结合,精确性和多样性都能明显提高[19]。尽管上面提到的这些算法效果很好,似乎也能够比较直观地进行理解,但是我们还没有办法就相关结果提供清晰而深刻的见解。多样性和精确性之间错综复杂的关系和隐匿其后的竞争,到目前为止还是一个很棘手的难题。

image

图4:对推荐系统实施攻击的示意图。

挑战五:推荐系统的脆弱性问题

受推荐系统在电子商务领域重大的经济利益的驱动,一些心怀不轨的用户通过提供一些虚假恶意的行为,故意增加或者压制某些商品被推荐的可能性[44]。图4展示了一个攻击的实例。假设我们现在要决定是否向用户h推荐商品7,如果系统中只有那些合法用户(a-g),通过上表我们发现用户a和f与用户h的品味比较相似,由于用户a和f都喜欢商品7,那么系统应该把商品7推荐给用户h。如果受到恶意攻击,系统会发现大多数由攻击者生成的虚假用户(i-m)的品味都与用户h相似,并且他们对商品7都给了负面的评价,那么在这种情况下,系统就不会把商品7推荐给用户h。这样一来,就达到了那些攻击者降低对商品7推荐可能性的目的。上面的例子仅仅是众多攻击方案中比较简单的一员,Burke等人2011年的研究报告中就分析了4大种类8种不同的攻击策略[45]。除了如图4这样的简单方法外,攻击者还通过将攻击对象和热销商品或特定用户群喜欢的商品绑定而提高攻击效果,甚至通过持续探测猜测系统的计算相似性的算法,从而有针对性地开展攻击。

从上面的介绍可以看出,一个推荐算法能否在一定程度上保持对恶意攻击的鲁棒性,成为需要认真考虑的一个特征。以最简单的关联规则挖掘算法为例,Apriori算法的鲁棒性就远胜于 k近邻算法[46]。有一些技术已经被设计出来提高推荐系统面对恶意攻击的鲁棒性,譬如通过分析对比真实用户和疑似恶意用户之间打分行为模式的差异,提前对恶意行为进行判断,从而阻止其进入系统或赋予疑似恶意用户比较低的影响力[47-49]。总体来说,这方面的研究相对较少,系统性的分析还很缺乏,反而是攻击策略层出不穷,有一种“道高一尺,魔高一丈”的感觉。

挑战六:用户行为模式的挖掘和利用

深入挖掘用户的行为模式有望提高推荐的效果或在更复杂的场景下进行推荐。譬如说,新用户和老用户具有很不一样的选择模式:一般而言,新用户倾向于选择热门的商品,而老用户对于小众商品关注更多[29],新用户所选择的商品相似度更高,老用户所选择的商品多样性较高[50]。上面曾经介绍过的能量扩散和热传导的混合算法[19]可以通过一个单参数调节推荐结果的多样性和热门程度,在这种情况下就可以考虑给不同用户赋予不同的参数(从算法结果的个性化到算法本身的个性化),甚至允许用户自己移动一个滑钮调节这个参数——当用户想看热门的时候,算法提供热门推荐;当用户想找点很酷的产品时,算法也可以提供冷门推荐。

用户行为的时空统计特性也可以用于提高推荐或者设计针对特定场景的应用(关于人类行为时空特性的详细分析请参考文献[51])。最简单的例子是在推荐前考虑用户从事相关活动随时间变化的活跃性。举个例子,在进行手机个性化阅读推荐的时候,如果曾经的数据显示某个用户只在7点到8点之间有一个小时左右的手机阅读行为(可能是上班时在地铁或者公交车上),那么9点钟发送一个电子书阅读的短信广告就是很不明智的选择。从含时数据中还可以分析出影响用户选择的长期和短期的兴趣,通过将这两种效应分离出来,可以明显提高推荐的精确度[52-54]。事实上,简单假设用户兴趣随时间按照指数递减,也能够得到改进的推荐效果[55,56]。随着移动互联网的飞速发展以及GPS及其他手机定位技术的发展和普及,基于位置的服务成为一个受到学术界和业界广泛关注的问题。基于位置信息的推荐可能会成为个性化推荐的一个研究热点和重要的应用场景,而这个问题的解决需要能够对用户的移动模式有深入理解,包括预测用户的移动轨迹和判断用户在当前位置是否有可能进行餐饮购物活动等[57,58],同时还要有定量的办法去定义用户之间以及地点之间的相似性[59,60]。事实上,即便简单把位置信息作为一个单独属性加以考虑,也可以明显提高广告推荐[61]和朋友推荐[62]的精确度。特别要提醒各位读者,知道了用户的位置信息,并不意味着可以随时向用户推荐近处的餐饮购物等等场所,因为频繁而不精确的推荐会让用户有一种被窥探和骚扰的感觉。从这个意义上讲,把握进行推荐的时间和地点非常重要!一般而言,在用户经常出没的地点,譬如工作地点、学校、住家等等进行推荐的效果往往是比较差的,因为用户对于这些地点比系统还熟悉,而且很难想象用户在上下班的路上会有特别地情致购物或者进餐。实际上可以预测的时空信息往往是商业价值比较低的,而用户在吃饭时间去了一个平常不太去的地方,往往有更大的可能是和朋友聚会就餐。这就要求系统更加智能,能够对用户当前行为所蕴含的信息量进行估计(要同时考虑时间和空间),并且在信息量充分大的时候进行推荐。

另外,不同用户打分的模式也很不一样[63,64],用户针对不同商品的行为模式也不一样[65,66](想象你在网上下载一首歌和团购房子时的区别),这些模式都可以挖掘刻画并利用来提高推荐的效果。总而言之,推荐引擎要做的是针对合适的对象在合适的时间和合适的地方推荐合适的内容(4S标准)。通过分析不同用户在选择、评价、时间、空间等等行为模式上的不同,我们最终的目的是猜测目标用户当前的意图,并且针对不同的意图匹配或组合不同的算法结果,将其推荐给用户。这不仅需要更高级的数据分析能力,还需要有丰富经验了解业务逻辑的工作人员配合完成。这种称为“情境计算”的思路,有可能较大程度提高推荐及其他信息服务(譬如搜索)的质量,百分点的推荐引擎就是在这种思路下架构的[67]。

image

图5:推荐系统评价指标一览。

挑战七:推荐系统效果评估

推荐系统的概念提出已经有几十年了,但是怎么评价推荐系统,仍然是一个很大的问题。常见的评估指标可以分为四大类,分别是准确度、多样性、新颖性和覆盖率,每一类下辖很多不同的指标,譬如准确度指标又可以分为四大类,分别是预测评分准确度、预测评分关联、分类准确度、排序准确度四类。以分类准确度为例,又包括准确率、召回率、准确率提高率、召回率提高率、F1指标和AUC值。图5总结了文献中曾经出现过的几乎所有的推荐系统指标[68]。之所以对推荐系统的评价很困难,是因为这些指标之间并不是一致的,一般而言,多样性、新颖性和覆盖率之间一致性较好,但不绝对,而这三者往往都和准确度有冲突。如前所述,解决多样性和精确性之间的矛盾本身就是一个重大的挑战!更不幸的是,即便是同一类指标,其表现也不完全一致。举个例子说,一些基于SVD分解的算法,以降低均方根误差(参考图5)为目标,在预测评分精确性方面表现上佳,但是在推荐前L个商品的准确率和召回率(参考图5)方面则表现得很不如人意,有些情况下甚至还不如直接按照流行度排序的非个性化算法[69]。

图5介绍的那些指标都是基于数据本身的指标,可以认为是第一层次。实际上,在真实应用时,更为重要的是另外两个层次的评价。第二个层次是商业应用上的关键表现指标,譬如受推荐影响的转化率,购买率,客单价,购买品类数等等,第三个层次是用户真实的体验。绝大部分研究只针对第一个层次的评价指标,而业界真正感兴趣的是第二个层次的评价(譬如到底是哪个指标或者哪些指标组合的结果能够提高用户购买的客单价),而第三个层细最难,没人能知道,只能通过第二层次的效果来估计。如何建立第一层面和第二层面指标之间的关系,就成为了关键,这一步打通了,理论和应用之间的屏障就通了一大半了。

image

图6:Facebook页面上用户注意力集中的区域的分布,其中红色的区域是用户注意力最集中的区域,黄色次之,绿色再次之,白色最次。

挑战八:用户界面与用户体验

这个问题更多地不是一个学术性质的问题,而是从真实应用中来的问题。十年前就有学者指出[70,71],推荐结果的可解释性,对于用户体验有至关重要的影响——用户希望知道这个推荐是怎么来的。基于相似性的推荐(例如协同过滤)在这个问题上具有明显的优势,譬如亚马逊基于商品的协同过滤的推荐[72]在发送推荐的电子邮件时会告诉用户之所以向其推荐某书,是因为用户以前购买过某些书,新浪微博基于局部结构相似性的“关注对象推荐”[73]在推荐的同时会说明哪些你的朋友也关注过他们。相对地,矩阵分解或者集成学习算法就很难向用户解释推荐结果的起源。一般而言,用户更喜欢来自自己朋友的推荐而不是系统的推荐,这一点在后面讲社会推荐的时候还会详细提到。另外,好的界面设计,能够让用户觉得推荐的结果更加多样化[74],更加可信[75]。

实际应用时,推荐列表往往含有很多项,这些推荐项最好能够区分成很多类别,不同类别往往来自于不同的推荐方法,譬如看过还看过(浏览过本商品的客户还浏览过的商品)、买过还买过(购买过本商品的客户还购买过的商品)、看过最终购买(浏览过本商品的客户最终购买的商品)、个性化热销榜(个性化流行品推荐)、猜你喜欢(个性化冷门商品推荐)等等。当然,每个推荐项呈现的结果往往都来自复杂的算法,绝不仅仅象它们的名字听起来那么简单。另外,推荐栏呈现的位置对于推荐的结果影响也很大,因为同一个网页上不同位置对于用户注意力的吸引程度大不一样。图6给出了EyeTrackShop针对Facebook个人页面不同位置受关注程度的示意,可以看出,不同的位置受到的关注相差很大。

如何更好呈现推荐,是一个很难建立理论模型和进行量化的问题,对于不同被推荐品而言,用户界面设计的准则也可能大不相同。建立一个可以进行A/B测试的系统(随机将用户分为两部分,各自看到不同的推荐页面和推荐结果),可以积累重要的实验数据,指导进一步地改善。

挑战九:多维数据的交叉利用

目前网络科学研究一个广受关注的概念是具有相互作用的网络的结构和功能。网络与网络之间的相互作用大体可以分成三类:一类是依存关系[76],譬如电力网络和Internet,如果发生了大规模停电事故,当地的自主系统和路由器也会受到影响,导致网络局部中断;第二类是合作关系[77],譬如人的一次出行,可以看作航空网络、铁路网络和公路网络的一次合作;第三类是交叠关系[78],主要针对社会网络,这也是我们最关注的。我们几乎每一个人,都参与了不止一个大型的社会网络中,譬如你可能既有新浪微博的帐号,又是人人网的注册用户,还是用手机,那么你已经同时在三个巨大的社会网络中了。与此同时,你可能还经常在淘宝、京东、麦包包、1号店、库巴网……这些地方进行网购,因此也是若干张用户-商品二部分图中的成员。

想象如果能够把这些网络数据整合起来,特别是知道每个节点身份的对应关系(不需要知道你真实身份,只需要知道不同网络中存在的若干节点是同一个人),可以带来的巨大的社会经济价值。举个例子,你可能已经在新浪微博上关注了很多数据挖掘达人的微博,并且分享了很多算法学习的心得和问题,当你第一次上当当网购书的时候,如果主页向你推荐数据挖掘的最新专著并附有折扣,你会心动吗?交叠社会关系中的数据挖掘,或称多维数据挖掘,是真正有望解决系统内部冷启动问题的终极法宝——只要用户在系统外部的其他系统有过活动。单纯从个性化商品推荐来讲,可以利用用户在其他电商的浏览购买历史为提高在目标电商推荐的精确度——当然,每一个电商既是付出者,也是获利者,总体而言,大家能够通过提高用户体验和点击深度实现共赢。与此同时,可以利用微博和其他社会网络的活动提高商品推荐的精度,还可以反过来利用商品浏览历史提高微博关注对象推荐的精度。给一个经常购买专业羽毛球和浏览各种专业羽毛球设备的用户推荐关注羽毛球的专业选手和业余教练的成功率应该很高,而且不会陷入“总在一个圈子里面来回推荐”的毛病中。

从机器学习的角度,杨强等人提出的“迁移学习”算法有望解决这种跨邻域的推荐[79],因为这种算法最基本的假设就是在一个领域所学习的知识在其他领域也具有一定的普适性。Nori等人最近的分析显示[80],在某系统中特定的行为(比如说在Delicious上收藏标签)可以被用于预测另外系统中的特定行为(比如说在Twitter上的信息选择),其核心的思想与杨强等人一致。事实上,这种跨网的学习已经被证明可以提高链路预测的效果[81,82]。尽管有上面的有利的证据,我们还是需要特别注意,迁移学习在不同领域间的效果差异很大,还依赖于相关系统内部连接产生的机制,并不是普遍都能产生良好地效果,因为有的时候在一个商品品类上表现出高相似性的用户在另外一些商品品类上可能表现出完全不同的偏好[83]。

image

图7:用户跨多个独立B2C电商网站浏览购物的示意图。

我们分析了百分点科技服务客户的真实数据,发现有相当比例的用户都具有交叉购物的习惯,即在多个独立B2C电商有浏览和购买行为,如图7所示[84,85]。即便只考虑两个点上,例如利用麦包包的浏览购买数据为名鞋库的用户进行个性化推荐(这些用户在名鞋库上是没有任何历史记录的新用户,但是在麦包包上有浏览购买行为),就可以明显提高推荐的准确度(比完全冷启动的随机推荐高数十倍)[84],而如果利用3家或以上的外部电商的数据,其推荐的精确度可以明显高于热销榜(注意,热销榜一点个性化都没有),特别在团购类网站上表现非常好[85]。拥有交叉用户使得不同系统之间的“迁移”更加容易(注意,“迁移学习”原始的方法[79]不要求系统之间具有相同的用户和商品),Sahebi和Cohen最近测试同时评价了书和电影的用户,也发现利用对书的评分信息可以相当程度上预测对电影的评分[86]。虽然针对多维数据挖掘的研究刚刚起步,但我相信其必将成为学术研究和商业应用上的双料热点和双料难点。

挑战十:社会推荐

很早以前,研究人员就发现,用户更喜欢来自朋友的推荐而不是被系统“算出来的推荐”[87]。社会影响力被认为比历史行为的相似性更加重要[88,89],例如通过社会关系的分析,可以大幅度提高从科研文献[90]到网购商品[91],从个人博客到[92]手机应用软件[93]推荐的精确度。最近有证据显示,朋友推荐也是淘宝商品销售一个非常重要的驱动力量[94]。来自朋友的社会推荐有两方面的效果:一是增加销售(含下载、阅读……)[95],二是在销售后提高用户的评价[96]。当然,社会推荐的效果也不是我们想象的那么简单:Leskovec等人[95]在同一篇论文中指出针对不同类型的商品社会推荐的效果大不一样;Yuan等人指出不同类型的社会关系对推荐的影响也不同[97];Abbassi等人指出朋友的负面评价影响力要大于正面评价[98];等等。

在社会推荐方向存在的挑战主要可以分为三类:一是如何利用社会关系提高推荐的精确度[89,99],二是如何建立更好的机制以促进社会推荐[100-102],三是如何将社会信任关系引入到推荐系统中[103-107]。社会推荐的效果可能来自于类似口碑传播的社会影响力,也可能是因为朋友之间本来就具有相似的兴趣或者兴趣相投更易成为朋友,对这些不同的潜在因素进行量化区别,也属学术研究的热点之一[108]。

参考文献:

[1] 刘建国,周涛,汪秉宏,个性化推荐系统的研究进展,自然科学进展19 (2009) 1-15.

[2] 项亮,陈义,推荐系统实践,图灵出版社,2012.

[3] 苏萌,柏林森,周涛,个性化:商业的未来,机械工业出版社,2012.

[4] F. Ricci, L. Rokach, B. Shapira, P. B. Kantor, Recommender Systems Handbook: A Complete Guide for Scientists and Practioners, Springer, 2011.

[5] D. Jannach, M. Zanker, A. Felfernig, G. Friedrich. Recommender Systems: An Introduction. Cambridge University Press, 2011.

[6] G. Adomavicius, A. Tuzhilin, Toward the next generation of recommender systems: a survey of the state-of-the-art and possible extensions, IEEE Transactions on Knowledge and Data Engineering 17 (2005) 734-749.

[7] L. Lü, M. Medo, C. H. Yeung, Y.-C. Zhang, Z.-K. Zhang, T. Zhou, Recommender Systems, Physics Reports (to be published). http://dx.doi.org/10.1016/j.physrep.2012.02.006

[8] M.-S. Shang, L. Lü, W. Zeng, Y.-C. Zhang, T. Zhou, Relevance is more significant than correlation: Information filtering on sparse data, EPL 88 (2009) 68008.

[9] W. Zeng, Y.-X. Zhu, L. Lü, T. Zhou, Negative ratings play a positive role in information filtering, Physica A 390 (2011) 4486-4493.

[10] J. S. Kong, K. Teague, J. Kessler, Just Count the Love-Hate Squares: a Rating Network Based Method for Recommender Systems, in: Proceedings of the KDD Cup’11. ACM Press, New York, 2011.

[11] Z. Huang, H. Chen, D. Zeng, Applying associative retrieval techniques to alleviate the sparsity problem in collaborative filtering, ACM Transactions on Information Systems 22 (2004) 116-142.

[12]T. Zhou, R.-Q. Su, R.-R. Liu, L.-L. Jiang, B.-H. Wang, Y.-C. Zhang, Accurate and diverse recommendations via eliminating redundant correlations, New Journal of Physics 11 (2009) 123008.

[13]J.-G. Liu, T. Zhou, H.-A.Che, B.-H.Wang, Y.-C. Zhang, Effects of high-order correlations on personalized recommendations for bipartite networks, Physica A 389 (2010) 881-886.

[14] J. Ren, T. Zhou, Y.-C. Zhang, Information filtering via self-consistent refinement, EPL 82 (2008) 58007.

[15] D. Sun, T. Zhou, J.-G. Liu, R.-R. Liu, C.-X. Jia, B.-H. Wang, Information filtering based on transferring similarity, Physical Review E 80 (2009) 017101.

[16] 田久乐,赵蔚,基于同义词词林的词语相似度计算方法,吉林大学学报(信息科学版) 28 (2010) 602-608.

[17] J. S. Breese, D. Heckerman, C. Kadie, Empirical analysis of predictive algorithms for collaborative filtering, in: Proceedings of the 14th Conference on Uncertainty in Artificial Intelligence, 1998, pp. 43-52.

[18] I. Esslimani, A. Brun, A. Boyer, Densifying a behavioral recommender system by social networks link prediction methods, Social Network Analysis and Mining 1 (2011) 159-172.

[19] T. Zhou, Z. Kuscsik, J.-G.Liu, M. Medo, J.R. Wakeling, Y.-C. Zhang, Solving the apparent diversity–accuracy dilemma of recommender systems, Proceedings of the National Academy of Sciences of the United States of America 107 (2010) 4511-4515.

[20] T. Zhou, J. Ren, M. Medo, Y.-C. Zhang, Bipartite network projection and personal recommendation, Physical Review E 76 (2007) 046115.

[21] M. J. Pazzani, D. Billsus, Content-Based Recommendation Systems, Lect. Notes Comput. Sci. 4321 (2007) 325-341.

[22] A. I. Schein, A. Popescul, L. H. Ungar, D. M. Pennock, Methods and metrics for cold-start recommendations, in: Proceedings of the 25th Annual International ACM SIGIR Conference on Research and Development in Information Retrieval, ACM Press, New York, 2002, pp. 253-260.

[23] X. N. Lam, T. Vu, T. D. Le, A. D. Duong, Addressing cold-start problem in recommendation systems, in: Proceedings of the 2nd International Conference on Ubiquitous Information Management and Communication, 2008, pp. 208-211.

[24] Z.-K. Zhang, C. Liu, Y.-C. Zhang, T. Zhou, Solving the cold-start problem in recommender systems with social tags, EPL 92 (2010) 28002.

[25] Z.-K. Zhang, T. Zhou, Y.-C. Zhang, Tag-Aware Recommender Systems: A State-of-the-Art Survey, Journal of Computer Science and Technology 26 (2011) 767-777.

[26] A. De Bruyn, J. C. Liechty, E. K. R. E. Huizingh, G. L. Lilien, Offering online recommendations with minimum customer input through conjoint-based decision aids, Marketing Science 27 (2008) 443-460.

[27] S. S. Anand, N. Griffiths, A Market-based Approach to Address the New Item Problem, in: Proceedings of the 5th ACM Conference on Recommender Systems, ACM Press, New York, 2011, pp. 205-212.

[28] J.-L. Zhou, Y. Fu, H. Lu, C.-J. Sun, From Popularity to Personality—A Heuristic Music Recommendation Method for Niche Market, Journal of Computer Science and Technology 26 (2011) 816-822.

[29] C.-J. Zhang, A. Zeng, Behavior patterns of online users and the effect on information filtering, Physica A 391 (2012) 1822-1830.

[30] I. Porteous, D. Newman, A. Ihler, A. Asuncion, P. Smyth, M. Welling, Fast Collapsed Gibbs Sampling for Latent Dirichlet Allocation, in: Proceedings of the 14th ACM SIGKDD International Conference on Knowledge Discovery and Data Mining, ACM Press, New York, 2008, pp. 569-577.

[31] F. Cacheda, V. Carneiro, D. Fernández, V. Formoso, Comparison of Collaborative Filtering Algorithms Limitations of Current Techniques and Proposals for Scalable, High-Performance Recommender Systems, ACM Transactions on the Web 5 (2011) 2.

[32] W.-Y. Chen, J.-C. Chu, J. Luan, H. Bai, Y. Wang, E. D. Chang, Collaborative Filtering for Orkut Community: Discovery of User Latent Behavior, in: Proceedings of the 18th International Conference on World Wide Web, ACM Press, New York, 2005, pp. 681-690.

[33] R. Gemulla, P. J. Haas, E. Nijkamp, Y. Sismanis, Large-scale matrix factorization with distributed stochastic gradient descent, in: Proceedings of the 17th ACM SIGKDD International Conference on Knowledge Discovery and Data Mining, ACM Press, New York, 2011, pp. 569-577.

[34] B. Sarwar, J. Konstan, J. Riedl, Incremental singular value decomposition algorithms for highly scalable recommender systems, in: International Conference on Computer and Information Science, 2002, pp. 27-28.

[35] C.-H. Jin, J.-G. Liu, Y.-C. Zhang, T. Zhou, Adaptive information filtering for dynamics recommender systems, arXiv:0911.4910.

[36] S. M. Mcnee, J. Riedl, J. A. Konstan, Being accurate is not enough: how accuracy metrics have hurt recommender systems, in: Proceedings of the CHI'06 Conference on Human Factors in Computing Systems, ACM Press, New York, 2006, pp. 1097-1101.

[37] Y. C. Zhang, D. O. Seaghdha, D. Quercia, T. Jambor, Auralist: introducing serendipity into music recommendation, in: Proceedings of the 5th ACM International Conference on Web Search and Data Mining, ACM Press, New York, 2012, pp. 13-22.

[38] J. Ugander, L. Backstrom, C. Marlow, J. Kleinberg, Structural diversity in social contagion, Proceedings of the National Academy of Sciences of the United States of America 109 (2012) 5962-5966.

[39] B. Smyth, P. Mcclave, Similarity vs. diversity, in: D.W. Aha, I. Watson (Eds.), Case-Based Reasoning Research and Development, Springer, 2001, pp. 347-361.

[40] C.-N. Ziegler, S.M. Mcnee, J.A. Konstan, G. Lausen, Improving recommendation lists through topic diversification, in: Proceedings of the 14 th International Conference on World Wide Web, ACM Press, New York, 2005, pp. 22-32.

[41] N. Hurley, M. Zhang, Novelty and diversity in top-N recommendation—analysis and evaluation, ACM Transactions on Internet Technology 10 (2011) 14.

[42] L. Lü, W. Liu, Information filtering via preferential diffusion, Physical Review E 83 (2011) 066119.

[43] J.-G. Liu, T. Zhou, Q. Guo, Information filtering via biased heat conduction, Physical Review E 84 (2011) 037101.

[44] B. Mobasher, R. Burke, R. Bhaumik, C. Williams, Towards trustworthy recommender systems: an analysis of attack models and algorithm robustness, ACM Transactions on Internet Technology 7 (2007) 23.

[45] R. Burke, M. P. O'mahony, N. J. Hurley, Robust Collaborative Recommendation, in: F. Ricci, L. Rokach, B. Shapira, P. B. Kantor (Eds.), Recommender Systems Handbook, Part 5, Springer, 2011, pp. 805-835 (Chapter 25).

[46] J. J. Sandvig, B. Mobasher, R. Burke, Robustness of collaborative recommendation based on association rule mining, in: Proceedings of the 2007 ACM Conference on Recommender Systems, ACM Press, 2007, pp. 105-112.

[47] S. K. Lam, D. Frankowski, J. Riedl, Do You Trust Your Recommendations? An Exploration of Security and Privacy Issues in Recommender Systems, Lecture Notes in Computer Science 3995 (2006) 14-29.

[48] P. Resnick, R. Sami, The influence limiter: provably manipulation-resistant recommender systems, in: Proceedings of the 2007 ACM Conference on Recommender Systems, ACM Press, 2007, pp. 25-32.

[49] C. Shi, M. Kaminsky, P. B. Gibbons, F. Xiao, DSybil: Optimal Sybil-Resistance for Recommendation Systems, in: Proceedings of the 30 th IEEE Symposium on Security and Privacy, IEEE Press, 2009, pp. 283-298.

[50] M.-S. Shang, L. Lü, Y.-C. Zhang, T. Zhou, Empirical analysis of web-based user-object bipartite networks, EPL 90 (2010) 48006.

[51] 刘怡君,周涛,社会动力学,科学出版社,2012.

[52] S.-H. Min, I. Han, Detection of the customer time-variant pattern for improving recommender systems, Expert Systems with Applications 28 (2005) 189-199.

[53] L. Xiang, Q. Yuan, S. Zhao, L. Chen, X. Zhang, Q. Yang, J. Sun, Temporal recommendation on graphs via long-and short-term preference fusion, in: Proceedings of the 16th ACM SIGKDD International Conference on Knowledge Discovery and Data Mining, ACM Press, New York, 2010, pp. 723-732.

[54] N. N. Liu, M. Zhao, E. Xiang, Q. Yang, Online evolutionary collaborative filtering, in: Proceedings of the 4th ACM Conference on Recommender Systems, ACM Press, New York, 2010, pp. 95-102.

[55] J. Liu, G. Deng, Link prediction in a user-object network based on time-weighted resource allocation, Physica A 39 (2009) 3643-3650.

[56] Y. Koren, Collaborative filtering with temporal dynamics, in: Proceedings of the 15th ACM SIGKDD International Conference on Knowledge Discovery and Data Mining, ACM Press, New York, 2009, pp. 447-456.

[57] C. Song, Z. Qu, N. Blumm, A.-L. Barabási, Limits of predictability in human mobility, Science 327 (2010) 1018-1021.

[58] E. Cho, S.A. Myers, J. Leskovec, Friendship and mobility: user movement in location-based social networks, in: Proceedings of the 17th ACM SIGKDD International Conference on Knowledge Discovery and Data Mining, ACM Press, New York, 2011, pp. 1082-1090.

[59] V. W. Zheng, Y. Zheng, X. Xie, Q. Yang, Collaborative location and activity recommendations with GPS history data, in: Proceedings of the 19th International Conference on World Wide Web, ACM Press, New York, 2010, pp. 1029-1038.

[60] M. Clements, P. Serdyukov, A. P. De Vries, M. J. T. Reinders, Personalised travel recommendation based on location co-occurrence, arXiv:1106.5213.

[61] T. H. Dao, S. R. Jeong, H. Ahn, A novel recommendation model of location-based advertising: context-aware collaborative filtering using GA approach, Expert Systems with Applications 39 (2012) 3731-3739.

[62] S. Scellato, A. Noulas, C. Mascolo, Exploiting Place Features in Link Prediction on Location-based Social Networks, in: Proceedings of the 17th ACM SIGKDD International Conference on Knowledge Discovery and Data Mining, ACM Press, New York, 2011, pp. 1046-1054.

[63] Y. Koren, J. Sill, OrdRec: An ordinal model for predicting personalized item rating distributions, in: Proceedings of the 5th ACM Conference on Recommender Systems, ACM Press, New York, 2011, pp. 117-124.

[64] Z. Yang, Z.-K. Zhang, T. Zhou, Uncovering Voting Patterns in Recommender Systems (unpublished).

[65] J. Vig, S. Sen, J. Riedl, Navigation the tag genome, in: Proceedings of the 16th International Conference on Intelligent User Interfaces, ACM Press, New York, 2011, pp. 93-102.

[66] L. Chen, P. Pu, Critiquing-based recommenders: survey and emerging trends, User Modeling and User-Adapted Interaction 22 (2012) 125-150.

[67] T. Lau,张韶峰,周涛,推荐引擎:信息暗海的领航员,中国计算机学会通讯 8(6) (2012) 22-25.

[68] 朱郁筱,吕琳媛, 推荐系统评价指标综述,电子科技大学学报 41 (2012) 163-175.

[69] P. Cremonesi, Y. Koren, R. Turrin, Performance of Recommender Algorithms on Top-N Recommendation Tasks, in: Proceedings of the 4th ACM Conference on Recommender Systems, ACM Press, New York, 2010, pp. 39-46.

[70] R. Sinha, K. Swearingen, The role of transparency in recommender systems, in: Proceedings of the CHI'06 Conference on Human Factors in Computing Systems, ACM Press, New York, 2002, pp. 830-831.

[71] A. D. J. Cooke, H. Sujan, M. Sujan, B. A. Weitz, Marketing the unfamiliar: the role of context and item-specific information in electronic agent recommendations, Journal of Marketing Research 39 (2002) 488-497.

[72] G. Linden, B. Smith, J. York, Amazon.com recommendations: item-to-item collaborative filtering, IEEE Internet Computing 7 (2003) 76-80.

[73] L. Lü, T. Zhou, Link prediction in complex networks: a survey, Physica A 390 (2011) 1150-1170.

[74] R. Hu, P. Pu, Enhancing recommendation diversity with organization interfaces, in: Proceedings of the 16th international conference on Intelligent user interfaces, ACM Press, New York, 2011, pp. 347-350.

[75] G. Lenzini, Y. van Houten, W. Huijsen, M. Melenhorst, Shall I trust a recommendation? Lecture Notes in Computer Science 5968 (2010) 121-128.

[76] S. V. Buldyrev, R. Parshani, G. Paul, H. E. Stanley, S. Havlin, Catastrophic cascade of failures in interdependent networks, Nature 464 (2010) 1025-1028.

[77] C.-G. Gu, S.-R. Zou, X.-L.Xu, Y.-Q.Qu, Y.-M.Jiang, D.-R.He, H.-K.Liu, T. Zhou, Onset of cooperation between layered networks, Physical Review E 84 (2011) 026101.

[78] M. Mognani, L. Rossi, The ML-model for multi-layer social networks, in: Proceedings of 2011 International Conference on Advances in Social Networks Analysis and Mining, IEEE Press, 2011, pp. 5-12.

[79] S. J. Pan, Q. Yang, A survey on transfer learning, IEEE Transactions on Knowledge and Data Engineering 22 (2010) 1345-1359.

[80] N. Nori, D. Bollegala, M. Ishizuka, Exploiting user interest on social media for aggregating diverse data and predicting interest, Artificial Intelligence 109(B3) (2011) 241-248.

[81] M. A. Ahmad, Z. Borbora, J. Srivastava, N. Contractor, Link Prediction Across Multiple Social Networks, in: Proceedings of the 2010 IEEE International Conference on Data Mining Workshop, IEEE Press, 2010, pp. 911-918.

[82] B. Cao, N. N. Liu, Q. Yang, Transfer learning for collective link prediction in multiple heterogeneous domains, in: Proceedings of the 27 th International Conference on Machine Learning, 2010.

[83] B. Xu, J. Bu, C. Chen, D. Cai, An exploration of improving collaborative recommender systems via user-item subgraphs, in: Proceedings of the 21st International Conference on World Wide Web, ACM Press, New York, 2012, pp. 21-30.

[84] 张亮,柏林森,周涛,基于跨电商行为的交叉推荐算法,电子科技大学学报(已接收).

[85] T. Zhou, L. Gong, C. Li, L.-S. Bai, L. Zhang, S. Huang, S. Guo, M.-S. Shang, Information filtering in interacting networks (unpublished).

[86] S. Sahebi, W. W. Cohen, Community-Based Recommendations: a Solution to the Cold Start Problem (unpublished).

[87] R. Sinha, K. Swearingen, Comparing recommendations made by online systems and friends, in: Proceedings of the DELOS-NSF Workshop on Personalization and Recommender Systems in Digital Libraries, 2001.

[88] M.J. Salganik, P.S.Dodds,D.J.Watts, Experimental study of inequality and unpredictability in an artificial culturalmarket, Science 311 (2006) 854-856.

[89] P. Bonhard,M.A. Sasse, "Knowingme knowing you"—using profiles and social networking to improve recommender systems, BT Technology Journal 24 (2006) 84-98.

[90] S.-Y. Hwang, C.-P.Wei, Y.-F.Liao, Coauthorship networks and academic literature recommendation, Electronic Commerce Research and Applications 9 (2010) 323-334.

[91] P. Symeonidis, E. Tiakas, Y.Manolopoulos, Product recommendation and rating prediction based onmulti-modal social networks, in: Proceedings of the 5th ACM Conference on Recommender Systems, ACM Press, New York, 2011, pp. 61-68.

[92] A. Seth, J. Zhang, A social network based approach to personalized recommendation of participatory media content, in: Proceedings of the 3rd International AAAI Conference on Weblogs and Social Media, AAAI Press, 2008, pp. 109-117.

[93] W. Pan, N. Aharonym, A. S. Pentland, Composite Social Network for Predicting Mobile Apps Installation, in: Proceedings of the 25th AAAI Conference on Artificial Intelligence, AAAI Press, 2011, pp. 821-827.

[94] S. Guo, M. Wang, J. Leskovec, The role of social networks in online shopping: information passing, price of trust, and consumer choice, in: Proceedings of the 12th ACM Conference on Electronic Commerce, ACM Press, New York, 2011, pp. 157-166.

[95] J. Leskovec, L.A. Adamic, B.A. Huberman, The dynamics of viral marketing, ACM Transactions on Web 1 (2007) 5.

[96] J. Huang, X.-Q.Cheng, H.-W.Shen, T. Zhou, X. Jin, Exploring social influence via posterior effect of word-of-mouth recommendations, in: Proceedings of the 5th ACM International Conference on Web Search and Data Mining, ACM Press, New York, 2012, pp. 573-582.

[97] Q. Yuan, L. Chen, S. Zhao, Factorization vs. Regularization: Fusing Heterogeneous Social Relationships in Top-N Recommendation, in: Proceedings of the 5th ACM Conference on Recommender Systems, ACM Press, New York, 2011, pp. 245-252.

[98] Z. Abbassi, C. Aperjis, B. A. Huberman, Friends versus the crowd: tradeoff and dynamics (unpublished).

[99] H. Ma, D. Zhou, C. Liu, M. R. Lyu, I. King, Recommender Systems with Social Regularization, in: Proceedings of the 4th ACM International Conference on Web Search and Data Mining, ACM Press, New York, 2011, pp. 287-296.

[100] M. Medo, Y.-C.Zhang, T. Zhou, Adaptive model for recommendation of news, EPL 88 (2009) 38005.

[101] T. Zhou, M. Medo, G. Cimini, Z.-K.Zhang, Y.-C. Zhang, Emergence of scale-free leadership strcuture in social recommender systems, PLoS ONE 6 (2011) e20648.

[102] G. Cimini, D.-B. Chen, M. Medo, L. Lü, Y.-C. Zhang, T. Zhou, Enhancing topology adaptation in information-sharing social networks, Physical Review E 85 (2012) 046108.

[103] J. O'Donovan, B. Smyth, Trust in recommender systems, Proceedings of the 10th international conference on Intelligent user interfaces, ACM Press, 2005, pp. 167-174.

[104] P. Massa, P. Avesani, Trust-aware recommender systems, in: Proceedings of the 2007 ACM conference on Recommender systems, ACM Press, 2007, pp. 17-24.

[105] H. Ma, I. King, M. R. Lyu, Learning to Recommend with Social Trust Ensemble, in: Proceedings of the 32nd international ACM SIGIR conference on Research and development in information retrieval, ACM Press, New York, 2009, pp. 203-210.

[106] M. Jamali, M. Ester, TrustWalker: A Random Walk Model for Combining Trust-based and Item-based Recommendation, in: Proceedings of the 15th ACM SIGKDD International Conference on Knowledge Discovery and Data Mining, ACM Press, New York, 2009, pp. 397-406.

[107] M. Jamali, M. Ester, A Matrix Factorization Technique with Trust Propagation for Recommendation in Social Networks, in: Proceedings of the 4th ACM Conference on Recommender Systems, ACM Press, New York, 2010, pp. 135-142.

[108] J. He, W. W. Chu, A Social Network-Based Recommender System (SNRS), Data Mining for Social Network Data 12 (2010) 47-74.


  青春就应该这样绽放   游戏测试:三国时期谁是你最好的兄弟!!   你不得不信的星座秘密

免费的晚餐--Google技术学习

$
0
0

作者: 江南白衣,原文出处:  http://blog.csdn.net/calvinxiu/archive/2007/01/31/1498597.aspx

 

如果说Google的搜索引擎是免费的早餐,Gmail们是免费的午餐的话,

     http://labs.google.com/papers/ 就是Google给开发人员们的一份免费的晚餐。

    不过,咋看着一桌饭菜可能不知道从哪吃起,在自己不熟悉的领域啃英文也不是一件愉快的事情。

一、一份PPT与四份中文翻译 

    幸好,有一位面试google不第的老兄,自我爆发搞了一份Google Interal的PPT:

     http://cbcg.net/talks/googleinternals/index.html,大家鼠标点点就能跟着他匆匆过一遍google的内部架构。

   然后又有崮崮山路上走9遍( http://sharp838.mblogger.cn)与美人他爹( http://my.donews.com/eraera/),翻译了其中最重要的四份论文:

二、Google帝国的技术基石

     Google帝国,便建立在大约45万台的Server上,其中大部分都是"cheap x86 boxes"。而这45万台Server,则建立于下面的key infrastructure:

     1.GFS(Google File System):

     GFS是适用于大规模分布式数据处理应用的分布式文件系统,是Google一切的基础,它基于普通的硬件设备,实现了容错的设计与极高的性能。    

     李开复说:Google最厉害的技术是它的storage。我认为学计算机的学生都应该看看 这篇文章(再次感谢翻译的兄弟)。    
    

     它以64M为一个Chunk(Block),每个Chunk至少存在于三台机器上,交互的简单过程见:
     
     

     2.MapReduce

    MapReduce是一个分布式处理海量数据集的编程模式,让程序自动分布到一个由普通机器组成的超大集群上并发执行。像Grep-style job,日志分析等都可以考虑采用它。

    MapReduce的run-time系统会解决输入数据的分布细节,跨越机器集群的程序执行调度,处理机器的失效,并且管理机器之间的通讯请求。这样的模式允许程序员可以不需要有什么并发处理或者分布式系统的经验,就可以处理超大的分布式系统得资源。

     我自己接触MapReduce是 Lucene-> Nutch-> Hadoop的路线。
      Hadoop是Lucene之父Doug Cutting的又一力作,是Java版本的分布式文件系统与Map/Reduce实现。
     Hadoop的文档并不详细,再看一遍 Google这篇中文版的论文,一切清晰很多(又一次感谢翻译的兄弟)。    

     孟岩也有一篇很清晰的博客: Map Reduce - the Free Lunch is not over?

     3.BigTable

     BigTable 是Google Style的数据库,使用结构化的文件来存储数据。
     虽然不支持关系型数据查询,但却是建立GFS/MapReduce基础上的,分布式存储大规模结构化数据的方案。

     BigTable是一个稀疏的,多维的,排序的Map,每个Cell由行关键字,列关键字和时间戳三维定位.Cell的内容是一个不解释的字符串。
     比如下表存储每个网站的内容与被其他网站的反向连接的文本。
     反向的URL com.cnn.www( www.cnn.com)是行的关键字;contents列存储网页内容,每个内容有一个时间戳;因为有两个反向连接,所以archor列族有两列:anchor:cnnsi.com和anchhor:my.look.ca,列族的概念,使得表可以横向扩展,archor的列数并不固定。

   

    为了并发读写,热区,HA等考虑,BigTable当然不会存在逗号分割的文本文件中,,是存储在一种叫SSTable的数据库结构上,并有BMDiff和Zippy两种不同侧重点的压缩算法。

4.Sawzall

    Sawzall是一种建立在MapReduce基础上的领域语言,可以被认为是分布式的awk。它的程序控制结构(if,while)与C语言无异,但它的领域语言语义使它完成相同功能的代码与MapReduce的C++代码相比简化了10倍不止。

1    proto "cvsstat.proto"
2    submits: table sum[hour: int] of count: int;
3    log: ChangelistLog = input;
4    hour: int = hourof(log.time)
5    emit submits[hour] <- 1;

     
     天书吗?慢慢看吧。

     我们这次是统计在每天24小时里CVS提交的次数。
     首先它的变量定义类似Pascal  (i:int=0; 即定义变量i,类型为int,初始值为0)

     1:引入cvsstat.proto协议描述,作用见后。
     2:定义int数组submits 存放统计结果,用hour作下标。
     3.循环的将文件输入转换为ChangelistLog 类型,存储在log变量里,类型及转换方法在前面的cvsstat.proto描述。
     4.取出changlog中的提交时间log.time的hour值。
     5.emit聚合,在sumits结果数组里,为该hour的提交数加1,然后自动循环下一个输入。

     居然读懂了,其中1、2步是准备与定义,3、4步是Map,第5步是Reduce。

三. 小结:

  本文只是简单的介绍Google的技术概貌,大家知道以后除了可作谈资外没有任何作用,我们真正要学习的骨血,是论文里如何解决高并发,高可靠性等的设计思路和细节.....



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



架构师的启蒙战歌

$
0
0

作者: 江南白衣  http://blog.csdn.net/calvinxiu/article/details/1524867

 

下面几个What的问题是架构师的入门Question。

1.什么是架构?
  如果自问自答,这是个相当鸡肋的问题。看看书里的答案可能有点收获。
  架构定义了系统元素的结构与行为,关注最重要的元素,记录决策原理,受涉众利益与环境的影响。
  架构不仅是结构,也不是企业架构,系统架构,信息架构,硬件架构.....
  架构设计也不是面向对象分析/设计,虽然架构设计基于面向对象设计,但补充了面向对象设计里没有关注的视点。

2.架构有什么用途?
  人类所有行为都是目的驱动的,不同目的下架构的过程也会不同,继续看书里的答案,经常的用途有:

  • 培训与沟通
  • 满足系统的质量属性,与涉众沟通达成一致的目标
  • 有效管理复杂性
  • 支持开发日程安排、工作分配、成本分析

3.各个流派用什么视图来表达架构?
   最经典的RUP 4+1,ISO/ITU-T联合制定的RM-ODP,Hofmeister在《Applied Software Architecture》中使用的西门子视图,《Documenting Software Architectures》使用的方法等。

4.架构师在软件生命周期中的工作和角色?
   
架构师的工作不只是设计(虽然最重要的的确如是),在分析、设计、实现、测试、维护、技术风险管理、问题域分析、团队技术领导与沟通者等流程里架构师都有自己的角色要担当。

5..架构师的技能?
  自己总结了一篇 《架构师核心技能养成计划》

答案:
1.IBM DW 中文站上有四篇不错的入门文章:
一、 什么是软件架构? 
二、 软件构架师的特点 
三、 软件架构的过程
四、 进行软件架构设计的益处 

2.《Large-Scale Software Architecture》的前50页也回答了同样的问题。

3. 另外所有和架构沾点边的书都会忍不住浪费篇幅去讨论这些问题。

    不过这些答案从字面上看都是大家熟悉的东西,很容易泛泛的看完了,过一阵子脑子里又十室九空,最好是能够看进去产出化学作用,再辛苦一点把它们总结成自己的文档。

And then? 可以开始一些How的问题了.....

 

 



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



FireEye:发现SCADA间谍软件Havex的最新变种

$
0
0

又是Havex,是的,之前介绍过的这个恶意软件,曾经以能源部门相关组织为目标,被用于针对一些欧洲公司实施工业间谍活动,并成功入侵1000多家欧洲和北美能源公司。

前几天,安全公司FireEye的研究人员已经发现了Havex RAT的一个新变种,这个变种有能力主动扫描用来控制关键基础设施、能源和制造领域的SCADA系统中的OPC 服务器。

OPC是一种通信标准,允许基于Windows的SCADA系统之间或者其他工业控制系统应用程序和过程控制硬件之间进行通信。新的Havex变种可以收集存储在使用OPC标准的被入侵客户端或服务端的系统信息和数据。

FireEye的研究人员在其 官方博文中称, “攻击者已经利用Havex攻击那些能源部门一年多了,但暂时仍然不清楚受影响行业及工业控制系统的的受害程度。我们决定更详尽地检查Havex的OPC扫描组件,以便更好地理解当扫描组件执行时发生了什么以及可能产生的影响。”

该安全公司的研究人员建立了一个典型的OPC服务器环境对新变种的功能进行实时测试。工业控制系统或SCADA系统包括OPC客户端软件以及与其直接交互的OPC服务端,OPC服务端与PLC串联工作,实现对工控硬件的控制。

一旦进入网络后,Havex下载器就会调用DLL导出功能,启动对SCADA网络中OPC服务器的扫描。为了定位潜在的OPC服务器,该扫描器模块使用微软的WNet(Windows networking)功能如WNetOpenEnum和WNetEnumResources,以此枚举网络资源或存在的连接。

博文中提到, “扫描器建立了一个可以通过WNet服务进行全局访问的服务器列表,然后检查这个服务器列表以确定是否有向COM组件开放的接口。”

通过使用OPC扫描模块,Havex新变种可以搜集有关联网设备的任何细节,并将这些信息发回到C&C服务器供攻击者分析。以此看来,这个新变种貌似被用作未来情报收集的工具。

这是第一个用作OPC扫描的"在野"样本,很有可能这些攻击者是把这个恶意软件作为试验品以供未来使用。

到目前为止,研究人员并未发现任何试图控制所连接硬件的行为,这个恶意软件背后使用的攻击路径、开发者以及意图还不得而知,但是研究人员正在调查,并试图收集所有关于这个新变种的信息。

原文地址

nginx框架总结

$
0
0

1、高性能:

a、内存池的设计,为一个多级链表结构,本身不负责内存的回收,减少内存碎片,提高内存的利用率,将多次向操作系统申请内存压缩为一次,减少向操作系统申请内存的次数,提高cpu资源的利用;

 

 

 

b、基于事件的master-worker异步处理。处理事件的机制是由事件分发进程加载事件处理模块来完成与传统将事件提交给handle-thread来处理的方式相比,减少了进程(线程)间的切换,从而降低了请求响应延迟的时间;
c、进程间通信采用自选锁(spinlock)即非睡眠锁,减少进程的阻塞等待时间,每个进程的epoll上瞬间会有成千上万个tcp连接,进程不能随便阻塞让出cpu,即使没有获取到锁,他仍然可以去做其他的事情。

d、请求的多阶段异步处理,减少了进程的休眠时间,让进程全力运转,同时进程数目不是很大,减少了内核的开销,提高了网络性能。

2、高可靠性。

     worker进程一旦挂了,会向master进程发送CHILD信号,置ngx_reap标识,master进程会对worker进程监控,发现终止的进程会对其进行重启等操作。
3、高伸缩性.

     nginx中存在ngx_modules数组,该数组存放的是各个模块,nginx在启动时会加载这些模块,用户可以根据需要动态添加或删除这些模块。
4、高修改性.

     nginx提供了配置文件,对一些重要信息进行设置,如侦听端口号,worker进程的个数等,在执行过程中,ngx_reconfigure标志位进行重新生成工作进程。



 

作者:zcc_0015 发表于2014-7-24 22:41:53 原文链接
阅读:113 评论:0 查看评论

J2EE事务并发控制策略总结

$
0
0
本文结合hibernate以及JPA标准,对J2EE当前持久层设计所遇到的几个问题进行总结:

第一:事务并发访问控制策略
当前J2EE项目中,面临的一个共同问题就是如果控制事务的并发访问,虽然有些持久层框架已经为我们做了很多工作,但是理解原理,对于我们开发来说还是很有用处的。

事务并发访问主要可以分为两类,分别是同一个系统事务和跨事务访问的并发访问控制,其中同一个系统事务可以采取乐观锁以及悲观锁策略,而跨多个系统事务时则需要乐观离线锁和悲观离线锁。在讨论这四种并发访问控制策略之前,先需要明确一下数据库事务隔离级别的问题,ANSI标准规定了四个数据库事务隔离级别,它们分别是:

读取未提交(Read Uncommitted):这是最低的事务隔离级别,读事务不会阻塞读事务和写事务,写事务也不会阻塞读事务,但是会阻塞写事务。这样造成的一个结果就是当一个写事务没有提交的时候,读事务照样可以读取,那么造成了脏读的现象。

读取已提交(Read Committed):采用此种隔离界别的时候,写事务就会阻塞读事务和写事务,但是读事务不会阻塞读事务和写事务,这样因为写事务会阻塞读取事务,那么从而读取事务就不能读到脏数据,但是因为读事务不会阻塞其它的事务,这样还是会造成不可重复读的问题。

可重复读(Repeatable Read):采用此种隔离级别,读事务会阻塞写事务,但是读事务不会阻塞读事务,但是写事务会阻塞写事务和读事务。因为读事务阻塞了写事务,这样以来就不会造成不可重复读的问题,但是这样还是不能避免幻影读问题。

序列化(serializable):此种隔离级别是最严格的隔离级别,如果设置成这个级别,那么就不会出现以上所有的问题(脏读,不可重复读,幻影读)。但是这样以来会极大的影响到我们系统的性能,因此我们应该避免设置成为这种隔离级别,相反的,我们应该采用较低的隔离界别,然后再采用并发控制策略来进行事务的并发访问控制)。

其实我们也可以把事务隔离级别设置为serializable,这样就不需要采用并发控制策略了,数据库就会为我们做好一切并发控制,但是这样以来会严重影响我们系统的伸缩性和性能,所以在实践中,我们一般采用读取已提交或者更低的事务隔离级别,配合各种并发访问控制策略来达到并发事务控制的目的。下面总结一下常用的控制策略:

1 乐观锁
乐观锁是在同一个数据库事务中我们常采取的策略,因为它能使得我们的系统保持高的性能的情况下,提高很好的并发访问控制。乐观锁,顾名思义就是保持一种乐观的态度,我们认为系统中的事务并发更新不会很频繁,即使冲突了也没事,大不了重新再来一次。它的基本思想就是每次提交一个事务更新时,我们想看看要修改的东西从上次读取以后有没有被其它事务修改过,如果修改过,那么更新就会失败,。

最后我们需要明确一个问题,因为乐观锁其实并不会锁定任何记录,所以如果我们数据库的事务隔离级别设置为读取已提交或者更低的隔离界别,那么是不能避免不可重复读问题的(因为此时读事务不会阻塞其它事务),所以采用乐观锁的时候,系统应该要容许不可重复读问题的出现。

了解了乐观锁的概念以后,那么当前我们系统中又是如何来使用这种策略的呢?一般可以采用以下三种方法:

版本(Version)字段:在我们的实体中增加一个版本控制字段,每次事务更新后就将版本字段的值加1.

时间戳(timestamps):采取这种策略后,当每次要提交更新的时候就会将系统当前时间和实体加载时的时间进行比较,如果不一致,那么就报告乐观锁失败,从而回滚事务或者重新尝试提交。采用时间戳有一些不足,比如在集群环境下,每个节点的时间同步也许会成问题,并且如果并发事务间隔时间小于当前平台最小的时钟单位,那么就会发生覆盖前一个事务结果的问题。因此一般采用版本字段比较好。

基于所有属性进行检测:采用这种策略的时候,需要比较每个字段在读取以后有没有被修改过,所以这种策略实现起来比较麻烦,要求对每个属性都进行比较,如果采用hiernate的话,因为hibernate在一级缓存中可以进行脏检测,那么可以判断哪些字段被修改过,从而动态的生成sql语句进行更新。

下面再总结一下如何在JDBC和Hibernate中使用乐观锁

JDBC中使用乐观锁:如果我们采用JDBC来实现持久层的话,那么就可以采用以上将的三种支持乐观锁的策略,在实体中增加一个version字段或者一个Date字段,也可以采用基于所有属性的策略,下面就采用version字段来做一演示:

假如系统中有一个Account的实体类,我们在Account中多加一个version字段,那么我们JDBC Sql语句将如下写:
Select a.version....from Account as a where (where condition..)
Update Account set version = version+1.....(another field) where version =?...(another contidition)

这样以来我们就可以通过更新结果的行数来进行判断,如果更新结果的行数为0,那么说明实体从加载以来已经被其它事务更改了,所以就抛出自定义的乐观锁定异常(或者也可以采用spring封装的异常体系)。具体实例如下:
.......
int rowsUpdated = statement.executeUpdate(sql);
If(rowsUpdated= =0){
throws new OptimisticLockingFailureException();
}
........

在使用JDBC API的情况下,我们需要在每个update语句中,都要进行版本字段的更新以及判断,因此如果稍不小心就会出现版本字段没有更新的问题,相反当前的ORM框架却为我们做好了一切,我们仅仅需要做的就是在每个实体中都增加version或者是Date字段。

Hibernate中使用乐观锁:如果我们采用hibernate做为持久层的框架,那么实现乐观锁将变得非常容易,因为框架会帮我们生成相应的sql语句,不仅减少了开发人员的负担,而且不容易出错。下面同样采用version字段的方式来总结一下:
同样假如系统中有一个Account的实体类,我们在Account中多加一个version字段,
public class Account{
Long id ;
.......
@Version //也可以采用XML文件进行配置
Int version
.......

}

这样以来每次我们提交事务时,hibernate内部会生成相应的SQL语句将版本字段加1,并且进行相应的版本检测,如果检测到并发乐观锁定异常,那么就抛出StaleObjectStateException.

2 悲观锁
所谓悲观锁,顾名思义就是采用一种悲观的态度来对待事务并发问题,我们认为系统中的并发更新会非常频繁,并且事务失败了以后重来的开销很大,这样以来,我们就需要采用真正意义上的锁来进行实现。悲观锁的基本思想就是每次一个事务读取某一条记录后,就会把这条记录锁住,这样其它的事务要想更新,必须等以前的事务提交或者回滚解除锁。

最后我们还是需要明确一个问题,假如我们数据库事务的隔离级别设置为读取已提交或者更低,那么通过悲观锁,我们控制了不可重复读的问题,但是不能避免幻影读的问题(因为要想避免我们就需要设置数据库隔离级别为Serializable,而一般情况下我们都会采取读取已提交或者更低隔离级别,并配合乐观或者悲观锁来实现并发控制,所以幻影读问题是不能避免的,如果想避免幻影读问题,那么你只能依靠数据库的serializable隔离级别(幸运的是幻影读问题一般情况下不严重)。

下面就分别以JDBC和hibernate来总结一下:

JDBC中使用悲观锁:在JDBC中使用悲观锁,需要使用select for update语句,假如我们系统中有一个Account的类,我们可以采用如下的方式来进行:
Select * from Account where ...(where condition).. for update.

当使用了for update语句后,每次在读取或者加载一条记录的时候,都会锁住被加载的记录,那么当其他事务如果要更新或者是加载此条记录就会因为不能获得锁而阻塞,这样就避免了不可重复读以及脏读的问题,但是其他事务还是可以插入和删除记录,这样也许同一个事务中的两次读取会得到不同的结果集,但是这不是悲观锁锁造成的问题,这是我们数据库隔离级别所造成的问题。

最后还需要注意的一点就是每个冲突的事务中,我们必须使用select for update 语句来进行数据库的访问,如果一些事务没有使用select for update语句,那么就会很容易造成错误,这也是采用JDBC进行悲观控制的缺点。

Hibernate中使用悲观锁:相比于JDBC使用悲观锁来说,在hibernate中使用悲观锁将会容易很多,因为hibernate有API让我们来调用,从而避免直接写SQL语句。下面就hibernate使用悲观锁做一总结:

首先先要明确一下hibernate中支持悲观锁的两种模式LockMode.UPGRADE以LockMode.UPGRADE_NO_WAIT.(PS:在JPA中,对应的锁模式是LockModeType.Read,这与hibernate是不一样的呵呵)
假如我们系统中有一个Account的类,那么具体的操作可以像这样:
.......
session.lock(account, LockMode.UPGRADE);
......

或者也可以采用如下方式来加载对象:
session.get(Account.class,identity,LockMode.UPGRADE).

这样以来当加载对象时,hibernate内部会生成相应的select for update语句来加载对象,从而锁定对应的记录,避免其它事务并发更新。

以上两种策略都是针对同一个事务而言的,如果我们要实现跨多个事务的并发控制就要采用其它两种并发控制策略了,下面做一总结:





3 乐观离线锁

乐观离线锁的思想和乐观锁是一致的,不同的地方就是乐观锁是针对一个事务周期的(也可以说是一个request周期),而乐观离线锁是跨多个事务周期的(可以理解为一个会话周期(conversation),它包括了用户思考的时间。

因为乐观离线锁是跨多个事务周期的,那么我们就遇到了一个问题,在几个事务之间通过什么来保存我们的实体状态呢?当然我们会想到httpsession,知道了这个,我们又会遇到一个问题,我们是通过业务层来判断有没有发生乐观并发异常还是通过持久层框架来实现(因为目前持久层框架一般都有一个脱管(detached object)的概念,我们通过它可以很容易实现乐观离线锁),下面就以上两种方式分别做一总结(假设持久层框架采用hibernate):

在进行总结之前,先让我们来选个场景来描述,因为最近在网上投了几份简历,我发现投简历之后,在HR处理简历之前,我们可以修改简历,我觉得这个例子就很符合乐观理线锁的问题域,所以我就以它来分析一下。我们先假设系统中有一个简历类Resume(因为只是一个例子,为了让问题更加清楚,我将其它的属性省略):


public class Resume implements Serializable{
public long id ;

.............

public int version ;

............

}
业务层负责判断乐观并发异常:在此种情况下,我们一般需要在Resume中增加一个获取字段的getVersion()方法,因为我们要将获得的版本字段值保存到Httpsession里面,首先第一个请求过来,我们获得版本字段将其保存到httpsession中,然后当用户修改完简历后打算更新的时候,那么我们的业务层怎么实现呢?可以如下:
..............
Resume resumeToModify = resumeRepository.getResume(id);
If(beforeModifyVersion == resumeToModify.getVersion()){
//这里进行修改操作
}else
throw new OptimisticLockFailureException();

这样我们就可以通过对比httpsession中的版本字段来和当前数据库系统中的字段进行比较,如果一样就提交,否则就抛出异常。(呵呵,当然这个仅仅是作为例子,其实如果你修改了很大一会简历,但是发现提交的时候不能完成,那你应该会很郁闷,觉得这网站做的不人性化,然而这是采用乐观离线锁不能避免的,所以这也就出现了使用乐观离线锁的一个前提:系统中并发更新少,并且即使冲突了,让用户重来一遍的开销也不会很大,如果开销大的话,那么我们就需要采用悲观离线锁模式)。

通过detached object实现:如果通过持久层提供的脱管对象来实现,那么我们就可以依赖于持久层框架的乐观检测来实现乐观离线锁,这样我们不需要在业务层进行比较,持久层框架会帮我们进行比较,如果发现数据已经被更改过,那么就会抛出相应的异常(比如hibernate将抛出StaleObjectStateExcpetion)。我们还是拿刚才的那个例子来做一描述,当用户要修改简历时,第一个请求会将简历的内容读取并显示给用户,我们需要做的就是将脱离了持久层的脱管对象保存到httpsession中,然后当用户修改完以后,要提交更新的时候,我们可以如下实现:
............
resumeRepository.update(detachedResume);//内部通过调用hibernate session接口的update方法,将脱管对象重附(reattch)到当前的持久化上下文
.............

这样以来,当持久层框架发现数据已经被修改后就会抛出相应的异常,我们的业务层只要捕获异常就OK啦。

好了,总结完了两种方式以后,我们来看看它们有哪些优缺点:

对于第一种策略,因为需要们显示的来判断到底有没有出现异常,所以略显麻烦,但是优点就是因为我们只需要保存一个版本字段,不会出现将整个对象都保存到httpsession中,这样造成httpsession被充爆。如果采用第二种策略(detached object),不需要我们显示来实现,只需要捕获持久层抛出的异常就OK,但是也有个不好地方,前面也说了,那就是会造成我们httpsession爆炸,这样很浪费内存。再说点题外话,既然httpsession会引起内存浪费太多,那么有没有更好的方案呢?当然我们会想到EJB的有状态会话bean,它为业务状态提供了钝化技术,所以可以避免占用太大的内存,并且它还有个更好的优点就是不仅有状态管理,而且还支持完整的事务语义,所以EJB的statefull session bean还是有很大好处的,并不像有些人所说的statefull session bean 用处非常小。

4 悲观离线锁
悲观离线锁的基本思想和离线锁差不多,不同的地方就是悲观锁是依赖于数据库的锁机制来实现,而悲观离线锁则需要我们开发人员自己来控制锁的释放和获取,所以增加了一定的复杂性。
下面需要明确一下什么情况下需要使用悲观离线锁,因为悲观离线锁会在整个会话期间锁住数据,所以会影响到系统的伸缩性和性能,选择时要谨慎考虑几方面的因素:

1 用例是否通过几个数据库事务来实现,第一个事务读取数据,最后一个事务更新数据,首先要满足这个条件我们才会选择用悲观离线锁,否则如果在同一个数据库事务,直接用离线锁即可。

2 用例是否要求用户提交的更新一定要成功,以及用户提交事务后重新来一次的开销大不大,如果要求用户提交更新一定要成功,或者用户提交更新失败后,再重新来一次的开销比较大,那么就采取悲观离线锁,否则的话,我们可以采用乐观离线锁。

已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



MongoDB的PHP驱动方法与技巧

$
0
0

创建索引有时会阻塞新的连接

当与mongodb复制集建立连接时,驱动程序首先尝试连接并验证集群中每个非 隐藏的节点。如果一个节点是“down”状态,将跳过。然而,如果一个节点是“UP”状态,但是持有写锁,那么验证将没法执行下去,因此该驱动程序将被挂起了。 在mongodb 2.6版本前,在建立索引时的通病。所有的在前台或后台创建索引,在secondary端将在前台创建。 在PHP驱动程序的1.5.3版本会有所改进,在创建索引时,允许验证到second节点验证。

减少is_master_interval值

对于对可用性要求高的应用程序来说,建议检查默认的驱动程序运行时的配置设置。 mongo.is_master_interval选项控制着在复制集重新选举时驱动如何快速恢复。 is_master_interval选项默认值为15s,设置驱动发送“isMaster”请求每个mongod实例的时间间隔。这些请求帮助驱动程序判断复制集的拓扑结构,具体的来说,就是请求检测哪个节点是primary并可以接收写操作。 建议将该值设置为1或2秒,以让驱动程序在集群选举或故障转移时,能够迅速的定位到primary节点。当然啦,这也取决于有多少客户端以及ping的频率。 注意,当primary节点发生变化时,如选举或故障转移,总是会有几秒钟驱动程序会收到一个“MongoConnectionException” 信息 “No candidate servers found”。这些异常需要在你的代码中进行处理,否则会终止应用程序。

理解连接处理方式并配置连接TimeoutMS

PHP驱动程序不使用连接池。因此,建议你每个PHP进程创建一个连接。但是,如果web应用程序有许多PHP工作进程,将会创建很多新的数据库连接,PHP驱动程序不能共享进程之间的连接。因此,当网络节点慢,服务器繁忙时,PHP应用程序创建初始数据库连接时特别容易受到阻碍。 在这种情况下,建议你自定义connectionTimeoutMS 选项和注意php.ini中的mongo.ping_interval选项。 connectionTimeoutMS PHP驱动程序不显示的定义一个默认的连接超时。相反,默认值由php.ini文件中的default_socket_timeout选项决定,默认是60秒。连接将等待60秒断开,时间有些长,需要降低些。 强烈建议通过连接字符串的URI选项中显示设置connectionTimeoutMS选项。将其设置为5到30秒之间的值。 mongo.ping_interval mongo.ping_interval默认值为5秒。该选项设置驱动程序发送ping请求到每个mongod实例发现“down”节点的时间间隔,用于跟踪驱动程序的服务器黑名单。告诉驱动程序哪些节点忽略。 转自:http://mongodb.info/2014/05/29/new-blog-mongodb-driver-tips-tricks-php/

[原]DevOps主要通过哪几方面的改变来提升发布软件效率和质量的?

$
0
0

传统的软件运营人员通常倾向于尽量避免修改功能,从而降低满足非功能性需求的风险。但如果拒绝了小的修改,而给定时间段内需要修改的总量不变,那么每次变更的规模就会变大,从而增加每次发布的风险(因为变更涉及的范围更大)。

DevOps的指导思想是“精益运维”。精益生产的很多原则,例如缩短交付周期、消除浪费、重视价值流动、拉动式生产、质量内建等,在DevOps中都得到了体现。与传统的软件发布方式相比,DevOps主要通过以下几方面的改变来提升效率和质量。

减少每次发布的变更范围。与传统的瀑布式开发模型相比,采用迭代的工作方式意味着更频繁的发布、每次发布包含的变化更少。由于部署经常进行,每次部署不会对生产系统造成巨大影响,应用程序会以平滑的速率逐渐生长(如图2所示)。与传统开发方法那种大规模的、不频繁的发布(通常以“季度”或“年”为单位)相比,具备DevOps能力的组织大大提升了发布频率(通常以“天”或“周”为单位)。

加强开发与运营协调。通过强有力的发布协调机制来弥合开发与运营之间的技能鸿沟和沟通鸿沟;采用电话会议、即时消息、企业门户(wiki、sharepoint)等协作工具来确保所有相关人员理解变更的内容;使用统一的流程和工具,例如故事墙、燃尽图、在线项目管理工具(例如Mingle、JIRA)、配置管理工具(例如Subversion、Git、Mercurial)等。

自动化。借助强大的部署自动化手段和标准化的环境管理来降低部署操作的成本,确保部署任务的可重复性,减少部署出错的可能性,例如以下列举的几种方式。

  (1)用VMWare或Xen等虚拟化技术标准化生产环境,实现生产环境的快速复制和快速恢复。

  (2)用Puppet或Chef等工具自动化环境设置、软件安装/配置等操作,将配置信息转化为源代码,实现环境配置的版本控制。

  (3)用Capistrano等工具自动化软件产品的部署,实现部署过程的版本控制。

  (4)用dbdeploy等工具自动化数据库变更,实现数据迁移的版本控制。

  (5)用Selenium、Cucumber等工具自动化生产环境的冒烟测试和回归测试。


 从工作流程、协调机制、技术工具等几个方面同时着手,就能在软件组织中建立起DevOps能力,从而将精益运维变成现实。

本文摘自即将在8月初上市的《软件开发践行录》


作者:zuoninger 发表于2014-7-25 16:04:32 原文链接
阅读:38 评论:0 查看评论

ganglia收集hbase的metrics

$
0
0

Ganglia 是 UC Berkeley 发起的一个开源监视项目,设计用于测量数以千计的节点。每台计算机都运行一个收集和发送度量数据(如处理器速度、内存使用量等)的名为 gmond 的守护进程。它将从操作系统和指定主机中收集。接收所有度量数据的主机可以显示这些数据并且可以将这些数据的精简表单传递到层次结构中。正因为有这种层次结构模式,才使得 Ganglia 可以实现良好的扩展。gmond 带来的系统负载非常少,这使得它成为在集群中各台计算机上运行的一段代码,而不会影响用户性能。


Ganglia监控软件主要是用来监控系统性能的软件,如:cpu 、mem、硬盘利用率, I/O负载、网络流量情况等,通过曲线很容易见到每个节点的工作状态,对合理调整、分配系统资源,提高系统整体性能起到重要作用。


hadoop和hbase很好地支持了ganglia这个开源监控工具,说明ganglia是hadoop生态中不可或缺的一部分。


本文介绍如果用ganglia收集hbase的各种指标,重点解决两个大家比较关注的问题:

(1) hbase指标过多,如何过滤? 

(2) 修改hadoop-metrics.properties 后,能否不用重启hadoop或hbase。


一、hbase  metrics 配置

        以 hbase-0.98为例,需要配置 hadoop-metrics2-hbase.properties


       

# syntax: [prefix].[source|sink].[instance].[options]
# See javadoc of package-info.java for org.apache.hadoop.metrics2 for details

#*.sink.file*.class=org.apache.hadoop.metrics2.sink.FileSink
default sampling period
*.period=10

# Below are some examples of sinks that could be used
# to monitor different hbase daemons.

# hbase.sink.file-all.class=org.apache.hadoop.metrics2.sink.FileSink
# hbase.sink.file-all.filename=all.metrics

# hbase.sink.file0.class=org.apache.hadoop.metrics2.sink.FileSink
# hbase.sink.file0.context=hmaster
# hbase.sink.file0.filename=master.metrics

# hbase.sink.file1.class=org.apache.hadoop.metrics2.sink.FileSink
# hbase.sink.file1.context=thrift-one
# hbase.sink.file1.filename=thrift-one.metrics

# hbase.sink.file2.class=org.apache.hadoop.metrics2.sink.FileSink
# hbase.sink.file2.context=thrift-two
# hbase.sink.file2.filename=thrift-one.metrics

# hbase.sink.file3.class=org.apache.hadoop.metrics2.sink.FileSink
# hbase.sink.file3.context=rest
# hbase.sink.file3.filename=rest.metrics

*.sink.ganglia.class=org.apache.hadoop.metrics2.sink.ganglia.GangliaSink31  
*.sink.ganglia.period=10  

hbase.sink.ganglia.period=10  
hbase.sink.ganglia.servers=172.18.144.198:8648


ganglia 3.1及以上版本需要用这个类:org.apache.hadoop.metrics2.sink.ganglia.GangliaSink31


重启hbase和gmod 就可以在ganglia  web界面上看到众多指标出来了。但是,指标太多了, 到region级别的了,每个 region属于哪个 table,每个table又一堆指标。从ganglia的rrd数据库中可以看到:

-rw-rw-rw- 1 hadoop root 12216 Jul 23 16:40 regionserver.Regions.Namespace_default_table_o_m_ocs_ordersplitamount_region_f15998ced89264146b3ec3888db625f6_metric_scanNext_max.rrd
-rw-rw-rw- 1 hadoop root 12216 Jul 23 16:40 regionserver.Regions.Namespace_default_table_o_m_forest_maps_region_cb5490455403d92ff2e6acd17c2b3877_metric_get_95th_percentile.rrd
-rw-rw-rw- 1 hadoop root 12216 Jul 23 16:40 regionserver.Regions.namespace_default_table_o_s_peking_orders_region_bc87e3d9b61ee8c14956914d407ad11c_metric_mutateCount.rrd
-rw-rw-rw- 1 hadoop root 12216 Jul 23 16:40 regionserver.Regions.namespace_default_table_o_m_ocs_ordersplitamount_region_5ca4ba7d83369781b41c939d260fdcdd_metric_mutateCount.rrd
-rw-rw-rw- 1 hadoop root 12216 Jul 23 16:40 regionserver.Regions.namespace_default_table_o_m_ocs_orderamount_region_3ecd8c8440eae5a1191ef9e6e523ea9f_metric_appendCount.rrd
-rw-rw-rw- 1 hadoop root 12216 Jul 23 16:40 regionserver.Regions.Namespace_default_table_o_m_ocs_orderamount20140722_region_985afc00551d4fb68ceaf9188f5b9d12_metric_get_75th_percentile.rrd
-rw-rw-rw- 1 hadoop root 12216 Jul 23 16:40 regionserver.Regions.namespace_default_table_o_m_ocs_orderamount20140722_region_9672e9b9ea759fc1aee838e4ae228fa9_metric_memStoreSize.rrd
-rw-rw-rw- 1 hadoop root 12216 Jul 23 16:40 regionserver.Regions.Namespace_default_table_o_m_chat_analysis_session20140722_region_d709618a5b60e44a03befe57ca480ef9_metric_get_num_ops.rrd



http://hbase.apache.org/book/hbase_metrics.html    这个上面提到一个warning to ganglia user:



Warning to Ganglia Users: by default, HBase will emit a LOT of metrics per RegionServer which may swamp your installation. Options include either increasing Ganglia server capacity, or configuring HBase to emit fewer metrics.


hbase 默认会吐那么多指标出来,我们得想办法过滤,保留我们需要的指标就行。下面说明如何过滤


二、指标过滤

 很幸运hadoop metrics system提供了过滤功能, hbase的指标监控计划:

(1) master:  去除Assignment,Balancer, Filtersystem.MetaHlog ,以及各percentile、max、median、min,保留mean平均值

(2) regionserver:去除WAL相关,以及各percentile、max、median、min,保留mean平均值

(3) region:太多,表级别的,全部去除。


*.source.filter.class=org.apache.hadoop.metrics2.filter.RegexFilter
#*.source.filter.class=org.apache.hadoop.metrics2.filter.GlobFilter
*.record.filter.class=${*.source.filter.class}
*.metric.filter.class=${*.source.filter.class}
*.period=10

*.sink.ganglia.class=org.apache.hadoop.metrics2.sink.ganglia.GangliaSink31  
*.sink.ganglia.period=10  


hbase.sink.ganglia.metric.filter.exclude=^(.*table.*)|(\\w+metric)|(\\w+region\\w+)|(Balancer\\w+)|(\\w+Assign\\w+)|(\\w+percentile)|(\\w+max)|(\\w+median)|(\\w+min)|(MetaHlog\\w+)|(\\w+WAL\\w+)$
hbase.sink.ganglia.period=10  
hbase.sink.ganglia.servers=<span style="font-family: Arial, Helvetica, sans-serif;">172.18.144.198</span><span style="font-family: Arial, Helvetica, sans-serif;">:8648</span>


用正则表达式的Filter。修改之后,重启 hbase和gmod,在ganglia web console  上就可以看到指标少了,清爽了很多,更有针对性了。

三、hadoop metrics system 管理 


注意到前两步的修改配置的操作后,都需要重启hbase才生效,因为metric system 是随着hbase 的启动而启动的。如果hbase已经在线服务了,暴力stop会影响服务,graceful_stop则会涉及到数据的临时迁移,都不理想,最好是不重启。

下面讲到如何单独管理hadoop metrics system的启动和停止。

 hadoop 生态是人多力量大,这个也考虑到了,metrics system 子系统向外暴露了 Mbean,利用 JMX就可以对它进行控制。首先我们要开启 JMX。

 hbase-env.sh

 
export HBASE_JMX_BASE="-Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"
export HBASE_MASTER_OPTS="$HBASE_MASTER_OPTS $HBASE_JMX_BASE -Dcom.sun.management.jmxremote.port=10101"
export HBASE_REGIONSERVER_OPTS="$HBASE_REGIONSERVER_OPTS $HBASE_JMX_BASE -Dcom.sun.management.jmxremote.port=10102"
export HBASE_THRIFT_OPTS="$HBASE_THRIFT_OPTS $HBASE_JMX_BASE -Dcom.sun.management.jmxremote.port=10103"
export HBASE_ZOOKEEPER_OPTS="$HBASE_ZOOKEEPER_OPTS $HBASE_JMX_BASE -Dcom.sun.management.jmxremote.port=10104"
export HBASE_REST_OPTS="$HBASE_REST_OPTS $HBASE_JMX_BASE -Dcom.sun.management.jmxremote.port=10105"

 这一步没办法,要重启hbase。其实一开始就可以只开启JMX,重启一次。后面的 metrics配置都可以单独来启动和停止的。

 



通过  Jconsole可以看到,MetricsSystem提供了四个操作,我们需要的是start和stop。测试环境或许可以用jconsole或其它工具去连,但是生产环境就不行了。想想我们的hadoop集群是用ganglia和nagios结合使用的,ganglia收集数据,图表展现,nagios 收集数据,监控报警,这简直绝配。如果将jmx的功能集成到nagios中就更好了。

查了下,nagios的确有jmx插件,但是nagios就是个check再报警的机制,没有操作的行为。 不好将metric system的启动和停止放在nagios中。只能另起一炉灶,用JMXToolkit 和 shell脚本搞定。


JMXToolkit 比较简单,网上资料很多,在使用过程中有一些要注意的地方。


git clone https://github.com/larsgeorge/jmxtoolkit.git
cd jmxtoolkit
vim  build.xml  # 修改hbase版本为 0.98.1-hadoop2
ant

ant编译后生成 build/hbase-0.98.1-hadoop2-jmxtoolkit.jar  ,这个jar是我们最终想要的。再看配置文件,build/conf/hbase-0.98.1-hadoop2-jmx.properties

不知道jmxtoolkit内部实现是怎么样的,这里面的指标名称都不对,也不全。而且我们只是需要操作metrics system,将这个文件自己编辑了下:


[hbaseMetricsSystem]
@object=Hadoop:service=HBase,name=MetricsSystem,sub=Control
@url=service:jmx:rmi:///jndi/rmi://${HOSTNAME}:${PORT}/jmxrmi
@user=${USER|controlRole}
@password=${PASSWORD|password}
*stop=VOID
*start=VOID

重命名 build/hbase-0.98-metrics.properties

注意 object 就是上面jconsole截图中的metrics system的 objectName,而 *stop和*start则是 Mbean 提供的方法。


测试:

java -cp hbase-0.98.1-hadoop2-jmxtoolkit.jar   org.apache.hadoop.hbase.jmxtoolkit.JMXToolkit -f hbase-0.98-metrics.properties  -o hbaseMetricsSystem -q stop

/etc/init.d/gmond restart

可以看到修改的指标生效了。


最后,写个简单的shell,扔到生产环境上去。

#!/bin/bash


# manage the metrics system of hbase individually.
# ./hbase-metrics-system.sh start
# ./hbase-metrics-system.sh stop

config_file_template=hbase-0.98-metrics.properties
config_file_product=hbase-0.98-metrics-product.properties

#for i in ${hbase_hostnames=[@]};do
for i in `cat $HBASE_HOME/conf/regionservers`;do
  cp ${config_file_template} ${config_file_product}
  sed -i "s/\${HOSTNAME}/$i/g" ${config_file_product}
  sed -i "s/\${PORT}/10102/g" ${config_file_product}
  java -cp hbase-0.98.1-hadoop2-jmxtoolkit.jar   org.apache.hadoop.hbase.jmxtoolkit.JMXToolkit -f ${config_file_product}  -o hbaseMetricsSystem -q $1
  echo "$1 metrics system of $i"
  /bin/rm ${config_file_product}
done

  echo "done"

办法有点简陋,先解决了再说。








作者:K_James 发表于2014-7-25 15:03:09 原文链接
阅读:43 评论:0 查看评论

[原]代码审查审什么

$
0
0
代码审查审什么


看着很多人做代码审查重点审格式和命名,制定的代码规范也主要偏重代码格式和命名,我真想骂一句操蛋,这真是浪费时间又解决不了问题。此篇文章就是骂完操蛋后奋笔快速敲下来的,有不妥之处请大家谅解。


一、目的:为啥要花费时间要搞人工代码审查?


1、有些问题是工具检查不出来的,需要人工审查
2、有些问题是不希望花大代价来发现、或者上线后才知道


二、重心:代码审查的重心是什么


1、函数
2、集成
3、性能
4、安全


三、详解:函数审查


函数审查的重点是:
1、函数的输入输出。这本质就是接口。接口不能根据需求变化变来变去,所以接口设计/修改需要非常小心,需要专人来分析识别接口、专人来设计维护接口。以不变应万变,很难


2、函数的分支/嵌套。很多开发人员不会分析分解业务场景、业务逻辑,不是缠绕在一起就是把场景逻辑拆的一地零件组合不起来。所以要拆要合都非常难。因此业务场景业务逻辑多复杂,函数就有多长。因此函数分支/嵌套也是需要多审查多指导如何合理划分。函数的分支/嵌套会影响代码未来的阅读学习难度/维护修改难度,这都是成本/效率/质量的关键根源。


3、函数的异常和日志。异常结构如何设计、日志结构如何设计、如何记录、如何上报给用户,这都是讲究。烂的异常截获、报告、记录,都让用户莫名其妙、让技术人员难以根据信息快速找到问题根源。甚至有人搞异常处理都把业务逻辑都搞乱了。


函数是一个开发人员的基本功、基本要求,所以重点审查函数该怎么合理设计,这是代码设计的一部分。想想你的程序员做代码设计吗?


四、详解:集成


集成的关键是自我封闭、有限联接、明确联接。


所以要关注:
1、功能点源代码包之间的互相调用、依赖
2、模块包之间的互相调用、依赖
3、模块组包之间的互相调用、依赖
4、系统包之间的互相调用、依赖
5、系统组包之间的互相调用、依赖


要做到最少联接,要减少双向依赖。


刚才讲到的都是横向关联,咱们还要关注纵向关联:
1、UI表现层、数据传输层、服务层、业务逻辑层、逻辑-数据服务层、数据处理层、数据存储层


尽量做到最小联接、单向调用、物理独立部署、明确接口。


让数据层集成尽量通过数据复制分发平台做、让业务逻辑层集成通过EBS做、让UI层集成通过统一门户做,这样功能之间的联接就少的多


五、详解:性能


性能分为:架构性能、功能性能、编码性能、部署性能、运维持续优化性能。我们这里做代码审查主要做编码性能审查,架构性能保证由架构师和架构评审来决定。


编码性能主要分为:UI渲染层性能、业务逻辑层性能、数据存储层性能


UI层编码性能关键看存取、增删、遍历DOM节点;业务逻辑层性能关注事务长度/复杂度、事务锁;数据存储层关注SQL/索引。


六、详解:安全


安全也是分为:架构安全、功能安全、编码安全、部署安全、运维持续监控优化安全。我们主要关注编码安全。


编码安全也主要分为:UI层安全、业务逻辑层安全、数据存储层安全。


UI层安全关键是:sesion使用、cookies使用、插件使用、Form/Field输入信息防注入、URL串防注入、防止客户端脚本编码中泄漏后台数据结构/数据账号的代码。


业务逻辑层安全关键是:业务场景逻辑漏洞、角色权限漏洞


数据存储层安全关键是:敏感数据是否明文存储、关键数据变更留痕日志审计


七、谁来做代码审查?


1、代码审查、代码质量提升,首要就是开发leader的职责,应该由开发Leader率领来做
2、可以人盯人、层层盯。高级开发审初级的代码、开发leader审中级的代码。这样隔层来审,会让技能提高很快。最怕就是让棋篓子审其他棋篓子的代码,两个人的技能都是半斤八两,越审越烂


八、应该审哪些代码?

按重要性来分:

1、复杂度高的想重写重构的代码

2、经常出BUG的代码


按人来分:

1、重点审新进的人写的代码,让新进的人形成良好的代码习惯,不要让代码池子受污染


九、怎么做代码审查?

方式1:开发Leader率领大家开代码分析例会,挑出想重构或经常出BUG的代码,然后一个个函数让大家挑问题,然后把改进建议记录下来,以后整理出代码规范让大家遵守和审查。这个方式适合刚刚开展代码审查的研发团队


方式2:高级开发审初级的代码、开发leader审中级的代码,标出具体代码具体问题。这个方式适合已经大家每个人都会做代码审查的研发团队


十、审完了该干啥?


1、有隐含BUG的就改隐含BUG,这是首要


2、对于未来隐患的,如可扩展、可维护的,在下一阶段包含进重构需求范围内


3、把代码审查建议完善增补到代码规范、代码审查制度中,供以后代码编写与审查时遵守



作者:david_lv 发表于2014-7-25 13:18:37 原文链接
阅读:80 评论:0 查看评论

dubbo服务化实施整理

$
0
0

随着快的业务的快速发展,我们逐步按照业务垂直划分,抽象出基础服务层。

一 服务化目标

  • 基础业务的服务为上游业务的灵活发展提供支持
  • 服务应用本身无状态化,可以随着系统的负荷灵活伸缩来提供服务能
  • 服务的稳定性,可用性达到99%

 

二 dubbo架构

dubbo来作为服务化中间件,dubbo作为一个RPC框架,大致的原理如下图

         

 

  • Registry: 注册中心;和服务的消费者,和服务提供者都建立长连接。服务提供者注册服务到注册中心;服务消费者从注册中心获取服务提供者列表;
  • Consumer: 服务消费端;服务消费者从注册中心获取到服务提供者列表后,根据负载均衡算法,选择一个服务提供者,和服务提供者直接建立连接,开始调用服务;
  • Provider:服务提供者;服务提供者提供RPC服务;
  • Monitor: 监控服务的调用情况统计;
  • 注册中心为N+1对等集群,一台挂掉后,会自动切换到另外一台注册中心
  • 注册中心全部挂掉后,消息消费者本地会缓存服务提供者列表,所以不影响当时的服务调用。
  • 服务提供者为集群,一台挂掉后,通过心跳过程,注册中心会立即刷到服务消费者告知;

 

三 工程分类

总体来说,服务化工程类型分为两种
standalong工程
以独立的应用形式提供远程服务,不依赖web容器,这是比较标准的服务提供形式,有3个子工程:
api
描述:业务域对外提供的服务接口,参数类型
输出:独立jar,例如user-api.jar
core
描述:业务域的模型,包括数据和行为,只关心领域内的数据和模型,不关心外部服务
输出:独立jar,例如user-core.jar,外部业务不能依赖该jar
impl
描述:业务域的远程服务实现,负责服务启动停止,内部业务组装,参数转换
输出:***-assembly.tar.gz,输出是一个压缩包
web工程
已有的web应用暴露服务,最好对原有的工程结构无侵入,只是再增加2个子工程:
api
描述:业务域对外提供的服务接口,参数类型
输出:独立jar,例如user-api.jar
impl
描述:业务域的远程服务实现,负责服务启动停止,内部业务组装,参数转换
无论是否服务化,某个业务域内部的数据和行为是不变的,服务化只是将业务域内的逻辑做一些裁剪或聚合,然后供外部调用。业务域的内部模型和提供的外部服务要划分一个边界,各自专注于自己的职责。以下是服务角色和子工程的对应关系:

  

 

 

四 standalong工程 

依赖关系
服务内部工程间的依赖都通过本地jar依赖,impl依赖api和core,没有其他依赖关系,严禁core依赖api。关系如下

 

工程结构

浅蓝色是目录,浅绿色是文件。assembly.xml不能修改,dubbo.properties和dubbo-provider.xml都可以修改,dubbo.properties描述服务公共配置,如注册中心,超时等;dubbo-provider.xml通过spring描述具体服务信息

 

 

 

部署结构

standalong型的应用启动停止都必须有对应的脚本,这些脚本文件不需要每个standalong型应用自己写,只需要工程结构遵循规范,执行mvn package/install后,会在target自动输出最后的部署包,部署包结构如下图:

浅蓝色是目录,浅绿色是文件。

lib目录:存放依赖的jar包,工程里java代码都会输出为jar包到lib里

start.sh:启动服务

restart.sh:重启服务

stop.sh:停止服务

server.sh:命令入口,如server.sh start

dump.sh:dump应用的线程栈,内存,GC;供排查问题用

 

 

 

 

 

五 web工程

依赖关系

web应用大致分为两层:biz和web,实际上biz可能由内部多个工程组成,这里biz只是一个抽象概念。impl依赖api和biz,web依赖impl和biz,没有其他依赖关系,严禁biz依赖api。关系如下

 

 

 

工程结构和部署结构

web应用的服务只需要配置在spring配置文件里,服务的启动停止依赖web容器的启动停止。所以web工程的服务化不需要调整原有工程结构,只是要增加两个子工程:api和impl。 这样做是希望某个已存在的web应用要做服务化,不会对原有工程发生改动。

 
 

六 svn目录结构

对应工程结构,svn目录也做相应的划分,每个业务域是个大目录,然后该目录内部又分为3个目录:api,core,impl。以会员为例,svn目录结构为,以会员为例:
user
trunk
api
core
impl
branch

 

 

七 一些注意事项

  • 所有服务接口都以"RemoteService"结尾
  • 不要传输超大对象
  • 服务化接口涉及的入参类型和返回类型都必须实现序列化接口,并且必须放到api包
  • 子类和父类不允许有同名同类型的属性,序列化容易出问题
  • 所有类型都应该有序列化id
  • 外部参数和内部参数必须做隔离,简单来说就是core里面的东西不能被透传到外部
以会员为例,假设core里存在UserService;有方法addUser(UserDO user);现在要将addUser(UserDO user)作为服务开放出去,则在api里加入UserRemoteService,UserRemoteService有对应的方法:
addUser(UserDTO user),该方法内部操作数据通过UserService.addUser实现。大概代码如下:
UserRemoteService{
  private UserService userService;
  public void addUser(UserDTO userDTO){
        UserDO userDO=***Converter.convert(userDTO);
        userService.addUser(userDO);
  }
}


已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



域名解析服务器 – OpenerDNS

$
0
0

OpenerDNS是面向国内普通互联网用户开放的“高速 安全 免费”的域名解析服务器。还在使用Google DNS或者Opendns吗?还在不断更改host文件吗?现在就切换到:OpenerDNS地址: 42.120.21.30

为什么使用OpenerDNS:

安全:我们保证解析数据的真实性,域名不被污染。你还在使用8.8.8.8吗?现在就切换到42.120.21.30,所有域名都准确解析了。

速度快:相比其他国外DNS,我们服务器托管在国内,速度更加快速,使你上网的反应更加迅速。

稳定:OpenerDNS自开通以来,短短半个月的时间就达到日均请求100万次。2014年6月,我们的日均请求数已经接近1亿次,我们依然很轻松。

OpenerDNS 加速的域名列表:

下列主域名及其子域名的A记录提供加速功能:

appspot.com(考虑到节省我们的流量,暂时去除,仍然返回google地址)
android.com
blogger.com
blogspot.com
ggpht.com
google-analytics.com
google.cn
google.com
google.com.hk
google.com.tw
google.com.sg
googleadservices.com
googlesyndication.com
googleapis.com
googlecode.com
googlelabs.com
googleusercontent.com
gstatic.com
youtube.com
ytimg.com
www.google.com | google.com
Gmail
twitter.com | www.twitter.com |
www.facebook.com | facebook.com
plus.google.com
play.google.com
en.wikipedia.org
zh.wikipedia.org
www.wikipedia.org
www.dropbox.com
www.youtube.com##实验性,需要chrome浏览器

项目地址

https://code.google.com/p/openerdns/

数据库安全审计

$
0
0
 用以下的方式可以监控登入登出的用户:
  创建如下的两张表:
create table login_log -- 登入登出信息表
(
session_id int not null, -- sessionid
login_on_time date, -- 登入进间
login_off_time date, -- 登出时间
user_in_db varchar2(30), -- 登入的db user
machine varchar2(20), -- 机器名
ip_address varchar2(20), -- ip地址
run_program varchar2(20) -- 以何程序登入
);
create table allow_user -- 网域用户表
(
ip_address varchar2(20), -- ip地址
login_user_name nvarchar2(20) -- 操作者姓名
);
  创建如下的两个触发器:
create or replace trigger login_on_info -- 记录登入信息的触发器
after logon on database
Begin
insert into login_log(session_id,login_on_time,login_off_time,user_in_db,machine,ip_address,run_program)
select AUDSID,sysdate,null,sys.login_user,machine,SYS_CONTEXT('USERENV','IP_ADDRESS'),program
from v$session where AUDSID = USERENV('SESSIONID'); --当前SESSION
END;
create or replace trigger login_off_info --记录登出信息的触发器
before logoff on database
Begin
update login_log set login_off_time = sysdate
where session_id = USERENV('SESSIONID'); --当前SESSION
exception
when others then
null;
END;

  方法二:
  用如下的方式可以审记执行drop动作的事件:
/**
* drop语句的审计日志表
*/
create table drop_log
(
session_id int not null, -- sessionid
drop_time date, -- drop的时间
ip_address varchar2(20), -- ip地址
object_owner varchar2(30), -- 对象的所有者
object_name varchar2(30), -- 对象名称
object_type varchar2(20), -- 对象类型
drop_by_user varchar2(30) -- 执行drop语句的用户
);
create or replace trigger drop_info
after drop on mfg0513user.schema -- 在mfg0513user用户上创建审记DROP的触发器
begin
insert into drop_log
(session_id,
drop_time,
ip_address,
object_owner,
object_name,
object_type,
drop_by_user)
values(USERENV('SESSIONID'),
sysdate,
SYS_CONTEXT('USERENV','IP_ADDRESS'),
sys.dictionary_obj_owner,
sys.dictionary_obj_name,
sys.dictionary_obj_type,
sys.login_user);
end;


顺其自然EVO 2014-07-25 13:22 发表评论
Viewing all 15843 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>