宁静·致远


  • 首页

  • 分类

  • 关于

  • 归档

  • 标签

  • RSS

ATPCO项目总结-技术篇

发表于 2015-12-28 | 阅读次数

接着上篇写,这一篇从技术方面来总结一下ATPCO项目。

1.项目特点

ATPCO是一个业务性非常强的项目,主要体现在以下几点:

1)规则复杂

ATPCO提供的数据主要分为:Fares、Rules、Routings三类数据。其中Fare与Rules、Routings存在强关联。Rules中包含了8类Record数据,即Record1-Record8。其中Record3中又划分成35项Category。每一个Category分别表示一类运价规则,如Cat1表示乘客类型的规则,Cat2表示适用航班的规则,Cat3表示淡旺季规则……

2)计算量大

ATPCO中公布运价数据有9000万条,Rules数据有1亿多条,每个Fare平均关联20多条Rules数据。Fare不仅需要与Rules、Routings进行匹配,还需要与Av(此AV指航班可用座位,不是那个AV)进行匹配。用户一次搜索可能会引起几十万的计算量,如果在短时间内快速计算出结果?可以肯定的一点是:普通的单机计算是行不通的。

3)算法复杂

这里说的算法复杂主要集中在航路规划方面。目前全球有4000多城市,5000多机场。理论上两个机场都是可以到达的。那么我们可以想象从一个城市到另外一个城市的路径图就是这个样子了:

是不是很晕?晕就对了,我们在这个坑里面已经1年了,估计航信、携程的同学更能感同身受吧。

2.业务抽象

上面已经提到,ATPCO的规则比较复杂,那么相应的转换到技术层面,其业务抽象也会比较有挑战。好在我们有Carl de Marcken(ITA创始人,后被Google收购)这位大牛,他写的几篇专利文章为我们提供了极大的帮助,在此再度膜拜一下。参考Carl的专利,我们抽象出了Itinerary、Journey、Pricing Unit、Fare Component几个关键模型,并根据这些模型搭建了整个系统的框架和接口。
在其他方面的抽象,我们做得其实不太好。比如搜索条件,我们的业务抽象并不能适应所有的场景,比如多程、缺口程。一方面是我们对业务的理解不够深入,另外一方面也是在设计方面,我们做得还不够好。有时间需要补一下DDD(领域驱动设计)的知识并加以实践了。

3.技术框架

空间换时间之殇

从技术框架的层面,应该是这篇总结里面需要长篇叙述的了,因为这个层面踩过的坑还是很多的。
在项目初期,考虑到ATPCO涉及的数据量和计算量比较大,我们决定采用空间换时间的方式,即事先把数据组装好,在搜索的时候直接使用事先计算好的数据以减少响应时间。这种方式后来被证明是错误的,因为事先组装数据的方式意味着一旦数据有变化,事先生成的数据就需要重新生成,而在我们对ATPCO规则理解不断深入的情况下,这种情况是非常频繁的。另一方面,如果需要调整计算模型,或者系统有bug,那么所有数据就需要重跑。在这种机制下,整个系统好比一艘航空母舰,庞大、笨重,如果需要转身,我们只能默默地等待……

简单为美

因为历史原因,我们在接手ATPCO的同事,还负责维护另外一个比较老的公布运价系统。为了兼容老的系统,我们在收到用户请求后,分别调用老系统、新系统,收到结果后进行合并,并返回给调用端。这样的好处是能够保证新、老系统互不影响,一个系统出问题的时候,另外一个系统依然能够提供服务。但是这样的设计也有弊端,因为增加了请求分发、数据合并的环节,中间环节变多,势必导致复杂性升高,同时效率降低,势必也会导致响应时间变长。

很多时候,简单跟高效是成正比的。就像AK47一样,因为设计简单,其结果就是很少出故障,而且维护简单。比如在越战时期,很多美军士兵宁可使用捡到的AK47步枪,也不愿使用自己的M16,因为M16结构复杂而且容易卡壳,在战场上枪支卡壳那可是丢性命的事情……

由此引申出来的问题就是,不管是做系统架构设计、还是模块设计,都应该遵循简单为美的原则。只有简单,系统才容易理解,才容易维护,才会高效运转。所以当我们头脑中有方案的时候,要多问问自己,这是否已经是最简单高效的方案了。

优雅的服务化

在构建一个复杂系统的时候,如果系统内部功能彼此独立,那么可以考虑采用服务化的方式:即各子系统之间协商好服务接口,彼此通过服务的方式进行调用。这样的好处是各系统之间互相独立,互不影响。这样能够提高系统自身的扩展性、可维护性,并降低系统间的耦合性。ATPCO系统按照功能特点,划分成了Processor、Planner、Searcher、Engine四个子系统,系统与系统之间通过dubbo的方式进行调用。这种方式使得开发人员只关注自己的业务逻辑实现即可,因此能够提升自身的开发效率。当然这种方式的弊端就是服务之间调用时的沟通、联调、排错成本相对较高,但是相比因此带来的整体效率提升,这点成本还是可以接受的。

目前在互联网行业,服务化的理念已经深入人心,包括现在逐渐流行的微服务。在此借鉴著名SOA专家Thomas Erl的归纳的服务设计原则:

  1. 标准化的服务契约 Standardized service contract
  2. 服务的松耦合 Service loose coupling
  3. 服务的抽象 Service abstraction
  4. 服务的可重用性 Service reusability
  5. 服务的自治性 Service autonomy
  6. 服务的无状态性 Service statelessness
  7. 服务的可发现性 Service discoverability
  8. 服务的可组合性 Service composability

当然如果如果一个系统中有很多同质化的服务或子系统,那么在系统稳定后,可以考虑将这些服务合并,以降低维护成本。我曾经听一个同事讲过他维护的一个与外部航司交互的接口群,每个接口都是一个子系统,一共有20多个子系统。这种系统维护的时候就比较麻烦,因为某些业务策略的修改可能要修改所有的子系统,这种情况下就可以考虑将服务合并,以降低维护的成本。

分布式的挑战

ATPCO系统是一个计算密集型的系统,一个用户请求可能会产生几十万次的计算。在这种情况下,单机的计算能力将会成为整个系统的瓶颈,因此必须采用分布式计算的方式将巨大的计算量拆分到不同的机器上进行处理,最终将结果合并。这里所说的分布式计算有2种方式:一种是采用MapReduce的方式,即将数据分发到不同的机器上进行处理;另外一种方式就是RDD(Resilient Distributed Dataset)的方式,即将算法分发到不同的机器上,由各自己自行读取数据进行处理。

在ATPCO系统中,我们计划采用第二种方式实现分布式计算。之所以说计划,是因为目前只是制定了方案,但并没有投入应用,相信这不会太久。

分布式的另外一个应用方向就是对于ATPCO文件的处理。ATPCO每个小时都会给我们推送新的数据,如果我们的系统有问题的时候,很有可能需要从最初的文件重新处理。如果堆积了几个月的文件需要重跑,那么这将是一个灾难。于是我们采用了分布式处理的方式,即由多台机器按发布航司分别处理数据,这样系统的处理能力可以成倍增加,比如我用10台机器重新处理数据,那么其速度将是以前单机处理时的10倍。这样的话,我们就再也不用担心重跑数据了,O(∩_∩)O哈哈~

4.技术管理

技术管理这个事情可以新开一篇blog来总结了,在此只说关键的几点吧:

  1. 招聘,人才是最重要的,创意人才(来自谷歌的《重新定义公司》一书)更重要!
  2. 技术路线/框架制定,并根据反馈适时调整
  3. 参与关键技术问题的攻克
  4. 代码review,这一点很重要,但是也很容易忽略
  5. 团队激励,将团队捏合成一个整体,团队协同,群策群力才能成功
  6. 技术创新,科技是第一生产力

【转】Java 编程中关于异常处理的 10 个最佳实践

发表于 2015-04-02 | 阅读次数

英文原文:10 Exception handling Best Practices in Java Programming

异常处理在编写健壮的Java应用的过程中,扮演着一个重要的角色。它并不是应用的功能需求,且需要优雅的处理任何错误情况,例如资源不可用,错误的输入,null输入等等。Java提供几个异常处理功能,并通过try, catch 和 finally关键字内嵌在语言的本身。Java编程语言同样允许创建新的异常和使用 throw 和 throws 抛出该异常。在实践中,异常处理不单单是知道语法这么简单。编写健壮的代码是更像是一门艺术,在本文中,将讨论Java异常处理最佳实践。这些 Java最佳实践遵循标准的JDK库,和几个处理错误和异常的开源代码。这还是一个提供给java程序员编写健壮代码的便利手册。

这里是我收集的10个Java编程中进行异常处理的10最佳实践。在Java编程中对于检查异常有褒有贬,强制处理异常是一门语言的功能。在本文中,我们将尽量减少使用检查型异常,同时学会在Java编程中使用检查型VS非检查型异常。

1. 为可恢复的错误使用检查型异常,为编程错误使用非检查型错误

选择检查型还是非检查型异常,对于Java编程人员来说,总是让人感到困惑。检查型异常保证你对错误条件提供异常处理代码,这是一种从语言到强制你编写健壮的代码的一种方式,但同时会引入大量杂乱的代码并导致其不可读。当然,如果你有替代品和恢复策略的话,捕捉异常并做些什么看起来似乎也在理。在Java编程中选择检查型异常还是运行时异常,更多信息参考 checked vs unchecked exceptions。

2. 在finally程序块中关闭或者释放资源

这在Java编程中,是一个广为人知的最佳实践,在处理网络和IO类的时候,相当于一个标准。在finally块中关闭资源, 在正常和异常执行的情况下,保证之前和稀缺资源的合理释放,这由y finally块保证。从Java7开始,该语言有了一项更有趣的功能:资源管理自动化或者ARM块能实现这一功能。尽管如此,我们仍然要记住在finally块中关闭资源,这是对于释放像FileDescriptors这类,应用在socket和文件编程的情况下的有限资源很重要的。

3. 在堆栈跟踪中包含引起异常的原因

很多时候,当一个由另一个异常导致的异常被抛出的时候,Java库和开放源代码会将一种异常包装成另一种异常。日志记录和打印根异常就变得非常重要。Java异常类提供了 getCause()方法来检索导致异常的原因,这些(原因)可以对异常的根层次的原因提供更多的信息。该Java实践对在进行调试或排除故障大有帮助。时刻记住,如果你将一个异常包装成另一种异常时,构造一个新异常要传递源异常。

4. 始终提供关于异常的有意义的完整的信息

异常信息是最重要的地方,因为这是程序员首先看到的第一个地方,这里你能找到问题产生的根本原因。这里始终提供精确的真实的信息。例如,对比IllegalArgumentException 异常的两条异常信息:
消息 1: “Incorrect argument for method”
消息 2: “Illegal value for ${argument}: ${value}
第一条消息仅说明了参数是非法的或者不正确,但第二条消息包括了参数名和非法值,而这对于找到错误的原因是很重要的。在用Java编程中编写异常处理代码的时候,始终遵循该Java最佳实践。

5. 避免过度使用检查型异常

检查型异常在强制执行方面有一定的优势,但同时它也破坏了代码,通过掩盖业务逻辑使代码可读性降低。只要你不过度使用检查型异常,你可以最大限度的减少这类情况,这样做的结果是你会得到更清洁的代码。你同样可以使用Java7的新功能,像one catch block for multiple exceptions 和 automatic resource management以移除重复项。

6. 将检查型异常转为运行时异常

这是在像Spring之类的多数框架中用来限制使用检查型异常的技术之一,大部分出自于JDBC的检查型异常,都被包装进DataAccessException中,而(DataAccessException)异常是一种非检查型异常。这是Java最佳实践带来的好处,特定的异常限制到特定的模块,像 SQLException 放到DAO层,将意思明确的运行时异常抛到客户层。

7. 记住对性能而言,异常代价高昂

需要记住的一件事是异常代价高昂,同时让你的代码运行缓慢。假如你有方法从ResultSet(结果集)中进行读取,这时常会抛出SQLException异常而不会移到下一元素,这将会比不抛出异常的正常代码执行的慢的多。因此最大限度的减少不必要的异常捕捉和移动,那里没有什么固定的原因。不要仅仅是抛出和捕捉异常,如果你能使用boolean变量去表示执行结果,可能会得到更整洁,更高性能的解决方案。修正错误的根源,避免不必须要的异常捕捉。

8. 避免catch块为空

没有什么比空的catch块更糟糕的了,因为它不仅隐藏了错误和异常,同时可能导致你的对象处于不可使用或者脏的状态。空的catch块只能变得无意义,如果你非常肯定异常不会继续以任何方式影响对象状态,但在程序执行期间,用日志记录错误依然是最好的(方法)。对于在Java编程中编写异常处理代码,这不仅仅是一个Java最佳实践,而是一个最通用的实践。

9. 使用标准异常

我们的第九条最佳实践建议使用标准和内置的Java异常。使用标准异常而不是每次创建我们自己的异常,对于维护性和一致性,不管是现在还是以后,都是最好的选择。重用标准异常使代码更具可读性,因为大部分Java开发人员对标准的像源自于JDK的RuntimeException 异常,IllegalStateException 异常,IllegalArgumentException 异常或者 NullPointerException异常,(开发者)他们能一眼就知道每种异常的目的,而不是在代码里查找或者在文档里查找用户定义的异常的目的。

10. 记录任何方法抛出的异常

Java提供了throw和throws关键字来抛出异常,在javadoc中用@throw记录任何方法可能会抛出的异常。如果你编写API或者公共接口,这就变得非常重要。任何方法抛出的异常都有相应的文档记录,这样你就能下意识的提醒任何使用(该方法)的人。

这些就是所有在Java编程中在处理异常的时候需要遵循的最佳实践。让我们知道了什么是在Java编程中编写异常处理代码时需要遵循的实践。

23种设计模式

发表于 2015-03-08 | 阅读次数

设计模式分为3大类(创建型、结构型、行为型),共23种。

  1. 创建型模式:单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式;

  2. 结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式;

  3. 行为型模式:模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式;

关于建造者模式和工厂模式:
当创造一个对象需要很多步骤时适合使用建造者模式。而当只需调用一个方法就可以简单地创建整个对象时适合使用工厂模式。

Intellij Idea快捷键大全

发表于 2015-02-21 | 阅读次数

说明

IntelliJ IDEA 的便捷操作性,快捷键的功劳占了一大半,对于各个快捷键组合请认真对待。IntelliJ IDEA 本身的设计思维是提倡键盘优先于鼠标的,所以各种快捷键组合层出不穷,对于快捷键设置也有各种支持,对于其他 IDE 的快捷键组合也有预设模板进行支持。

关于各个快捷键的频率分类上可能每个人都有各自的看法,下面的整理也只是已我个人的使用习惯来划分的,而我应该是可以代表某一部分小众人员。但是我个人还是建议你可以在我的基础上整理一份属于的快捷键目录,本篇文章也只是起到一个工具和引子的作用。

对于下面各个快捷键的使介绍描述也许用我个人语言翻译起来不够准确或是不全面,且在不同的文件类型上按出来的效果也可能结果不太一样,所以 强烈建议 你自己把各个快捷键都亲自操作下体会下各个快捷键的实际用法。

前提

由于文化的不同,我们使用的电脑必备一个软件就是中文输入法,而目前大多数人都使用搜狗拼音输入法或是其他类似的。而这些输入法跟 IntelliJ IDEA 有一个万恶的冲突永恒不变:快捷键冲突。所以为了配合 IntelliJ IDEA,我们要去掉这些输入法下的所有快捷键。

前提

  • 如上图红色圈住内容所示,默认是 逗号 我改为了 ESC 键下的 波浪号,Ctrl + 逗号 这个快捷键适合做智能提示用,下面的快捷键列表会讲。

前提

  • 如上图红色圈住内容所示,这些快捷键很容易跟 IntelliJ IDEA 快捷键冲突,需要全部去掉。

前提

  • 如上图红色圈住内容所示,QQ 这些快捷键也很容易跟 IntelliJ IDEA 快捷键冲突,需要全部去掉,最多保持一个截图功能即可。

可能还有其他一些软件,比如网易云音乐、有道词典等等这些软件都可能存在快捷键冲突,所以为了 IntelliJ IDEA 这些软件的快捷键都是值得舍弃的,如果你在开发的时候。

Ctrl

快捷键 介绍
Ctrl + F 在当前文件进行文本查找 (必备)
Ctrl + R 在当前文件进行文本替换 (必备)
Ctrl + Z 撤销 (必备)
Ctrl + Y 删除光标所在行 或 删除选中的行 (必备)
Ctrl + X 剪切光标所在行 或 剪切选择内容
Ctrl + C 复制光标所在行 或 复制选择内容
Ctrl + D 复制光标所在行 或 复制选择内容,并把复制内容插入光标位置下面 (必备)
Ctrl + W 递进式选择代码块。可选中光标所在的单词或段落,连续按会在原有选中的基础上再扩展选中范围 (必备)
Ctrl + E 显示最近打开的文件记录列表
Ctrl + N 根据输入的 类名 查找类文件
Ctrl + G 在当前文件跳转到指定行处
Ctrl + J 插入自定义动态代码模板
Ctrl + P 方法参数提示显示
Ctrl + Q 光标所在的变量 / 类名 / 方法名等上面(也可以在提示补充的时候按),显示文档内容
Ctrl + U 前往当前光标所在的方法的父类的方法 / 接口定义
Ctrl + B 进入光标所在的方法/变量的接口或是定义出,等效于 Ctrl + 左键单击
Ctrl + K 版本控制提交项目,需要此项目有加入到版本控制才可用
Ctrl + T 版本控制更新项目,需要此项目有加入到版本控制才可用
Ctrl + H 显示当前类的层次结构
Ctrl + O 选择可重写的方法
Ctrl + I 选择可继承的方法
Ctrl + + 展开代码
Ctrl + - 折叠代码
Ctrl + / 注释光标所在行代码,会根据当前不同文件类型使用不同的注释符号 (必备)
Ctrl + [ 移动光标到当前所在代码的花括号开始位置
Ctrl + ] 移动光标到当前所在代码的花括号结束位置
Ctrl + F1 在光标所在的错误代码出显示错误信息
Ctrl + F3 调转到所选中的词的下一个引用位置
Ctrl + F4 关闭当前编辑文件
Ctrl + F8 在 Debug 模式下,设置光标当前行为断点,如果当前已经是断点则去掉断点
Ctrl + F9 执行 Make Project 操作
Ctrl + F11 选中文件 / 文件夹,使用助记符设定 / 取消书签
Ctrl + F12 弹出当前文件结构层,可以在弹出的层上直接输入,进行筛选
Ctrl + Tab 编辑窗口切换,如果在切换的过程又加按上delete,则是关闭对应选中的窗口
Ctrl + Enter 智能分隔行
Ctrl + End 跳到文件尾
Ctrl + Home 跳到文件头
Ctrl + Space 基础代码补全,默认在 Windows 系统上被输入法占用,需要进行修改,建议修改为 Ctrl + 逗号 (必备)
Ctrl + Delete 删除光标后面的单词或是中文句
Ctrl + BackSpace 删除光标前面的单词或是中文句
Ctrl + 1,2,3…9 定位到对应数值的书签位置
Ctrl + 左键单击 在打开的文件标题上,弹出该文件路径
Ctrl + 光标定位 按 Ctrl 不要松开,会显示光标所在的类信息摘要
Ctrl + 左方向键 光标跳转到当前单词 / 中文句的左侧开头位置
Ctrl + 右方向键 光标跳转到当前单词 / 中文句的右侧开头位置
Ctrl + 前方向键 等效于鼠标滚轮向前效果
Ctrl + 后方向键 等效于鼠标滚轮向后效果

Alt

快捷键 介绍
Alt + ` 显示版本控制常用操作菜单弹出层
Alt + Q 弹出一个提示,显示当前类的声明 / 上下文信息
Alt + F1 显示当前文件选择目标弹出层,弹出层中有很多目标可以进行选择
Alt + F2 对于前面页面,显示各类浏览器打开目标选择弹出层
Alt + F3 选中文本,逐个往下查找相同文本,并高亮显示
Alt + F7 查找光标所在的方法 / 变量 / 类被调用的地方
Alt + F8 在 Debug 的状态下,选中对象,弹出可输入计算表达式调试框,查看该输入内容的调试结果
Alt + Home 定位 / 显示到当前文件的 Navigation Bar
Alt + Enter IntelliJ IDEA 根据光标所在问题,提供快速修复选择,光标放在的位置不同提示的结果也不同 (必备)
Alt + Insert 代码自动生成,如生成对象的 set / get 方法,构造函数,toString() 等
Alt + 左方向键 按左方向切换当前已打开的文件视图
Alt + 右方向键 按右方向切换当前已打开的文件视图
Alt + 前方向键 当前光标跳转到当前文件的前一个方法名位置
Alt + 后方向键 当前光标跳转到当前文件的后一个方法名位置
Alt + 1,2,3…9 显示对应数值的选项卡,其中 1 是 Project 用得最多

Shift

快捷键 介绍
Shift + F1 如果有外部文档可以连接外部文档
Shift + F2 跳转到上一个高亮错误 或 警告位置
Shift + F3 在查找模式下,查找匹配上一个
Shift + F4 对当前打开的文件,使用新Windows窗口打开,旧窗口保留
Shift + F6 对文件 / 文件夹 重命名
Shift + F7 在 Debug 模式下,智能步入。断点所在行上有多个方法调用,会弹出进入哪个方法
Shift + F8 在 Debug 模式下,跳出,表现出来的效果跟 F9 一样
Shift + F9 等效于点击工具栏的 Debug 按钮
Shift + F10 等效于点击工具栏的 Run 按钮
Shift + F11 弹出书签显示层
Shift + Tab 取消缩进
Shift + ESC 隐藏当前 或 最后一个激活的工具窗口
Shift + End 选中光标到当前行尾位置
Shift + Home 选中光标到当前行头位置
Shift + Enter 开始新一行。光标所在行下空出一行,光标定位到新行位置
Shift + 左键单击 在打开的文件名上按此快捷键,可以关闭当前打开文件
Shift + 滚轮前后滚动 当前文件的横向滚动轴滚动

Ctrl + Alt

快捷键 介绍
Ctrl + Alt + L 格式化代码,可以对当前文件和整个包目录使用 (必备)
Ctrl + Alt + O 优化导入的类,可以对当前文件和整个包目录使用 (必备)
Ctrl + Alt + I 光标所在行 或 选中部分进行自动代码缩进,有点类似格式化
Ctrl + Alt + T 对选中的代码弹出环绕选项弹出层
Ctrl + Alt + J 弹出模板选择窗口,讲选定的代码加入动态模板中
Ctrl + Alt + H 调用层次
Ctrl + Alt + B 在某个调用的方法名上使用会跳到具体的实现处,可以跳过接口
Ctrl + Alt + V 快速引进变量
Ctrl + Alt + Y 同步、刷新
Ctrl + Alt + S 打开 IntelliJ IDEA 系统设置
Ctrl + Alt + F7 显示使用的地方。寻找被该类或是变量被调用的地方,用弹出框的方式找出来
Ctrl + Alt + F11 切换全屏模式
Ctrl + Alt + Enter 光标所在行上空出一行,光标定位到新行
Ctrl + Alt + Home 弹出跟当前文件有关联的文件弹出层
Ctrl + Alt + Space 类名自动完成
Ctrl + Alt + 左方向键 退回到上一个操作的地方 (必备)
Ctrl + Alt + 右方向键 前进到上一个操作的地方 (必备)
Ctrl + Alt + 前方向键 在查找模式下,跳到上个查找的文件
Ctrl + Alt + 后方向键 在查找模式下,跳到下个查找的文件

Ctrl + Shift

快捷键 介绍
Ctrl + Shift + F 根据输入内容查找整个项目 或 指定目录内文件 (必备)
Ctrl + Shift + R 根据输入内容替换对应内容,范围为整个项目 或 指定目录内文件 (必备)
Ctrl + Shift + J 自动将下一行合并到当前行末尾 (必备)
Ctrl + Shift + Z 取消撤销 (必备)
Ctrl + Shift + W 递进式取消选择代码块。可选中光标所在的单词或段落,连续按会在原有选中的基础上再扩展取消选中范围 (必备)
Ctrl + Shift + N 通过文件名定位 / 打开文件 / 目录,打开目录需要在输入的内容后面多加一个正斜杠 (必备)
Ctrl + Shift + U 对选中的代码进行大 / 小写轮流转换 (必备)
Ctrl + Shift + T 对当前类生成单元测试类,如果已经存在的单元测试类则可以进行选择
Ctrl + Shift + C 复制当前文件磁盘路径到剪贴板
Ctrl + Shift + V 弹出缓存的最近拷贝的内容管理器弹出层
Ctrl + Shift + E 显示最近修改的文件列表的弹出层
Ctrl + Shift + H 显示方法层次结构
Ctrl + Shift + B 跳转到类型声明处
Ctrl + Shift + I 快速查看光标所在的方法 或 类的定义
Ctrl + Shift + A 查找动作 / 设置
Ctrl + Shift + / 代码块注释 (必备)
Ctrl + Shift + [ 选中从光标所在位置到它的顶部中括号位置
Ctrl + Shift + ] 选中从光标所在位置到它的底部中括号位置
Ctrl + Shift + + 展开所有代码
Ctrl + Shift + - 折叠所有代码
Ctrl + Shift + F7 高亮显示所有该选中文本,按Esc高亮消失
Ctrl + Shift + F8 在 Debug 模式下,指定断点进入条件
Ctrl + Shift + F9 编译选中的文件 / 包 / Module
Ctrl + Shift + F12 编辑器最大化
Ctrl + Shift + Space 智能代码提示
Ctrl + Shift + Enter 自动结束代码,行末自动添加分号 (必备)
Ctrl + Shift + Backspace 退回到上次修改的地方
Ctrl + Shift + 1,2,3…9 快速添加指定数值的书签
Ctrl + Shift + 左方向键 在代码文件上,光标跳转到当前单词 / 中文句的左侧开头位置,同时选中该单词 / 中文句
Ctrl + Shift + 右方向键 在代码文件上,光标跳转到当前单词 / 中文句的右侧开头位置,同时选中该单词 / 中文句
Ctrl + Shift + 左方向键 在光标焦点是在工具选项卡上,缩小选项卡区域
Ctrl + Shift + 右方向键 在光标焦点是在工具选项卡上,扩大选项卡区域
Ctrl + Shift + 前方向键 光标放在方法名上,将方法移动到上一个方法前面,调整方法排序
Ctrl + Shift + 后方向键 光标放在方法名上,将方法移动到下一个方法前面,调整方法排序

Alt + Shift

快捷键 介绍
Alt + Shift + N 选择 / 添加 task
Alt + Shift + F 显示添加到收藏夹弹出层
Alt + Shift + C 查看最近操作项目的变化情况列表
Alt + Shift + F 添加到收藏夹
Alt + Shift + I 查看项目当前文件
Alt + Shift + F7 在 Debug 模式下,下一步,进入当前方法体内,如果方法体还有方法,则会进入该内嵌的方法中,依此循环进入
Alt + Shift + F9 弹出 Debug 的可选择菜单
Alt + Shift + F10 弹出 Run 的可选择菜单
Alt + Shift + 左键双击 选择被双击的单词 / 中文句,按住不放,可以同时选择其他单词 / 中文句
Alt + Shift + 前方向键 移动光标所在行向上移动
Alt + Shift + 后方向键 移动光标所在行向下移动

Ctrl + Shift + Alt

快捷键 介绍
Ctrl + Shift + Alt + V 无格式黏贴
Ctrl + Shift + Alt + N 前往指定的变量 / 方法
Ctrl + Shift + Alt + S 打开当前项目设置
Ctrl + Shift + Alt + C 复制参考信息

其他

快捷键 介绍
F2 跳转到下一个高亮错误 或 警告位置 (必备)
F3 在查找模式下,定位到下一个匹配处
F4 编辑源
F7 在 Debug 模式下,进入下一步,如果当前行断点是一个方法,则进入当前方法体内,如果该方法体还有方法,则不会进入该内嵌的方法中
F8 在 Debug 模式下,进入下一步,如果当前行断点是一个方法,则不进入当前方法体内
F9 在 Debug 模式下,恢复程序运行,但是如果该断点下面代码还有断点则停在下一个断点上
F11 添加书签
F12 回到前一个工具窗口
Tab 缩进
ESC 从工具窗口进入代码文件窗口
连按两次Shift 弹出 Search Everywhere 弹出层

官网快捷键资料

  • Windows / Linux:https://www.jetbrains.com/idea/docs/IntelliJIDEA_ReferenceCard.pdf
  • Mac OS X:https://www.jetbrains.com/idea/docs/IntelliJIDEA_ReferenceCard_Mac.pdf

第三方快捷键资料

  • 来自 eta02913:http://xinyuwu.iteye.com/blog/1005454

快捷键修改

修改快捷键

  • 按 Ctrl + Alt + S 弹出 IDE 设置,如上图选择左侧的 Keymap。
  • IntelliJ IDEA 支持两种方式来筛选我们要找的快捷键,一种是上图标注 1 所示的,通过输入快捷键描述内容;一种是上图标注 2 所示的,通过 按 指定快捷键快捷键,这里需要再次强调的是,这个输入框是自动监听你当前按下的按键,而不是用来输入的。
  • 上图标注 3 所示,初安装的 IntelliJ IDEA 使用的是 Default 的快捷键模板,IntelliJ IDEA 默认的快捷键模板都是不可修改的。如果你直接修改,当前这个位置 IntelliJ IDEA 会自动变成 Default Copy,建议你养成习惯,修改之前先点击 Copy,拷贝一套快捷键模板,然后输入自己的命名。

修改快捷键

  • IntelliJ IDEA 是支持一个操作命令同时设置多个快捷键组合,就如上图的 Backspace,同时支持 Backspace 和 Shift + Backspace 两组快捷键。
  • 要修改某个快捷键,选中快捷键介绍内容,右键,就会弹出如上图标注 1 所示操作选择。
  • 命令 Add Keyboard Shortcut 用来添加新纯键盘快捷键组合。
  • 命令 Add Mouse Shortcut 用来添加新 键盘 + 鼠标 快捷键组合,比如设置 Ctrl + 左键单击 这类快捷组合。其中在弹出的添加面板中 Click Pad 是用来监听当前鼠标是左键单击还是右键单击。
  • 命令 Add Abbreviation 根据 IntelliJ IDEA 的版本文档解释,添加简称主要是为了方便 Search Everywhere 中使用,但是我尝试之后发现没办法根据我设置的简称搜索,暂时无法了解其作用。
  • 命令 Remove 快捷键 移出当前操作命令已设置的快捷键组合,由于 IntelliJ IDEA 默认就占用了很多快捷键组合,所以如果你要修改某个快捷键,建议还是删除掉旧的。

修改快捷键

  • IntelliJ IDEA 对其他 IDE 用户很友好,比如如上图对于其他主流的 IDE,快捷键上已经默认了有其过度快捷键模板了,但是我还是建议你专心使用 IntelliJ IDEA 的默认。

其他

vim插件推荐

  • 如果你是一个 Vim 粉,IntelliJ IDEA 也为你准备了一个方案:如上图安装 IdeaVim 插件即可。

Java的SPI机制

发表于 2015-02-21 | 阅读次数

什么是SPI

SPI是Service Provider Interface(服务提供者接口)的缩写,从字面意思也能看出,SPI是面向服务提供者的。也就是说,有人定义了接口,那么围绕该接口提供服务的第三方则针对接口提供自己的服务。说到这里,最典型的应该是我们常用的日志框架,比如common-logging、jdbc Driver。

SPI的优点

基于SPI机制,我们可以很方便地构建出易于扩展的应用。我们使用的很多应用框架,比如dubbo,其中的extension就是基于SPI构建的。

Java SPI规范

  1. 在META-INF/services/目录下创建以接口全名命名的文件,该文件内容为接口具体实现类的全名;如我有一个实现类io.fengfu.learning.spi.HelloWorldServiceBB8Impl,实现了io.fengfu.learning.spi.HelloWorldService接口,那么就需要在META-INF/services目录下创建一个名称为io.fengfu.learning.spi.HelloWorldService的文件,文件的内容为io.fengfu.learning.spi.HelloWorldServiceBB8Impl。如果我有很多个实现类,那么只需要将实现类全名按行编写即可;
  2. 接口具体实现类必须有一个不带参数的构造方法;
  3. 使用ServiceLoader类动态加载META-INF中的实现类;
  4. 如SPI的实现类为Jar则需要放在主程序classPath中;

SPI示例

1,定义一个接口

package io.fengfu.learning.spi;

/**
 * Created by fengfu.qu on 2014/2/4.
 */
public interface HelloService {
    public String sayHello(String name);
}

2, 实现之:

package io.fengfu.learning.spi;

/**
 * Created by fengfu.qu on 2014/2/4.
 */
public class HelloServiceC3poImpl implements HelloService {
    public String sayHello(String name) {
        return "Hello, " + name + ", I'm C3po.";
    }
}

package io.fengfu.learning.spi;

/**
 * Created by fengfu.qu on 2014/2/4.
 */
public class HelloServiceR2D2Impl implements HelloService {
    public String sayHello(String name) {
        return "Hello, " + name + ", I'm R2D2.";
    }
}

package io.fengfu.learning.spi;

/**
 * Created by fengfu.qu on 2014/2/4.
 */
public class HelloServiceBB8Impl implements HelloService {
    public String sayHello(String name) {
        return "Hello, " + name + ", I'm BB8.";
    }
}

3, 在META-INF/services目录下创建io.fengfu.learning.spi.HelloService文件,在文件中添加以下内容:

io.fengfu.learning.spi.HelloServiceC3poImpl
io.fengfu.learning.spi.HelloServiceR2D2Impl
io.fengfu.learning.spi.HelloServiceBB8Impl

4, 创建测试类:

package io.fengfu.learning.spi;

import java.util.Iterator;
import java.util.ServiceLoader;

/**
 * Created by fengfu.qu on 2014/2/4.
 */
public class SPITest {
    public static void main(String[] args) {
        ServiceLoader<HelloService> loader = ServiceLoader.load(HelloService.class);
        Iterator<HelloService> it = loader.iterator();
        while(it.hasNext()){
            HelloService helloSPI = it.next();
            System.out.println(helloSPI.sayHello("Fengfu"));
        }
    }
}

5, 运行,结果为:

Hello, Fengfu, I'm C3po.
Hello, Fengfu, I'm R2D2.
Hello, Fengfu, I'm BB8.

由此看出,3个是实现类都被运行了。

利特尔法则

发表于 2015-02-14 | 阅读次数

##概念##

利特尔法则由麻省理工大学斯隆商学院(MIT Sloan School of Management)的教授John Little﹐于1961年所提出与证明。其英文名称为:Little’s Law。
利特尔法则是一个有关提前期与在制品关系的简单数学公式,这一法则为精益生产的改善方向指明了道路。 如何有效地缩短生产周期呢?利特尔法则已经很明显地指出了方向:一个方向是提高产能,从而降低生产节拍;另一个方向就是压缩存货数量。然而,提高产能往往意味着增加很大的投入。另外,生产能力的提升虽然可以缩短生产周期,但是生产能力的提升总有个限度,我们无法容忍生产能力远远超过市场的需求。一般来说,每个公司在一定时期内的生产能力是大致不变的,而从长期来看,各公司也会力图使自己公司的产能与市场需求相吻合。因此,最有效地缩短生产周期的方法就是压缩在制品数量。
利特尔法则不仅适用于整个系统,而且也适用于系统的任何一部分。

##公式##

L=系统中的平均项目数量;

W=一个项目在系统中所需等待的平均时间;

λ=单位时间内项目到达的平均数量;

在一个稳定的系统中,长时间观察到的平均顾客数量L,等于,长时间观察到的有效到达速率λ与平均每个顾客在系统中花费的时间W之乘积。

##举例##

比如我们要去一个快餐店吃饭,这个快餐店平均每分钟有2个人进入,每个人平均用餐时间是0.5小时,那么算一下这个饭店当前有多少人在用餐?
答案:2 0.5 60 = 60人
其中2为速率λ,0.5为花费时间W,60为小时换算成与速率一致的单位的系数。

再举一个估算系统QPS的例子:
假如我们有个系统,每天的访问量是200万次,系统处理每次请求平均花费的时间是3秒,那么系统能够支撑的QPS是多少?
QPS = 2000000/24/3600/3 = 7.7

线程初始化参数

发表于 2015-02-08 | 阅读次数

线程池创建时,有3个参数比较关键,分别是:corePoolSize、maximumPoolSize,还有一个queueSize。

corePoolSize

线程池基本大小,即在任务没有需要执行的时候线程池的大小。在刚刚创建了ThreadPoolExecutor的时候,线程并不会立即启动,只有在有任务提交的时候才会启动;除非你调用了prestartCoreThread/prestartAllCoreThreads。

maximumPoolSize

线程池中允许的最大线程数,即线程池中运行的线程不可能超过这个数量。

queueSize

线程池队列大小,即排队的队列容量。

如果线程池中线程已达到corePoolSize,并且还有新的任务提交,那么线程池首先将任务存到队列中排队。如果队列也满了,那么线程池将继续在增加 线程,直到达到maximumPoolSize。到这里细心的同学可能会问了:如果队列没设置大小呢?那么我只能告诉你:看运气吧,如果任务少,你的队列 顶多长一些;如果任务很多,那么很可能你要oom了O(∩_∩)O

架构设计原则

发表于 2015-02-03 | 阅读次数

1. 单一职责原则(Single Responsibility Principle)

简而言之,就是一个类只负责一个职责。则样做的好处就是类的职责比较单一,可以降低类的复杂度,简单为美。代码的可读性提高了,维护的复杂性降低了。

2. 里氏替换原则(Liskov Substitution Principle)

原则:子类可以扩展父类的功能,但不能改变父类原有的功能。具体总结为以下2点:

1) 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
2) 子类中可以增加自己特有的方法。

例如,当类B继承类A时,除添加新的方法完成新增功能外,尽量不要重写父类A的方法,也尽量不要重载父类A的方法。

3. 依赖倒置原则(Dependency Inversion Principle)

DIP(Dependency Inversion Priciple),如果你觉得陌生的话,那么IOC(Inversion of Control,控制反转)你一定不会陌生,Spring的核心思想啊。一句话:面向接口编程。

1) 底层模块尽量都要有抽象类或接口,或者两者都有;
2) 变量的声明类型尽量是抽象类或接口;
3) 使用继承时遵循里氏替换原则。

4. 接口隔离原则(Interface Segregation Principle)

不要创建过大的接口类,即接口中的方法不要过多。本着高内聚的原则,将接口中的方法归类,拆分为几个独立的接口。

5. 迪米特法则

也叫最少知道原则,原则是一个对象应该对其他对象保持最少的了解,出发点是降低类与类之间的耦合度。

6. 开闭原则(Open Closed Principle)

对扩展开放,对修改关闭。这个原则很虚,虚得感觉跟没说一样,索性总结一句:遵循前面5条原则,就相当于遵循了开闭原则O(∩_∩)O

第一篇Markdown

发表于 2015-01-28 | 阅读次数

真的是Out了,Markdown都流行了这么久了,才开始用,惭愧啊……
更惭愧的是,博客都好久没有更新了……
更更惭愧的是,还有许多想要做的事情,都还没开始;或者开始了,就没有然后了……
比如15年想要看50本书,目前还差十几本……
比如15年想要去的西藏,如今已无法成行……
比如一直想写的互联网研发体系博客,到现在才寥寥数语……
…… ……
想要太多,付出太少;担心太多,决心太少;借口太多,行动太少……
不管怎么样,不能这样纵容自己了,千里之行始于足下,从今天开始,行动起来把!
加油!

写在开博时

发表于 2015-01-24 | 阅读次数

盘算着搞一个独立域名的博客已经很久的时间了,今天终于如愿以偿,搞定了独立的域名和空间,在这里还是要先感谢godaddy.com一下,让我很便捷地就能创建自己的空间。

虽然我是个IT工程师,但是这个博客不是专门的技术博客。作为一个老程序员,除了对技术有很多的了解,对于人生也有了很多的感触。所以在这个空间里,我会写写技术,也聊聊人生,甚至天马行空……

曾经有朋友问我为什么用.io做后缀,我的解释是:io = input + output。

input = 索取

output = 奉献

虽然我只是一个普通人,但是我认为我来到这个世界上,不只是为了索取,我相信我可以以自己的绵薄之力为这个世界做一些贡献。如果这样,也不枉我在这个世界走一遭了。

祝福所有在我的博客驻足的人,世界很大,能遇到已属不易,转眼间可能又擦肩而过……

1…56
宁静·致远

宁静·致远

60 日志
9 标签
友情链接
  • 卡拉搜索
© 2020 宁静·致远
由 Hexo 强力驱动
主题 - NexT.Gemini