分页: 1/38 第一页 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 下页 最后页 [ 显示模式: 摘要 | 列表 ]

再会,上海

[不指定 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 ]
今天乘机升级了所有的lucene平台,很顺利没有遇到太多问题.但lucene确实有些变化,至少我能看到的就是, 他把hits这个类给杀掉了,hits这个类以前也说过,可以说他就是缓存结果的最大障碍,但这个问题不难解决,只要记住:一切query都是filter就可以了.不过如今lucene废掉了hits这个类和他连带的方法,今后query只有topdocs了,这样的变化让我引起一些兴趣,稍稍研究后,再做汇报.

memcached的性能极限

[不指定 2008/10/10 07:40 | by edwardproAdmin ]
最近测试memcached的并发查询效率(并非连接效率),基本上在每秒100次,超过之后性能下降非常严重.测试中当查询超过100之后,memcached进程的cpu占用率可达到平均35%(8线程cpu)左右,并且速度效率下降非常明显,因此在memcached使用需要注意:
1 控制socket连接数,连接数大量增加会导致机器负荷上升,因此建议使用连接池,我测试使用的连接池最大连接数才15
2 控制缓存使用颗粒度尺寸,不适宜过小的颗粒度,基本保持在30次/s查询比较好
早上在编译PHP的是发现了一个PHP的bug...

早上编译php 5.2.6总是说我的mysqlclient没有装,我很奇怪,然后就去找了发现/usr/lib64下确实有.so啊为什么? 后来想想是不是愚蠢的php不知道/usr/lib64啊(因为操作系统是64位版本) 于是我把lib64下的so cp到lib下居然发现一切ok了!!!

实在要鄙视PHP这东西的,早就说过了php用过了就会有冲动自己写脚本编译器去的,事实很多次证明了这东西,但是这么垃圾的东西还有那么多人用(包括自己)实在想不通,看来生命就是在于折腾啊...呵呵
一直觉得lucene的reopen有问题,今天特地研究了下,顺便熟悉了整个IndexReader的结构体系

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 ]
按照文档说明reopen可以解决更新索引的问题,但试用下来有几个问题:

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也是一大抄袭. sns网站在技术上算是相对复杂的站点,从三家站点构架来看 海内和校内都是java平台,并且有完善的api机制(当然这是抄袭facebook的),开心网则基于php,而且它的系统构架和海内和校内比起来差距非常之大.

难怪公司有人可以信誓旦旦地称,业务是不能复制的,但是产品可以,于是这就成为了没文化还很拽的理由.表面看起来的确如此在中国这样的抄袭环境下, 任何产品技术只要是新的就会被迅速复制,而且复制得非常像(我说是非常像,不是一样).其实往往这样就带来很多错觉,觉得我们可以毫无止境地抄袭只要有好的东西. 事实上并不完全如此,总需要自己的东西的,就像海内和校内一样,他们最终只能活下一个吧,可是这是谁造成的,是他们自己,自己要走向死亡又有谁能拯救他们,拯救他们的只有他们自己.
首先看成绩表:

名次车手差距第一节第二节第三节
1汉密尔顿1.5231:19.3761:19.4731:20.899
2科瓦莱宁1.661:19.9451:19.4801:21.140
3马萨2.1231:19.5781:19.0681:21.191
4库比卡1.5051:20.0531:19.7761:21.281
5格洛克2.081:19.9801:19.2461:21.326
6莱科宁1.971:20.0061:19.5461:21.516
7阿隆索1.8821:20.2291:19.8161:21.698
8韦伯1.6861:20.0731:20.0461:21.732
9特鲁利1.8851:19.9421:19.4861:21.767
10皮奎特2.241:20.5831:20.1311:22.371
11维泰尔红牛二队-法拉利1:20.1571:20.144-
12巴顿本田1:20.8881:20.332-
13库特哈德红牛-雷诺1:20.5051:20.502-
14波尔戴红牛二队-法拉利1:20.6401: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--


其实麦克拉伦采用了超轻载油,当然鉴于麦克拉伦的技术优势,不能说他这是为了排位赛而排位赛的,要看具体战术,看他和法拉利之间进站差距吧.

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其实并不起眼,大家对其对性能的影响也不是很关注,但实际上filter是除了单纯搜索以外,其他搜索附加功能的必选组件,其性能很大程度上会直接影响搜索的性能,之前我一直认为filter的性能比query高,但事实说明并不完全如此(这里所说的负荷是指io消耗并不是cpu),实际上在lucene中充满着各种io流,也就是说很多东西都无法从根本上保存,这也给缓存带来了很大难度(这个问题看似简单,但是在超复杂的组合查询下,缓存可能会几乎无用,原因就是key怎么把握)

首先来看看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 ]
上个月买了包普洱,当时送了一个放茶叶的钢丝小漏斗,但是口径比较小,所以一直觉得不好用.今天想了个主意,既然正不行,那么就翻扣在底部,这样一样可以保证茶叶渣不漏出来了么?是的,事实的确如此,真是简单,为什么没有想到呢?

这就是典型的思维定式,把思维限定死了,结果就是这样,有时候工作也是,如果换换思路呢?也许问题很容易就解决了只要反过来扣就行了,呵呵.
分页: 1/38 第一页 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 下页 最后页 [ 显示模式: 摘要 | 列表 ]