本来只打算考一次免费的BULATS,那个ACCA的比赛居然让我去雇主见面日。。。要是到会场看到一群正装革履跃跃欲试的叔叔阿姨们,我就直接匿了。。。
Category: Space导入
使用JMeter进行压力测试
JMeter是一款优秀的开源压力测试工具。网上有不少介绍的文章,比如DeveloperWorks上有一篇。不过脚本的制作比较麻烦,自己手动输入参数比较不实际。cnblog上有一测试达人在这里介绍了一个免费工具Badboy,可以录制在IE上的操作并导出JMeter脚本。Badboy本身是一个Web自动化测试工具。不足的是,一些Badboy录制的动作JMeter并不支持,比如多窗口操作。在导出jmx文件的时候Badboy会提示不能导出的内容。Badboy也有一些不够智能的地方,比如写死了服务器地址,而不是创建一个默认的HTTP访问器。
一开始使用JMeter进行压力测试完全是冲着JMeter的Cookie Manager功能去的。如果使用简单的工具,比如apache自带的ab工具,则要想方设法绕过应用程序的登录机制。使用Cookie Manager虽然不需要配置,但要注意的是,要把发来初始cookie的地址给包括进来。我测试的系统在/login.do这里发放cookie,但我直接访问/acegi_login登录,结果就丢掉了cookie,导致后续步骤失败。
你出生日的照片
在百合十大上看到的,挺有意思的。转载如下:
这个人—— 从1979到1997。18年。pola相机。每天一张照片。 第一张是1979年3月31日,最后一张是1997年10月25日。后头的就没了。 因为,1997年10月25日那天,拍照片那人死在病床上。 他的朋友把他生前拍的照片整理了出来,放在网上。 网址http://photooftheday.hughcrawford.com 18年都在里面。
下面这张是我出生日的照片,居然是比萨斜塔:
国家地理的新节目:Megastructures/Megafactories
虽然标题上说是新节目,不过估计也开播一两年了。主要都是从emule上拖的,地址在这里。主要介绍了一些大型工程产品的制作过程。对engineering有爱的同学可以看看。最近看了以下几集:
M1A2埃布拉姆斯坦克。M1现在已经不生产了,纪录片里拍的是M1的翻新和改进过程,比如把M1A1翻新成M1A2。令人印象深刻的莫过于重量,特别是M1厚重的装甲。另一个让人感兴趣的东西是库存的零件,放在一个巨大的仓库里,所有的零件的存取都通过机器人操作。
Apache直升机。感觉阿帕奇的生存设计就是冗余。两台发动机,一台1800马力。两个电子吊舱,两套配线,典型一个有重生技能的牛头人。出乎意料的是,阿帕奇的生产装配是全手工的,几乎没有引入机器人进行装配,还有就是邮箱居然是橡胶的。
弗吉尼亚级攻击型核潜艇。美国海军的当家宝贝,藏着掖着没说啥具体的东西。主要就是一些训练的内容和一笔带过的制造工艺。
C-5银河运输机。C-5的制造也是很早以前的事了,大概在上世纪80年代,所以纪录片讲述的主要是C-5的使用和保养。虽然C-5已经很大了,最大起飞重量有近400吨,可是它上面还有老毛子的近乎变态的An-124和An-225。这一集拍的是一架C-5搭载M1的强化装甲去伊拉克的故事,和M1坦克那集倒是有呼应。夜晚空中加油那段拍得很不错。
卡特彼勒巨型卡车。大家经常看到在施工场所里的重型设备上的CAT,指的就是卡特彼勒毛毛虫(CATerpiller)。直到这次金融危机前,我还一直以为CAT是某个日本的重工集团,类似三菱或者川崎。卡特彼勒本来是生产拖拉机的,由于在田野里的样子像毛毛虫,这个名字就保留了下来。这集里说的巨型卡车,就像大家在电视上看到三峡大坝施工场地里跑的那种巨型装卸卡车,车轮子就有两人高,整个车大概有3层楼高。这么大型的车辆,由于无法进行整车运送,所以都是把装配好的主要大件,比如车架、轮子、驾驶室、装卸斗用火车分别运到施工场地(片子里是加拿大的油砂产地),再进行组装。全车150吨,马力惊人。制造过程中使用了大量的自动焊接,牛B的是,焊接处的结构强度比铸造部分的结构强度还要大。。。
UPS高速递送。只介绍了UPS美国国内的业务。所有的邮件,除了目的地是本地附近地区的以外,全都空运送到美国中部Kentucky的一个镇上进行redistribution(不知道我有没有理解对,不过片子里似乎就是这样的)。邮件分为大中小三种类型进行分拣,大量的人工+更大量的自动化分拣。不知国内的那些快递公司,包括EMS,何时能达到如此业务水平。
宝马Z4跑车。主要讲了Z4的组装过程。Z4的发动机居然是用铝和镁组成的,气缸内壁用铝,外面用镁,整个V6的发动机气缸只有30公斤。为了减重,MS进气道用的是塑料。
SHL
今天去考了ACCA那个比赛的初试,考了一次SHL玩玩。算是我第三次正式考SHL了吧,第一次是去年的ACCA比赛,第二次是年初的UBS,都挂了。囧
不过更囧的是今天去考试的路上。考试在南审,我差不多算好时间骑车出门,没想到到清凉门的时候前轮的挡泥板居然由于年久失修,掉下来了。要是全部掉下来就算了,我直接摔垃圾桶走人,只掉了一个螺丝,结果导致挡泥板直接卡在了前轮上,不能骑了。前面离南审还有1公里多的路,还有10多分钟就要进场了,又没公交车直达,而且我还不知道考试地点的具体位置(只知道教室门牌和楼宇的大概位置)。。。还好急中生智硬是把挡泥板拗弯,架在以前车篮的残骸上,居然又能骑了。
仍然提前赶到考场(还好提前留了10分钟),入场,顺利答完题目。SHL这个东东的确是能够训练的考试,去年这个时候考这个的时候,我只做了大概18题不到;今天之前做了点准备,在考官喊停笔前半分钟正好做完。看到周围有很多人没做完,想起了去年这个时候的我,信心满满去考试却被打击得不行。
下半年找工作,应该还会碰上一些SHL。不过IT的笔试更主要还是考专业上的知识,算法、数据结构还有语言吧。
p.s. 要是过了这一轮,就可以免费考一次博思,嗯,主要是冲着这个去的。
p.s. 最近写文档。。恨APIS九个洞。。。
雨乌线运转小记
乌江和安徽一江之隔。两地各辖一个乌江镇。乌江桥东为浦口,西为安徽和县。项羽当年在此自刎。
起点,科苑宾馆,浦葛线
要坐的雨乌
612,来的时候坐的这部车,从江浦到乌江
车内图,全车大概50个位子,包括加座(就像校车的加座,要翻下来坐过道上)
路过桥林
这镇上还有电影院
京沪高铁,H型柱子,城际是Y型的柱子
从这里离开宁乌路,上三桥
大奔用来运货了,车牌还挺牛B,苏A ST001,三桥收费站
终点,雨花区议会山
雨花台纪念馆
雨花台纪念碑
雨花台群雕
一早鼓扬到开发区,3元
吃过午饭后
浦葛到浦六路,1元
汉江到江浦,1.5元
612到乌江,3元
雨乌到雨花台南门,5元
26到丹凤街,1.2元
全程约110公里,14.7元
家门口似乎要通BRT了
虽然家就住在国道旁,可每次去岛内也算麻烦,因为家门口不设站,基本靠和司机打招呼或者招手停车。今天看到一则BRT新闻“BRT成功大道专线站点设置确定 岛内外共设19站”:
东南快报讯(记者 林晓琪)BRT成功大道专线从厦港出发,经过成功大道、厦门大桥,延伸至集美灌口,全长30.6公里,岛内和岛外各设9个和10个站,共19个站。
昨日,记者从有关部门获悉,BRT成功大道线岛内9个站点分别为:厦港枢纽站、文曾路站、谊爱路站、吕岭路站、仙岳路站、金湖路站、安兜站、机场大道站、联检站;岛外10个站点为:航海学院站、园博苑站、杏林村站、厦门十中站、内茂立交站、杏北新城站、中亚城站、厦工机械站、灌口镇政府站、灌口枢纽站。
看来附近房价有上升的空间。。。
投了GE的Summer
学校用Bras很慢,直接上到远程服务器上网申。居然没有OQ,改了改大摩的cover letter,半个小时搞定。GE的网申系统的牛B之处在于,可以从你提交的CV里取出你的Work Experience(虽然他把我的Education Background给当成了Work Exp了)。其他问题也不多。
上次的微软估计没戏了,过了一个半月了;大摩上次说还没有计划开始。
P.S. 博客最近都是“阳春白雪”的stuff,搞点“下里巴人”的。嗯,突然想起了高中的生物老头郑家骥郑老师,课上突然冒出这一句把大伙儿雷得不行。
Selenium, Eclipse, Junit折腾记
很早以前就想搞自动化Web功能测试,知道了Selenium,看了些文档,但当真正到开发项目的关头,测试总是草草而过,跑完一遍手工的拉倒,回归测试更是无从谈起。前几天终于痛下决心写起使用Selenium的Web自动测试代码。
先扯扯Selenium(字面上是“硒”的意思)。当初出来的时候结结实实把我震撼了一回。原来搞Web自动化测试基本上走的是GUI的那条老路(当然可能也是我当年孤陋寡闻),而这种GUI自动测试工具往往是功能强大的私有软件(比如WinRunner),另外对Web这种多变的测试元素用起来也是很别扭。Selenium另辟蹊径,从JS入手调用浏览器,同时允许通过跨平台的代码调用。从API就可以看出来这个东西的直观易用:
1: selenium.windowMaximize();
2: selenium.click("link=信息系统");
3: selenium.waitForPageToLoad("30000");
4: selenium.click("//a[contains(@href, 'projects.do?method:view&project.id=1')]");
5: selenium.waitForPageToLoad("30000");
另外,Selenium还提供了一个Firefox插件-Selenium IDE,用于录制用户的操作(虽然部分动作无法录制)。录制的动作可以直接导出成HTML/Java/Ruby/C#/PHP等格式的代码,配合提供的SeleneseTestCase,当作JUnit的TestCase使用。
不过这折腾就折腾在这TestCase上。Selenium的开发提供的SeleneseTestCase是Junit3风格的,放在JUnit4底下跑,JUnit4的Annotation功能就用不起来了(这点经我浏览代码查证)。Selenium要启动浏览器,如果用不上@BeforeClass的话,每次启动都初始化一下Selenium,开个IE或者Firefox,这个测试的效率可吃不消(也有比较麻烦的Workaround,但总觉得不是很好)。而甩开SeleneseTestCase的话,又舍不得那个在测试没有通过的时候自动截屏的功能。于是开始Google,兼看Junit4的源代码。
最后终于在这里找到了方案。但问题又来了:Eclipse自带的Junit 4.3还没有这个方案需要的类(JUnit4ClassRunner )!自己手动把原来的Library移除换上Junit 4.5,又发现这个类才用了一个版本就Deprecated了。于是换上了新类(BlockJUnit4ClassRunner)。
接下去的工作就是用上Decorator模式,把原来SeleneseTestCase的代码给移到新的BaseTestCase上。期间还遇上了一些Override的问题。上代码:
SeleniumTestListner类,用于拦截异常的抛出
1: public class SeleniumTestListener extends RunListener {
2: private SeleneseTestCaseAdapter stca;
3: @Override
4: public void testFailure(Failure failure) throws Exception{
5: Selenium selenium = stca.getSeleniumTestBase().getSelenium();
6: if(!stca.isCaptureScreenShotOnFailure()){
7: return;
8: }
9: if (selenium != null) {
10: String filename = failure.getDescription().getDisplayName() + ".png";
11: try {
12: selenium.captureScreenshot(filename);
13: System.err.println("Saved screenshot " + filename);
14: } catch (Exception e) {
15: System.err.println("Couldn't save screenshot " + filename + ": " + e.getMessage());
16: e.printStackTrace();
17: }
18: }
19:
20: }
21:
22: public void setSeleneseTestCaseAdapter(SeleneseTestCaseAdapter stca){
23: this.stca = stca;
24: }
25: }
SeleniumTestRunner类:加入SeleniumTestListener监听器,得到Test实例并注入监听器
1: public class SeleniumTestRunner extends BlockJUnit4ClassRunner {
2: private SeleniumTestListener stl;
3: public SeleniumTestRunner(Class<?> c) throws Exception{
4: super(c);
5: stl = new SeleniumTestListener();
6: }
7:
8: @Override
9: public void run(RunNotifier rn){
10: rn.addListener(stl);
11: super.run(rn);
12: }
13:
14: /**
15: * Copy from BlockJUnit4ClassRunner.methodBlock(FrameworkMethod method)
16: * to get tested instance
17: * @author Marshall
18: */
19: @Override
20: protected Statement methodBlock(FrameworkMethod method) {
21: Object test;
22: try {
23: test= new ReflectiveCallable() {
24: @Override
25: protected Object runReflectiveCall() throws Throwable {
26: return createTest();
27: }
28: }.run();
29: } catch (Throwable e) {
30: return new Fail(e);
31: }
32:
33: //Marshall added
34: stl.setSeleneseTestCaseAdapter((SeleneseTestCaseAdapter)test);
35:
36: Statement statement= methodInvoker(method, test);
37: statement= possiblyExpectingExceptions(method, test, statement);
38: statement= withPotentialTimeout(method, test, statement);
39: statement= withBefores(method, test, statement);
40: statement= withAfters(method, test, statement);
41: return statement;
42: }
43: }
SeleniumTestCaseAdapter, 打上了@RunWith。所有的TestCase都继承这个Adapter。但这个Adapter并不继承JUnit的TestCase类
1: /**
2: * Decorator pattern which makes this class have the same capability as the
3: * SeleneseTestCase class had provided. Copy a lot of source code from the
4: * decorated class.
5: * @author Marshall
6: */
7: @RunWith(SeleniumTestRunner.class)
8: public class SeleneseTestCaseAdapter {
9: private static SeleniumTestBase stb = new SeleniumTestBase();
10: private boolean isCaptureScreenShotOnFailure = false;
11:
12: /** Use this object to run all of your selenium tests */
13: protected static Selenium selenium;
14:
15: @BeforeClass
16: public static void setUpSelenium() throws Exception{
17: stb.setUp("http://127.0.0.1:8080/", "*iexplore");
18: selenium = stb.getSelenium();
19: }
20:
21: @AfterClass
22: public static void tearDownSelenium() throws Exception{
23: stb.tearDown();
24: }
25:
26: ......
27: }
样例测试类:
1: public class TestRiskRepo extends SeleneseTestCaseAdapter {
2: public TestRiskRepo(){
3: setCaptureScreenShotOnFailure(true);
4: }
5: @Before
6: public void set() throws Exception {
7: selenium.open("/apis/login.do");
8: selenium.type("j_username", "marshall");
9: selenium.type("j_password", "xxxx");
10: selenium.click("//input[@value='登录']");
11: selenium.waitForPageToLoad("30000");
12: }
13:
14: @Test
15: public void repo() throws Exception {
16: selenium.windowMaximize();
17: selenium.click("link=信息系统");
18: selenium.waitForPageToLoad("30000");
19: selenium.click("//a[contains(@href, 'projects.do?method:view&project.id=1')]");
20: selenium.waitForPageToLoad("30000");
21: verifyTrue(selenium.isTextPresent("xxx"));
22: selenium.click("link=组织风险库");
23: selenium.waitForPageToLoad("30000");
24: verifyEquals("组织风险库 | APIS", selenium.getTitle());
25: verifyTrue(selenium.isTextPresent("可能性"));
26: verifyFalse(selenium.isVisible("//button[contains(text(), '搜索')]"));
27: selenium.click("css=.x-tool");
28: verifyTrue(selenium.isVisible("//button[contains(text(), '搜索')]"));
29: }
30:
31: }
多数据库SaaS尝试
手上的APIS要上SaaS,其实就是ASP(Application Service Provider)。要求一份程序,一或多份数据库跑服务。设计的时候参考了阿里软件人写的《互联网时代的软件革命-SaaS架构设计》。这本书从广度上覆盖了做SaaS的很多内容,但深度却显不足,很多地方只是浅尝辄止,一笔带过。
虽然如此,作为算是这个领域的第一本入门书,还是有一些参考价值的。比如,定义了SaaS四个成熟度模型:定制开发、可配置、高性能的多租户架构、可伸缩的多租户架构;APIS只有一个运行实例,但租户数量不会很多,短期内不会超过50,所以算入第3级,高性能的多租户架构。
在数据库设计上,这本书对SaaS应用,提供了四个可选方案。独立数据库、共享数据库+隔离数据架构(Schema)、共享数据库+共享数据架构(Schema)。由于采用了MySQL,没有schema的概念,所以去掉了中间的选项。虽然技术人员有上高精尖技术的冲动,不过鉴于我们的用户数不会很多,用户和用户之间也完全隔离,采用了第一种方案。而这种方案,对开发其他功能完全没有任何影响,算是非常unobstructive(中文不好翻译,一般叫“无侵入”)的方案。只要在入口的Filter留个ThreadLocal标志,最底层的SessionFactory做个拦截,功能上算是搞定了。大概就是事先准备好几个SessionFactory,然后哪个用户上来了就给谁相应的SessionFactory。
但革命的道路是曲折的,过程是漫长的。虽然前期通过阅读Spring代码做过一些技术调查,但Spring+Hibernate这对组合的复杂性还是折腾我老半天。Spring对Hibernate支持很好,几乎是全方位的,这也造成了我要到处扩展用到的Spring支持类。本来只打算对LocalSessionFactoryBean动手,却忘了spring大部分的Bean都是singleton,但又舍不得换成prototype后的性能下降,于是抡起袖子又扩展了BasicDataSource(DBCP)、HibernateTemplate、HibernateDaoSupport、HibernateTransactionManager。好不容易有点眉目,开始从配置的租户数据库载入数据,却屡屡抛session提前关闭的异常。而且诡异的是,这个异常出现的概率约为75%,而一旦我用Debug跟进去后,反而基本不抛异常了。这让我异常胸闷,要知道这种问题十有八九是并发的原因,这问题找起来很头大,往往陷入Spring+Hibernate的汪洋大海。
接下来几天感冒发烧,好好休息了一阵子,思路却打开了。从网上重新搜出一堆资料。这个是Stack Overflow的一个问答,几个回答都比较靠谱;这个是通过HotSwappableTargetSource提供的DataSource动态替换;这个是Spring开发人员利用AbstractRoutingDataSource提供对DataSource的路由。权衡再三,决定使用AbstractRoutingDataSource。只扩展了两个类就成功实现功能,不抛异常。全局一个SessionFactory,后面几个DataSource轮流上。
但问题又来了。二级缓存这个东西和SessionFactory绑定,多个DataSource要求要多个Cache,因为各个租户之间的数据主键很可能重复。我试图搜寻通过改造SessionFactory的CacheProvider来搞定,但未果,SessionFactory的Cache埋得有点深。
最后,参考了这篇和这篇,用一个简单的Decorator,退回了多SessionFactory,虽然代码不是很好看,但再无异常。今天收工。