Apache Avro 是类似于 Google protobuf 那样的数据交换协议,但 Avro 可以用 JSON 格式来定义 Schema, 所以相比而言更容易上手。它也是 Hadoop, Kafka 所采用的交换格式。对于生成的 avro 序列化文件如果要编写代码来解读其中内容的话就太过于麻烦,Apache 给了我们一个便捷的工具来处理 Avro Schema 和数据。
Java 版的 Avro Tools 可点击链接 avro-tools-1.8.2.jar 下载,当前版本 1.8.2(发布于 2017/05/20),执行命令是java -jar avro-tools-1.8.2.jar ..............
如果是 Mac 平台,还可以通过brew install avro-tools
来安装,执行命令就只是avro-tools了。
在本文中还会用到一个 JSON 格式化高亮显示的工具jq, 在 Mac 下通过以下命令安装brew install jq
avro-tools 和 jq 已准备就绪,接下来演示下如何使用它们。
Read More
当 AWS Lambda 由 Kinesis 消息来触发时,一个 Kinesis Shard 会相应启动一个 Lambda 实例,比如说 Kinesis Stream 有 5 个 Shards, 那同时只会启动 5 个 Lambda 实例。那么把多条消息发送到同一个 Kinesis Shard 中去,这些消息会被如何消费呢?答案是按顺消息,不管这些消息是否被不同的 Lambda 实例处理。本文就是关于怎么去理解 https://aws.amazon.com/lambda/faqs/ 的下面那段话的内容:Q: How does AWS Lambda process data from Amazon Kinesis streams and Amazon DynamoDB Streams?
可以做几个试验,下面的代码可以保证消息总是被发送到同一个 Kinesis Shard,因为 PartitionKey 参数是一个常量 Read More
AWS Lambda 如何处理来自于 Amazon Kinesis 和 DynamoDB 的数据 The Amazon Kinesis and DynamoDB Streams records sent to your AWS Lambda function are strictly serialized, per shard. This means that if you put two records in the same shard, Lambda guarantees that your Lambda function will be successfully invoked with the first record before it is invoked with the second record. If the invocation for one record times out, is throttled, or encounters any other error, Lambda will retry until it succeeds (or the record reaches its 24-hour expiration) before moving on to the next record. The ordering of records across different shards is not guaranteed, and processing of each shard happens in parallel. 从 Kinesis 和 DynamoDB 单个 Shard 上的记录会被 Lambda 严格的按序处理。这意味着如果你送两条记录到相同的 Shard, Lambda 将会保证第一条记录成功处理后才会处理第二条记录。假如处理第一条记录时超时,或超过资源使用上限,或碰到任何错误, Lambda 将会不断重试直到成功(或记录在 24 小时后过期), 而后才会去处理下一条记录。跨 Shard 的记录不保证到达顺序,且是并行处理多个 Shard 来的记录。
一路阅读完第五章直接跳跃到第十章有关宏的内容,宏的录制和回放功能是多数编辑器都会提供,但我却基本不用这一功能。在 Vim 中宏比点号(.) 更有效的处理重复性操作,既然作为一个 Vimer, 以重复操作为耻,因而十分有必要对它加以了解和掌握的,又是一个一分钟上手需一辈子来掌握的东西。
1. 宏是录制在某个寄存器中,用 q{register}启动录制,例如qa, 然后执行一系操作,再次按q结束录制,第一次回放用@{register}, 如@a,以后多次执行上一次宏回放可用@@.也可以支持数字,如10@a, 或10@@.:reg a可查看寄存器a中宏的内容
2. 录制宏的一条金律是:确保每一条命令是可重复的。h,j,k,l, 没什么意义,而0e,A,I就是可重复的。当回放宏时遇到光标移动失败时, 如光标已在行首,宏中有h操作,那么后续的操作被放弃,根据visualbell配置可能有响声提示。宏的 motion 失败即放弃的特性让我们在重复执行宏时可以用一个足够大的数字,如200@@, 反正无副作用
3. 宏的序列执行与并发执行,当用5@a执行宏"a 0f.r)w~j, 其实是把宏放在一个队列里,往下跳行执行,其他任何一行执行失败,后续行将得不到更新。如果针对宏"a 0f.r)w~, 对选择的多行执行:'<,'>normal @a时效果就不一样了,它的意思是针对选择的多行单独执行宏,任何行的失败互不影响,所以它是并发执行的 Read More
很早就想写下这篇日志的,因为实际使用 AWS S3 来存取文件使用什么样的 Key 对性能的影响是极其大的。当然,如果你对 S3 的并发请求在 50 以内是无所谓的,要是并发要求很高的话,Key 的选择就变得至关重要的,不可不察。S3 Key 从第一个字符算起的任意长度子字符串都被称作前缀(prefix), 而对 S3 文件访问性能影响不在完整的 Key, 恰恰是那个前缀。
背景:我们最初在使用 S3 时,存储的文件的 Key 直接用了数据库的自增 ID,于是保存到 Bucket 中大概下面那样子的examplebucket/12134850.csv
Bucket 中有百万个文件,当初测试时 60 个左右的 Lambda 实例同时访问这个 Bucket 中不同的文件时,加载每个 S3 文件的时间大约在几百毫秒,然后并发上到 70, 80 后加载同样大小的 S3 文件的时间陡然增加到 10 秒以上,并发继续上到 100 以上直接导致众多 S3 的请求超时。后来了解到虽然一个 Bucket 中放多少个文件是没有限制的,而且官方文档说了文件多了并不影响访问的性能,但背后却有一个文件的分区存储机制,这个才是关键。
examplebucket/12134851.csv
examplebucket/12134852.csv
examplebucket/12134853.csv
examplebucket/12134854.csv
examplebucket/12134855.csv
examplebucket/12134856.csv
examplebucket/12134857.csv
examplebucket/12134858.csv
S3 的分区存储就像是硬盘分区,或文件分布在不同硬盘上的效果。试想一下,如果我们多个线程同时从一块硬盘上读取数据,每个线程需共同一个磁头来读取数据,性能就差; 但如果那些线程同时从不同的硬盘上读取各自的数据,那性能就大大提升了,它们互不干扰。在使用机械硬盘时我有过这样的体验,在同一个磁盘上拷贝文件比从一个磁盘拷贝到另一个磁盘要慢很多。 Read More
1. 学习完前面的三大模式:正常模式,插入模式和可视模式后,现在步入了命令模式。
:,/或?, 和<C-r>=分别进入到Ex命令,搜索和表达式,它们都被称作命令模式, 以前还常常把正常模式说成是命令模式。:后的是Ex命令,是因为我们仍然沿袭了 Vim 的前身 Ex。我在 Mac 下输入命令ex就会看到这个
进入到 Vim 的 Ex mode, 输入
visual命令就是正常的 Vim 界面。Vim 之于 Ex 就像 Gui 之于 Shell, Vim 的 Ex 命令可以做任何操作,见:h ex-cmd-index。2. 命令模式在
:提示符下输入时可以使用插入模式下的很多命令,如<C-w>,<C-u>,<C-k>,<C-v>,<C-r>{reg}, 甚至是<C-r>=,就是不能用 motion 命令,光标移得靠方向键3. 在阅读本章之前对 Vi 的命令模式只能用不觉明厉来形容,它对我的贡献仅仅是
:wq之类的,模式查找,简单替换,再就是执行一些未设置快捷键插件功能。也一直未理解替换怎么是%s/abc/dev, 只是依葫芦画瓢,现在终于可以在本章中理解那些命令了。从中真的能体会到 Ex 命令模式的乐趣,Ex 是不会把整个文件内容显示在屏幕上的, Ex 命令通常由 范围 + 命令 组成。:2 -- 光标跳到第一行,正常模式下可以 2G, 或 2gg
:print -- 打印当前行的内容范围和命令一般会写在一块,范围可借用同样意义的特殊字符,默认操作当前行,因此下面一系列的命令就好理解了 Read More
Spring 项目中把大量的 SQL 分散在 Java 代码中,无 Here Doc 的情况下用加号来连接写着实在是不爽,于是之前思考这个 Spring 项目中把 SQL 语句写在 .sql 文件中 -- 把它们写在 *.sql 文件中,但是这个 *.sql 需要特定的格式来标识属性 Key--!select.user
而且还需要一个额外的类 SqlPropertySourceFactory 来解析上面的 *.sql 文件, 识别出 select.user 是 Key, 紧接着后面的块是相应的属性值,用注解引用它时还有点额外的 factory 属性来配置,如
select id, firstname, lastname, address --!update.user
update ........@PropertySource(value = "classpath:sql/queries.sql", factory = SqlPropertySourceFactory.class)
所以一直在思考是否能够再简单些,是否能用一个自定义的注解,如@SqlPropertySource("classpath:sql/queries.sql")
捉摸了很久,似乎有点难度,不过再不断发掘的过程中找到了这个类org.springframework.core.io.support.PropertiesLoaderUtils, 有下面的代码片断 Read More第八章:边界
本章关于如何学习使用第三方组件
- 第三组件或框架追求普适性,而使用者则想要集中满足特定的需求
- 学习第三方组件首当其冲当然还是文档,其次重要的是它的单元测试,我甚至是把单元测试当作文档的一部分来看待。其实更重要的方法是学习性测试(learning tests), 通过测试来学习才是切实的体验,是一种精确的试验,我们甚至可以用自己的测试来验证第三方组件的新版本
- 边界上会发生有趣的事,这就要求我们对它清晰的分割和定义,以避免我们的代码过多地了解第三方组件中的特定信息,依靠你能控制的东西好过依靠你控制不了的东西,免得日后受它控制。简而言之就是尽可能隔离边界,在边界改动时只需要修改一下适配器,对的适配
第九章:单元测试
这一章我自认为是很重要的,特别是正在采用测试驱动开发/设计(TDD) 的程序员来讲。充分,良好的单元测试是保证我对生产代码大刀阔斧的关键,这也就是 TDD 的最后一个 D 又能理解了 Design 的原由
Read More
三个月前写过一篇 Mockito 中捕获 mock 对象方法的调用参数,一般项目中 Mockito 不决求助于 JMockit, 同样的在 JMockit 也需对捕获被 Mock 的方法调用参数。当我们用new Expectations(){{}}打桩并在后面断言了返回值,那就无需捕获参数来断言,匹配到了方法调用即证明传入的参数也是对的,如下面的代码所示1public class UserServiceTest { 2 3 @Mocked 4 private UserDao userDao; 5 6 @Test 7 public void couldCallUserDaoToAddUser() { 8 new Expectations(){{ 9 userDao.findById(123); 10 result = "Yanbin"; 11 }}; 12 13 UserService userService = new UserService(userDao); 14 String user = userService.findBy(123); 15 16 assertThat(user).isEqualTo("Yanbin"); //这里断言成功也就证明了 userDao.findById(123) 方法被调用,参数必须是 123 17 } 18}
但如果是未打桩的方法,或打桩是用的模糊参数(withInstanceOf(String.class)), 或是无返回值的方法就要事后对是否调用了某个方法以及传入什么参数的情况进行断言。 Read More- 对于同步方法的测试很简单,调用完后可立马检查执行状态; 而异步方法,由于我们无法确切的知道何时结束,因此以往的办法是用
Thread.sleep(500)来预估一个执行时间。然后通常我们估计的要长于实际的时间,这就很浪费,况且偶然的超过预估的等待时间也并不意味着代码有问题。还有sleep方法还抛出一个检测异常InterruptedException, 一般会要对Thread.sleep(500)作下简单包装。
于是今天要介绍的 Awaitility 就应运而生了,专门针对异步方法的测试。它的官方文档在 https://github.com/awaitility/awaitility/wiki/Usage。本文主要关注在 Java 8 环境下用 Lambda 的代码书写方式。Awaitlity 实际运行是以某种轮询的方式来检查是否达到某个运行状态,可设定最多,最少等待时间,或永久等待,或自定义轮询策略,之后就开始进行需要的断言,所以它可以尽可能的节省测试异步方法所需的时长。而不像Thread.sleep(500)一路等到黑,并且没有回头路。
通常我会在项目中给 JUnit 配上三个最佳伴侣,它们是(按mvn dependency:tree中的显示方式):- org.awaitility:awaitility:2.0.0:test
- org.assertj:assertj-core: version: 3.8.0:test
- org.mockito:mockito-core:2.7.22:test
当然如果项目中没有异步调用自然是不需要 Awaitility, 在我的项目中是基本不可能的。以上三种都追求 DSL,以流畅的方式进行愉快的测试。
现在来尝试下 Awaitility 的几种基本的用法,先假定有下面的代码UserServiceRead More 
第四章:注释
- 别给糟糕的代码加注释 -- 重新写吧
- 什么也不会比乱七八糟的注释更有本事搞乱一个模块。什么也不会比陈旧,提供错误信息的注释更有破坏性
- 若编程语方足够有表达力,或者我们长于用这些语方来表达意图,就不那么需要注释 -- 也许根本不需要
- 注释的恰当用法是弥补我们在用代码表达意图时遭遇的失败。注释总是一种失败
- 不准确的注释要比没注释坏得多。它们满口胡言。代码,只有代码能忠实的告诉你它做的事
- 好注释不多,如法律信息,警示等。我都不觉得
TODO是多好的东西 - 大多数注释都是坏注释,甚至一些 IDE 自动生成的代码也加一堆的废话注释,分散阅读注意力。我也写过那种日志式注释,这本是版本控制系统干的事。
- 很奇怪 IntelliJ IDEA 还在自动为新建的类加下创建者,代码是团队共有,时间长了根本就与作者无半毛关系。我经常碰到这种写有作者的代码哭笑不得,直接把原作者改了或删了也不太好,还不如把文件删了,新建一个没有作者注释的类
- 直接把代码注释掉是种讨厌的做法,自己可能不会再启用,其他人以为很重要更不敢删。不用了最好果断删掉,版本控制系统会帮你记住历史。
- 个人认为,注释会胡说八道,产品代码和测试代码不会撒谎。有功夫写注释还不如写好测试用例,测试需要维护,它本身就是最真实的文档。