再会,上海
2008/11/13 10:43 | by edwardproAdmin ]
思量了很久, 思索了很久, 终于决定离开上海这片土地, 就像许巍唱的:
我像风一样自由
就像你的温柔无法挽留
你推开我伸出的双手
你走吧最好别回头
无尽的漂流自由的渴求
所有沧桑独自承受
我给你温柔你拒绝接受
我给你双手真实的感受
我给你自由记忆的长久
我给你所有但不能停留
我像风一样自由
1年多的困惑没有给我带来多少价值, 所以终于决定要离开了, 而牵挂的却始终有一个人, 她默默地给了我支持, 才让我最终选择远方的城市, 陌生而又熟悉的街头, 已经不算年轻的我终于来临了, 人生第二个弯角, 期待 向往 畏缩... 明天会是怎样的太阳呢? 只有我去了才知道.
感谢所有上海的朋友, 我们杭州见.
感谢给我巨大信任的凤凤同学, 我会用鲜花簇拥着来迎你.
变化总是带来希望, 也有很多荆棘, 倒下没有关系, 重要的是还能站起来.
让ibatis插上c3p0的翅膀
2008/11/05 01:29 | by edwardproAdmin ]
ibatis真是个不错的东西,它特别合适小型的数据库应用使用,不过它的连接池比较简单,估计很难适应真正的生产环境,得改改,其实之前网上也看到别人写的,但是我不是很明白意图...
其实这个ds只需要继承一个接口,首先来看看接口是什么样子的:
package com.ibatis.sqlmap.engine.datasource;
import javax.sql.DataSource;
import java.util.Map;
/**
* Interface to provide a way to create and configure a DataSource for iBATIS
*/
public interface DataSourceFactory {
/**
* Simple method to initialize/configure a datasource
*
* @param map - the configuration information
*/
public void initialize(Map map);
/**
* Returns a datasource
*
* @return an implementation of DataSource
*/
public DataSource getDataSource();
}
目的是很明确,只需要解决初始化和get两个方法,第一个是真正的参数初始化方法,后一个是系统得到ds对象的方法,目标明确了,这样下一步操作就简单了,看我写的:
public class C3p0DataSource implements DataSourceFactory {
/**
* Logger for this class
*/
private static final Logger logger = Logger.getLogger(C3p0DataSource.class);
private DataSource ds;
private static final String DB_URL = "JDBC.ConnectionURL";
private static final String DB_USER = "JDBC.Username";
private static final String DB_PASS = "JDBC.Password";
private static final String DB_DRIVER = "JDBC.Driver";
private String dbUrl;
private String dbUser;
private String dbPass;
private String dbDriver;
public C3p0DataSource() {
logger.info("start for c3p0 ds");
}
@Override
public DataSource getDataSource() {
// TODO Auto-generated method stub
return this.ds;
}
@Override
public void initialize(Map map) {
// TODO Auto-generated method stub
this.setDbUrl(String.valueOf(map.get(C3p0DataSource.DB_URL)));
this.setDbUser(String.valueOf(map.get(C3p0DataSource.DB_USER)));
this.setDbPass(String.valueOf(map.get(C3p0DataSource.DB_PASS)));
this.setDbDriver(String.valueOf(map.get(C3p0DataSource.DB_DRIVER)));
try {
Class.forName(this.getDbDriver());
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
this.ds = DataSources.unpooledDataSource(this.getDbUrl(), this
.getDbUser(), this.getDbPass());
this.ds = DataSources.pooledDataSource(this.ds);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
测试已经能正常工作了^^准备休息,累了...
听许巍的歌
2008/10/27 02:43 | by edwardproAdmin ]
听许巍的歌可以让你感觉感动就是那么简单
听许巍的歌可以感动你心中最底层的心灵
听许巍的歌让你感觉到平淡无奇和心底的火花
听许巍的歌...
喜欢就是喜欢....
许巍-难忘的一天
作词:许巍作曲:许巍
阳光正温暖
一直照进我心里
如果没有你
怎么会有我今天
有时我会想起
和你经历的故事
那些情景在飞扬
甜蜜又伤感
再次走过熟悉的地方
如今的你不知在何方
你曾给我的温暖感觉
依然在我心
如果再见你
又是怎样的情景
会不会将你
再次拥进我怀里
阳光真温暖
一直照进我心里
往事已遥远
一年又一年
竟然在这一天
在不经意之间
人群拥挤的街头
你走过我身边
风吹起的青色衣衫
夕阳里的温暖容颜
你比以前更加美丽
像盛开的花
这是我难忘的一天
在隐忍和冲动之间
看着你渐渐的远去
消失人海中
lucene 2.4的变化
2008/10/22 08:16 | by edwardproAdmin ]
memcached的性能极限
2008/10/10 07:40 | by edwardproAdmin ]
1 控制socket连接数,连接数大量增加会导致机器负荷上升,因此建议使用连接池,我测试使用的连接池最大连接数才15
2 控制缓存使用颗粒度尺寸,不适宜过小的颗粒度,基本保持在30次/s查询比较好
PHP我不说你是垃圾真对不起祖师爷...
2008/10/01 12:25 | by edwardproAdmin ]
早上编译php 5.2.6总是说我的mysqlclient没有装,我很奇怪,然后就去找了发现/usr/lib64下确实有.so啊为什么? 后来想想是不是愚蠢的php不知道/usr/lib64啊(因为操作系统是64位版本) 于是我把lib64下的so cp到lib下居然发现一切ok了!!!
实在要鄙视PHP这东西的,早就说过了php用过了就会有冲动自己写脚本编译器去的,事实很多次证明了这东西,但是这么垃圾的东西还有那么多人用(包括自己)实在想不通,看来生命就是在于折腾啊...呵呵
IndexReader的reOpen彻底研究
2008/09/21 22:00 | by edwardproAdmin ]
IndexReader是一个体系,他是search的核心io之一
rg.apache.lucene.index
Class IndexReader
java.lang.Object org.apache.lucene.index.IndexReader
- Direct Known Subclasses:
- FilterIndexReader, MultiReader, ParallelReader
先来看看IndexReader下面的类体系我们平常会使用的三个派生: FilterIndexReader MultiReader ParallelReader
下面来看看我犯错误的reopen:public IndexReader reopen() throws CorruptIndexException, IOException
之前为什么我错了,我以为这个方法是自省的(根本没有注意它的返回不是void的道理么...),所以我怎么写的?
iSearch.getIndexReader().reopen();
误以为这样就可以reopen,结果总是被投诉没有更新,所以一直觉得更新有问题,实际上是我用法的问题,应该是这样的.
if (iSearch != null) {
// 新修改的reopen方法
try {
IndexReader irOld = this.reOpen(iSearch);
// isearchMap.remove(searchBean.getIsearchName());
isearchMap.put(searchBean.getIsearchName(),
new IndexSearcher(irOld));
} catch (Exception e) {
e.printStackTrace();
} finally {
// 注意要关闭掉原来的iSearch
iSearch.close();
iSearch = null;
logger.info(">>>>>>>>>>>>>one search has been reopen:"
+ searchBean.getIsearchName());
}
}
上面是从程序里截取的看看意思就好了.
但后来发现这样还是不对的,看源码上的说明:
If the index has not changed since this instance was (re)opened, then this
* call is a NOOP and returns this instance. Otherwise, a new instance is
* returned. The old instance is <b>not</b> closed and remains usable.
如果没有改变的话,他会返回null的,这样不得不判断一下,所以代码要这样:
if (iSearch != null) {
// 新修改的reopen方法
try {
IndexReader irOld = reOpen(iSearch);
if (irOld != null) {
// isearchMap.remove(searchBean.getIsearchName());
isearchMap.put(searchBean.getIsearchName(),
new IndexSearcher(irOld));
// 注意要关闭掉原来的iSearch
iSearch.close();
iSearch = null;
}
} catch (Exception e) {
logger.error(e);
} finally {
logger.info(">>>>>>>>>>>>>one search has been reopen:"
+ searchBean.getIsearchName());
}
}
这样可以在索引不变下不产生更多io消耗,那么我们可以这样理解reopn的实质:
首先判断 lastmodify时间,如果被更新了则, new 一个IndexReader, 否则就直接返回null,而操作方法都扔给了用户. 所以官方所谓的reopen能减少消耗说法并不准确,它做的只是在改变时重新连接,但这个他官方文档所说的只更新更新部分有很大不同,现在只是选择性地降低了io,但是如果每次探测时索引都变了就变得没有意义,这样的好吃是,索引的跟踪可以变得更频繁,也不再需要程序过多干预了.
但是目前这个方法写得不够成熟,至少我认为应该更"傻瓜"而不是把那么多细节都扔给开发者.
好了reopen这个新兴方法应该算是彻底搞明白了,原来看似吹嘘的很神的新东西,还是老瓶子装新酒呀,呵呵....
末了,代码再完善下:
if (iSearch != null) {
// 新修改的reopen方法
Boolean isNew = false;
try {
IndexReader irOld = reOpen(iSearch);
if (irOld != null) {
isNew = true;
// isearchMap.remove(searchBean.getIsearchName());
isearchMap.put(searchBean.getIsearchName(),
new IndexSearcher(irOld));
}
} catch (Exception e) {
logger.error(e);
} finally {
if (isNew) {
//保证关闭掉资源,否则连接数一多把机器io挂死
iSearch.close();
iSearch = null;
}
logger.info(">>>>>>>>>>>>>one search has been reopen:"
+ searchBean.getIsearchName());
}
lucene 的 reopen的问题
2008/09/16 13:32 | by edwardproAdmin ]
1 无法真正reopen,索引往往没有更新,原因不详,需要查lucene的源代码
2 lucene的实例id不会改变,原来以为里边的版本id标识会变,现在似乎不变,这直接导致了,我的缓存标记无法得到更新而读取了老的缓存.
另外在使用memeched时,大家一定要用:
http://bleu.west.spy.net/~dustin/projects/memcached/
另外一个链接池有很大问题,导致大访问量出现链接死锁问题.
另外jdk方面觉得可以直接放弃sun的了,用它的套壳版本: oracle的 jrmc 或者 jrrc都行,性能和设置都比sun的jdk简单.
wordCamp 2008
2008/09/13 11:10 | by edwardproAdmin ]

虽然我不用wordpress但是身边好友或多或少在用,所以报名参加了这样一次小活动,也许也会有所收获...
多年以前
2008/09/06 09:41 | by edwardproAdmin ]
那时的欢笑,那时的忧伤,又仿佛想起来点什么,只是那张上面默默地记录了很多.想起来现在总是说小朋友们读书不好好上都谈朋友了,我上课不也在...记得这是最最难的通讯原理,当然也是最无聊的,课间无聊居然抓起铺子来,还记得超载,还记得 现在到永远 ,永远到底在哪里,在奔去的未来吗?我不清楚,那虽然只是铅笔写的随性之物但依然让我淡淡地想起了很多.自动上次硬盘事故,早年录音的分轨早就不在了,当年的事还依稀,人却不同了许多,记得上次同学聚会的时候,真的发现大家都变了,而这个变化只是短短几年.淡淡的记忆,就让这张画面定格在我的储藏室里说不定哪天我会再拿出来看.
天下网站一大抄,SNS也不例外
2008/08/03 11:16 | by edwardproAdmin ]
难怪公司有人可以信誓旦旦地称,业务是不能复制的,但是产品可以,于是这就成为了没文化还很拽的理由.表面看起来的确如此在中国这样的抄袭环境下, 任何产品技术只要是新的就会被迅速复制,而且复制得非常像(我说是非常像,不是一样).其实往往这样就带来很多错觉,觉得我们可以毫无止境地抄袭只要有好的东西. 事实上并不完全如此,总需要自己的东西的,就像海内和校内一样,他们最终只能活下一个吧,可是这是谁造成的,是他们自己,自己要走向死亡又有谁能拯救他们,拯救他们的只有他们自己.
F1 匈牙利站排位赛成绩分析
2008/08/02 22:47 | by edwardproAdmin ]
| 名次 | 车手 | 差距 | 第一节 | 第二节 | 第三节 |
| 1 | 汉密尔顿 | 1.523 | 1:19.376 | 1:19.473 | 1:20.899 |
| 2 | 科瓦莱宁 | 1.66 | 1:19.945 | 1:19.480 | 1:21.140 |
| 3 | 马萨 | 2.123 | 1:19.578 | 1:19.068 | 1:21.191 |
| 4 | 库比卡 | 1.505 | 1:20.053 | 1:19.776 | 1:21.281 |
| 5 | 格洛克 | 2.08 | 1:19.980 | 1:19.246 | 1:21.326 |
| 6 | 莱科宁 | 1.97 | 1:20.006 | 1:19.546 | 1:21.516 |
| 7 | 阿隆索 | 1.882 | 1:20.229 | 1:19.816 | 1:21.698 |
| 8 | 韦伯 | 1.686 | 1:20.073 | 1:20.046 | 1:21.732 |
| 9 | 特鲁利 | 1.885 | 1:19.942 | 1:19.486 | 1:21.767 |
| 10 | 皮奎特 | 2.24 | 1:20.583 | 1:20.131 | 1:22.371 |
| 11 | 维泰尔 | 红牛二队-法拉利 | 1:20.157 | 1:20.144 | - |
| 12 | 巴顿 | 本田 | 1:20.888 | 1:20.332 | - |
| 13 | 库特哈德 | 红牛-雷诺 | 1:20.505 | 1:20.502 | - |
| 14 | 波尔戴 | 红牛二队-法拉利 | 1:20.640 | 1:20.963 | - |
| 15 | 罗斯伯格 | 威廉姆斯-丰田 | 1:20.748 | 无成绩 | - |
| 16 | 海德菲尔德 | 宝马-索伯 | 1:21.045 | - | - |
| 17 | 中岛一贵 | 威廉姆斯-丰田 | 1:21.085 | - | - |
| 18 | 巴里切罗 | 本田 | 1:21.332 | - | - |
| 19 | 费斯切拉 | 印度力量-法拉利 | 1:21.670 | - | - |
| 20 | 苏蒂尔 | 印度力量-法拉利 | 1:22.113 | - | - |
其实麦克拉伦采用了超轻载油,当然鉴于麦克拉伦的技术优势,不能说他这是为了排位赛而排位赛的,要看具体战术,看他和法拉利之间进站差距吧.
dz你这也叫程序? 你在侮辱我的智商
2008/07/13 13:56 | by edwardproAdmin ]
diszus估计是目前最流行的论坛了, 他的程序自然是大家都觉得还不错的, 可是事实呢? 不客气讲这也叫程序? 估计大群粉丝要开始骂我了,包括那些热衷于收购dz论坛的vc们,呵呵,那就来举个例子看看吧.
大家都知道最简单的就是通用登录,有人说了他不是有usercenter么?usercenter?我不敢用也不会用,你能保证你的程序也是php吗?他就没有考虑这点,其实解决这个问题太简单了,做一个数据网关就好了,如此简单的思维都没有,dz的构架师是要付全部责任的,特别是这样一个2007年开始发展的项目.
好了,回过来继续,那么uc这条路不通,我又搞不懂dz这个写得那么好的uc范例,那么直接调用吧,dz里负责这些事情的地方叫logging.php,这个文件打开之后就目瞪口呆了:
if($action == 'logout' && !empty($formhash)) {
...
}
所有所谓的动作就是这样通篇ifelse出来的,换句话说如果我要include你都没办法调用你,而它的所有逻辑都在这个代码里,我的妈呀!我忍不住又重复一次,你这也叫程序?你侮辱我了,真的.最起码的你要写一个类里边放些static方法吧,也好歹可以装B下叫做focade模式,可你这个算什么?换句话说我include一点没有价值,而这种思路我坦白说在我们公司的程序里也比比皆是,只要一个改动就完全没有办法调试,调试效率低下到了极点.
可是我又真的不想重构你,因为你的程序经常升级不太想改除了模板以外的部分,但这意味着某些时候我要妥协你了,当然妥协很简单,只要参数和你一直就好了,伪装下.
前几天我闹了笑话,我一直以为$_GET是一个只读变量但是在PHP5.2.5下居然可写的,实在令我无法理解php.net的思路,这样的做法只会把php带去一个更不需要讲求封装的低级世界,真可怕.
震惊过了,牢骚过了,该做的还得继续,但这不断让我膨胀的自满感,对我绝对不是一件好事,因为真实水平远远没有独孤求败,只是环境有些低而已,世界在进步,你进步慢就是退步了,呵呵,要自我清醒才行.
lucene中的filter器群组及其缓存大盘点
2008/07/03 14:08 | by edwardproAdmin ]
首先来看看filter的接口定义:
public abstract class Filter implements java.io.Serializable {
public abstract BitSet bits(IndexReader reader) throws IOException;
}
简单明了从reader中知道哪些记录是可以读出来的用true false放在bitsets中,然后再用这去和总集合做and操作得到剩余记录数,然后再通过query查询.原理知道了,下面来考虑下它的缓存:
缓存filter本身,由于他是序列化对象,那么已经具备了缓存的条件,但是这是一个错误,因为你缓存了这个类,而当你把参数reader拿出来依然会和机器产生io,因此这是极其不恰当的方法,应该缓存它的结果.
在lucene中有这么几个和filter有关的类:
CachingWrapperFilter
CachingSpanFilter
RemoteCachingWrapperFilter
FilterManager
其实我想质疑前两个,为什么呢,请看他的源码:
protected transient Map cache;
他放置缓存的map居然是transient的,这意味着即使你把它实例在static中这个变量依然会每次要new的,这样的缓存有意义吗?我看不出他怎么缓存的
/**
* A transient Filter cache. To cache Filters even when using {@link org.apache.lucene.search.RemoteSearchable} use
* {@link org.apache.lucene.search.RemoteCachingWrapperFilter} instead.
*/
上面这句注释总算明了了,呵呵.
那么其实RemoteCachingWrapperFilter才是真正的cache类,他的实现借助于filterManager,这个类是我们平时能理解的那种cache结构
public BitSet bits(IndexReader reader) throws IOException {
Filter cachedFilter = FilterManager.getInstance().getFilter(filter);
return cachedFilter.bits(reader);
}
但这个还不够,第一他的性能我心里没谱,遇到上万的访问怎么办?所以还是要用第三方的缓存,我使用的是memcached,这个东西不介绍了,只有一个问题,就是必须要求对象是可序列化的,这个不难理解,要想网络传输只能治么搞.
我的缓存策略:把最细胞的filter用memached缓存他的结果集,而他的组合fliter用自带的filtermanager管理就好了.filter怎么合在一起上次写过一个,看这里: http://www.edwardpro.com/post/572/
而我这样的道理也是基于filtermanager的key是reader的hashcode,因此他是对应不同的索引的.那么肯定有朋友问怎么刷新呢?太简单了啊,你的key只要有reader或者search的hashcode就可以了,你一旦更新的源hashcode就变化了.(如果你的search和reader的hash不是固定的那么你肯定承受不了100以上的并行访问,io会高得惊人.)
另外一个技巧,是关于rangefilter的,这个东西不错,但是有一点难,在哪里呢?因为他的查询似乎效率不高,因此一定要缓存! 但是key呢?比如我常用的key是timestamp,但是实际中就会发现如果用毫秒的timestamp那么key几乎无用,因为很少相同的,经过改进,我把时间可以用月做单位,查询也是如此,如果你的要求高我觉得做到天就ok了,如果你数据再多用到小时肯定也够了吧,这样filter的缓存会带来极大的性能提升.
那么实际效果呢,在原来使用时候2台集群机(nginx作为前端代理,后部用resin作为应用服务器)io平均1.xx 现在加了缓存之后常年保持在0.2左右!性能得到了几乎5~6倍的提升.而一般查询一个十万当量的+ 5个关键字 + 3个filter 时间大约是<10ms 非命中时大约是 70~80ms 这个速度如果得到同样结果的数据库至少要放大1000倍的时间.
由于我memcached没有做集群是独立的(事实也应该如此,因为你两台机器的reader的hashcode肯定是不一样的,放一起也是这样的结果,这样也没有不好,当一台机器出现问题或者需要更新代码可以用时间差来保证负荷平稳过渡,不像以前一台机器每次重启都是有点怕怕的,只能找空闲时间才敢这么做.
最后要讲的query,其实前面我说了半天没有提到query,query的缓存呢? 其实在lucene中有这么个类:
QueryFilter
这个类简单说就是把query变成filter,那干什么呢?很简单啊,这样任何查询都会变成filter的,所以所有的缓存都是filter!那么从缓存中取出来的filterquery怎么用?
MatchAllDocsQuery matchAll = new MatchAllDocsQuery();
result = isearch.search(matchAll, filter, sort);
filter是用我的合成filter组合的,这样消耗就更低了,当然不建议无限制增加系统负荷,因为那样就几乎无法重启了,呵呵.好了基本说到这里,其实最后我想说我的核心思想: 任何query都是filter,lucene就是filter查询,事实是如此的,呵呵.
大家有什么其他方案也可以讨论和交流一下,呵呵.
其实有些东西很简单
2008/06/30 13:14 | by edwardproAdmin ]
这就是典型的思维定式,把思维限定死了,结果就是这样,有时候工作也是,如果换换思路呢?也许问题很容易就解决了只要反过来扣就行了,呵呵.




