#
SPRING BATCH 柯以测试的内容:JOB, STEP, INTEMPROCESSOR, ITEMREADER, ITEMWRITER。
JOB, STEP属于功能测试(黑盒)的范畴,INTEMPROCESSOR, ITEMREADER, ITEMWRITER属于单元测试(白盒)的范畴。
/*
* Copyright 2006-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.util.Date;
import java.util.concurrent.Callable;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemStream;
import org.springframework.batch.item.NonTransientResourceException;
import org.springframework.batch.item.ParseException;
import org.springframework.batch.item.UnexpectedInputException;
import org.springframework.batch.test.JobLauncherTestUtils;
import org.springframework.batch.test.MetaDataInstanceFactory;
import org.springframework.batch.test.StepScopeTestExecutionListener;
import org.springframework.batch.test.StepScopeTestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
@ContextConfiguration(locations = { "/test-context.xml",
"classpath:/META-INF/spring/batch/hello-tasklet-context.xml",
"classpath:/META-INF/spring/batch/jdbc-job-context.xml",
"classpath:/META-INF/spring/integration/hello-integration-context.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
//测试ITEMREADER/ITEMPROCESSOR/ITEMWRITER时用到
@TestExecutionListeners( { DependencyInjectionTestExecutionListener.class,
StepScopeTestExecutionListener.class })
public class HelloTaskletTests {
@Autowired
private JobLauncher jobLauncher;
@Autowired
private Job helloWorldJob;
@Autowired
private JobLauncherTestUtils jobLauncherTestUtils;//测试JOB/STEP的入口
@Autowired
private ItemReader xmlReader;
public void testLaunchJobWithJobLauncher() throws Exception {
JobExecution jobExecution = jobLauncher.run(helloWorldJob, new JobParameters());
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
}
/**
* Create a unique job instance and check it's execution completes
* successfully - uses the convenience methods provided by the testing
* superclass.
*/
@Test
public void testLaunchJob() throws Exception {
JobExecution jobExecution = jobLauncherTestUtils.launchJob(jobLauncherTestUtils.getUniqueJobParameters());
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
}
public void testIntegration()
{
while(true)
{
}
}
/**
* 测试某个STEP
*/
@Test
public void testSomeStep()
{
JobExecution jobExecution = jobLauncherTestUtils.
launchStep("xmlFileReadAndWriterStep",getJobParameters());
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
}
/**
* 测试READER的方式1时,所需的方法
* @return
*/
public StepExecution getStepExecution() {
StepExecution execution = MetaDataInstanceFactory
.createStepExecution(getJobParameters());
return execution;
}
/**
* 测试READER的方式1
* @throws Exception
*/
@Test
@DirtiesContext
public void testReader() throws Exception {
int count = StepScopeTestUtils.doInStepScope(getStepExecution(),
new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int count = 0;
try {
((ItemStream) xmlReader)
.open(new ExecutionContext());
while (xmlReader.read() != null) {
count++;
}
return count;
} finally {
((ItemStream) xmlReader).close();
}
}
});
assertEquals(3, count);
}
/**
* 测试READER的方式2
* @throws UnexpectedInputException
* @throws ParseException
* @throws NonTransientResourceException
* @throws Exception
*/
@Test
@DirtiesContext
public void testReader2() throws UnexpectedInputException, ParseException, NonTransientResourceException, Exception
{
assertNotNull(xmlReader.read());
}
/**
* 测试READER的方式2时,必须加的方法
*/
@Before
public void setUp() {
((ItemStream) xmlReader).open(new ExecutionContext());
}
/**
*
* @return
*/
private JobParameters getJobParameters() {
String inputFile = "/Users/paul/Documents/PAUL/DOWNLOAD/SOFTWARE/DEVELOP/"
+ "SPRING BATCH/spring-batch-2.1.9.RELEASE/samples/"
+ "spring-batch-simple-cli/file/trades1.xml";
String outputFile = "/Users/paul/Documents/PAUL/DOWNLOAD/SOFTWARE/DEVELOP/"
+ "SPRING BATCH/spring-batch-2.1.9.RELEASE/samples/"
+ "spring-batch-simple-cli/file/output/out.xml";
JobParameters jobParameters = new JobParametersBuilder()
.addString("input.file.path", inputFile)
.addString("output.file.path", outputFile)
.addDate("date", new Date()).toJobParameters();
return jobParameters;
}
}
参考例子:
前段时间招聘。因为我一直在我的部门推行一些有效却被绝大多数中国公司忽视的开发理念,比如平级人事结构、测试驱动开发、制度化绩效、设计先行、迭代开发等等,所以招软件设计师非常困难。最终问题还算解决了吧。以下是我的面试总结。
一般来说,作为迎接面试的人,我会借着询问路况、接引进会议室或者索要简历,来表达出自己的礼貌,让对方有一定紧张感。这样我认为有利于对方表现出自然状态的思路。
然后我会根据简历,大概咨询下对方工作中所做过的设计工作。因为我在招软件设计师,所以只问设计,看看他对设计的理解是什么样子。通过这种询问,可以考察对方的简历是否作假,如果作假那么无法明确讲述其过往工作。还需要看对方的表达能力,即主动展现自己思路的能力。按照我这一批面试的人看,大多数人会讲述其项目经历的业务或者架构。只有一个人能够把软件设计和架构设计、软件开发分离出来。
然后就会开始做面试题了。面试题附在下边。我会先让其看第一大题,设计能力,请ta选择一道题目作答。看题之后,对方一般会陷入思考沉默。那么根据对方眼神不再在题目间扫动,表示对方已经针对某一个题目思考。当然,如果是沟通能力好的人,这这之前会主动告诉我,ta准备作答哪道题目。此时需要打断沉默,对对方说,希望对方谈一谈想法,任何一点想法都可以说出来。这个时候如果对方能够针对题目问一些具体没表示明白的细节,或者自行设定细节,都表明此人沟通能力极好,否则应该认为其沟通能力打折扣。
当对方陆陆续续讲述自己的设计时,作为面试的人,需要指出其没有思考到的地方,或者赞扬对方想的很合理。一般来说,面试的人在经历了之前的客套和紧张之后,不太容易沉下心来仔细思考。凡是这时候依然能保持有序高效的思考能力,说明这个人抗压能力极强,至少是心理调节能力极好。通过这时的回答,就可以看出此人对设计是否了解,设计能力怎么样。
以 1.1 为例,我随便说几个要点。比如说,我们应该抽象出牌局状态这么一个类,作为传输给每一个玩家的内容。比如说,我们可以抽象出一张牌这么一类,作为出牌的玩家上报的内容。比如说,此场景应该有一个短连接请求处理类,还应该有一个房间控制类;房间控制类里边维护一个个开通的房间;当每一个进入房间的请求来临时,都应该通过房间控制类,将连接转移到这个对应的房间编号;那么房间编号可以由客户端通过参数传递上来。比如说,每一个房间能够维护一组长连接,这可以开一个线程来处理;线程由一个单例的线程维护器对象来启动。比如说,房间在新线程中执行的代码,应该是轮询每个长连接看谁发来了“牌”,然后调用数据计算模块,通过牌和原有牌局(保存在房间对象里),得到新牌局,发送给每一个连接。当然了,如果能对断链识别什么的做出设计就更好了。不过控制此题时间不要超过 10 分钟,所以一般不可能讲述出太多内容。
接下来,要根据刚才设计方面的能力,中转到 2 或者 3 。如果设计能力不错,则应该转移到 3 ;如果设计能力不好,则应该转移到 2 。这个设计能力的好不好,是根据面试者觉得达标不达标,够用不够用来说的。
先来说说转到 2 编程能力测试的情况。首先,应让其在 2.1 和 2.2 中选一道作答。2.1 的要点,是建立合适的数据结构。最佳的是 money 与 id 双排序的基础对象构建的 TreeSet ,通过 NavigableSet 提供的子树功能,以 size() 获取名次。自己前后几位可通过正反向迭代器取得。当然,也可以使用链表和散列集合的二重结构。这要慢不少,但是也还算在可接受范围内能实现功能。
此题非常多的面试者会联想到 SQL 的 order by 子句。问题是这些人应该设计不出合适的二维表结构,配合 SQL 实现想要的功能。如果面试者能够实现,那也至少算能够做出来一种实现,不应苛求。
2.2 我认为用二维表传统关系型数据库很合适。应该有二个表,一个是历史表,一个是实际操作记录表。每次受影响玩家登录,都会激活从实际操作记录表到历史记录表的总结过程;这是一种最小型的数据挖掘。受试者能够正确写出总结过程,能够正确写出读取历史表的合并过程,则是满分。
编程过程测试,可以随时提供设计思路指导。旨在督促受试者利用短暂的面试时间,认真思考,发挥出最强的思维能力,以检测其通过自己思考解决问题的水平。如果能在不断的设计方案提示下,指出语句写法,则应该认为设计能力为零,但编程基本功很扎实。如果算法也需要提示,则认为编写程序的技能本身不好,但语言知识尚可。
做完 2.1 和 2.2 其中一题,应准备查资料的条件,也就是连接互联网的浏览器,让其做第 3 题。如果其对 Java5.0 多线程类库非常了解,则认为此人关注技术新闻,有很强的技术敏感性和学习能力。否则则应通过观察其查资料选用关键词的方式、查看内容的耐心敏感度,来判断此人的学习能力和心性。至此,您应该对面试的人有了较全面的认识。推荐记录下来,以便比较不同的面试者,谁综合看来更适用。
我们再来说说直接从 1 题转移到 3 题的情况。同样是 3.1 和 3.2 选做一题,来测试受试者的架构能力。以和设计能力测评同样的节奏进入互动,然后探寻受试者思路。架构的核心,在于指派分布,即指派哪些逻辑运行在什么地方,以及这些“地方”的布局方式。非常多的受试者无法分清软件设计和架构设计的区别。每当这个时候,面试官就应该主动给受试者讲述架构的含义,同时记录受试者不具备可用的架构工作经验,但应根据接下来的思路表现,来判断其架构见识、思路以及资质。
3.1 的要点在于网络存档应该与应用服务分离。应用服务做负载均衡的话,应制作单独的数据服务以支持网络存档。这样可以保证无论是运营后台还是玩家都能够看到全部的数据。如果受试者无法摆脱分区的概念,则应指出,运营后台页面,能够选择分区,每次选择,程序能够连接到不同区的数据服务。
3.2 首先这种全球性质或者打地理范围的系统,肯定要使用地域分流的域名服务(DNS)负载均衡。那么也需要建立专门的用户数据传输机制,以保证用户大范围移动物理位置之时,能够在对应区域取得自己的数据。为此,也就需要设置统一的数据中心,以登记不同区域的位置关系以及服务器地址。考虑到系统不可能一次性完成,使用能够自动加载的云基础结构,应该会是一个最合理的选择。
通过架构测评,如果受试者能够展现出关于自由软件的很多知识,则说明此人在技术进步上有较高追求。架构这边如果用时很短,也就是受试者表示出了对架构的恐惧和放弃,应该认为受试者面对困难问题的解决决心坚韧程度打折扣。这时应将其引导至 2.3 题,以考察其通过查资料解决问题的水平和心性。面试完毕之后,您应该记录面试情况,以便比较不同的面试者,谁综合看来更适用。
最后我想说一句。很多面试官都相信自己对受试者的感觉。其实面试流程的目的,就是通过流程,让受试者更多地表现自己,以丰富面试官的感觉,以防以偏概全、认识不足等情况发生。
下边附面试题目
1 设计能力(选做其一)
---------- ---------- ---------- ----------
1.1 现有多人卡牌游戏,由用户根据场上情况出牌。玩家出的牌可被其它玩家看到。每一种游戏牌将会对场上局面造成某些影响。请设计本游戏服务器端关于数据传输部分;其它部分如需指明,也可以指出。
1.2 现有某军事对战型网络游戏。架构设计安排在战斗发起之前,通过短连接方式进行信息处理。对战为一对一,对战开始之后,进行长连接,传输双方对军队控制的操作。请设计本游戏开始建立长连接以及传输操作数据部分的服务器程序。
2 编程能力(2.1 和 2.2 选作其一)
---------- ---------- ---------- ----------
2.1 数据结构
有一种数据,结构是
{
id: 38,
name: "Shane",
money: 3010.50
}
数据量大概有 100 0000 份。
请你设计一种方式,能够支持以下要求。
首先,需要能往已有数据集里边追加数据。如果 id 重复,则为修改。
其次,需要得到某指定 id 对应的人,全部数据按照 money 排序,其所在的名次。此过程不能太慢。
最后,能得到这个 id 对应的人,全部数据按照 money 排序,其名次的前、后几名的 name 。此过程不能太慢。
2.2 逻辑
一个社交游戏,玩家可以互相访问,并在访问时对对方进行某些操作。被访问的人在登录时统一接收上次登录到这次登录之间被访问的报告。
获取记录通过 InteractSysRecord.INST.getRecord(int userId) 方法,获得一个 List<ActRecord> 。
ActRecord 具有如下属性
int fromUserId
int toUserId
Kind actKind
int actEffect
其中 Kind 是一个枚举,包括一些类型,比如“赠送礼金”、“伤害”、“偷钱”等。
玩家获取的记录,需要按人整理。也就是说,在一个玩家登录之时,与其上次登录之间,某一另外玩家多次访问此玩家生成的多个 ActRecord ,应该合成一条。
请编程完成记录整理。允许设计合理的 InteractSysRecord 结构。
2.3 多线程(允许查资料,希望观点能独特、精辟、有实效)
请简述 Java5.0 多线程框架的机制和要点
3 架构能力(选做其一)
---------- ---------- ---------- ----------
3.1 请为如下功能需求设计架构。文字或图示都行。
现有某多客户端弱联网网络游戏,需要实现网络存档。请简述网络游戏存档的架构方案。注意,后台操作人员应该能任意查看、修改任何人的存档。要求说明设计理由。
3.2 现有基于位置的移动网络游戏。在游戏界面中,当玩家离开自己实际位置之时,就会在游戏中受到一个吸引力,吸引游戏中的角色回到玩家现实中的位置。此吸引力随着距离增加而增加。所有的玩家在统一的世界地图上进行对战。请设计此网络游戏的架构方案。并说明设计理由。
@import url(http://www.blogjava.net/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);
扩展 Rational Team Concert 进行缺陷和代码质量分析
基于 Rational Team Concert 和 Maven 的差分发布
利用 Rational Team Concert 实现 Rational Functional Tester 自动化测试脚本的版本控制
在敏捷开发中如何使用 Rational Quality Manager 缩短软件测试周期
《孙子兵法》在敏捷项目管理中的应用
Rational Team Concert 用户的自动化管理
敏捷项目管理实战之进度管理
基于 Rational Team Concert 实现持续集成
利用 Selenium 和 Rational Performance Tester 轻松实现 Web 性能测试
急速性能测试之旅: 使用 Rational Performance Tester 8.2 完成 web 应用性能测试,第 2 部分
用 Rational Team Concert SCM Tool 实现代码自动化部署
stream 简介
Stream(流)是 Jazz 源代码管理系统中的重要成员,体现项目开发中的一个版本,由一个或者多个 component( 组件)构成。流的建立使得团队成员在不同的版本之间进行代码的共享与管理,满足项目迭代开发中代码管理的需要,促进团队成员之间更好的协作。组件是代码共享与管理的基本单位,由一个文件或者文件夹所构成,团队成员可以通过使用流中的组件进行代码的共享。
多个 stream 的应用场景
目前,软件开发中迭代开发非常普遍,项目的开发经常涉及两个或多个版本。即在一个版本尚未发布就需要进入下一个软件版本的开发,这样使得项目开发人员需要同时维护多个软件版本。特别是一个缺陷需要在多个版本上解决,这样在这些不同的软件版本上进行代码同步就成为团队开发人员日常的工作内容。如何简化代码同步的流程,提高多个软件版本代码同步的效率以及减少在多个软件版本中由于代码同步引起的缺陷数,对于降低软件开发成本和提高软件质量具有重要的意义。
RTC 对多 stream 代码同步的支持
源代码管理与共享是项目开发中的重要内容,而 RTC 作为主流的源代码管理工具,对项目的源代码的管理与共享提供了很好的机制。在实际的项目开发过程中,项目拥有者需要在远程 Jazz 服务器上创建 stream,项目开发人员在远程 Jazz 服务器上基于此 stream 创建自己的版本库工作空间。除此之外,开发人员还需要在本地的机器上创建自己的本地工作空间。开发人员通过 Check-in 操作将本地工作空间的改动同步到远程 Jazz 服务器上的自己的版本库工作空间,通过 Deliver 操作将远程版本库工作空间的代码同步到团队共享的 stream 上。其他团队人员则通过 Accept 操作将代码从 stream 同步到自己远程的版本库工作空间,并通过 Load 操作将版本库工作空间的源代码下载到本地的工作空间。依此类推,RTC 就可以同时管理多个 stream 并进行有效的共享,其具体的源代码管理与共享机制如图 1 所示:
图 1. RTC 中多 stream 源代码管理与共享机制
通过 RTC 中对源代码的管理与共享机制可以看出,如果不进行任何修改与配置,在多个 stream 上,开发人员只能通过单独维护每个 stream 上的源代码,并在这些 stream 上进行代码的来回复制达到多 stream 上的源代码同步,这样无疑增加了维护和同步多个 stream 上的源代码成本。事实上 RTC 可以通过更改相应的 flow target(目标流指向)来简化多 stream 上的源代码同步,有效的提高项目开发的效率。
总的来说,RTC 对多 stream 上源代码的同步提供了三种方式:各 stream 单独同步,stream 由低版本向高版本同步及 stream 由高版本向低版本同步,具体操作如下图所示(图中以两个 stream 为例,多个 stream 可以依此进行类推):
图 2. 各 stream 单独同步
图 3. stream 由低版本向高版本同步
图 4. stream 由高版本向低版本同步
从图 2 可以看出,同步 stream1 和 stream2 上的源代码需要在每个 Local workspace 上 Check-in 相应的代码,在同步第二个 stream 时需要从一个 Local workspace 上拷贝源代码到另一个 Local workspace 上,这样加大了开发人员的工作量,并且增加了一些由拷贝源代码所造成的缺陷。图 3 与图 4 的同步机制没有多大差别,图 3 所示的为源代码从低版本(stream1)向高版本(stream2)同步,开发人员先将代码提交到 stream1,然后改变 stream2 的目标流指向,接受之前提交到 stream1 上的变更集,最后通过 Jazz Server 上的 Repository workspace 将变更集提交到 stream2 上。图 4 所示为源代码从高版本(stream2)低高版本(stream1)同步,具体过程与低版本(stream1)向高版本(stream2)同步相反。由于高版本往往会包含低版本没有实现的功能,所以对于多 stream 的源代码同步,采用从低版本向高版本同步的方式遇到的潜在的源代码冲突相对较少,开发人员解决这些冲突并合并变更集所付出的成本也随之降低。因此,在实际项目开发中,对于多 stream 的源代码同步,采用低版本(stream1)向高版本(stream2)同步的方式最佳。
RTC 中多 stream 代码同步的示例
由于多 stream 从低版本中向高版本同步时潜在的冲突最少,因此本文将以这种同步方式作为示例说明如何在两个 stream 实现源代码的同步。对于三个或三个以上 stream 的代码同步,读者可以依此类推。在示例中,我们拥有两个 stream,6.3.1.1 和 6.3.2。并且我们已经创建好两个 stream 上对应的两个我们自己的版本库工作空间。
无代码冲突情况下的代码同步
打开 Pending Changes 视图,在 6.3.1.1 版本库工作空间上有个新的可交付变更集,右击选择 deliver 对其进行交付,具体操作如图 5 所示:
图 5. 交付变更集界面
打开更新版本的 6.3.2 版本库工作空间,更改其 Flow Targets 到 6.3.1.1 stream。如图 6 所示,将 6.3.1.1 stream 置成当前 flow target 且是默认 flow target,然后确认保存。
图 6. 版本库工作空间界面
保存后结果如图 7 所示。
图 7. 更改 Flow Target 界面
如图 8 所示,此时再次查看 Pending Changes,6.3.2 版本库工作空间已经指向 6.3.1.1 stream,刚才在 6.3.1.1 版本库工作空间中交付的变更集出现在 6.3.2 版本库工作空间的可接受变更集中。此时该变更集与 6.3.2 版本库工作空间中代码不存在冲突,可以直接选择接受。
图 8. 接受 Incoming 变更集界面
接受后,将 6.3.2 版本库工作空间的 flow target 改回 6.3.2 stream ,如图 9 所示:
图 9. FlowTarget 配置界面
从图 10 中可以看出,在 Pending Changes 中,6.3.2 版本库工作空间中有了新的可交付变更集,该变更集正是 6.3.1.1 版本库工作空间之前交付的变更集。由于不存在冲突,我们可以直接将该变更集交付到 6.3.2 stream 中,从而实现两个 stream 上针对该变更集的源代码同步。在下一节中我们将给出存在代码冲突的同步示例,为了模拟这种情况,我们暂且不交付此变更集。
图 10. 交付 Outgoing 变更集界面
有代码冲突情况下的代码同步
同样,打开 Pending Changes 视图,在 6.3.1.1 版本库工作空间提交一个新的变更集模拟代码冲突,右击选择 deliver 对其进行交付,具体操作如图 11 所示:
图 11. 交付 Outgoing 变更集界面
更改 6.3.2 版本库工作空间 flow target 到 6.3.1.1 stream。从 Pending Changes 视图中可以看到 6.3.1.1 版本库工作空间中新提交的变更集。该变更集前出现橙色高亮,说明该变更集和 6.3.2 版本库工作空间中可交付的变更集存在代码冲突。具体操作如图 12 所示:
图 12. 接受 Incoming 变更集界面
选择接受该变更集,此时会弹出是否自动解决冲突的对话框如图 13,我们这里建议选择 Resolve Later,以防止出现代码合并错误的情况发生。下面我会给出具体的情况说明在接受冲突变更集后如何 Resolve。Resolve 有两种方法:自动合并和手动合并。什么时候选择自动合并,什么时候需要手动合并,这对于维护代码的同步和准确性十分重要。
图 13. 冲突提示界面
选择 Resolve Later 后,将 6.3.2 版本库工作空间的 flow target 改回 6.3.2 stream。如图 14 所示,出现未解决代码冲突。双击打开冲突比较视图。
图 14. 未解决冲突界面
如果视图中仅有蓝色标识,说明没有严重冲突。如图 15 所示,可以选择 Auto Merge 自动合并代码。
图 15. 自动解决冲突操作
图 15 大图
如果视图中有红色标识,说明存在严重冲突,自动合并代码将导致代码错乱。如图 16 所示,将红色部分右侧代码拷贝覆盖到左侧,手工完成覆盖后,选择 Resolve As Merge 完成合并。
图 16. 手动解决冲突界面
图 16 大图
在弹出的对话框中选择 OK 确定手动合并操作,如图 17 所示:
图 17. 手动解决冲突确认界面
这样合并完成后,6.3.2 版本库工作空间可交付变更集如图 18 所示。此时可看到被合并的原变更集后出现 1 merged 字样,合并操作完成。对其交付后,6.3.1.1 stream 和 6.3.2 stream 在该变更集存在冲突的情况下实现了代码同步。
图 18. 交付合并后变更集界面
撤销已交付的错误代码
在同步两个或多个 stream 的时候,很可能误将不属于本 stream 的代码 deliver 到 stream 上。如果错误交付的代码过多的话,手动去删除错误交付的代码将会需要花费很大的工作量来将代码还原到原来的状态。而且,还会带来代码被错误修改的风险。而 RTC 提供了一种回退(Reverse)的功能,将错误交付的代码自动清除掉,并恢复到原来的状态。
回退(Reverse)功能的具体操作步骤如下:
- 右击错误提交的代码所相关的 work iteam,并单击弹出的 Reverse 菜单。
图 19. work iteam 的回退
- 点击 Reverse 菜单后,会弹出如下图 20 所示的窗口,指示修改前的文件将会在 Pending Patches 以 patch 的形式显示出来。然后,点击 OK 按钮。
图 20. 添加到 Pending Changes
- 如图 21 所示,在 patch 中你可以通过双击 java 文件来查看原来文件里的内容和错误交付代码后的文件的差异,来查看是否是你想要恢复的结果。
图 21. Patch 显示界面
- 将这个 patch 添加到现有的 workspace 上面,从而将原有的变更集先恢复到本地的 workspace 上。
图 22. 合并 Patch 界面
- 原有的文件将会作为未提交的文件显示出来。
图 23. 未解决冲突界面
- 通过将原有的代码重新 check-in 和 deliver,从而将被错误修改的代码从项目组共享的 stream 上删除出去。
图 24. Check-in 变更集界面
如果将代码错误提交到 stream 后,有其他开发人员提交了自己的代码,这个时候要进行回退的时候,需要查看需要回退的文件是否和其他开发人员修改的文件有冲突。如果有冲突,可以通过前面提过的方法进行代码冲突的整合;或者把错误提交代码以后其他开发人员提交的代码先回退回去,在把错误提交的代码从 stream 上删除以后,重新将其他开发人员提交的代码 deliver 到 stream 上。
致谢
衷心的感谢钱巧能工程师在本文大纲方面的讨论所提供的意见和支持。
结束语
本文详细介绍了 RTC 代码同步的原理,并且比较了目前 RTC 中多 stream 上代码同步的几种方法,最后以具体示例说明了如何在 RTC 中进行多 stream 的代码同步,解决代码同步中出现的冲突及如何撤销错误的代码同步。读者可以通过阅读本文,掌握各种在 RTC 中多 stream 代码同步的方法,并根据项目实际情况选择,灵活运用,提高项目开发的整体效率。
简介
本文介绍了 IBM Rational Team Concert(RTC)的代码评审功能(Code Review)。这一功能可以使代码评审流程变得更加规范,完善代码提交流程;对于不同区域的成员可以更高效的协同工作,在代码提交前发现到潜在的问题,尽快修复,提高代码质量,有效减少缺陷数。
代码评审的重要性
多数情况下,评审者能够发现开发人员自身不能发现的问题或潜在问题,毕竟思维方式和考虑的点等会有差异
- 优秀的评审者通过评审,不仅可以发现开发人员的不足,更可以通过开发人员的代码学到很多知识
- 对团队而言,通过评审以及互相评审,了解到其他团队成员负责的任务,必要时互相帮忙,互为后援,提高项目开发效率,降低项目风险
代码评审的规则
- 从逻辑上讲,本次修改是否符合需求,是否有类似的地方需要修改
- 可读性上,是否有足够的解释说明,以便以后维护
- 就代码规范而言,是否符合公司代码规范,以保持代码风格一致性
- 从代码性能上讲,是否有更有效的方案可以实现
- 单元测试(如果必要),测试用例是否能覆盖本次的修改
RTC 对代码评审的支持
作为目前主流的代码管理工具,RTC 对代码评审的功能已经有了很好的支持,比如利用邮件作为代码提交者与评审者通信的工具,代码评审过程中各个角色的划分,代码提交的权限设置等等,本节将具体介绍 RTC 在代码评审过程中所涉及的概念及详细配置。
对邮件的支持
在整个代码评审过程中,邮件是作为代码提交者及其他相关人员之间的重要通信工具,比如在代码评审前提交、对代码添加修改意见,团队相关人员都应收到相应的邮件提醒,用户可以通过如下配置是否启用邮件支持:
- 打开 Repository Connections 界面并连接
图 1. Repository 连接界面
- 打开用户编辑窗口,如图 2 所示:
图 2. Open My User Editor
- 找到 Mail Configuration 页面,进行相应的配置并保存,如图 3 所示:
图 3. 邮件配置界面
代码评审中所涉及的角色的划分
在整个代码评审过程中,RTC 将会涉及到如下几种角色
- Submitter:代码提交者,由实现新功能的开发者自己担任,可执行的操作有 Submit for review、Check-in,Deliver、Add Approver 等等。
- Review:代码审查人员,负责代码提交前细粒度的代码审查工作,排除潜在的缺陷,一般由团队中对所要修改的代码比较熟悉的人员担任。可执行的操作有 Add comment、Rejected、Approved 等。
- Approver:代码评阅者,负责代码提交前粗粒度的代码审查工作,一般由资深开发人员及 tech lead 担任,可执行的操作有 Add comments,Rejected、Approved 等。
- Verifier:功能的验证者,对功能的实现作一些单元测试等等。
事实上对于以上这些角色,Submitter,Reviewer 和 Approver 是必须,Verifier 是可选的,用户可以根据团队实际情况决定在代码评审过程中是否需要 Verifier。
代码提交前的权限控制
RTC 在代码提交时有了较好权限配置的支持,用户(一般由 Project owner 或团队的 tech lead 配置此权限)可以根据如下步骤进行配置:
- 连接自己所在的项目并打开,如图 4 所示:
图 4. 项目连接界面
- 切换到 Project Area 中的 Process Configuration 页面,如图 5 所示:
图 5. 流程配置界面
- 双击 Team Configuration 中的 Operation Behavior 选项,如图 6 所示:
图 6. 操作行为界面
- 从右边列表中选择 Source Control 中的 Deliver(server)选项,双击对应 Everyone(default) 图标,并双击 Add ... 按钮添加代码提交时的 Preconditions. 如图 7 所示:
图 7. Add Preconditions
- 在弹出的 Add Preconditions 对话框中选择 Require Work Item Approval 选项,如图 8 所示:
图 8. Add Preconditions 界面
- 双击 Required approvals 栏中 new ... 按钮添加代码提交时的 Required Approval,针对不同的 Approval 类型选择相应的 Approvers,单击 ok 按钮,最后保存所有配置。如图 9 所示:
图 9. 添加 Approval
经过上述一系列配置后,代码提交者必须先取得相应的 Approval 之后才能提交代码,从而达到代码提交时的权限控制,保证代码的质量。
RTC 代码评审使用示例
在 RTC 中,代码评审的流程大致如下,各个团队可以根据实际情况进行优化。
图 10. 代码评审时序图
下面我将以一个简单的实例来说明在 RTC 中是如何进行代码评审的:
- Check in 变更集
修改源代码,在 RTC 客户端中找到 Pending Changes 视图,并将这些变更集 check in 到在 Jazz repository 上的 workspace,如图 11 所示:
图 11. Check in 变更集界面
- 关联 work item
在 Pending Changes 视图中,将 check in 的变更集关联到相应的 work item(story 上的 task work item 或者是 issue 类型的 work item) 上。
图 12. 关联 work item 界面
- 提交代码给相应的 approver review
在 Pending Changes 视图中,找到相应的 Outgoing 变更集,点击右键菜单中的 Submit for Review... 选项。
图 13. Submit for Review 界面
- 添加注释和相应的 Approver
在弹出的 submit for review 的对话框中添加相应的注释及添加相应的 Approver 类型(具体类型请参考 RTC 中对代码评审章节)。
图 14. 添加注释和 Approver 对话框
- 确定需要评审代码关联的 work item
Approver 将会根据 RTC 中关于代码评审的邮件找到相应的 work item,并从 work item 中找到链接页面。
图 15. work item 链接界面
- 查找对应的变更集
Approver 从链接页面中找到需要评审的变更集,双击此变更集,变更集将会在 RTC 客户端的变更视图中显示。
图 16. 查看变更集界面
- 给出评审结果及意见
每个 Approver 根据代码评审的实际情况给出相应的修改意见和评审意见,如对于哪一行代码需要修改或者同意提交等等,具体操作如图 17 所示:
图 17. 代码评审界面
如果给出的评审结果为同意提交,则 submitter 直接进入代码提交阶段。如在此阶段给出的评审结果为拒绝,则 submitter 需要从 work item 的 overview 视图或 RTC 邮件中查看 Approver 添加的修改意见,并根据意见进行代码修改,重新提交代码评审。
- 代码提交
代码 Submitter 在 Pending Changes 视图中找到相应的 Outgoing 变更集并提交。
RTC 代码评审的注意事项
一次代码评审和所提交的一个变更集是一一对应的关系,当对一个变更集提交代码评审后,这个变更集就被冻结,此后对其中任何文件的修改都将通过另一新的变更集来跟踪。所以,对于新的修改,需要再次提交代码评审。
评审者在应用被评审者的变更集进行代码评审时,有时会和评审者本地的变更集产生冲突,此时只要将产生冲突的本地变更集暂停(Suspend),就能暂时避免冲突从而继续进行评审。
RTC 代码评审的不足及展望
虽然 RTC 已经能够支持完整的代码评审功能,但在实际使用中还有一些改进之处。比如无法动态嵌入或链接到评论所涉及的代码中的指定行,方便被评审者阅读修改意见。而且评审的粒度较大,基于每个评审者的修改意见无法针对每条评论进行接收或拒绝。此外,如果加入代码静态分析功能,就能更快的找到问题,避免人为的疏漏。
为什么很多看起来不是很复杂的网站比如 Facebook、淘宝,需要大量顶尖高手来开发?
就拿淘宝来说说,当作给新人一些科普。
先说你看到的页面上,最重要的几个:
【搜索商品】——这个功能,如果你有几千条商品,完全可以用select * from tableXX where title like %XX%这样的操作来搞定。但是——当你有10000000000(一百亿)条商品的时候,任何一个数据库都无法存放了,请问你怎么搜索?这里需要用到分 布式的数据存储方案,另外这个搜索也不可能直接从数据库里来取数据,必然要用到搜索引擎(简单来说搜索引擎更快)。好,能搜出商品了,是否大功告成可以啵 一个了呢?早着呢,谁家的商品出现在第一页?这里需要用到巨复杂的排序算法。要是再根据你的购买行为做一些个性化的推荐——这够一帮牛叉的算法工程师奋斗 终生了。
【商品详情】——就是搜索完毕,看到你感兴趣的,点击查看商品的页面,这个页面有商品的属性、详细描述、评价、卖家信息等等,这个页面的每天展示次数在 30亿以上,同样的道理,如果你做一个网站每天有10个人访问,你丝毫感觉不到服务器的压力,但是30亿,要解决的问题就多了去了。首先,这些请求不能直 接压到数据库上,任何单机或分布式的数据库,承受30亿每天的压力,都将崩溃到完全没有幸福感,这种情况下要用到的技术就是大规模的分布式缓存,所有的卖 家信息、评价信息、商品描述都是从缓存里面来取到的,甚至更加极致的一点“商品的浏览量”这个信息,每打开页面一次都要刷新,你猜能够从缓存里面来取吗? 淘宝做到了,整个商品的详情都在缓存里面。
【商品图片】——一个商品有5个图片,商品描述里面有更多图片,你猜淘宝有多少张图片要存储?100亿以上。这么多图片要是在你的硬盘里面,你怎么去查找 其中的一张?要是你的同学想拷贝你的图片,你需要他准备多少块硬盘?你需要配置多少大的带宽?你们的网卡是否能够承受?你需要多长时间拷贝给他?这样的规 模,很不幸市面上已经没有任何商业的解决方案,最终我们必须自己来开发一套存储系统,如果你听说过google的GFS,我们跟他类似,叫TFS。顺便说 一下,腾讯也有这样的一套,也叫TFS。
【广告系统】——淘宝上有很多广告,什么,你不知道?那说明我们的广告做的还不错,居然很多人不认为它是广告,卖家怎么出价去买淘宝的广告位?广告怎么展示?怎么查看广告效果?这又是一套算法精奇的系统。
【BOSS系统】——淘宝的工作人员怎么去管理这么庞大的一个系统,例如某时刻突然宣布某位作家的作品全部从淘宝消失,从数据库到搜索引擎到广告系统,里面的相关数据在几分钟内全部消失,这又需要一个牛叉的后台支撑系统。
【运维体系】——支持这么庞大的一个网站,你猜需要多少台服务器?几千台?那是零头。这么多服务器,上面部署什么操作系统,操作系统的内核能否优 化?Java虚拟机能否优化?通信模块有没有榨取性能的空间?软件怎么部署上去?出了问题怎么回滚?你装过操作系统吧,优化过吧,被360坑过没,崩溃过 没?这里面又有很多门道。
不再多写了,除了上面提到的这些,还有很多很多需要做的技术,当然并不是这些东西有多么高不可攀,任何复杂的庞大的东西都是从小到大做起来的,里面需要牛叉到不行的大犇,也需要充满好奇心的菜鸟,最后这一句,你当我是别有用心好了。
功能上面虽然不复杂,但是要完成的细节却很多. 比如news feed里面的推荐算法就很重要,要根据用户之前的记录和与好友的关系来生成. 另外就是根据用户的信息和行为,要做机器学习和数据挖掘,从而来挑出最匹配的广告.这也是比较花人力的事情.
另外Facebook的用户量奇大无比. 假设你只是做一个学校内部用的社交网站, 那肯定很简单. 但是如果考虑到上亿人在上面用. 你首先服务器就必 须是一个分布式的机群,还要保证能抗住那么大的流量. 同时为了性能够好,不得不加上mem cache和网页分块加载等功能. 还有就是每天用户产生的总数据量(状态,留言,照片,分享等)有TB的数量级,你数据库是否撑住等等.
另外树大招风,你要一个很强的安全小组来保证网站在受攻击的时候能防御好,还有要防止垃圾信息和恶心广告或者程序的散播. 另外还有为了全球化而带来的多语言问题.
总之,一个网站做大了之后,很多问题就会产生,不是在校园里面做一个学期作业那么简单.狼大人,休,行。
某个历史上的大神曾经说过一句话:
要判断一个算法的好坏,只要给它足够的数据。
当用户、数据和系统规模上到一个程度之后,所有曾经可以忽视的问题都会变得必须用从来不可想象的精力和技术去解决。
来来来 看看这个 刚看到的《当用户点击“举报”后,Facebook在后台会做哪些事情》
很多东西并不是表面看到的那样简单
身为前端攻城师,就这方面说一下这样一个Facebook看起来“很简单的网站”需要顶尖高手来开发和维护
写前端程序要考虑很多,如下:可维护性,JS的执行高效性,JS文件的大小,用户体验等等
1. 可维护性
并不是所有人写的程序都具有可维护性,清晰易懂的,这个区别在刚接触编程和高手直接的差异体现的特别明显
2. JS的执行高效性
一个网页加载JS并执行,浏览器执行并渲染了半天还在渲染,相信很多用户都不想看到吧?非常上海用户体验。
如何提升JS的执行速度呢?相信我,初学者大部分都是不知道的(排除写过浏览器内核的同学),了解浏览器如何执行JS,如何渲染DOM能帮助开发者提升执行速度
3. JS文件的大小
JS文件或者HTML或者CSS文件过大,有很多缺点
第一,受网速影响,文件大,加载速度慢
第二,Facebook的用户量非常巨大,每个人访问就算多加载1KB(即使有cache,第一次总需要加载吧),可想而知,这个流量非常巨大,流量都是要钱的啊
4. 用户体验
略之
高手往往比非高手要注意的东西要多很多,这点相信大家不会质疑吧,只是就前端方面发表一些建议
如果要把一件简单的事情搞复杂,你需要的是市场总监
如果要把一件简单的事情搞简单,你需要的是产品经理
如果要把一件复杂的事情搞复杂,你需要的是一堆码农
如果要把一件复杂的事情搞简单,你需要的是顶尖高手
明白了吧,顶尖高手的意义就在于让你觉得这个东西看起来很简单。
学“懂”计算机系统结构之后,理解这个问题比较容易点吧。
比如,你打开知乎,简单的敲下几行字,点击提交,然后你看到了你的评论,这复杂吗?不复杂。
1、为什么知乎要用浏览器才能打开呢,记事本不能访问知乎?为什么用输入自己的知乎用户名和密码才能访问呢,我QQ密码为嘛不能用?为什么我只能看到我关注内容,而不是网站所有用户的所有内容呢?
2、当敲下键盘时,电脑是如何识别按下的具体哪个字符呢?是如何经过到达的CPU呢?CPU进行了如何的处理操作呢?显示器为什么可以显示相应的字符呢?
3、当点击“提交”时,发生哪些变化呢?显示的内容是保存了在本地电脑上了,还是保存在了知乎服务器上了呢?从本地的数据是如何传输到知乎服务器呢?从物 理层、数据链路层、网络层、传输层、会话层、表示层、应用层这一系列的过程提交的评论内容传输形式是经过一系列的如何变化过程呢?
PS.对于一个字符从输入到最后结果输出的处理流程我也没搞彻底清楚。随便说说。。。希望有高手能具体解释下啊。

手上没有网站的结构层次示意图,随便拿张图说明下原因吧。像计算机最初问世的时候,操作计算机的用户既是设计者也是维护者的时代已经过去。每个节点都应该 是一个属于一个节点的专属内容,对个每个节点都应该是更清晰化更简单化的:用户的操作的应该简单化,网络的传输应该简单化,数据的存储也应该是简单化。因 此对于用户来说只需要用户界面操作就足够了,没必要把数据库之类的东西也交给用户处理的。
TEAMLEAD这部分指任务分配
- PROJECT AREA
根据计划来,不同的计划建立不同的PROJECT AREA。
- TIME LINE FOR PROJECT AREA
定义起尺和结束时间,也指一组ITERATION和TEAM AREA的组合。
一个PROJECT AREA可以搞几个TIME LINE。
- ITERATION FOR TIME LINE
一个TIME LINE可以分若干个ITERATION阶段去完成。
- TEAM AREA FOR TIME LINE
这段时间的TIME LINE的PROJECT 是需要人来完成,先定义TEAM AREA。就是USER的组合。
- USER FOR TEAM AREA
就是TEAM AREA的组织成员。
- CATEGROY FOR TEAM AREA
CATEGORY就是WORK ITEM的类型,但要和TEAM AREA关联。
- PLAN FOR ITERATION
在上面的时间段ITERATION增加PLAN,也是一组WORK ITEM的组合。
- WORK ITEM FOR ITERATION,USER
工作内容,父类是ITERATION,USER等。
上面的这八项的建立顺序是从前到后,只有前面建立了,才能建后面的。
如何与SOURCE CONTROL结合?每次提交代码的时候,要指定CHANGE SET,CHANGE SET的名字取WORK ITEM的名字,这样就可以关联上了。
配置管理中的一站式工具,JAZZ,需求人员可以录入需求,项目管理人员可以录入计划,开发人员可以从上面领取任务,测试人员可以提交BUG。
摘要: 场景:由SPRING INTEGRATION每10秒监控数据库中的POLL表,如果有符合条件的记录,则启动JDBC JOB,此JOB要做的也是和数据库交互的动作,如用JDBCITEMREADER读取数据库记录,用JDBCITEMWRITER写记录进数据库 。
SPRING INTEGRATION与SPRING BATCH JOB交互的点是JOBPARAMETERS,即以KEY-V...
阅读全文
JOB的XML配置相当于命令模式中的一个命令,每次运行,会生成一个JOB INSTANCE,同时也会生成一个JOB EXCETION,JOB INSTANCE表示有一个JOB,JOB EXECUTION表示进行第一次尝试。失败运行的JOB再次运行时,只生成一个JOB EXCETION,表示进行的第二次尝试。当SPRING BATCH JOB引擎接到运行请求时,有以下几种情况:@import url(http://www.blogjava.net/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);
- 判断此JOB以前是否有运行过,判断依据:根据JOB NAME和JOB PARAMETERS信息去这两张表查找,如找到记录,则是有运行过,如确定有则引擎退出并提示。
- 判断是否有重复运行的JOB,如果没,查找此JOB上次运行的情况,如果有FAIL的情况,则重新从上次失败的地方运行继续运行,判断根据:根据JOB INSTANCE关联的JOB EXECUTION查找是否有状态为FAIL的记录,如有则是,就新建一个JOB EXECUTION表示进行第二次尝试,同时又生成关于新JOB的JOB EXECUTION。上次的JOB运行完后,再运行此次新JOB。判断上次失败的STEP的依据:查找对应的JOB EXECUTON的STEP EXECUTON状态为FAIL的STEP即可,会从此步骤开始运行,而不是从第一步开始运行。
参考文档: