《代码简洁之道–Clean Code》 摘记

第 2 章 有意义的命名

  • 名副其实: 变量、函数或类的名称应该告诉你,它为什么会存在,它做什么事,应该怎么用。如果名称需要注释来补充,那就不算名副其实。
    代码的模糊度:即上下文在代码中未被明确体现的程度。

  • 避免误导: 提防使用不同之处较小的名称。

  • 做有意义的区分: 要区分名称,就要以读者能鉴别不同之处的方式来区分。

  • 使用读得出来的名字。

  • 使用可搜索的名称 : 长名称胜于短名称,搜得到的名称胜于自造编码代写就的名称。单字母名称仅用于端方法中的本地变量。名称长短应与其作用域大小相对应。

  • 避免使用编码 : 不要用类型前缀、特定前缀来标记成员。

  • 避免思维映射 : 聪明程序员和专业程序员之间的区别在于,专业程序员了解,明确是王道

  • 类名 : 类名和对象名应该是名词或名词短语。

  • 方法名 : 方法名应当是动词或动词短语。重载构造器时,使用描述了参数的静态工厂方法名。

  • 每个概念对应一个词 : 给每个抽象概念选一个词,并且一以贯之。

  • 别使用双关语;

  • 使用解决方案领域名称 :

  • 使用源自所涉问题领域的名称;

  • 添加有意义的语境

  • 不要添加没用的语境 : 精确是命名的要点。

取好名字最难的地方在于需要良好的描述技巧和共有文化背景。

继续阅读

《 Reactive Microservices Architecture 》 响应式微服务架构 摘记

第三部分未完结,挖坑先。。。

第一章 介绍

我们只在我们没有其他选择时改变单体 (monolithic) 系统。与其说是敏捷地抓住机会,我们考虑它是否确实值得倾覆我们称为纸牌屋的企业系统的微妙平衡。通常,机会很快就消失了,被快速的公司抓住。

Flaus Schwab: 在新世界,不是大鱼吃小鱼,是快鱼吃慢鱼。

基于微服务的架构是个简单的概念:它提倡用一组小的、隔离的服务来创建系统。这些服务拥有它自己的数据,各自是独立的、可扩展且对失败有弹性。服务集成其他服务来构建一个内聚的系统,它比我们当前构建的典型企业系统更灵活。

传统企业系统设计为单体应用,难以扩展,难以理解且难以维护。单体应用可能很快就变成噩梦,扼杀创新、进度和乐趣。单体应用导致的负面作用对一个公司可能是灾难性的,从底层的士气到高层雇员的周转,从阻止公司雇佣顶尖工程天才到失去市场机会,在极端情况下,甚至是公司的失败。

战争故事通常听起来是这样的:“我们终于做了决定来改变我们的 Java EE 应用,在得到管理层批准后。然后我们经过长达数月的大设计,在我们最终开始构建点东西之前。但我们构建期间的大多数时间都花在尝试弄清楚单体应用真正做了什么。我们因害怕而瘫痪,担心一个小的错误可能导致非预期的、未知的副作用。最终,在数月的担忧、害怕和艰难工作后,改变实现了,但地狱也打开了。”

这样的经历会加强害怕,使我们瘫痪更严重。这就是系统、公司如何停滞的。如果有更好的方式呢?

Steve Jobs:You’ve got to start with the customer experience and work back towards the technology 。

微服务的客户是投资于系统的组织,所以让我们从客户开始:开发人员、架构师和关键利益相关者。

需要拯救的服务

Helen Keller:Although the world is full of suffering, it is also full of the overcomming of it .

因为技术原因,微服务是软件里不纯粹的下一代设计演进。微服务这个词汇呈现的思想在我们进入面向服务的架构(SOA, Service Oriented Architecture)之前已经遍布各处。阻止我们采用微服务内嵌的概念的特定技术约束已经进入下一水平:单台机器运行着单个内核处理器,慢速的网络,昂贵的磁盘,昂贵的 RAM,组织结构是个单体。像把系统组织成一个定义良好的、有单一职责的系统的想法不是新的。

快进到 2016 年,阻止我们采用微服务的技术限制已经消失了。网络是快速的,磁盘是便宜的(且更快速),RAM 是便宜的,多核处理器是便宜的,云架构正在彻底改革我们如何设计、部署系统。现在,我们终于可以考虑着客户来构建我们的系统。

设计和编写软件是有趣的,这是大多数的我们进入软件行业的初衷。微服务不仅仅是一序列的原则和技术。它们以一种更专业的方式来达成复杂问题的系统设计。

微服务允许我们以组织我们团队的方式来构建系统,在团队成员之间划分职责,保证他们自由处理自己的工作。当我们理顺了我们的系统,我们把权利从中央治理主体移交到小团队里,小团队可以快速抓住机会并保持敏捷,因为他们明白 他们控制的、有定义良好的边界的软件。

继续阅读

Spring AOP 与 事务实现

1. AOP 与方法调用

对于 接口 interface,可以通过 java.lang.reflect.Proxy 来生成动态代理对象;
对于 类 class,可以通过字节码技术,如 CBLIB 来生成一个子类作为代理对象,此时类不能声明为 final 。

当 Spring 把 Bean 注入到另一个 Bean 时,其实注入的是它生成的代理对象,进行方法调用时,首先调用的是代理对象上的方法,代理对象的方法最终再调目标对象的方法,也就是开发人员编写的方法。Spring 通过在调用目标方法前后做处理来实现一些特性,例如事务管理、缓存等。

Spring 的 AOP 是基于对代理对象的方法调用的拦截的,只能拦截外部对象 对 某个对象的方法的调用,对象的方法调用同一个类的方法是不会被拦截的。

@Service
public class SpringTxService {
    private static final Logger LOGGER = LoggerFactory
            .getLogger(SpringTxService.class);

    @Transactional(propagation = Propagation.REQUIRED)
    public void add() {
        LOGGER.info("do add");
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void requireNew() {
        LOGGER.info("do requireNew");
    }

    public void composite() {
        add();
        requireNew();
    }
}

上面的代码是基于注解进行事务控制的,composite 方法没有声明事务属性,它会调用 add、 requireNew
方法。当把 SpringTxService 注入到另一个 BeanB:
1. 在 BeanB 的方法里调用代理对象 composite 时,最终执行 add、 requireNew 是没有在事务里执行的,只是普通的方法调用。
2. 在 BeanB 的方法里调用代理对象的 add、 requireNew 方法时,这两个方法都会分别在相应的事务里执行。

当发现基于 AOP 实现的特性没有预期的效果时,一定要看看是不是在代理对象上调用还是目标类内部的方法调用。

继续阅读

Object Extract Mapping 与 网页 API

最近的项目需要访问一些外部的网页,比如百度、必应搜索等,有的只是要求把响应内容的文本提取出来,有的是要求做结构化解析的,比如百度的搜索结果,解析为一个结构化的列表,每个列表项有(标题、概要信息、详细信息的链接)。

开始时感觉是用爬虫去爬网页,然后做解析,但有个问题困扰着:比如要保存百度、必应的搜索结果到数据库,肯定只希望定义一个类来对应搜索结果的一条记录,然后写一套保存到数据库的代码。看了一些爬虫框架对于内容解析部分其实都只能自己写代码来解析网页,这样的话,如果要接搜狗就写一套解析网页的逻辑、接 360 也得写一套。。。。每接一个就得写一套。。。这不是我需要的生活。。。

怎么减少这些匹配代码?

后面看到 webmagic 的文档提到其基于注解实现的 Object/Extraction Mapping 功能,在类的字段上定义一个注解,表示这个字段用什么样的方式抽取,这样在爬虫有关的代码里就不会出现这些解析网页的逻辑。

Object Extract Mapping

如果我只需要解析百度的搜索结果,这是很好的方式,但我还要解析必应、搜狗等更多的搜索结果时就不行了。因此需要换一种方式来实现:把抽取方式的定义放到代码外面来

我就实现了一个基于 XML 的 Object-Extrac-Mapping (OEM) 小工具。

继续阅读

Quartz

1. 简介

Quartz 是个任务调度工具,就是定时执行指定的任务。

Quartz 提供了极为广泛的特性如持久化任务,集群和分布式任务等,用 Java 写成,与 Spring 集成方便,伸缩性,负载均衡,高可用性。

本文只关注基于数据库的 Quartz 集群,基于 Quartz 2.2.3 来说明。

2. 核心类

2.1. org.quartz.Job

任务接口,任务类代表要调度执行的业务逻辑实现,必须实现该接口。

如果任务不允许并发执行,则任务类必须添加注解 @DisallowConcurrentExecution 。

该接口只定义了一个方法:
void execute(JobExecutionContext context) throws JobExecutionException;

通过 context 可以访问配置给任务的数据 context.getJobDetail().getJobDataMap(),如果这个 JobDataMap 需要在修改后持久化到数据库里,则需要给任务类加上注解 @PersistJobDataAfterExecution 。Quartz 在下次调度执行这个任务时,会把持久化的数据反序列化成 JobDataMap 供应用使用。

不建议通过 Quartz 的这个机制持久化任何与业务有关的数据。因为业务数据应该由应用来存储,Quartz 只关注于调度执行。

2.2. org.quartz.JobDetail

任务在内存的表示。表示任务详细信息的接口,任务用调度器名称、任务名称和任务所归属的组名 来做唯一标识。

2.3. org.quartz.Trigger

触发器。用调度器名称、触发器名称和触发器所归属的组名 来做唯一标识。用于定义在什么情况、什么时间点执行任务。最常用的触发器类型就是基于 cron 表达式的。

2.4. org.quartz.spi.JobStore

数据存储抽象。该接口定义了 任务、触发器的存储、检索、更新 钩子,该接口还定义了任务执行完成后的回调方法。

Quartz 内建支持把任务、触发器等数据存储在JVM内存、文件、数据库,通过这个接口,就隔离了底层存储机制的差异。可以在配置文件里通过 org.quartz.jobStore.class 属性来指定该接口的实现,比如基于 Redis 做数据存储的实现。

继续阅读

Spring 导入资源文件

Spring 有多种方式可以导入资源文件,可以把这些属性文件里的属性值注入到 bean 的属性上。

1. 通过 <context:property-placeholder> 标签导入

通过这种方式导入的所有属性在同一个命名空间下,如果多个属性文件里有相同的属性名,以先导入的属性文件的属性为准,不会出现覆盖。可以通过 @Value("${propName}") 的方式注入到 bean 的属性上。

这种方式不能对属性文件指定 id,没法引用指定属性文件的属性,如果有同名的属性,引用到的总是第一个导入的属性。

配置如下:

<!-- 导入外部的资源文件,可以通过 @Value("${propName}") 的方式注入到 bean 的属性上 。 -->
<context:property-placeholder
    location="classpath:/learn/properties/1.properties"
    ignore-unresolvable="true" />
<!-- 要使用 context:property-placeholder 导入多个资源文件,必须指定 ignore-unresolvable="true" -->
<!-- 多个属性文件中,属性名相同的,以第一个加载的为准,不会出现后面加载的覆盖前面的 -->
<context:property-placeholder location="learn/properties/2.properties"
    ignore-unresolvable="true" />

继续阅读

基于POI检查 excel 文件的行数、列数是否超出限制

用 POI 解析 xlsx 时,如果用这样的方式 Workbook workbook = WorkbookFactory.create(new File("super-many-row.xlsx")); 解析具有超过 100 万行的 excel ,很可能内存就被耗尽了。

因为这样虽然简单,但是要等整个 excel 都解析完成、转换成 POI 定义的对象后才会返回到业务代码,这时候才判断行数是否超过限制就晚了。

需要一种更高效的方式,在解析完成前就判断是否超过限制,了解了 POI 的 API 和 xlsx 的基本格式后,就有了下面这个工具类,基于 POI 3.13。

2016.02.28 更新:增加对 sheet 数量的校验才够完整。

继续阅读

2015 年终总结

一、工作

工作上做了些功能、逻辑的优化,取得了一些的效果,大概有 4 个月几乎没有出现 IO 告警;系统优化需要持续进行,当前的 top sql 消除后,以前的次 top sql 又会变成新的 top。

主要的优化措施就是对大表做分区,尽量做到分区消除;对大表有关的统计不再实时统计,采用统计汇总表加增量统计的方式来优化;在业务上进行优化。

一大感触就是,起始设计非常重要,一开始的设计不合理,后期的优化、修复成本极其高昂。比如用户积分表只维护的用户的当前可用总积分,而没有维护历史总积分,而页面展示却需要历史总积分,导致每次展示用户历史总积分都需要实时从明细日志里累加出来。随着历史明细数据越来越多、用户数量和活跃度提高,这样的设计是没法支撑的,而如果要维护其历史总积分,现有3千万的用户,也非常不好弄。这种设计上的债务是长期存在的。有时候下定决心重新设计数据结构,迁移历史数据、代码上做平滑迁移的过程真的很酸爽。。。

多花精力在初始设计上!

二、学习

学习主要集中在 Oracle 数据库上,基本上把《基于 Oracle 的 SQL 优化》看了一遍。

因为工作需要,写了个 MyBatis 的批量插入的插件http://coderbee.net/index.php/open-source/20150721/1274

在学 Scala,看《深入理解 Scala》。

四、不足

看书太少,危机!

博客也写得很少。。。

五、高兴的事

找到女朋友啦,,^^,^^,,程序员知道这件事的重要意义啦。。。


欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。

《高性能 MySQL》 — 第五章 创建高性能的索引

索引基础

索引的使用:现在索引中找到对应值,然后根据匹配的索引记录找到对应的数据行。

对于有多列的索引,MySQL 只能高效地使用索引的最左前缀列。因此列的顺序很重要。

MySQL 的唯一限制和主键限制都是通过索引实现。

索引的类型

MySQL 中,索引是在存储引擎层而不是服务器层实现的,所以没有统一的索引标准。

B-Tree 索引

B-Tree 通常意味着所有的值都是按顺序存储的,并且每一个叶子页到根的距离相等,适合范围查询。

btree-structure

B-Tree 索引适用于全键值、键值范围或键前缀查找,其中键前缀查找只适用于根据最左前缀的查找。

索引还可以用于查询中的 order by 操作。

B-Tree 索引的限制:

  • 如果不是安装索引的最左列开始查找,则无法使用索引。
  • 不能跳过索引中的列。
  • 如果查询中有某个列的范围查询,则其右边所有列都无法使用索引优化查找。

这些限制是 MySQL 优化器和存储引擎使用索引的方式导致的。

继续阅读

《高性能 MySQL》 — 第四章 Schema 与数据类型优化

选择优化的数据类型

选择数据类型的一般原则:

  • 更小的通常更好:尽量使用可以正确存储数据的最小数据类型。

  • 简单就好:简单数据类型的操作通常需要更少的 CPU 周期。例如整型比字符操作代价更低,以为字符集和校对规则(排序规则)使字符比较比整型更复杂。举例,应该使用 MySQL 内建的类型(date, time, datetime) 而不是字符串来存储日期和时间,应该使用整型来存储 IP 地址。

  • 尽量避免 NULL:如果查询中包含可为 NULL 的列,对 MySQL 来说更难优化,因为可为 NULL 的列使得索引、索引统计和值比较都更复杂。可为 NULL 的列会使用更多的存储空间,在 MySQL 里也需要特殊处理。当可为 NULL 的列被索引时,每个索引记录需要一个额外的字节,在 MyISAM 里甚至还可能导致固定大小的索引变成可变大小的索引。例外,对于稀疏数据(很多列的值为 NULL),InnoDB 使用单独的位(bit)存储 NULL 值,有很好的空间效率。

在为列选择数据类型时,第一步需要确定合适的大类型:数字、字符串、时间等。下一步是选择具体类型。

继续阅读