
用 IDE 写了一段代码,目的是遍历某一目录下的所有文件,并对所有以
.xml结尾的文件进行处理,代码如下:1try (Stream<Path> pathStream = Files.list(Paths.get("data"))) { 2 pathStream.filter(path -> path.endsWith(".xml")) 3 .forEach(System.out::println); // do something with the file 4} catch(IOException e) { 5 //... 6}看起来很直截了当,filter 中对 path 用
endsWith(String)方法过滤出所有以.xml结尾的文件,然而在目录中存在*.xml文件的情况下却没有任何输出。假如测试时未该发现问题,在运行期大有可能当成是该目录中未找到任何以.xml结尾的文件而怀疑是数据问题。这样的代码即使是让人工进行 review 也未必能发现有什么问题,遍历
data目录下的文件,如果文件以.xml结尾就进行处理,很自然,正确啊。要是前面的代码中使用了
Read Morevar来修改pathStream就更不太可能在 Code Review 时发现问题,像
本文初衷是要专注在 Docker 内的应用进程如何与外部发过来的 Linux 信号进行响应。具体应用在当运行为一个 ECS 的 Docker 容器时,对 ECS 的 AutoScaling 以及部署时如何让应用能正确收到相应的信号。可是一提及这一话题,思维就产生了极大的发散,很想好好捋一捋进程与 Linux 信号之间关系和相关的概念。 关于 Linux 的 Signal 请参考 Wikipedia 的 Signal(IPC).
本文以渐进的方式,从信号的简介,本地 C, Bash, Java 程序与信号,然后将在下一篇学习本地 Docker 容器内进程与信号的处理,ECS 的容器与 ASG,部署行为的信号响应。
Read More- 本书的阅读又搁置了许久,虽然感觉 Manning 出版社的这一
100 Mistakes系列从书的质量不是那么的高,但开了头还是继续从本书 40% 的位置往下。
开始要讲述到异常了,异常还是有必要认真对待的,比如- Java 中很容易被 CheckedException 弄得代码不整洁
- 缺少必要的参数检查,不舍得抛出异常,视异常为 Bug
- 不明确出现异常时后续如何处理,
- 或者是捕获而隐藏了异常致使定位错误变得更难。
Java 的主要异常大分类是Throwable
NullPointerException, 这恐怕是一个最常见的异常,Java 对一个对象是否能为 null 值没什么约束,甚至用 null 来表示业务上的空。比如说方法的参数与返回值,Java 都可以是 null 值,而在 Kotlin 中非明确可为 null 的时不能为 null Read More
├── Error
└── Exception
└── RuntimeException
继续阅读本书,编程语言处理数值都有可能出现问题,如溢出,整数的最大最小值不对称,Double.NaN 等。
由于 Java 学了 C,也用 0 开始的数字来表示 8 进制数,如 037, 010 分别是十进制的 31 和 8,这与现实不相符。因为如果你在纸上写下 037, 010, 几乎所有人(除了某些程序员)都会认为它们就是十进制的 37 和 10。但是 Java 表示 2 进制, 16 进制的方式没有问题的,如 0b10, 0x37。IntelliJ IDEA 看到使用 0 开头的 8 进制数会不建议那么使用. 8 进制数字的范围是 0~8, 所以 09 是错误的, 但是 Java 编译器似乎对此很陌生.
int a = 09;
IntelliJ IDEA 会提示Integer number too large, 编译器提示说java: ';' expected, 有点驴唇不对马嘴.
现在几乎没有必要使用 0 开始的 8 进制数的方式, 或许还有用的就是表示 Unix 下文件权限, 如
int fileMode = 0644
所以任何时候看到 0 开头的数字都必须仔细检视, 基本可以禁止使用这种方式 Read More- 现在流行数据都有 Schema 的概念,一般作为数据库对象(表,函数,存储过程等)的命名空间。所以在数据库端往往存在 实例/数据库/Schema 这样层级划分。对于 DB2 和 Oracle 用客户端创建一个新的数据库并非易事,灵活的在数据库中较轻量的划分隔离空间的办法因数据库类型而异
- MySQL: 创建数据库(create database), create schema 是 create database 的别名
- PostgreSQL: create database 创建新的数据库,或在当前数据库下用 create schema 创建 schema
- SQLServer: 和 PostgreSQL 一样的自由,create database 创建新的数据库,或在当前数据库下用 create schema 创建 schema
- DB2: 用 create schema 创建新的 schema, 或创建数据库对象时直接加上前缀,create table abc.test1..., 没有 abc schema 则会自动创建
- Oracle: create schema 较麻烦,涉及到 authorization. 但可以通过 create user 创建新用户后就有了对应的新 schema
下面我们来了解下在 PostgreSQL/SQLServer 中创建新的 schema,如何在 JDBC 连接字串中指定默认 schema, 同时也涉及到 database/schema/user 的创建以及在 SQL 中如何切换。 Read More
Java 一路突突突, 版本 16 在 2021-03-16 都发布了, 而我们一直碍于 Java 9 的大改还在 Java 8 上原地踏步, 以往每当有新版本 JDK 发布后都是很快就验证,立马升级。Java SE versions history) 列出了所有 Java 的历史版本的发布日期。在今天(2021-05-04) 网站 Java SE Downloads 上直接提供下载的 Java SE 版本有以下三- Java SE 16.0.1
- Java SE 11.0.11(LTS)
- Java SE 8u291
两个 LTS(长期支持) 版 8 和 11,外加一个目前最新的非 LTS 版本 16, 其他的版本都被归档到了 Java Archive. 查看一下 Java 支持的 roadmap, 几个 LTS 版本的服务支持年限版本本 发布日 原定支持至 延期支持至
注意到 Java 8 将比 Java 11 和将来的 Java 17 生命力还顽强,一下就觉得这么久坚守在 Java 8 的阵地上不应觉得有什么好害羞的。眼看着下一个 LTS 版本的 Java 17 就要在今年 9 月份发布了,Java 11 看来是要错过了,等准备好和 Java 8 告别时要直接跳到 Java 17 了。 Read More
Java 8 2014/3 2022/3 2030/12
Java 11 2018/9 2023/9 2026/9
Java 17 2021/9 2026/9 2029/9
在进行数据流处理过程中,需要一个高效苗条的流处理组件,比如对输入流能进行分组(窗口),能进行流量控制(Back Pressure - 背压),这也就涉及到响应式编程,流处理框架。这方面如果直接基于 Akka actor 来构建 Akka ActorSystem 也是比较复杂,依赖的组件也不少。还有构筑在 Akka actor 之上的 Akka Streams,再往上的 Flink Streaming,它们都有像滑动,滚动窗口的概念,但是依赖更不得了。一个基本的 Flink Streaming 的项目会依赖到 45 M 以上的第三方组件,如果用它来写一个数据流处理的共享组件,那真是要命。Spring 5 也开始带上了自己的 Reactive-Streams 实现 Spring Reactor, 想要把它从 Spring 中单独抽离出也非易事。Flink Streaming 组件依赖:org.apache.fling:flink-streaming-java_2.12:1.80, 会依赖于其他诸如 akka-stream, akka-actor, flink-core, flink-clients, scala-library 等非常多的东西
而另一个著名的响应式框架 RxJava 2 就清爽多了,完全没有第三方依赖,要说有也就是定义了四个接口的 reactive-streams(2 KB 大小),就自身那个 rxjava-2.2.9.jar 包只有 2.3 M,这才叫轻量级。因为它设计来是能被应用于 Android 客户端应用的,Andriod 上的 rxandriod-1.2.1.aar 只有 9 K。所以 RxJava 2.x 太适合用来写一些小的共享组件了。 Read More
在我们对数据库 DAO 类进行单元测试时,通常不应该依赖于一个外部数据库,所以会选用特定比较接近于真实数据库类型的内存或嵌入式数据库,如 HSQLDB(HyperSQL), H2, Derby 等。但总难免会用到特定数据库的特性,这时候就无法用前述各种数据库进行测试了。非要单元测试中覆盖到所用的数据库特性的话可以选择用 docker,如 Testcontainers, 经过模块扩展,它可以由 docker 来启动许多种类型的数据库,MySQL, Postgres, Oracle-XE, MS SQL Server, Couchbase 等等,详情见 Database containers。刚了解到的是它的模块化的无限可能,像支持 Kafka Containers 和 Localstack Module 等。
这里就不走 Testcontainers 那条路 -- 要求构建服务器上也要有 docker。早先希望能找到一种嵌入式或内存 PostgreSQL 数据库,后来发现 PostgreSQL 未能提供 In-Process 和 In-Memory 的启动方式,好在 PostgreSQL 是开源,有人可以把它改造为小型的可由测试代码启停的本地数据库。有两个具有代表性的组件,分别是 OpenTable Embedded PostgreSQL Component 和 Embedded PostgreSQL Server,它们都号称是 Embedded,所谓嵌入式,其实是进测试进程外的数据库。
下面简单体验下两个组件的用法 Read More
- 实际中可能有这样的应用场景,得到一个记录不需要立即去处理它,而是等累积到一定数量时再批量处理它们。我们可以用一个计数器,来一个加一个,量大时一块处理,然后又重零开始计数。如果记录的来源单一还好办,要是有多个数据源来提供记录就会有多线程环境下数据丢失的问题。
这里我编写了一个最简单的任务批处理的队列,构造了告诉它批处理数量,消费者,然后就只管往队列里添加记录,队列在满足条件时自动进行批处理。因为内部使用的是BlockingQuque来存储记录,所以多线程往里同时添加记录也没关系,最后的未达到batchSize, 的那些记录可主动调用completeAll()函数或在达到 timeout 后来触发批处理,并且结束队列内的循环线程。
注意: 多线程环境下往一个无线程保护的集合或结构中,如 ArrayList, LinkedList, HashMap, StringBuilder 中添加记录非常容易造成数据的丢失,而往有线程保护的目的地写东西就安全了,如 Vector, Hashtable, StringBuffer, BlockingQueue。当然性能上要付出一点代价,不过对于使用了可重入锁(ReentrantLock), 而非同步锁(synchronized) 的数据结构还是可以放心使用的。
下面是 BatchQueue 的简单实现 Read More - Java 在定义字符串的时候不支持字符串插值, 即不能在字符串中捕获作用域中的变量, 用来组成当前字符串. 而这可以说是其他各种言都具备的基本特性. 例如 Bash 中可以这样
bash-3.2$ name=Yanbin
Java 中不支持类似的方式
bash-3.2$ echo "Hello $name"
Hello YanbinString name = "Yanbin";
而使用 Java 必须用 String 的静态方法 format (注意是静态方法哦) 来间接的格式化出一个字符串, 可以这么写
String greeting = "Hello $name"; //Java 中无法把 $name 替换为 name 变量值 "Yanbin"String.format("Hello %s", "World");
我们看到在用 format 格式化时匹配参数会是一个不小的问题, 相同使用法的打印输出方法是
String.format("%1$s %1$s %s %s %2$s", "aa", 10); //aa aa aa 10 10System.out.printf(). 别说字符串插值了, 如果 format 方法是 String 的一个实例方法应用起来都会便利些, 如"Hello %s".format("World")会容易读些, Scala 的字符串实例就有format方法了. Read More